diff --git a/README.md b/README.md index 54af306ff..e0cbbb7d4 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,9 @@ git clone --recursive https://github.com/Coldcard/firmware.git cd firmware # Apply address patch -git apply unix/linux_addr.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 diff --git a/cli/signit.py b/cli/signit.py index fd7bc0b1e..62a2bb43b 100755 --- a/cli/signit.py +++ b/cli/signit.py @@ -319,13 +319,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: limited only by final binary size, not SPI flash + # 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 USB_MAX_LEN = 1472 * 1024 assert hdr.firmware_length <= USB_MAX_LEN, \ diff --git a/docs/bip-21-extensions.md b/docs/bip-21-extensions.md new file mode 100644 index 000000000..17a6b73bf --- /dev/null +++ b/docs/bip-21-extensions.md @@ -0,0 +1,15 @@ +## 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 +``` diff --git a/docs/generic-wallet-export.md b/docs/generic-wallet-export.md index f98231161..0a5032021 100644 --- a/docs/generic-wallet-export.md +++ b/docs/generic-wallet-export.md @@ -57,7 +57,7 @@ to be the first (non-change) receive address for the wallet. 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 is the root of the subkey paths found in the file, and +(`0F056943` in this example) 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 `tb1qc58ys2dphtphg6yuugdf3d0kufmk0tye044g3l`, the input section of your PSBT would need to specify `(m=0F056943)/84'/1'/123'/0/0`. diff --git a/docs/key-teleport.md b/docs/key-teleport.md new file mode 100644 index 000000000..9056e2dd8 --- /dev/null +++ b/docs/key-teleport.md @@ -0,0 +1,224 @@ + +# 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. diff --git a/docs/limitations.md b/docs/limitations.md index c9c3cb5c4..7a5bb54af 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -14,11 +14,12 @@ # PIN Codes - 2-2 through 6-6 in size, numeric digits only -- pin code 999999-999999 is reserved (means 'clear pin') +- pin code 999999-999999 was reserved (meaning 'clear pin'), but now available again # 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 @@ -55,14 +56,18 @@ - 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. -- we do not support PSBT combining or finalizing of transactions involving - P2SH signatures (so the combine step must be off-device) +- 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 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 520 byte script limitation in consensus layer with classic P2SH (same limit applies to segwit even though consensus allows up to 20 co-signers) +- 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 - (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. @@ -74,6 +79,7 @@ - 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) @@ -128,12 +134,17 @@ 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 @@ -198,3 +209,16 @@ We will summarize transaction outputs as "change" back into same wallet, however - 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 + diff --git a/docs/menu-tree.txt b/docs/menu-tree.txt index 724a17c03..8a9dea571 100644 --- a/docs/menu-tree.txt +++ b/docs/menu-tree.txt @@ -16,7 +16,6 @@ Advanced 12 Word Dice Roll 24 Word Dice Roll - Migrate COLDCARD Import Existing 12 Words [SEED WORD ENTRY] @@ -30,6 +29,8 @@ Import XPRV Tapsigner Backup Seed XOR + Migrate Coldcard + Key Teleport (start) Help Advanced/Tools View Identity @@ -57,14 +58,18 @@ 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 Warm Reset - Restore Txt Bkup + Restore Bkup + Reflash GPU [IF QWERTY KEYBOARD] Secure Logout Settings Login Settings @@ -106,6 +111,11 @@ NFC Sharing Default Off Enable NFC + NFC Push Tx + coldcard.com + mempool.space + Custom URL... + Disable Display Units BTC mBTC @@ -140,8 +150,9 @@ 50% 60% 70% - 80% (default) + 80% 90% + 95% (default) 100% Delete PSBTs Default Keep @@ -149,17 +160,20 @@ Menu Wrapping Default Off Enable + Home Menu XFP [IF SECRET AND NOT TMP SEED] + Only Tmp + Always Show --- [NORMAL OPERATION] Ready To Sign Passphrase [IF WORD BASED SEED] - Restore Saved [MAYBE] - A*********** - [0C52BAD4] + Restore Saved + c******* + [3A14F788] Restore Delete - Edit Phrase [MAYBE] + Edit Phrase [IF QWERTY] Add Word [IF NOT QWERTY] [SEED WORD MENUS] Add Numbers [IF NOT QWERTY] @@ -183,35 +197,44 @@ Account Number Custom Path CC-2-of-4 - Secure Notes & Passwords [IF ENBALED] - 1: note1 - "note1" + Secure Notes & Passwords [IF ENBALED] [MAYBE] + 1: note0 + "note0" View Note Edit Delete Export - SHORTCUT - SHORTCUT - 2: nostr - "nostr" - ↳ scg - ↳ brb.io + Sign Note Text + 2: secret-PWD + "secret-PWD" + ↳ satoshi + ↳ abc.org View Password Send Password [MAYBE] Export Edit Metadata Delete Change Password - SHORTCUT - SHORTCUT + Sign Note Text New Note New Password Export All + Sort By Title Import Type Passwords [MAYBE] Seed Vault [MAYBE] - 1: [B14E9AE0] - [B14E9AE0] + 1: [7126EB3C] + [7126EB3C] + Use This Seed + Rename + Delete + 2: [CCEE13B9] + [CCEE13B9] + Use This Seed + Rename + Delete + 3: [03EE9989] + [03EE9989] Use This Seed Rename Delete @@ -222,12 +245,18 @@ Restore Backup Clone Coldcard Export Wallet + Sparrow + Cove Bitcoin Core - Sparrow Wallet + Nunchuk + Bull Bitcoin + Zeus Electrum Wallet Wasabi Wallet + Fully Noded Unchained - Lily Wallet + Theya + Bitcoin Safe Samourai Postmix Samourai Premix Descriptor @@ -235,7 +264,7 @@ Export XPUB Segwit (BIP-84) Classic (BIP-44) - P2WPKH/P2SH (49) + P2WPKH/P2SH (BIP-49) Master XPUB Current XFP Dump Summary @@ -247,12 +276,18 @@ Verify Backup Backup System Export Wallet + Sparrow + Cove Bitcoin Core - Sparrow Wallet + Nunchuk + Bull Bitcoin + Zeus Electrum Wallet Wasabi Wallet + Fully Noded Unchained - Lily Wallet + Theya + Bitcoin Safe Samourai Postmix Samourai Premix Descriptor @@ -260,42 +295,44 @@ Export XPUB Segwit (BIP-84) Classic (BIP-44) - P2WPKH/P2SH (49) + P2WPKH/P2SH (BIP-49) Master XPUB Current XFP 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: note1 - "note1" + 1: note0 + "note0" View Note Edit Delete Export - SHORTCUT - SHORTCUT - 2: nostr - "nostr" - ↳ scg - ↳ brb.io + Sign Note Text + 2: secret-PWD + "secret-PWD" + ↳ satoshi + ↳ abc.org View Password Send Password [MAYBE] Export Edit Metadata Delete Change Password - SHORTCUT - SHORTCUT + Sign Note Text New Note New Password Export All + Sort By Title Import Derive Seeds (BIP-85) View Identity @@ -314,18 +351,24 @@ Import XPRV Tapsigner Backup Coldcard Backup + Key Teleport (start) + Spending Policy + 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 - Enable HSM [IF HSM AND SECRET] - Default Off - Enable - User Management [IF HSM AND SECRET] 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 @@ -334,13 +377,15 @@ Split Existing [IF WORD BASED SEED] Restore Seed XOR Destroy Seed [IF SECRET AND NOT TMP SEED] - Lock Down Seed + Lock Down Seed [MAYBE] Export SeedQR [IF WORD BASED SEED] I Am Developer. Serial REPL Warm Reset - Restore Txt Bkup - Seed Vault [IF SECRET] + Restore Bkup + BKPW Override + Reflash GPU [IF QWERTY KEYBOARD] + Seed Vault [IF SECRET AND NOT TMP SEED] Default Off Enable Perform Selftest @@ -353,30 +398,43 @@ Warn Testnet Mode Bitcoin - Testnet3 + Testnet4 Regtest - AE Start IDX + AE Start Index Default Off Enable + B85 Idx Values + Default Off + Unlimited Settings Space MCU Key Slots Bless Firmware - Reflash GPU [IF QWERTY KEYBOARD] Wipe LFS Settings Login Settings Change Main PIN Trick PINs [IF SECRET AND NOT TMP SEED] Trick PINs: - ↳123-254 - PIN 123-254 + ↳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 Add New Trick - Add If Wrong Delete All Set Nickname Scramble Keys @@ -421,17 +479,27 @@ View Details Delete Coldcard Export + Electrum Wallet Descriptors View Descriptor Export Bitcoin Core - Electrum Wallet Import from File + Import from QR [IF QR SCANNER] Import via NFC [IF NFC ENABLED] 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 @@ -466,8 +534,9 @@ 50% 60% 70% - 80% (default) + 80% 90% + 95% (default) 100% Delete PSBTs Default Keep @@ -475,25 +544,171 @@ Menu Wrapping Default Off Enable + Home Menu XFP [IF SECRET AND NOT TMP SEED] + Only Tmp + Always Show Keyboard EMU Default Off Enable Secure Logout 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] - Version: 5.x.x Bag Me Now + Version: 5.x.x DFU Upgrade Ship W/O Bag Debug Functions Perform Selftest --- +[SSSP] + 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] + 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 + Advanced/Tools + File Management + Sign Text File + Batch Sign PSBT + List Files + Export Wallet + Sparrow + Cove + Bitcoin Core + Nunchuk + Bull Bitcoin + Zeus + Electrum Wallet + Wasabi Wallet + Fully Noded + Unchained + Theya + Bitcoin Safe + Samourai Postmix + Samourai Premix + Descriptor + Generic JSON + Export XPUB + Segwit (BIP-84) + Classic (BIP-44) + P2WPKH/P2SH (BIP-49) + Master XPUB + Current XFP + 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] + Export Wallet + Sparrow + Cove + Bitcoin Core + Nunchuk + Bull Bitcoin + Zeus + Electrum Wallet + Wasabi Wallet + Fully Noded + Unchained + Theya + Bitcoin Safe + Samourai Postmix + Samourai Premix + Descriptor + Generic JSON + Export XPUB + Segwit (BIP-84) + Classic (BIP-44) + P2WPKH/P2SH (BIP-49) + Master XPUB + Current XFP + Dump Summary + Teleport Multisig PSBT [MAYBE] + View Identity + Temporary Seed [IF SSSP RELATED KEYS ENABLED] + 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 + Paper Wallets + NFC Tools [IF NFC ENABLED] + Sign PSBT + Show Address + Sign Message + Verify Sig File + Verify Address + File Share + Push Transaction [IF PUSHTX ENABLED] + Destroy Seed + Secure Logout + EXIT TEST DRIVE [MAYBE] + SHORTCUT [IF NFC ENABLED] + Sign PSBT + Show Address + Sign Message + Verify Sig File + Verify Address + File Share + Push Transaction [IF PUSHTX ENABLED] +--- + diff --git a/docs/miniscript.md b/docs/miniscript.md new file mode 100644 index 000000000..93c8a3166 --- /dev/null +++ b/docs/miniscript.md @@ -0,0 +1,28 @@ +# Miniscript + +**COLDCARD®** 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` -> `` -> `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 `` 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 +* both keys with key origin info `[xfp/p/a/t/h]xpub/<0;1>/*` & blinded keys `xpub/<2;3>/*` allowed +* use of blinded keys for co-signers requires PSBT provider to supply path from current key fingerprint +* maximum number of keys allowed in segwit v0 miniscript is 20 +* check MiniTapscript limitations in `docs/taproot.md` \ No newline at end of file diff --git a/docs/msg-signing.md b/docs/msg-signing.md index 72a48826e..a79b6dd75 100644 --- a/docs/msg-signing.md +++ b/docs/msg-signing.md @@ -41,20 +41,26 @@ IFOvGVJrm31S0j+F4dVfQ5kbRKWKcmhmXIn/Lw8iIgaCG5QNZswjrN4X673R7jTZo1kvLmiD4hlIrbuL ### What is signed -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//0/0` +### 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/h/'h/h/0/0`. * Bitcoin Core * Electrum Wallet * Wasabi Wallet * Samourai Postmix * Samourai Premix * Descriptor -3. **Generic single sig exports**. Signed by key that corresponds to address at derivation `m/44'/'/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. +3. **Generic single sig exports:** Signed by key that corresponds to first (0th) external address at derivation `m/44h/h/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/h/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//` ### What is NOT signed diff --git a/docs/seed-xor.md b/docs/seed-xor.md index e2ac16158..b79fb8436 100644 --- a/docs/seed-xor.md +++ b/docs/seed-xor.md @@ -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 any number of existing SEEDPLATES + 2, 3 or 4 new SEEDPLATES. You could also use 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. @@ -157,6 +157,12 @@ 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 diff --git a/docs/spending-policy.md b/docs/spending-policy.md new file mode 100644 index 000000000..693819834 --- /dev/null +++ b/docs/spending-policy.md @@ -0,0 +1,209 @@ +# 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. + + diff --git a/docs/taproot.md b/docs/taproot.md new file mode 100644 index 000000000..df58eba8f --- /dev/null +++ b/docs/taproot.md @@ -0,0 +1,77 @@ +# Taproot + +**COLDCARD®** 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 2 methods to provide provably unspendable internal key, if users wish to only use tapscript script path. + +1. **(recommended)** Origin-less extended key serialization with H from [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs) as BIP-32 key and random chaincode. + + `tr(xpub/<0:1>/*, sortedmulti_a(2,@0,@1))` which is the same thing as `tr(xpub, sortedmulti_a(2,@0,@1))` because `/<0;1>/*` is implied if not derivation path not provided. + +### Below option was deprecated in version 6.3.5X & 6.3.5QX +2. Use `unspend(` [notation](https://gist.github.com/sipa/06c5c844df155d4e5044c2c8cac9c05e#unspendable-keys). Has to be ranged. + + `tr(unspend(77ec0c0fdb9733e6a3c753b1374c4a465cba80dff52fc196972640a26dd08b76)/<0:1>/*, sortedmulti_a(2,@0,@1))` + +### Below option were deprecated in version 6.3.5X & 6.3.5QX +3. use **static** provably unspendable internal key H from [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs). + + `tr(50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0, sortedmulti_a(2,@0,@1))` + +4. 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))` + +5. 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))` + +Option 3. leaks the information that key path spending is not possible and therefore is not recommended privacy-wise. +Options 4. and 5. are problematic to some extent as internal key is static. Use recommended options 1. and 2. if the fact that internal key is unspendable should remain private. + + +## 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 whole 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. + +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. \ No newline at end of file diff --git a/docs/web2fa.md b/docs/web2fa.md new file mode 100644 index 000000000..3923b3f34 --- /dev/null +++ b/docs/web2fa.md @@ -0,0 +1,94 @@ +# 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 (16 bytes, or 8 digits for 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?ss={shared_secret}&q={is_q}&g={nonce}&nm={label_text} + +- `shared_secret`: 16 chars of Base32-encoded pre-shared secret +- `is_q`: flag indicating use of QR to provide nonce back to user +- `nonce`: text string that is either 8 digits for Mk4, or hex digits for QR +- `nm`: human readable label for the transaction/purpose + +Server will accept plaintext arguments as above, but normally everything +after the question mark is encrypted. + diff --git a/external/ckcc-protocol b/external/ckcc-protocol index 0e686dbda..2afc7d34d 160000 --- a/external/ckcc-protocol +++ b/external/ckcc-protocol @@ -1 +1 @@ -Subproject commit 0e686dbda686f76c4d3e8069558b2a31f9d1c2b1 +Subproject commit 2afc7d34d27568f984022c6e006408bf6b50e369 diff --git a/external/libngu b/external/libngu index 1cccb25ef..537519a82 160000 --- a/external/libngu +++ b/external/libngu @@ -1 +1 @@ -Subproject commit 1cccb25ef7736efae4a1de83d5dbdc13a2db0e80 +Subproject commit 537519a829259622ea6b0334fbafd6cae852852f diff --git a/external/micropython b/external/micropython index 97d35f058..4107246f8 160000 --- a/external/micropython +++ b/external/micropython @@ -1 +1 @@ -Subproject commit 97d35f058f504a354fc6df79a8b3db5c91862501 +Subproject commit 4107246f8a080807b62c3b4838e71e812ea68b6f diff --git a/graphics/graphics_mk4.py b/graphics/graphics_mk4.py index c2ca930d3..4db6a7ddb 100644 --- a/graphics/graphics_mk4.py +++ b/graphics/graphics_mk4.py @@ -19,7 +19,7 @@ class Graphics: 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') + 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') diff --git a/graphics/mono/selected.txt b/graphics/mono/selected.txt index 827efe94c..faebfd6b7 100644 --- a/graphics/mono/selected.txt +++ b/graphics/mono/selected.txt @@ -1,12 +1,12 @@ - xx - xx - xx - xx - xx xx - xx xx - xx xx - xxx - x + X + XX + X + XX +X X +XX XX + XX X + XXXX + XX diff --git a/misc/binfonter/config.py b/misc/binfonter/config.py index e028da502..dbe190b31 100644 --- a/misc/binfonter/config.py +++ b/misc/binfonter/config.py @@ -74,4 +74,9 @@ x x x x x '''), +# thin space +('\u2009', dict(y=0, w=5), '''\ + +'''), + ]) diff --git a/releases/ChangeLog.md b/releases/ChangeLog.md index f9390be1c..2e09779de 100644 --- a/releases/ChangeLog.md +++ b/releases/ChangeLog.md @@ -4,53 +4,25 @@ This lists the changes in the most recent firmware, for each hardware platform. # Shared Improvements - Both Mk4 and Q -- New Feature: Opt-in support for unsorted multisig, which ignores BIP-67 policy. Use - descriptor with `multi(...)`. Disabled by default, Enable in - `Settings > Multisig Wallets > Legacy Multisig`. Recommended for existing multisig - wallets, not new ones. -- New Feature: Named multisig descriptor imports. Wrap descriptor in json: - `{"name:"ms0", "desc":""}` to provide a name for the menu in `name`. - instead of the filename. Most useful for USB and NFC imports which have no filename, - (name is created from descriptor checksum in those cases). -- New Feature: XOR from Seed Vault (select other parts of the XOR from seeds in the vault). -- Enhancement: upgrade to latest - [libsecp256k1: 0.5.0](https://github.com/bitcoin-core/secp256k1/releases/tag/v0.5.0) -- Enhancement: Signature grinding optimizations. Now about 30% faster signing! -- Enhancement: Improve side-channel protection: libsecp256k1 context randomization now happens - before each signing session. -- Enhancement: Allow JSON files in `NFC File Share`. -- Change: Do not require descriptor checksum when importing multisig wallets. -- Bugfix: Do not allow import of multisig wallet when same keys are shuffled. -- Bugfix: Do not read whole PSBT into memory when writing finalized transaction (performance). -- Bugfix: Prevent user from restoring Seed XOR when number of parts is smaller than 2. -- Bugfix: Fix display alignment of Seed Vault menu. -- Bugfix: Properly handle null data in `OP_RETURN`. -- Bugfix: Do not allow lateral scroll in Address Explorer when showing single address - from custom path. -- Change: Remove Lamp Test from Debug Options (covered by selftest). +- Enhancement: Address format guessing changed away from using PSBT XPUB's derivation paths. + Now based on witness/redeem script of first PSBT input instead. +- Enhancement: Show master XFP of backup secret and ask user for confirmation before loading backup. +- Enhancement: Show firmware version added to hobbled Advanced/Tools menu. +- Bugfix: Exiting text input of Custom Backup Password caused yikes. +- Bugfix: Temporary seeds in SSSP mode were not able to update block height. # Mk4 Specific Changes -## 5.4.0 - 2024-09-12 +## 5.4.5 - 2025-11-03 -- Shared enhancements and fixes listed above. -- Bugfix: Correct intermittent card inserted/not inserted detection error. +- None. # Q Specific Changes -## 1.3.0Q - 2024-09-12 - -- New Feature: Seed XOR can be imported by scanning SeedQR parts. -- New Feature: Input backup password from QR scan. -- New Feature: (BB)QR file share of arbitrary files. -- New Feature: `Create Airgapped` now works with BBQRs. -- Change: Default brightness (on battery) adjusted from 80% to 95%. -- Bugfix: Properly clear LCD screen after BBQR is shown. -- Bugfix: Writing to empty slot B caused broken card reader. -- Bugfix: During Seed XOR import, display correct letter B if own seed already added to the mix. -- Bugfix: Stop re-wording UX stories using a regular expression. -- Bugfix: Fixed "easy exit" from quiz after split Seed XOR. +## 1.3.5Q - 2025-11-03 + +- Enhancement: Show backup filename at the top of the screen during backup password entry. diff --git a/releases/EdgeChangeLog.md b/releases/EdgeChangeLog.md new file mode 100644 index 000000000..bbb0c2076 --- /dev/null +++ b/releases/EdgeChangeLog.md @@ -0,0 +1,62 @@ +# Change Log + +## Warning: Edge Version + +```diff +- This preview version of firmware has not yet been qualified +- and tested to the same standard as normal Coinkite products. +- It is recommended only for developers and early adopters +- for experimental use. +``` + +This lists the changes in the most recent EDGE firmware, for each hardware platform. + +# Shared Improvements - Both Mk4 and Q + +### WARNING: 6.4.0X is not backwards-compatible with previous EDGE firmware versions. +#### 6.4.0X stores multisig wallet internally as Miniscript wallets. Newly created multisig wallets won't be visible if you downgrade after creating them on 6.4.0X. Existing multisig wallets will be converted into Miniscript, yet preserved in old format if downgrade is desired. + +- New Feature: Key Teleport +- New Feature: Spending Policy for Miniscript Wallets +- New Feature: Internal descriptor cache speeding up sequential operation with miniscript wallets. + To take full advantage of the feature work with miniscript wallets sequentially. First, do all operations + needed with `wallet1` before changing to `wallet2`. +- New Feature: Add ability to import/export [BIP-388](https://github.com/bitcoin/bips/blob/master/bip-0388.mediawiki) Wallet Policies. + BIP-388 policies are now also used as our wallet serialization format, which optimized setting storage. +- New Feature: Sign with specific miniscript wallet. `Settings -> Multisig/Miniscript -> -> Sign PSBT` +- New Feature: Miniscript wallet name can be specified for `sign` USB command +- New Feature: Rename Miniscript wallet via UX. `Settings -> Multisig/Miniscript -> -> Rename`. +- New Feature: Export [BIP-380](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki) extended key expression. + Navigate to `Advanced/Tools -> Export Wallet -> Key Expression` +- Enhancement: Slightly faster HW accelerated tagged hash +- Enhancement: PSBT class optimizations. Ability to sign bigger txn. +- Enhancement: Signing TXN UI shows Miniscript wallet name. +- Change: Deprecation of legacy mulitsig import format. Ability to import/export in this format was removed. + Old functionality - renaming by reimporting descriptor with different name was removed. + Use descriptors or BIP-388 wallet policies +- Change: Deprecated `p2sh` USB command. Use `miniscript` USB commands to handle multisig wallets. +- Change: Descriptor template was remove from Generic JSON export, and `key_exp` was added + with BIP-380 extended key expression `[xfp/origin_path]xpub`. +- Bugfix: Disjoint derivation in miniscript wallets +- Bugfix: Disallow P2SH legacy miniscript +- Bugfix: Do not allow to import miniscripts with relative lock without consensus meaning. + Only allow to import block-based in range `older(1 - 65535)` & time-based in range `older(4194305 - 4259839)` + +# Mk4 Specific Changes + +## 6.4.0X - 2025-11-20 + +- synced with master up to `5.4.5` +- Enhancement: Show QR of XOR-split seeds + + +# Q Specific Changes + +## 6.4.0QX - 2025-11-20 + +- synced with master up to `1.3.5Q` + + +# Release History + +- [`History-Edge.md`](History-Edge.md) diff --git a/releases/History-Edge.md b/releases/History-Edge.md new file mode 100644 index 000000000..4d7223847 --- /dev/null +++ b/releases/History-Edge.md @@ -0,0 +1,110 @@ +## Warning: Edge Version + +```diff +- This preview version of firmware has not yet been qualified +- and tested to the same standard as normal Coinkite products. +- It is recommended only for developers and early adopters +- for experimental use. DO NOT use for large Bitcoin amounts. +``` + +# 6.3.5X & 6.3.5QX Shared Improvements - Both Mk4 and Q + +Change: Allow origin-less extended keys in multisig & miniscript descriptors +Change: Static internal keys disallowed - all keys need to be ranged extended keys + +# Mk4 Specific Changes + +- all updates from `5.4.1` + +# Q Specific Changes + +- all updates from version `1.3.1Q` + + +# 6.3.4X & 6.3.4QX Shared Improvements - Both Mk4 and Q + +- Bugfix: Complex miniscript wallets with keys in policy that are not in strictly ascending order were incorrectly filled + upon load from settings. All users on versions `6.2.2X`+ needs to update. +- Bugfix: Single key miniscript descriptor support +- Enhancement: Hide Secure Notes & Passwords in Deltamode. Wipe seed if notes menu accessed. +- Enhancement: Hide Seed Vault in Deltamode. Wipe seed if Seed Vault menu accessed. +- Bugfix: Do not allow to enable/disable Seed Vault feature when in temporary seed mode +- Bugfix: Bless Firmware causes hanging progress bar +- Bugfix: Prevent yikes in ownership search +- Change: Do not allow to purge settings of current active tmp seed when deleting it from Seed Vault + +# Mk4 Specific Changes + +- all updates from `5.4.0` +- Enhancement: Export single sig descriptor with simple QR + +# Q Specific Changes + +- all updates from version `1.3.0Q` +- Bugfix: Properly re-draw status bar after Restore Master on COLDCARD without master seed. + + +## 6.3.3X & 6.3.3QX Shared Improvements - Both Mk4 and Q (2024-07-04) + +- New Feature: Ranged provably unspendable keys and `unspend(` support for Taproot descriptors +- New Feature: Address ownership for miniscript and tapscript wallets +- Enhancement: Address explorer simplified UI for tapscript addresses +- Bugfix: Constant `AFC_BECH32M` incorrectly set `AFC_WRAPPED` and `AFC_BECH32`. +- Bugfix: Trying to set custom URL for NFC push transaction caused yikes + +### Mk4 Specific Changes + +- Bugfix: Fix yikes displaying BIP-85 WIF when both NFC and VDisk are OFF +- Bugfix: Fix inability to export change addresses when both NFC and Vdisk id OFF +- Bugfix: In BIP-39 words menu, show space character rather than Nokia-style placeholder + which could be confused for an underscore. + +### Q Specific Changes + +- Enhancement: Miniscript and (BB)Qr codes +- Bugfix: Properly clear LCD screen after simple QR code is shown + + +## 6.2.2X - 2024-01-18 + +- New Feature: Miniscript [USB interface](https://github.com/Coldcard/ckcc-protocol/blob/master/README.md#miniscript) +- New Feature: Named miniscript imports. Wrap descriptor in json + `{"name:"n0", "desc":""}` with `name` key to use this name instead of the + filename. Mostly usefull for USB and NFC imports that have no file, in which case name + was created from descriptor checksum. +- Enhancement: Allow keys with same origin, differentiated only by change index derivation + in miniscript descriptor. +- Enhancement: HSM `wallet` rule enabled for miniscript +- Enhancement: Add `msas` in to the `share_addrs` HSM [rule](https://coldcard.com/docs/hsm/rules/) + to be able to check miniscript addresses in HSM mode. +- Enhancement: HW Accelerated AES CTR for BSMS and passphrase saver +- Bugfix: Do not allow to import duplicate miniscript + wallets (thanks to [AnchorWatch](https://www.anchorwatch.com/)) +- Bugfix: Saving passphrase on SD Card caused a freeze that required reboot + +## 6.2.1X - 2023-10-26 + +- New Feature: Enroll Miniscript wallet via USB (requires ckcc `v1.4.0`) +- New Feature: Temporary Seed from COLDCARD encrypted backup +- Enhancement: Add current temporary seed to Seed Vault from within Seed Vault menu. + If current active temporary seed is not saved yet, `Add current tmp` menu item is + present in Seed Vault menu. +- Reorg: `12 Words` menu option preferred on the top of the menu in all the seed menus +- Enhancement: Mainnet/Testnet separation. Only show wallets for current active chain. +- contains all the changes from the newest stable `5.2.0-mk4` firmware + +## 6.1.0X - 2023-06-20 + +- New Feature: Miniscript and MiniTapscript support (`docs/miniscript.md`) +- Enhancement: Tapscript up to 8 leafs +- Address explorer display refined slightly (cosmetic) + +## 6.0.0X - 2023-05-12 + +- New Feature: Taproot keyspend & Tapscript multisig `sortedmulti_a` (tree depth = 0) +- New Feature: Support BIP-0129 Bitcoin Secure Multisig Setup (BSMS). + Both Coordinator and Signer roles are supported. +- Enhancement: change Key Origin Information export format in multisig `addresses.csv` according to [BIP-0380](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#key-expressions) + `(m=0F056943)/m/48'/1'/0'/2'/0/0` --> `[0F056943/48'/1'/0'/2'/0/0]` +- Bugfix: correct `scriptPubkey` parsing for segwit v1-v16 +- Bugfix: do not infer segwit just by availability of `PSBT_IN_WITNESS_UTXO` in PSBT \ No newline at end of file diff --git a/releases/History-Mk4.md b/releases/History-Mk4.md index e1a65346a..3a78f39e5 100644 --- a/releases/History-Mk4.md +++ b/releases/History-Mk4.md @@ -1,6 +1,156 @@ *See ChangeLog.md for more recent changes, these are historic versions* +## 5.4.4 - 2025-09-30 + +- Spending policies for "Single Signers" adds new spending policy options: + - limit your Coldcard so it refuses to sign transactions that are "too big" + - require 2FA authentication before signing any transaction (NFC+web) + - velocity limits can restrict how often new transactions can be signed + - see `docs/spending-policy.md` for more details + - "Enable HSM" and "User Management" have moved into `Advanced > Spending Policy`. + - Old "CCC" feature has been renamed and moved into that menu as well: "Co-Sign Multisig" +- Added `Bull Bitcoin` export to `Export Wallet` menu. +- Enhancement: Added warning for zero value outputs if not `OP_RETURN`. +- Enhancement: Show QR codes of output addresses in transaction output explorer. Explorer is + now offered for transactions of all sizes, not just complex ones. +- Enhancement: Added file rename, when listing contents of SD card. +- Enhancement: Added ability to restore Coldcard backup via USB (needs latest of ckcc version) +- Enhancement: Address ownership allows to specify particular multisig wallet in which to search, + if `wallet` query parameter is provided via trivial extension to + [BIP-21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki). + Example: `tb1q4d67p7stxml3kdudrgkg5mgaxsrgzcqzjrrj4gg62nxtvnsnvqjsxjkej0?wallet=Haystack` +- Bugfix: If all change outputs have `nValue=0`, they were not shown in UX. +- Bugfix: Disallow negative input/output amounts in PSBT. +- Bugfix: Fix filesystem initialization after Wipe LFS or Destroy Seed. +- Bugfix: Fix MicroSD selftest code. +- Bugfix: NFC loop exporting secrets would not work after first value exported. +- Bugfix: Multisig address format handling. +- Bugfix: Ownership check failing to find addresses near max (~760), needed to be re-run to succeed +- (Mk4 only) Bugfix: Part of extended keys (xpubs) were not always visible. +- (Mk4 only) Change: Mk4 default menu wrap-around lowered from 16 to 10 items. + + +## 5.4.3 - 2025-05-14 + +- Enhancement: Text word-wrap done more carefully so never cuts off any text, and yet + doesn't waste space. +- Bugfix: `Add current tmp` option, which could be shown in `Seed Vault` menu under + specific circumstances, would corrupt master settings if selected. +- Bugfix: PUSHDATA2 in bitcoin script caused yikes. +- Bugfix: Warning for unknown scripts was not shown at the top of the signing story. +- Bugfix: With both NFC & Virtual Disk OFF, user cannot exit `Export Wallet` menu. Gets stuck + in export loop and needs reboot to escape. +- Bugfix: Part of extended keys in stories were not always visible. + + +## 5.4.2 - 2025-04-16 + +- Huge new feature: CCC - ColdCard Cosign + - COLDCARD holds a key in a 2-of-3 multisig, in addition to the normal signing key it has. + - it applies a spending policy like an HSM: + - velocity and magnitude limits + - whitelisted destination addresses + - 2FA authentication using phone app ([RFC 6238](https://www.rfc-editor.org/rfc/rfc6238)) + - but will sign its part of a transaction automatically if those condition are met, + giving you 2 keys of the multisig and control over the funds + - spending policy can be exceeded with help of the other co-signer (3rd key), when needed + - cannot view or change the CCC spending policy once set, policy violations are not explained + - existing multisig wallets can be used by importing the spending-policy-controlled key +- New Feature: Multisig transactions are finalized. Allows use of [PushTX](https://pushtx.org/) + with multisig wallets. Read more [here](https://github.com/Coldcard/firmware/blob/master/docs/limitations.md#p2sh--multisig) +- New Feature: Signing artifacts re-export to various media. Now you have the option of + exporting the signing products (transaction/PSBT) to different media than the original source. + Incoming PSBT over QR can be signed and saved to SD card if desired. +- New Feature: Multisig export files are signed now. Read more [here](https://github.com/Coldcard/firmware/blob/master/docs/msg-signing.md#signed-exports) +- Enhancement: NFC export usability upgrade: NFC keeps exporting until CANCEL/X is pressed +- Enhancement: Add `Bitcoin Safe` option to `Export Wallet` +- Enhancement: 10% performance improvement in USB upload speed for large files +- Bugfix: Do not allow change Main PIN to same value already used as Trick PIN, even if + Trick PIN is hidden. +- Bugfix: Fix stuck progress bar under `Receiving...` after a USB communications failure +- Bugfix: Showing derivation path in Address Explorer for root key (m) showed double slash (//) +- Bugfix: Can restore developer backup with custom password other than 12 words format +- Bugfix: Virtual Disk auto mode ignores already signed PSBTs (with "-signed" in file name) +- Bugfix: Virtual Disk auto mode stuck on "Reading..." screen sometimes +- Bugfix: Finalization of foreign inputs from partial signatures. Thanks Christian Uebber +- Bugfix: Temporary seed from COLDCARD backup failed to load stored multisig wallets +- Change: `Destroy Seed` also removes all Trick PINs from SE2. +- Change: `Lock Down Seed` requires pressing confirm key (4) to execute + +## 5.4.1 - 2025-02-13 + +- New signing features: + - Sign message from note text, or password note + - JSON message signing. Use JSON object to pass data to sign in form + `{"msg":"","subpath":"","addr_fmt": ""}` + - Sign message with key resulting from positive ownership check. Press (0) and + enter or scan message text to be signed. + - Sign message with key selected from Address Explorer Custom Path menu. Press (2) and + enter or scan message text to be signed. +- Enhancement: New address display format improves address verification on screen (groups of 4). +- Deltamode enhancements: + - Hide Secure Notes & Passwords in Deltamode. Wipe seed if notes menu accessed. + - Hide Seed Vault in Deltamode. Wipe seed if Seed Vault menu accessed. + - Catch more DeltaMode cases in XOR submenus. Thanks [@dmonakhov](https://github.com/dmonakhov) +- Enhancement: Add ability to switch between BIP-32 xpub, and obsolete SLIP-132 format + in `Export XPUB` +- Enhancement: Use the fact that master seed cannot be used as ephemeral seed, to show message + about successful master seed verification. +- Enhancement: Allow devs to override backup password. +- Enhancement: Add option to show/export full multisg addresses without censorship. Enable + in `Settings > Multisig Wallets > Full Address View`. +- Enhancement: If derivation path is omitted during message signing, derivation path + default is no longer root (m), instead it is based on requested address format + (`m/44h/0h/0h/0/0` for p2pkh, and `m/84h/0h/0h/0/0` for p2wpkh). Conversely, + if address format is not provided but subpath derivation starts with: + `m/84h/...` or `m/49h/...`, then p2wpkh or p2sh-p2wpkh respectively, is used. +- Bugfix: Sometimes see a struck screen after _Verifying..._ in boot up sequence. + On Q, result is blank screen, on Mk4, result is three-dots screen. +- Bugfix: Do not allow to enable/disable Seed Vault feature when in temporary seed mode. +- Bugfix: Bless Firmware causes hanging progress bar. +- Bugfix: Prevent yikes in ownership search. +- Bugfix: Factory-disabled NFC was not recognized correctly. +- Bugfix: Be more robust about flash filesystem holding the settings. +- Bugfix: Do not include sighash in PSBT input data, if sighash value is `SIGHASH_ALL`. +- Bugfix: Allow import of multisig descriptor with root (m) keys in it. + Thanks [@turkycat](https://github.com/turkycat) +- Change: Do not purge settings of current active tmp seed when deleting it from Seed Vault. +- Change: Rename Testnet3 -> Testnet4 (all parameters unchanged). +- Mk4 Specific Change: + - Enhancement: Export single sig descriptor with simple QR. + + +## 5.4.0 - 2024-09-12 + +- New Feature: Opt-in support for unsorted multisig, which ignores BIP-67 policy. Use + descriptor with `multi(...)`. Disabled by default, Enable in + `Settings > Multisig Wallets > Legacy Multisig`. Recommended for existing multisig + wallets, not new ones. +- New Feature: Named multisig descriptor imports. Wrap descriptor in json: + `{"name:"ms0", "desc":""}` to provide a name for the menu in `name`. + instead of the filename. Most useful for USB and NFC imports which have no filename, + (name is created from descriptor checksum in those cases). +- New Feature: XOR from Seed Vault (select other parts of the XOR from seeds in the vault). +- Enhancement: upgrade to latest + [libsecp256k1: 0.5.0](https://github.com/bitcoin-core/secp256k1/releases/tag/v0.5.0) +- Enhancement: Signature grinding optimizations. Now about 30% faster signing! +- Enhancement: Improve side-channel protection: libsecp256k1 context randomization now happens + before each signing session. +- Enhancement: Allow JSON files in `NFC File Share`. +- Change: Do not require descriptor checksum when importing multisig wallets. +- Bugfix: Do not allow import of multisig wallet when same keys are shuffled. +- Bugfix: Do not read whole PSBT into memory when writing finalized transaction (performance). +- Bugfix: Prevent user from restoring Seed XOR when number of parts is smaller than 2. +- Bugfix: Fix display alignment of Seed Vault menu. +- Bugfix: Properly handle null data in `OP_RETURN`. +- Bugfix: Do not allow lateral scroll in Address Explorer when showing single address + from custom path. +- Change: Remove Lamp Test from Debug Options (covered by selftest). +- Shared enhancements and fixes listed above. +- Bugfix: Correct intermittent card inserted/not inserted detection error. + + ## 5.3.3 - 2024-07-05 - New Feature: PushTX: once enabled with a service provider's URL, you can tap the COLDCARD diff --git a/releases/History-Q.md b/releases/History-Q.md index 6553be86f..b0023eb53 100644 --- a/releases/History-Q.md +++ b/releases/History-Q.md @@ -1,5 +1,222 @@ *See ChangeLog.md for more recent changes, these are historic versions* +## 1.3.4Q - 2025-09-30 + +- Spending policies for "Single Signers" adds new spending policy options: + - limit your Coldcard so it refuses to sign transactions that are "too big" + - require 2FA authentication before signing any transaction (NFC+web) + - velocity limits can restrict how often new transactions can be signed + - see `docs/spending-policy.md` for more details + - "Enable HSM" and "User Management" have moved into `Advanced > Spending Policy`. + - Old "CCC" feature has been renamed and moved into that menu as well: "Co-Sign Multisig" +- Added `Bull Bitcoin` export to `Export Wallet` menu. +- Enhancement: Added warning for zero value outputs if not `OP_RETURN`. +- Enhancement: Show QR codes of output addresses in transaction output explorer. Explorer is + now offered for transactions of all sizes, not just complex ones. +- Enhancement: Added file rename, when listing contents of SD card. +- Enhancement: Added ability to restore Coldcard backup via USB (needs latest of ckcc version) +- Enhancement: Address ownership allows to specify particular multisig wallet in which to search, + if `wallet` query parameter is provided via trivial extension to + [BIP-21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki). + Example: `tb1q4d67p7stxml3kdudrgkg5mgaxsrgzcqzjrrj4gg62nxtvnsnvqjsxjkej0?wallet=Haystack` +- Bugfix: If all change outputs have `nValue=0`, they were not shown in UX. +- Bugfix: Disallow negative input/output amounts in PSBT. +- Bugfix: Fix filesystem initialization after Wipe LFS or Destroy Seed. +- Bugfix: Fix MicroSD selftest code. +- Bugfix: NFC loop exporting secrets would not work after first value exported. +- Bugfix: Multisig address format handling. +- Bugfix: Ownership check failing to find addresses near max (~760), needed to be re-run to succeed +- (Q only) Enhancement: Enters "forever calculator" mode when Q would otherwise be electronic waste + (ie. after 13 PIN failures). Always enabled, regardless of "login calculator" setting. +- (Q only) Bugfix: Correct line positioning when 24 seed words displayed. + + +## 1.3.3Q - 2025-05-14 + +- Enhancement: Text word-wrap done more carefully so never cuts off any text, and yet + doesn't waste space. +- Bugfix: `Add current tmp` option, which could be shown in `Seed Vault` menu under + specific circumstances, would corrupt master settings if selected. +- Bugfix: PUSHDATA2 in bitcoin script caused yikes. +- Bugfix: Warning for unknown scripts was not shown at the top of the signing story. + +- Bugfix: Do not allow to teleport PSBTs from SD card when CC has no secrets. +- Bugfix: Calculator login mode: added "rand()" command, removed support + for variables/assignments. + + +## 1.3.2Q - 2025-04-16 + +- Feature: Key Teleport -- Easily and securely move seed phrases, secure notes/passwords, + multisig PSBT files, and even full Coldcard backups, between two Q using QR codes + and/or NFC with helper website. See protocol spec in + [docs/key-teleport.md](https://github.com/Coldcard/firmware/blob/master/docs/key-teleport.md) + - can send master seed (words, xprv), anything held in seed vault, secure notes/passwords + (singular, or all) and PSBT involved in a multisig to the other co-signers + - full COLDCARD backup is possible as well, but receiver must be "unseeded" Q for best result + - ECDH to create session key for AES-256-CTR, with another layer of AES-256-CTR using a + short password (stretched by PBKDF2-SHA512) inside + - receiver shows sender a (simple) QR and a numeric code; sender replies with larger BBQr + and 8-char password +- Enhancement: Always choose the biggest possible display size for QR +- Bugfix: Only BBQr is allowed to export Coldcard, Core, and pretty descriptor +- Huge new feature: CCC - ColdCard Cosign + - COLDCARD holds a key in a 2-of-3 multisig, in addition to the normal signing key it has. + - it applies a spending policy like an HSM: + - velocity and magnitude limits + - whitelisted destination addresses + - 2FA authentication using phone app ([RFC 6238](https://www.rfc-editor.org/rfc/rfc6238)) + - but will sign its part of a transaction automatically if those condition are met, + giving you 2 keys of the multisig and control over the funds + - spending policy can be exceeded with help of the other co-signer (3rd key), when needed + - cannot view or change the CCC spending policy once set, policy violations are not explained + - existing multisig wallets can be used by importing the spending-policy-controlled key +- New Feature: Multisig transactions are finalized. Allows use of [PushTX](https://pushtx.org/) + with multisig wallets. Read more [here](https://github.com/Coldcard/firmware/blob/master/docs/limitations.md#p2sh--multisig) +- New Feature: Signing artifacts re-export to various media. Now you have the option of + exporting the signing products (transaction/PSBT) to different media than the original source. + Incoming PSBT over QR can be signed and saved to SD card if desired. +- New Feature: Multisig export files are signed now. Read more [here](https://github.com/Coldcard/firmware/blob/master/docs/msg-signing.md#signed-exports) +- Enhancement: NFC export usability upgrade: NFC keeps exporting until CANCEL/X is pressed +- Enhancement: Add `Bitcoin Safe` option to `Export Wallet` +- Enhancement: 10% performance improvement in USB upload speed for large files +- Bugfix: Do not allow change Main PIN to same value already used as Trick PIN, even if + Trick PIN is hidden. +- Bugfix: Fix stuck progress bar under `Receiving...` after a USB communications failure +- Bugfix: Showing derivation path in Address Explorer for root key (m) showed double slash (//) +- Bugfix: Can restore developer backup with custom password other than 12 words format +- Bugfix: Virtual Disk auto mode ignores already signed PSBTs (with "-signed" in file name) +- Bugfix: Virtual Disk auto mode stuck on "Reading..." screen sometimes +- Bugfix: Finalization of foreign inputs from partial signatures. Thanks Christian Uebber +- Bugfix: Temporary seed from COLDCARD backup failed to load stored multisig wallets +- Change: `Destroy Seed` also removes all Trick PINs from SE2. +- Change: `Lock Down Seed` requires pressing confirm key (4) to execute + +## 1.3.1Q - 2025-02-13 + +- New signing features: + - Sign message from note text, or password note + - JSON message signing. Use JSON object to pass data to sign in form + `{"msg":"","subpath":"","addr_fmt": ""}` + - Sign message with key resulting from positive ownership check. Press (0) and + enter or scan message text to be signed. + - Sign message with key selected from Address Explorer Custom Path menu. Press (2) and + enter or scan message text to be signed. +- Enhancement: New address display format improves address verification on screen (groups of 4). +- Deltamode enhancements: + - Hide Secure Notes & Passwords in Deltamode. Wipe seed if notes menu accessed. + - Hide Seed Vault in Deltamode. Wipe seed if Seed Vault menu accessed. + - Catch more DeltaMode cases in XOR submenus. Thanks [@dmonakhov](https://github.com/dmonakhov) +- Enhancement: Add ability to switch between BIP-32 xpub, and obsolete SLIP-132 format + in `Export XPUB` +- Enhancement: Use the fact that master seed cannot be used as ephemeral seed, to show message + about successful master seed verification. +- Enhancement: Allow devs to override backup password. +- Enhancement: Add option to show/export full multisg addresses without censorship. Enable + in `Settings > Multisig Wallets > Full Address View`. +- Enhancement: If derivation path is omitted during message signing, derivation path + default is no longer root (m), instead it is based on requested address format + (`m/44h/0h/0h/0/0` for p2pkh, and `m/84h/0h/0h/0/0` for p2wpkh). Conversely, + if address format is not provided but subpath derivation starts with: + `m/84h/...` or `m/49h/...`, then p2wpkh or p2sh-p2wpkh respectively, is used. +- Bugfix: Sometimes see a struck screen after _Verifying..._ in boot up sequence. + On Q, result is blank screen, on Mk4, result is three-dots screen. +- Bugfix: Do not allow to enable/disable Seed Vault feature when in temporary seed mode. +- Bugfix: Bless Firmware causes hanging progress bar. +- Bugfix: Prevent yikes in ownership search. +- Bugfix: Factory-disabled NFC was not recognized correctly. +- Bugfix: Be more robust about flash filesystem holding the settings. +- Bugfix: Do not include sighash in PSBT input data, if sighash value is `SIGHASH_ALL`. +- Bugfix: Allow import of multisig descriptor with root (m) keys in it. + Thanks [@turkycat](https://github.com/turkycat) +- Change: Do not purge settings of current active tmp seed when deleting it from Seed Vault. +- Change: Rename Testnet3 -> Testnet4 (all parameters unchanged). + +- New Feature: Verify Signed RFC messages via BBQr +- New Feature: Sign message from QR scan (format has to be JSON) +- Enhancement: Sign/Verify Address in Sparrow via QR +- Enhancement: Sign scanned Simple Text by pressing (0). Next screen query information + about which key to use. +- Enhancement: Add option to "Sort By Title" in Secure Notes and Passwords. Thanks to + [@MTRitchey](https://x.com/MTRitchey) for suggestion. +- Bugfix: Properly re-draw status bar after Restore Master on COLDCARD without master seed. + + +## 1.3.0Q - 2024-09-12 + +- New Feature: Opt-in support for unsorted multisig, which ignores BIP-67 policy. Use + descriptor with `multi(...)`. Disabled by default, Enable in + `Settings > Multisig Wallets > Legacy Multisig`. Recommended for existing multisig + wallets, not new ones. +- New Feature: Named multisig descriptor imports. Wrap descriptor in json: + `{"name:"ms0", "desc":""}` to provide a name for the menu in `name`. + instead of the filename. Most useful for USB and NFC imports which have no filename, + (name is created from descriptor checksum in those cases). +- New Feature: XOR from Seed Vault (select other parts of the XOR from seeds in the vault). +- Enhancement: upgrade to latest + [libsecp256k1: 0.5.0](https://github.com/bitcoin-core/secp256k1/releases/tag/v0.5.0) +- Enhancement: Signature grinding optimizations. Now about 30% faster signing! +- Enhancement: Improve side-channel protection: libsecp256k1 context randomization now happens + before each signing session. +- Enhancement: Allow JSON files in `NFC File Share`. +- Change: Do not require descriptor checksum when importing multisig wallets. +- Bugfix: Do not allow import of multisig wallet when same keys are shuffled. +- Bugfix: Do not read whole PSBT into memory when writing finalized transaction (performance). +- Bugfix: Prevent user from restoring Seed XOR when number of parts is smaller than 2. +- Bugfix: Fix display alignment of Seed Vault menu. +- Bugfix: Properly handle null data in `OP_RETURN`. +- Bugfix: Do not allow lateral scroll in Address Explorer when showing single address + from custom path. +- Change: Remove Lamp Test from Debug Options (covered by selftest). +- New Feature: Seed XOR can be imported by scanning SeedQR parts. +- New Feature: Input backup password from QR scan. +- New Feature: (BB)QR file share of arbitrary files. +- New Feature: `Create Airgapped` now works with BBQRs. +- Change: Default brightness (on battery) adjusted from 80% to 95%. +- Bugfix: Properly clear LCD screen after BBQR is shown. +- Bugfix: Writing to empty slot B caused broken card reader. +- Bugfix: During Seed XOR import, display correct letter B if own seed already added to the mix. +- Bugfix: Stop re-wording UX stories using a regular expression. +- Bugfix: Fixed "easy exit" from quiz after split Seed XOR. + + +## 1.3.0Q - 2024-09-12 + +- New Feature: Opt-in support for unsorted multisig, which ignores BIP-67 policy. Use + descriptor with `multi(...)`. Disabled by default, Enable in + `Settings > Multisig Wallets > Legacy Multisig`. Recommended for existing multisig + wallets, not new ones. +- New Feature: Named multisig descriptor imports. Wrap descriptor in json: + `{"name:"ms0", "desc":""}` to provide a name for the menu in `name`. + instead of the filename. Most useful for USB and NFC imports which have no filename, + (name is created from descriptor checksum in those cases). +- New Feature: XOR from Seed Vault (select other parts of the XOR from seeds in the vault). +- Enhancement: upgrade to latest + [libsecp256k1: 0.5.0](https://github.com/bitcoin-core/secp256k1/releases/tag/v0.5.0) +- Enhancement: Signature grinding optimizations. Now about 30% faster signing! +- Enhancement: Improve side-channel protection: libsecp256k1 context randomization now happens + before each signing session. +- Enhancement: Allow JSON files in `NFC File Share`. +- Change: Do not require descriptor checksum when importing multisig wallets. +- Bugfix: Do not allow import of multisig wallet when same keys are shuffled. +- Bugfix: Do not read whole PSBT into memory when writing finalized transaction (performance). +- Bugfix: Prevent user from restoring Seed XOR when number of parts is smaller than 2. +- Bugfix: Fix display alignment of Seed Vault menu. +- Bugfix: Properly handle null data in `OP_RETURN`. +- Bugfix: Do not allow lateral scroll in Address Explorer when showing single address + from custom path. +- Change: Remove Lamp Test from Debug Options (covered by selftest). +- New Feature: Seed XOR can be imported by scanning SeedQR parts. +- New Feature: Input backup password from QR scan. +- New Feature: (BB)QR file share of arbitrary files. +- New Feature: `Create Airgapped` now works with BBQRs. +- Change: Default brightness (on battery) adjusted from 80% to 95%. +- Bugfix: Properly clear LCD screen after BBQR is shown. +- Bugfix: Writing to empty slot B caused broken card reader. +- Bugfix: During Seed XOR import, display correct letter B if own seed already added to the mix. +- Bugfix: Stop re-wording UX stories using a regular expression. +- Bugfix: Fixed "easy exit" from quiz after split Seed XOR. + ## 1.2.3Q - 2024-07-05 diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md index 50dcaa013..5bcf6d922 100644 --- a/releases/Next-ChangeLog.md +++ b/releases/Next-ChangeLog.md @@ -4,19 +4,20 @@ This lists the new changes that have not yet been published in a normal release. # Shared Improvements - Both Mk4 and Q -# Mk4 Specific Changes +- New Feature: Export [BIP-380](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki) extended key expression. + Navigate to `Advanced/Tools -> Export Wallet -> Key Expression` -- tbd +# Mk4 Specific Changes +## 5.4.5 - 2025-12-xx -## 5.4.? - 2024-??-?? +- Enhancement: Show QR of XOR-split seeds -- tbd +# Q Specific Changes +## 1.3.6Q - 2025-12-xx -# Q Specific Changes +- tbd -## 1.3.?Q - 2024-??-?? -- Bugfix: Properly re-draw status bar after Restore Master on COLDCARD without master seed. diff --git a/releases/signatures.txt b/releases/signatures.txt index 6c697e424..2c35f95d4 100644 --- a/releases/signatures.txt +++ b/releases/signatures.txt @@ -2,104 +2,45 @@ Hash: SHA256 95eff9e044cdb6b3d00961ae72d450684d5441c6a3661ab550a3c3aa0882e754 README.md -97107b5be1c8b65efa4bd36b7d1798e4ed15917861bd2d40784d66302a61d335 Next-ChangeLog.md -f6d8a1edf0993cdecea7cdc34f48ce344f249ec0fc2d28fbc4da9ebc163c6148 History-Q.md -3e98b0f292b30460e128c3d41e9dd33428524516ce433fe4a3b99132025ca64c History-Mk4.md +52985f02188c59ff78cd03d496a9ae18a73d5c27a7d0cf98961394e1b6ea4007 Next-ChangeLog.md +c708e41529f07f845e5217cce919d59235932693586aab3cf7cadb0d959e0d65 History-Q.md +3a914286f544cd5c2ed9ef1196451dcd24aec2416045efb61672a6204c9843e0 History-Mk4.md c8ad43b4e3f9d77777026da6d1210c6fc5cfe435bcfcd241c0f67c9392ad7b82 History-Mk3.md -7c06aa1d5168e02d928da087f13c74b94e40f52e5eb281af21edcfdf6cabe5ce ChangeLog.md -237cfcb3fdf9217550eae1d9ea6fc828c1c8d09470bd60c9f72f9b00a3bb2d11 2024-09-12T1734-v5.4.0-mk4-coldcard.dfu -6d1178f07d543e1777dbbdca41d872b00ca9c40e0c0c1ffb8ef96e19c51daa52 2024-09-12T1734-v5.4.0-mk4-coldcard-factory.dfu -d840fa4e83ebc7b0f961f30f68d795bed61271e2314dda4ab0eb0b8bfe7192f4 2024-09-12T1733-v1.3.0Q-q1-coldcard.dfu -4db89ecffa1376bfc68a37110c2041a29afe52b005d527ecde701131168fc19c 2024-09-12T1733-v1.3.0Q-q1-coldcard-factory.dfu -4d83715772b31643abde3b9a0bb328003f4a31d14e2fe9c1e038077a518acaea 2024-07-05T1348-v5.3.3-mk4-coldcard.dfu -020d6d5c3baa724713b2f906112bb95f7eff43c3f5a4f8f11b77d8c2e96ccc88 2024-07-05T1348-v5.3.3-mk4-coldcard-factory.dfu -54da941c8df84fcb84adcc62fdd3ee97d1fc12e2a9a648551ca614fcbacade3f 2024-07-05T1342-v1.2.3Q-q1-coldcard.dfu -7f704aa37887ed84d6a25f124e9b4a31187430d7cf6b198eb83b86af8ae4e5ea 2024-07-05T1342-v1.2.3Q-q1-coldcard-factory.dfu -ddf5ce1ef1ee2e6ba2922b333213d0cb939a2658b294c0f24c0e489de3fe7c75 2024-07-04T1501-v6.3.3X-mk4-coldcard.dfu -9a2c5ef80a6f8212caa3b455e203da3549a79b08b473113662cf80fff587566a 2024-07-04T1459-v6.3.3QX-q1-coldcard.dfu -a990cc94066486a37071c011cd85a29caed433cb4ca3f1c4dce7f715ef81dc3c 2024-06-26T1741-v5.3.2-mk4-coldcard.dfu -218d17069d05c0ec2829e5629c5216121028d15b145c31b552e2f52daa7bf172 2024-06-26T1741-v5.3.2-mk4-coldcard-factory.dfu -b87505b407b0477e2d15f71cfb20645ac55ac5b7c74493d25a2c9c97e807b2b3 2024-06-26T1739-v1.2.2Q-q1-coldcard.dfu -efff41069f3f82d4e69d08a02a565ae0d2cd55c07dbbbe4c1328e6e3b6d8faa1 2024-06-26T1739-v1.2.2Q-q1-coldcard-factory.dfu -90b1edfbe194b093258f9cda8f4add4aa3317e9ea205ff35914da7d91410fdae 2024-05-09T1529-v1.2.1Q-q1-coldcard.dfu -c7889532323f7b0c08e84589c7cc756e2c46e209b4eea031bdfef4a633a813c1 2024-05-09T1529-v1.2.1Q-q1-coldcard-factory.dfu -ef6526d37bc1a929c94dc8388f3863f6cc1582addf26495f761123f0bfb7aa30 2024-05-09T1527-v5.3.1-mk4-coldcard.dfu -98c675e98a18b2437c52e30a9867c271bbca9969771caa34299556ef3fcb1a43 2024-05-09T1527-v5.3.1-mk4-coldcard-factory.dfu -c7c79a21c206e8b0e816c86ef1b43cd6932cb767ed97291d5fbc2f0e749f95b7 2024-05-06T1812-v1.2.0Q-q1-coldcard.dfu -5c6b69948f0193b3a7bd252195136d6d9f84ab14fbc8c5349150e7d238708c6f 2024-05-06T1812-v1.2.0Q-q1-coldcard-factory.dfu -bab6818787eec45ef28b6c297e2504ffd4fa041ab19da8a3fd27543dffe876b8 2024-05-06T1811-v5.3.0-mk4-coldcard.dfu -3da458c0dabe9a17eaeb92ee959006a64a3e6838eeb31f887a18840f020ef8b9 2024-05-06T1811-v5.3.0-mk4-coldcard-factory.dfu -101f336310b9b460d717d91d2572ea9e9ef7ac3edbdaf132c7c3aa46bb89050a 2024-04-02T1416-v1.1.0Q-q1-coldcard.dfu -5d034bc6b1abec49a067a90766bdb769faf9a1b52b2c9b7e541d32484cf783fc 2024-04-02T1416-v1.1.0Q-q1-coldcard-factory.dfu -6ea843a56e87d7d811d90be6bfa4703794bbc8318d9709e88ada05740e03b12d 2024-03-14T1419-v1.0.1Q-q1-coldcard.dfu -f53c79c64f02dd1e860a8d32f9319edd279485d97f07815b2a1eb180a1305459 2024-03-14T1419-v1.0.1Q-q1-coldcard-factory.dfu -122e6d757eb5a8ce073d98a85851f376adec97856336c5a8f05b953b5c87a533 2024-03-10T1537-v1.0.0Q-q1-coldcard.dfu -ae04aaac47f07e10143c75b5c772b54739830214c8234356d003137897f3f4f4 2024-03-10T1537-v1.0.0Q-q1-coldcard-factory.dfu -6aaa9d5bf1726fe4d4a4834010d9b9b6525e8592bb97945cd08cc728fc884068 2024-03-02T1750-v0.0.8Q-q1-coldcard.dfu -a0cd556693fae5b8b03f2a498c0abb1e6d747f91a92bd8f2559a676f8707d840 2024-03-02T1750-v0.0.8Q-q1-coldcard-factory.dfu -18fe081d84a950e1fddb2151ad50917697dfc218cd68e2e359229b0bdadbff37 2024-02-26T1442-v0.0.7Q-q1-coldcard.dfu -e4f4fe89cf3743d794568fd5b32b14551966139e9199602ea10468f925fab1cf 2024-02-26T1442-v0.0.7Q-q1-coldcard-factory.dfu -2dc7a27f43958f2de9851f221183c94258ac915ae43d997b39b644e7b9daff8f 2024-02-22T1423-v0.0.6Q-q1-coldcard.dfu -1e4f4d4c04835d78fcc4857d3264034a56dccf594e307d7408d7c4cdcdb0a926 2024-02-22T1423-v0.0.6Q-q1-coldcard-factory.dfu -d51573c72d8958ea35357d4e0a36ce6aaa2d05924577efb219e2cc189be63f08 2024-02-16T1635-v0.0.5Q-q1-coldcard.dfu -55f4ef9c3ae116f50db938acfc3a4b09717965f82cf6de8cc7385f68cd66d285 2024-02-16T1635-v0.0.5Q-q1-coldcard-factory.dfu -8fd1ced0d5e0338d845f6d5ec5ab069a5143cceade02d4f17e86b7d182b489eb 2024-02-15T1843-v0.0.4Q-q1-coldcard.dfu -43fac084727b0e69bae7fc040a62854673fd585dc2435d93bf146c80762e41cf 2024-02-15T1843-v0.0.4Q-q1-coldcard-factory.dfu -3064bf7f1a039e7cd5c1a13c6aff8cc4338e52ef2177abbdca4b196955f9e434 2024-02-08T2005-v0.0.3Q-q1-coldcard.dfu -788e7a1b182f920016617411b875fa7095ae007c6a53fc476afb1c93f0eed1c9 2024-02-08T2005-v0.0.3Q-q1-coldcard-factory.dfu +01a68d9397830935bce570d6c2cd319b773dc8eefd3e0cad43ba231c2ebc9732 History-Edge.md +8d36e8433af21b021daff1d0743ff0a441a2bf7d29ba280e5c99982bbef06742 EdgeChangeLog.md +c14793ad2ef0ebca0a97dba9a0b41657ce48d7b121eb107101977385564fdf5a ChangeLog.md +f04617b52fc0db6e95cac0dddd9ddd90754219f38b63a26d08c848e208069edb 2025-11-20T1602-v6.4.0X-mk4-coldcard.dfu +993ef645ca83988c576febfaa248c0a5044e948d3d1e4443f31d5f9fd5734fe1 2025-11-20T1602-v6.4.0X-mk4-coldcard-factory.dfu +371f13f3e1a5ef28d14933daf03820f0e51d26ffa96008dd5595da0dfac646cf 2025-11-20T1601-v6.4.0QX-q1-coldcard.dfu +f7e73850b3c3dc33b1cd0fa7a94909931c1a4bbd881a7224a71da77807976640 2025-11-20T1601-v6.4.0QX-q1-coldcard-factory.dfu +495f37ce7ddaba2e9fc3f03dec582f1646f258a3d0cec5e71c04d127357b2fa3 2025-02-19T1941-v6.3.5X-mk4-coldcard.dfu +580701fb2de24362d8de6cf998d5fd42ca9ab003aff75f3c0140d915a06a6803 2025-02-19T1941-v6.3.5X-mk4-coldcard-factory.dfu +605ebb5acde19447e5c1d7c8cfd0302c89de5c5870d85f06b185ecab3437f94e 2025-02-19T1939-v6.3.5QX-q1-coldcard.dfu +245db07574a535a3f068ed9a759bf0088f0d0e1e39704a0e0727f90119833602 2025-02-19T1939-v6.3.5QX-q1-coldcard-factory.dfu +eb750a4f095eacc6133b2c8b38fe0738a22b2496a6cdf423ca865acde8c9bc4e 2025-02-13T1415-v5.4.1-mk4-coldcard.dfu +4236453fea241fe044a462a560d8b42df43e560683110306a2714a2ef561eac5 2025-02-13T1415-v5.4.1-mk4-coldcard-factory.dfu +2e1aad0a7a3ceb84db34322b54855a0c5496699e46e53606bfa443fcc992adec 2025-02-13T1413-v1.3.1Q-q1-coldcard.dfu +e43932d04bf782f7b9ba218b54f29b9cd361b83ac3aadff9722714bca1ab7ee9 2025-02-13T1413-v1.3.1Q-q1-coldcard-factory.dfu +681874256bcfca71a3908f1dd6c623804517fdba99a51ed04c73b96119650c13 2024-12-18T1413-v6.3.4X-mk4-coldcard.dfu +73f31fbcb064a6b763d50852aafcdff01d7ec72906b5cb0af6cf28328fd80a89 2024-12-18T1413-v6.3.4X-mk4-coldcard-factory.dfu +93ab7615bcedeeff123498c109e5859dae28e58885e29ed86b6f3fd6ba709cce 2024-12-18T1407-v6.3.4QX-q1-coldcard.dfu +7e284bcead1f9c2f468230a588ddf62064014682772a552d05f453d91d55b6ae 2024-12-18T1407-v6.3.4QX-q1-coldcard-factory.dfu a9d0b416c3cb4f122f2826283fce82bbc5fe4464817b601a3a5787b1f8aaba20 2024-01-18T1507-v6.2.2X-mk4-coldcard.dfu -4651fb81dc04ac07ae53535f4246ef7f32611c50853de9edaefa68f3c64e1fac 2023-12-21T1526-v5.2.2-mk4-coldcard.dfu -a49cd00808732c67b359c9f86814ddeafc63a1040823b6c1d2035a870575c9ed 2023-12-21T1526-v5.2.2-mk4-coldcard-factory.dfu -06d1048bea43c5d7c72c5e5f395a676620ce884aed0cd152627a86d922e2f3ab 2023-12-19T1444-v5.2.1-mk4-coldcard.dfu -3eb9c4b1add88a6fe412d783b8f4b895241a67e423bbacc6a13816a5216a30fe 2023-12-19T1444-v5.2.1-mk4-coldcard-factory.dfu +cc93209e800bc05386b5613969e62c27b9acd4388e3a922686525da90a505778 2024-01-18T1507-v6.2.2X-mk4-coldcard-factory.dfu f4457dc44d08cbed9517e6260aa7163ecc254457276d3cdb0c2611af0f49ba9b 2023-10-26T1343-v6.2.1X-mk4-coldcard.dfu 1dcfb450f81883afe8f655239f06e238de7bae51e740cd4aa5ae6a0541772ad8 2023-10-26T1343-v6.2.1X-mk4-coldcard-factory.dfu -7fbed097d2757b21fde920f4b10f5f50d7e1aeca01ff52186dfde4883af5cace 2023-10-10T1735-v5.2.0-mk4-coldcard.dfu -4e3023676be88d6c6480c7f37de302f3a865077f9a2214de9c5a55b24afcba2c 2023-10-10T1735-v5.2.0-mk4-coldcard-factory.dfu -fd707f2f69d006c9db84ceacd2a0dde79c3cb71730750e2676af610942898717 2023-09-08T2009-v5.1.4-mk4-coldcard.dfu -d2a4a8b71b0b102971bf8a6c98968dee776a77e0a5707db862e34be5276fbc78 2023-09-08T2009-v5.1.4-mk4-coldcard-factory.dfu -c03d4e2d1115e9440d1762c95fc82ae5a31122e84ee88d6537a8e75f26f66954 2023-09-07T1501-v5.1.3-mk4-coldcard.dfu -3602f307df06b6658d7731172c2eb3f192a0bc8ee02c606e3cb97c1aa8d49af2 2023-09-07T1501-v5.1.3-mk4-coldcard-factory.dfu -f6fb19d95bd1e38535f137bed60cafbfcd52379a686e3d12f372f881d78e640e 2023-06-26T1241-v4.1.9-coldcard.dfu 489e161f686a0c631fc605054f8e7271208b16191b669174b8a58f5af28b0f4a 2023-06-20T1506-v6.1.0X-mk4-coldcard.dfu -233398cc8f6b9e894072448eb8b8a82a4f546219ce461dd821f0ed0a38b61900 2023-06-19T1627-v4.1.8-coldcard.dfu +66c83c3f95fd3d0796b1e452d2e8ed8ac6a4abead53faf5ae793eceb6f7bbdb5 2023-06-20T1506-v6.1.0X-mk4-coldcard-factory.dfu 2e8ed970f518a476d0b34752ecbad75bab246669aa65de8f43801364c6f5753e 2023-05-12T1316-v6.0.0X-mk4-coldcard.dfu -7aefd5bcce533f15337e83618ebbd42925d336792c82a5ca19a430b209b30b8a 2023-04-07T1330-v5.1.2-mk4-coldcard.dfu -a6c007992139a847f0f238769023727e8cbc05c54c916b388a4dd8bc7490f0aa 2023-04-07T1330-v5.1.2-mk4-coldcard-factory.dfu -99804b440f41ea47675456b4e20e7bb4e9cb434556c5813ab83c26fcda0f4e80 2023-02-27T2105-v5.1.1-mk4-coldcard.dfu -8b37d0f2bf9ca8990f424e5a79fe62405e1ec3aca515760e509afec8f2dbacbc 2023-02-27T2105-v5.1.1-mk4-coldcard-factory.dfu -bcf4284f7733e9de8d4dba238368552b056a27308e466721be7ca624192e257f 2023-02-27T1509-v5.1.0-mk4-coldcard.dfu -cc946bcb63211e15d85db577e25ab2432d4a74d5dad77d710539e505dce7914a 2022-11-14T1854-v4.1.7-coldcard.dfu -010827a60ebfc25b8a6e2bb94cc69b938419957ac6d4a9b6c0b1357c4c6c8632 2022-10-05T1724-v5.0.7-mk4-coldcard.dfu -bc4d0b2b985aea3a78eb9351cdadf60d1ab00801ed1e7192765b94181cb8933b 2022-10-05T1517-v4.1.6-coldcard.dfu -884f373717c9c605920a1dc29e0f890bf7b3cc6b141666814e396094aeedb3f8 2022-07-29T1816-v5.0.6-mk4-coldcard.dfu -3c680195ef49cd0eb86d8e2426443511e8834bcea2d0a86ab52a35cc9365a801 2022-07-20T1508-v5.0.5-mk4-coldcard.dfu -7bd2b98186370f2d895e1e43949694f6ba61a1c021f72a63f0f86a30f338a0fc 2022-05-27T1500-v5.0.4-mk4-coldcard.dfu -5aa2ccc65e2e5279db78b3068b9f3c60c34dd7cc330c2cc1243160db31a2d0f0 2022-05-04T1258-v4.1.5-coldcard.dfu -6dbf0aca0f98fb7bdc761eeead4786617b804dad4afb42ee02febf23d31b5e9b 2022-05-04T1254-v5.0.3-mk3-coldcard.dfu -d5d9bf50892a4aab6e2ffb106a3d206853a60f879daa94a6f90d68a69bf4fa33 2022-05-04T1252-v5.0.3-mk4-coldcard.dfu -9bb028d3e60239f0fcdb3b1f91075785e2c21795789b38c4c619c1f64c2950ef 2022-04-25T1618-v4.1.4-coldcard.dfu -a363b1f0d1b27b8f21dbaac32844a59dacab8c2fee126815cda84c4df31fd7cd 2022-04-19T1805-v5.0.2-mk4-coldcard.dfu -afb6048397af4093e63567563544098e1cfb45b7ca673536253eb6494d60125c 2022-03-24T1645-v5.0.1-mk3-coldcard.dfu -605807bd448711d54e14057892a100bac299a103f5b5fb6466d73f9a36d0694b 2022-03-24T1643-v5.0.1-mk4-coldcard.dfu -badd10c078996516c6464c9bfa5f696747dd7206c97d1e6a75d6f5ee0436619a 2022-03-14T1907-v5.0.0-mk4-coldcard.dfu -dedfcf8385e35dbdbb26b92f8c0667105404062ad83c8830d809cf9193434d9c 2021-09-02T1752-v4.1.3-coldcard.dfu -d01d81305b209dadcf960b9e9d20affb8d4f11e9f9f916c5a06be29298c80dc2 2021-07-28T1347-v4.1.2-coldcard.dfu -08e1ec1fd073afbbc9014db6da07fd96c6b20a6710fe491eb805afeba865fe3f 2021-04-30T1748-v4.1.1-coldcard.dfu -2c39330bef467af8dcd7e2f393a970e1ca177b1812f830269916657ff79598eb 2021-04-29T1725-v4.1.0-coldcard.dfu -5e0c5f4ba9fa0e5fd7f9846e25c6cd28821a86ff5e1207c56cc3a4f4c3741f15 2021-04-07T1424-v4.0.2-coldcard.dfu -f05bc8dfed047c0c0abe5ed60621d2d14899b10717221c4af0942d96a1754f33 2021-03-29T1927-v4.0.1-coldcard.dfu -3097fa3c173247637aa27376036e384940adeb67ce727c9795471f46deaa5210 2021-01-14T1617-v3.2.2-coldcard.dfu -9e4aeee48d4399a761fec5d4c65cb2495ef5bc0b46995c085d63a65cf67362cb 2021-01-07T1439-v3.2.1-coldcard.dfu -bea27f263b524a66b3ed0a58c16805e98be0d7c3db20c2f7aab3238f2c6a6995 2019-12-19T1623-v3.0.6-coldcard.dfu +8dd5ff029bb2b08c857604f0c9b5773931f6683ee331ecbc35d9ab4c460b745f 2023-05-12T1316-v6.0.0X-mk4-coldcard-factory.dfu -----BEGIN PGP SIGNATURE----- -iQEzBAEBCAAdFiEERYl3mt/BTzMnU06oo6MbrVoqWxAFAmbjJicACgkQo6MbrVoq -WxAnMwf/e2kR1aK6AJiriRa1n3XDomw8ivaUQXUApmK0kawBhVBDLKw5aa3lvTcS -dg80wnenzNdE/QxctL+FkaZzKYsKbFpstkBEbZKcgbHVcinypKJJfICrhIBVVyZw -wdhJMGOLEyWMysqfaYMtYJQPkg5nIn0rRxn4yWXIeXAQLcFgdlWzVykqfGZW1xYr -CcVvxMqufXfc6c5aRFQzBO/YVHiRYzvK1NGDPztJEjXYU3zxnExAZFxk0vgpxvE3 -CahKfSSTNv54u4CTLxYCdHPRq9OM6yL/w3OUyUQFklCizk2PjrObsJQW4szbbjlx -r7+587Pc5cpJCZn73Q0Y5/SWgnqm4g== -=/h9F +iQEzBAEBCAAdFiEERYl3mt/BTzMnU06oo6MbrVoqWxAFAmkfO5QACgkQo6MbrVoq +WxBh/wf/XRyXjpnLjIdLnX9302U/bWMSMONUoyTlEVbGpv3fAt5nZOm6PATjK7Sf +TEnoRcteI7kJkoFKj5rFiNyOma1H0VcLJ2eKvKQXdYCYqKByhhrHW1n8Epi+/+fB +hIRjiRxbMtHBDa8g6TntClHti5jkMW5u1mLUx6ZykO5yMjTuxHV4VuqYAjGv80UE +F9VJM847xUpm9xafA5iOQCnfyECPuTv/g+OCeKVR6SpdMz0mZgm0OSeh9rqWqKvO +9g/kpJMa3DPkkEa9RZUqG++3BwyRY49GwHXDWY8cgPVnT5nlJAn1qfswMpNwtaUx +jHUn6S6YXsw7fJ6Vt0FzGLOZ56F3Vw== +=Yx2T -----END PGP SIGNATURE----- diff --git a/shared/actions.py b/shared/actions.py index 5194b6048..f02e8549e 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -4,19 +4,19 @@ # # Every function here is called directly by a menu item. They should all be async. # -import ckcc, pyb, version, uasyncio, sys, uos +import ckcc, pyb, version, uasyncio, sys, uos, chains from uhashlib import sha256 from uasyncio import sleep_ms from ubinascii import hexlify as b2a_hex from utils import imported, problem_file_line, get_filesize, encode_seed_qr -from utils import xfp2str, B2A, addr_fmt_label, txid_from_fname +from utils import xfp2str, B2A, txid_from_fname, wipe_if_deltamode from ux import ux_show_story, the_ux, ux_confirm, ux_dramatic_pause, ux_aborted -from ux import ux_enter_bip32_index, ux_input_text, import_export_prompt, OK, X -from export import make_json_wallet, make_summary_file, make_descriptor_wallet_export +from ux import ux_enter_bip32_index, ux_input_text, import_export_prompt, OK, X, ux_render_words +from export import export_contents, make_summary_file, make_descriptor_wallet_export from export import make_bitcoin_core_wallet, generate_wasabi_wallet, generate_generic_export -from export import generate_unchained_export, generate_electrum_wallet +from export import generate_unchained_export, generate_electrum_wallet, make_key_expression_export from files import CardSlot, CardMissingError, needs_microsd -from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, MAX_TXN_LEN_MK4 +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR from glob import settings from pincodes import pa from menu import start_chooser, MenuSystem, MenuItem @@ -24,8 +24,6 @@ from charcodes import KEY_NFC, KEY_QR, KEY_CANCEL -CLEAR_PIN = '999999-999999' - async def start_selftest(*args): # selftest is harmless, no need to warn anymore, # but this layer saves memory in typical cases @@ -321,7 +319,7 @@ async def initial_pin_setup(*a): if ch == '6': break # do the actual picking - pin = await lll.get_new_pin(title) + pin = await lll.get_new_pin() del lll if pin is None: return @@ -525,7 +523,7 @@ async def new_from_dice(menu, label, item): async def any_active_duress_ux(): from trick_pins import tp - tp.reload() + # if TPs are hidden this msg will not be shown if any(tp.get_duress_pins()): await ux_show_story('You have one or more duress wallets defined ' 'under Trick PINs. Please empty them, and clear ' @@ -562,7 +560,7 @@ async def convert_ephemeral_to_master(*a): msg += 'A reboot is part of this process. ' msg += 'PIN code, and %s funds are not affected.' % _type - if not await ux_confirm(msg): + if not await ux_confirm(msg, confirm_key='4'): return await ux_aborted() # settings.save is part of re-building fs @@ -574,24 +572,30 @@ async def clear_seed(*a): # This is super dangerous for the customer's money. import seed - if await any_active_duress_ux(): - return await ux_aborted() + # in hobble mode, they cannot reach duress wallets and/or maybe we don't + # want to reveal them? So don't block them based on that. + if not pa.hobbled_mode: + if await any_active_duress_ux(): + return await ux_aborted() if not await ux_confirm('Wipe seed words and reset wallet. ' 'All funds will be lost. ' - 'You better have a backup of the seed words.' + 'You better have a backup of the seed words. ' 'All settings like multisig wallets are also wiped. ' - 'Saved temporary seed settings and Seed Vault are lost.'): + 'Saved temporary seed settings and Seed Vault are lost. ' + 'Trick PINs are also completely removed.'): return await ux_aborted() - ch = await ux_show_story('''Are you REALLY sure though???\n\n\ + if not await ux_confirm('''Are you REALLY sure though???\n\n\ This action will certainly cause you to lose all funds associated with this wallet, \ unless you have a backup of the seed words and know how to import them into a \ -new wallet.\n\nPress (4) to prove you read to the end of this message and accept all \ -consequences.''', escape='4') - if ch != '4': +new wallet.''', 'AGAIN...', confirm_key='4'): return await ux_aborted() + # clear all trick PINs from SE2 + from trick_pins import tp + tp.clear_all() + # clear settings, address cache, settings from tmp seeds / seedvault seeds from files import wipe_flash_filesystem wipe_flash_filesystem(False) @@ -603,7 +607,6 @@ async def clear_seed(*a): def render_master_secrets(mode, raw, node): # Render list of words, or XPRV / master secret to text. import stash, chains - from ux import ux_render_words c = chains.current_chain() qr_alnum = False @@ -617,7 +620,12 @@ def render_master_secrets(mode, raw, node): qr = ' '.join(w[0:4] for w in words) qr_alnum = True - msg = 'Seed words (%d):\n' % len(words) + title = 'Seed words (%d):' % len(words) + msg = "" + if not version.has_qwerty: + msg += title + "\n" + title = None + msg += ux_render_words(words) if stash.bip39_passphrase: @@ -627,28 +635,30 @@ def render_master_secrets(mode, raw, node): elif mode == 'xprv': + title = "Extended Private Key" if version.has_qwerty else None msg = c.serialize_private(node) qr = msg elif mode == 'master': + title = "Master Secret" if version.has_qwerty else None msg = '%d bytes:\n\n' % len(raw) qr = str(b2a_hex(raw), 'ascii') msg += qr else: raise ValueError(mode) - return msg, qr, qr_alnum + return title, msg, qr, qr_alnum async def view_seed_words(*a): - import stash - if not await ux_confirm('The next screen will show the seed words' ' (and if defined, your BIP-39 passphrase).' '\n\nAnyone with knowledge of those words ' 'can control all funds in this wallet.'): return - from glob import dis + import stash + from glob import dis, NFC + dis.fullscreen("Wait...") dis.busy_bar(True) @@ -658,33 +668,35 @@ async def view_seed_words(*a): raw = mode = None if stash.bip39_passphrase: # get main secret - bypass tmp - with stash.SensitiveValues(bypass_tmp=True) as sv: - if not sv.deltamode: - assert sv.mode == "words" - raw = sv.raw[:] - mode = sv.mode + with stash.SensitiveValues(bypass_tmp=True, enforce_delta=True) as sv: + assert sv.mode == "words" + raw = sv.raw[:] + mode = sv.mode stash.SensitiveValues.clear_cache() - with stash.SensitiveValues(bypass_tmp=False) as sv: - if sv.deltamode: - # give up and wipe self rather than show true seed values. - import callgate - callgate.fast_wipe() - + with stash.SensitiveValues(bypass_tmp=False, enforce_delta=True) as sv: dis.busy_bar(False) - msg, qr, qr_alnum = render_master_secrets(mode or sv.mode, - raw or sv.raw, - sv.node) - + title, msg, qr, qr_alnum = render_master_secrets(mode or sv.mode, + raw or sv.raw, + sv.node) + esc = "1" if not version.has_qwerty: - msg += '\n\nPress (1) to view as QR Code.' + msg += '\n\nPress (1) to view as QR Code' + if NFC: + msg += ", (3) to share via NFC" + esc += "3" + msg += "." while 1: - ch = await ux_show_story(msg, sensitive=True, escape='1'+KEY_QR) + ch = await ux_show_story(msg, title=title, sensitive=True, escape=esc, + hint_icons=KEY_QR+(KEY_NFC if NFC else '')) if ch in '1'+KEY_QR: from ux import show_qr_code - await show_qr_code(qr, qr_alnum) + await show_qr_code(qr, qr_alnum, is_secret=True) + continue + elif NFC and (ch in '3'+KEY_NFC): + await NFC.share_text(qr, is_secret=True) continue break @@ -707,12 +719,7 @@ async def export_seedqr(*a): # Note: cannot reach this menu item if no words. If they are tmp, that's cool. - with stash.SensitiveValues(bypass_tmp=False) as sv: - if sv.deltamode: - # give up and wipe self rather than show true seed values. - import callgate - callgate.fast_wipe() - + with stash.SensitiveValues(bypass_tmp=False, enforce_delta=True) as sv: if sv.mode != 'words': raise ValueError(sv.mode) @@ -724,7 +731,7 @@ async def export_seedqr(*a): del words from ux import show_qr_code - await show_qr_code(qr, True, msg="SeedQR") + await show_qr_code(qr, True, msg="SeedQR", is_secret=True) stash.blank_object(qr) @@ -748,6 +755,10 @@ async def version_migration(): # version 5.0.6 is installed settings.remove_key('vdsk') + # 6.4.0 multisig migration + from wallet import do_640_multisig_migration + await do_640_multisig_migration() + async def version_migration_prelogin(): # same, but for setting before login # these have moved into SE2 for Mk4 and so can be removed @@ -795,26 +806,37 @@ async def start_login_sequence(): # If that didn't work, or no skip defined, force # them to login successfully. - + sp_unlock = False try: + from trick_pins import tp + # Get a PIN and try to use it to login # - does warnings about attempt usage counts await block_until_login() + sp_unlock = tp.was_sp_unlock() + if sp_unlock: + # Trying to unlock spending policy: ask for main PIN next. + await ux_show_story("Spending Policy Unlock: Please provide Main PIN next.") + pa.reset() + await block_until_login() + + # we don't really know if that was the Main PIN (could easily be the bypass + # PIN again) and if it's a duress wallet, that's cool... + # Do we need to do countdown delay? (real or otherwise) - # Q/Mk4 approach: - # - wiping has already occured if that was picked + # - wiping has already occured if that was selected by trick details # - delay is variable, stored in tc_arg - from trick_pins import tp delay = tp.was_countdown_pin() - # Maybe they do know the right PIN, but do a delay anyway, because they wanted that + # Maybe they do know the right PIN, but always do a delay anyway, because they wanted that if not delay: delay = settings.get('lgto', 0) if delay: # kill some time, with countdown, and get "the" PIN again for real login pa.reset() + await ux_login_countdown(delay * (60 if not version.is_devmode else 1)) # keep it simple for Mk4+: just challenge again for any PIN @@ -828,7 +850,7 @@ async def start_login_sequence(): # safe to do so. Remember the bootrom checks PIN on every access to # the secret, so "letting" them past this point is harmless if they don't know # the true pin. - sys.print_exception(exc) + # sys.print_exception(exc) if not pa.is_successful(): raise @@ -842,16 +864,32 @@ async def start_login_sequence(): # handle upgrades/downgrade issues try: await version_migration() - except: - pass + except: pass # Maybe insist on the "right" microSD being already installed? try: from pwsave import MicroSD2FA MicroSD2FA.enforce_policy() - except BaseException as exc: - # robustness: keep going! - sys.print_exception(exc) + except: pass + + # apply the hobbling for the spending policy, if appropriate + try: + from ccc import sssp_spending_policy, sssp_word_challenge + + if sp_unlock and sssp_spending_policy('words'): + # challenge them also for first and last seed word! (will reboot on fail) + await sssp_word_challenge() + dis.fullscreen("Startup...") + + if sp_unlock: + # Disable spending policy going forward; user has to re-enable. + pa.hobbled_mode = False + sssp_spending_policy('en', set_value=False) + else: + # normal entry mode, but might have policy enabled, if so enable it now. + pa.hobbled_mode = sssp_spending_policy('en') + + except: pass # implement idle timeout now that we are logged-in IMPT.start_task('idle', idle_logout()) @@ -872,6 +910,13 @@ async def start_login_sequence(): # is early in boot process print("XFP save failed: %s" % exc) + # Version warning before HSM is offered + if version.is_edge and not ckcc.is_simulator(): + await ux_show_story("This firmware version is qualified for use with wallets (such as" + " AnchorWatch, Liana, and Nunchuk) that keep redundant key schemas for recovery" + " independent of COLDCARD. We support the very latest Bitcoin innovations" + " in the Edge Version.", title="Edge Version") + dis.draw_status(xfp=settings.get('xfp')) # If HSM policy file is available, offer to start that, @@ -889,6 +934,14 @@ async def start_login_sequence(): await ar.interact() except: pass + if pa.is_deltamode(): + # pretend Secure Notes & Passwords is disabled + # pretend SeedVault is disabled + try: + settings.remove_key("secnap") + settings.master_set("seedvault", False) + except: pass + if version.has_nfc and settings.get('nfc', 0): # Maybe allow NFC now import nfc @@ -932,7 +985,7 @@ async def restore_main_secret(*a): goto_top_menu() def make_top_menu(): - from flow import VirginSystem, NormalSystem, EmptyWallet, FactoryMenu + from flow import VirginSystem, NormalSystem, EmptyWallet, FactoryMenu, HobbledTopMenu from glob import hsm_active, settings from pincodes import pa @@ -948,7 +1001,9 @@ def make_top_menu(): assert pa.is_successful(), "nonblank but wrong pin" if pa.has_secrets(): - _cls = NormalSystem[:] + # let them do a few things, but not all the things, when "hobbled" + _cls = HobbledTopMenu[:] if pa.hobbled_mode else NormalSystem[:] + if pa.tmp_value or settings.get("hmx", False): active_xfp = settings.get("xfp", 0) sl, sr = ("[", "]") if pa.tmp_value else ("<", ">") @@ -980,7 +1035,7 @@ def goto_top_menu(first_time=False): The file created is sensitive--in terms of privacy--but should not \ compromise your funds directly.''' -PICK_ACCOUNT = '''\n\nPress (1) to enter a non-zero account number.''' +PICK_ACCOUNT = '\n\nPress %s to continue. Press (1) to enter a non-zero account number.' % OK async def dump_summary(*A): @@ -1001,6 +1056,7 @@ async def export_xpub(label, _2, item): chain = chains.current_chain() acct = 0 + slip132 = False # non-slip is default from Oct 2024 # decode menu code => standard derivation mode = item.arg @@ -1014,26 +1070,46 @@ async def export_xpub(label, _2, item): path = "m" addr_fmt = AF_CLASSIC else: - remap = {44:0, 49:1, 84:2}[mode] + remap = {44:0, 49:1, 84:2,86:3}[mode] _, path, addr_fmt = chains.CommonDerivations[remap] - path = path.format(account='{acct}', coin_type=chain.b44_cointype, change=0, idx=0)[:-4] - - # always show SLIP-132 style, because defacto - show_slip132 = (addr_fmt != AF_CLASSIC) + path = path.format(account=acct, coin_type=chain.b44_cointype, + change=0, idx=0)[:-4] while 1: - msg = '''Show QR of the XPUB for path:\n\n%s\n\n''' % path - - if '{acct}' in path: - msg += "Press (1) to select account other than zero. " + msg = 'Show QR of the XPUB for path:\n\n%s\n\n' % path + esc = "" + if path != "m": + esc += "1" + msg += "Press (1) to select account other than %s." % (acct or "zero") + if addr_fmt not in (AF_CLASSIC, AF_P2TR): + esc += "2" + slp_af = addr_fmt + if slip132: + slp_af = AF_CLASSIC + + slp = chain.slip132[slp_af].hint + "pub" + msg += " Press (2) to show %s %s." % ( + slp, "(BIP-32)" if slip132 else "(SLIP-132)" + ) if glob.NFC: - msg += "Press %s to share via NFC. " % (KEY_NFC if version.has_qwerty else "(3)") + if version.has_qwerty: + esc += KEY_NFC + key_hint = KEY_NFC + else: + esc += "3" + key_hint = "(3)" + msg += " Press %s to share via NFC. " % key_hint - ch = await ux_show_story(msg, escape='13') + ch = await ux_show_story(msg, escape=esc) if ch == 'x': return + if ch == "2": + slip132 = not slip132 + continue if ch == '1': acct = await ux_enter_bip32_index('Account Number:') or 0 - path = path.format(acct=acct) + pth_split = path.split("/") + pth_split[-1] = ("%dh" % acct) + path = "/".join(pth_split) continue # assume zero account if not picked @@ -1045,7 +1121,7 @@ async def export_xpub(label, _2, item): # render xpub/ypub/zpub with stash.SensitiveValues() as sv: node = sv.derive_path(path) if path != 'm' else sv.node - xpub = chain.serialize_public(node, addr_fmt) + xpub = chain.serialize_public(node, addr_fmt if slip132 else AF_CLASSIC) from ownership import OWNERSHIP OWNERSHIP.note_wallet_used(addr_fmt, acct) @@ -1055,8 +1131,6 @@ async def export_xpub(label, _2, item): else: await show_qr_code(xpub, False) - break - def electrum_export_story(background=False): # saves memory being in a function @@ -1079,9 +1153,9 @@ async def electrum_skeleton(*a): return rv = [ - MenuItem(addr_fmt_label(af), f=electrum_skeleton_step2, + MenuItem(chains.addr_fmt_label(af), f=electrum_skeleton_step2, arg=(af, account_num)) - for af in [AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH] + for af in chains.SINGLESIG_AF ] the_ux.push(MenuSystem(rv)) @@ -1094,19 +1168,21 @@ def ss_descriptor_export_story(addition="", background="", acct=True): async def ss_descriptor_skeleton(_0, _1, item): # Export of descriptor data (wallet) - int_ext, addition, f_pattern = None, "", "descriptor.txt" - allowed_af = [AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH] + addition, f_pattern = "", "descriptor.txt" + int_ext = direct_way = None + allowed_af = chains.SINGLESIG_AF if item.arg: - int_ext, allowed_af, ll, f_pattern = item.arg + int_ext, allowed_af, ll, f_pattern, direct_way = item.arg addition = " for " + ll - ch = await ux_show_story(ss_descriptor_export_story(addition), escape='1') - account_num = 0 - if ch == '1': - account_num = await ux_enter_bip32_index('Account Number:', unlimited=True) or 0 - elif ch != 'y': - return + if not direct_way: + ch = await ux_show_story(ss_descriptor_export_story(addition), escape='1') + + if ch == '1': + account_num = await ux_enter_bip32_index('Account Number:', unlimited=True) or 0 + elif ch != 'y': + return if int_ext is None: ch = await ux_show_story( @@ -1117,17 +1193,58 @@ async def ss_descriptor_skeleton(_0, _1, item): int_ext = False if ch == "1" else True if len(allowed_af) == 1: - await make_descriptor_wallet_export(allowed_af[0], account_num, - int_ext=int_ext, - fname_pattern=f_pattern) + await make_descriptor_wallet_export(allowed_af[0], account_num, int_ext=int_ext, + fname_pattern=f_pattern, direct_way=direct_way) else: rv = [ - MenuItem(addr_fmt_label(af), f=descriptor_skeleton_step2, - arg=(af, account_num, int_ext, f_pattern)) + MenuItem(chains.addr_fmt_label(af), f=descriptor_skeleton_step2, + arg=(af, account_num, int_ext, f_pattern, direct_way)) for af in allowed_af ] the_ux.push(MenuSystem(rv)) + +async def key_expression_skeleton_step2(_1, _2, item): + # pick a semi-random file name, render and save it. + orig_path = item.arg + await make_key_expression_export(orig_path) + +async def key_expression_skeleton(_0, _1, item): + # Export key expression -> [xfp/d/e/r]xpub + + acct_num = 0 + ch = await ux_show_story("This saves a extended key expression." + + PICK_ACCOUNT + SENSITIVE_NOT_SECRET, escape='1') + if ch == '1': + acct_num = await ux_enter_bip32_index('Account Number:', unlimited=True) or 0 + elif ch != 'y': + return + + todo = [ + ("Segwit P2WPKH", "m/84h/%dh/%dh"), + ("Taproot P2TR", "m/86h/%dh/%dh"), + ("Classic P2PKH", "m/44h/%dh/%dh"), + ("P2SH-Segwit", "m/49h/%dh/%dh"), + ("Multi P2WSH", "m/48h/%dh/%dh/2h"), + ("Multi P2TR", "m/48h/%dh/%dh/3h"), + ("Multi P2SH-P2WSH", "m/48h/%dh/%dh/1h"), + ] + + from address_explorer import KeypathMenu + + async def doit(*a): + return KeypathMenu(ranged=False, done_fn=make_key_expression_export) + + ct = chains.current_chain().b44_cointype + + rv = [ + MenuItem(label, f=key_expression_skeleton_step2, arg=orig_der % (ct, acct_num)) + for label, orig_der in todo + ] + rv += [MenuItem("Custom Path", menu=doit)] + + the_ux.push(MenuSystem(rv)) + async def samourai_post_mix_descriptor_export(*a): name = "POST-MIX" post_mix_acct_num = 2147483646 @@ -1157,9 +1274,9 @@ async def samourai_account_descriptor(name, account_num): async def descriptor_skeleton_step2(_1, _2, item): # pick a semi-random file name, render and save it. - addr_fmt, account_num, int_ext, f_pattern = item.arg + addr_fmt, account_num, int_ext, f_pattern, dw = item.arg await make_descriptor_wallet_export(addr_fmt, account_num, int_ext=int_ext, - fname_pattern=f_pattern) + fname_pattern=f_pattern, direct_way=dw) async def bitcoin_core_skeleton(*A): @@ -1185,9 +1302,9 @@ async def bitcoin_core_skeleton(*A): async def electrum_skeleton_step2(_1, _2, item): # pick a semi-random file name, render and save it. addr_fmt, account_num = item.arg - await make_json_wallet('Electrum wallet', - lambda: generate_electrum_wallet(addr_fmt, account_num), - "new-electrum.json") + await export_contents('Electrum wallet', + lambda: generate_electrum_wallet(addr_fmt, account_num), + "new-electrum.json", is_json=True) async def _generic_export(prompt, label, f_pattern): # like the Multisig export, make a single JSON file with @@ -1199,7 +1316,8 @@ async def _generic_export(prompt, label, f_pattern): elif ch != 'y': return - await make_json_wallet(label, lambda: generate_generic_export(account_num), f_pattern) + await export_contents(label, lambda: generate_generic_export(account_num), + f_pattern, is_json=True) async def generic_skeleton(*A): # like the Multisig export, make a single JSON file with @@ -1234,7 +1352,8 @@ async def wasabi_skeleton(*A): return # no choices to be made, just do it. - await make_json_wallet('Wasabi wallet', lambda: generate_wasabi_wallet(), 'new-wasabi.json') + await export_contents('Wasabi wallet', lambda: generate_wasabi_wallet(), + 'new-wasabi.json', is_json=True) async def unchained_capital_export(*a): # they were using our airgapped export, and the BIP-45 path from that @@ -1251,9 +1370,8 @@ async def unchained_capital_export(*a): xfp = xfp2str(settings.get('xfp', 0)) fname = 'unchained-%s.json' % xfp - await make_json_wallet('Unchained', - lambda: generate_unchained_export(account_num), - fname) + await export_contents('Unchained', lambda: generate_unchained_export(account_num), + fname, is_json=True) async def backup_everything(*A): @@ -1275,17 +1393,13 @@ async def verify_backup(*A): # do a limited CRC-check over encrypted file await backups.verify_backup_file(fn) -async def import_extended_key_as_secret(extended_key, ephemeral, meta=None): +async def import_extended_key_as_secret(extended_key, ephemeral, origin=None): try: import seed if ephemeral: - await seed.set_ephemeral_seed_extended_key(extended_key, meta=meta) + await seed.set_ephemeral_seed_extended_key(extended_key, origin=origin) else: await seed.set_seed_extended_key(extended_key) - except ValueError: - msg = ("Sorry, wasn't able to find a valid extended private key to import. " - "It should be at the start of a line, and probably starts with 'xprv'.") - await ux_show_story(title="FAILED", msg=msg) except Exception as e: await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) @@ -1331,7 +1445,7 @@ def contains_xprv(fname): else: # only get here if NFC was not chosen # pick a likely-looking file. - fn = await file_picker(suffix='txt', min_size=50, max_size=2000, taster=contains_xprv, + fn = await file_picker(suffix='.txt', min_size=50, max_size=2000, taster=contains_xprv, none_msg="Must contain " + label + ".", **choice) if not fn: return @@ -1343,57 +1457,91 @@ def contains_xprv(fname): extended_key = ln break - await import_extended_key_as_secret(extended_key, ephemeral, meta='Imported XPRV') + await import_extended_key_as_secret(extended_key, ephemeral, origin='Imported XPRV') # not reached; will do reset. -EMPTY_RESTORE_MSG = '''\ +async def need_clear_seed(*a): + await ux_show_story('''\ You must clear the wallet seed before restoring a backup because it replaces \ the seed value and the old seed would be lost.\n\n\ -Visit the advanced menu and choose 'Destroy Seed'.''' - -async def restore_temporary(*A): +Visit the advanced menu and choose 'Destroy Seed'.''') +async def restore_backup(a, b, item): + # normal word based imports (tmp or master depending on item.arg) fn = await file_picker(suffix=".7z") - if fn: import backups - await backups.restore_complete(fn, temporary=True) - -async def restore_everything(*A): + await backups.restore_complete(fn, item.arg, True) - if not pa.is_secret_blank(): - await ux_show_story(EMPTY_RESTORE_MSG) +async def restore_backup_dev(*a): + # used ONLY for Restore Bkup in I Am Developer + fn = await file_picker(suffix=[".7z", ".txt"]) + if fn: + words = False if fn[-3:] == ".7z" else None + import backups + await backups.restore_complete(fn, not pa.is_secret_blank(), words) + +async def bkpw_override(*A): + # allows user to: + # 1.) manually set bkpw + # 2.) remove existing bkpw setting + # 3.) view current active bkpw + # - some truncation of titles here on Mk4, + # which is okay because re-using strings to save space. + from backups import bkpw_min_len + + if pa.is_secret_blank(): return - # restore everything, using a password, from single encrypted 7z file - fn = await file_picker(suffix='.7z') + wipe_if_deltamode() - if fn: - import backups - await backups.restore_complete(fn) + while True: + pwd = settings.get("bkpw", None) + + msg = ("Password used to encrypt COLDCARD backup files." + "\n\nPress (0) to change backup password") + esc = "0" + if pwd: + esc += "12" + msg += ", (1) to forget current password, (2) to show current active backup password." + else: + msg += "." -async def restore_everything_cleartext(*A): - # Asssume no password on backup file; devs and crazy people only + ch = await ux_show_story(title="BKPW Override", msg=msg, escape=esc) + if ch == "x": return + elif ch == "1": + if await ux_confirm("Delete current stored password?"): + settings.remove_key("bkpw") + settings.save() + await ux_dramatic_pause("Deleted.", 2) + + elif ch == "2": + if await ux_confirm('The next screen will show current active backup password.' + '\n\nAnyone with knowledge of the password will ' + 'be able to decrypt your backups.'): + await ux_show_story(pwd, title="Your Backup Password") + + elif ch == "0": + if version.has_qwerty: + from notes import get_a_password + npwd = await get_a_password(pwd, min_len=bkpw_min_len) + else: + npwd = await ux_input_text(pwd, prompt="Your Backup Password", + min_len=bkpw_min_len, max_len=128) - if not pa.is_secret_blank(): - await ux_show_story(EMPTY_RESTORE_MSG) - return + if (npwd is None) or (npwd == pwd): continue - # restore everything, using NO password, from single text file, like would be wrapped in 7z - fn = await file_picker(suffix='.txt') + settings.set('bkpw', npwd) + settings.save() + await ux_dramatic_pause("Saved.", 2) - if fn: - import backups - prob = await backups.restore_complete_doit(fn, []) - if prob: - await ux_show_story(prob, title='FAILED') async def wipe_filesystem(*A): if not await ux_confirm('''\ Erase internal filesystem and rebuild it. Resets contents of internal flash area \ used for settings, address search cache, and HSM config file. Does not affect funds, \ -or seed words but will reset settings used with other BIP-39 passphrases. \ -Does not affect MicroSD card, if any.'''): +or seed words but will reset settings used with other temporary seeds & BIP-39 passphrases. \ +Does not affect MicroSD card, if any.''', confirm_key="4"): return from files import wipe_flash_filesystem @@ -1416,57 +1564,64 @@ async def wipe_sd_card(*A): wipe_microsd_card() -async def qr_share_file(*A): +async def qr_share_file(_1, _2, item): # Pick file from SD card and share as (BB)Qr from files import CardSlot, CardMissingError, needs_microsd from export import export_by_qr + force_bbqr = item.arg + def is_suitable(fname): f = fname.lower() return f.endswith('.psbt') or f.endswith('.txn') \ or f.endswith('.txt') or f.endswith(".json") or fname.endswith(".sig") - while 1: - txid = None - fn = await file_picker(min_size=10, max_size=MAX_TXN_LEN_MK4, taster=is_suitable) - if not fn: return + try: + while 1: + txid = None + fn = await file_picker(min_size=10, max_size=MAX_TXN_LEN, taster=is_suitable) + if not fn: return - basename = fn.split('/')[-1] - ext = fn.split('.')[-1].lower() + basename = fn.split('/')[-1] + ext = fn.split('.')[-1].lower() - try: - with CardSlot() as card: - with open(fn, 'rb') as fp: - data = fp.read() + try: + with CardSlot() as card: + with open(fn, 'rb') as fp: + data = fp.read() - except CardMissingError: - await needs_microsd() - return + except CardMissingError: + await needs_microsd() + return - if ext == "txn": - tc = "T" - txid = txid_from_fname(basename) - if data[2:8] == b'000000': - # it's a txn, and we wrote as hex + if ext == "txn": + tc = "T" + txid = txid_from_fname(basename) + if data[2:8] == b'000000': + # it's a txn, and we wrote as hex + data = data.decode() + else: + assert data[2:8] == bytes(6) + data = b2a_hex(data).decode() + elif data[0:5] == b'psbt\xff': + tc = "P" + elif data[0:6] in (b'cHNidP', b'707362'): + tc = "U" + data = data.decode().strip() + elif ext in ('txt', 'json', 'sig'): + tc = "U" + if ext == "json": + tc = "J" data = data.decode() else: - assert data[2:8] == bytes(6) - data = b2a_hex(data).decode() - elif data[0:5] == b'psbt\xff': - tc = "P" - elif data[0:6] in (b'cHNidP', b'707362'): - tc = "U" - data = data.decode().strip() - elif ext in ('txt', 'json', 'sig'): - tc = "U" - if ext == "json": - tc = "J" - data = data.decode() - else: - raise ValueError(ext) - - await export_by_qr(data, txid, tc) + raise ValueError(ext) + await export_by_qr(data, txid, tc, force_bbqr=force_bbqr) + except Exception as e: + await ux_show_story( + title="ERROR", + msg="Failed to share file via QR.\n\n%s\n%s" % (e, problem_file_line(e)) + ) async def nfc_share_file(*A): # Share txt, txn and PSBT files over NFC. @@ -1576,41 +1731,58 @@ async def list_files(*A): from pincodes import pa digest = chk.digest() - basename = fn.rsplit('/', 1)[-1] - msg_base = 'SHA256(%s)\n\n%s\n\nPress ' % (basename, B2A(digest)) - escape = "6" + path, basename = fn.rsplit('/', 1) + msg_base = 'SHA256(%s)\n\n' + B2A(digest) + '\n\nPress (1) to rename file, ' + escape = "61" if pa.has_secrets(): - msg_sign = '(4) to sign file digest and export detached signature, ' + msg_base += '(4) to sign file digest and export detached signature, ' escape += "4" - else: - msg_sign = "" - msg_delete = '(6) to delete.' - msg = msg_base + msg_sign + msg_delete + msg_base += '(6) to delete.' + while True: - ch = await ux_show_story(msg, escape=escape) + ch = await ux_show_story(msg_base % basename, escape=escape) if ch == "x": break - if ch in '46': + if ch in '461': with CardSlot() as card: if ch == '6': card.securely_blank_file(fn) break + elif ch == '1': + new_basename = await ux_input_text(basename, max_len=32, min_len=3) + if new_basename: + try: + # prohibit both slashes and space in filenames + for s in "\/ ": + assert s not in new_basename, "illegal char" + uos.rename(path + "/" + basename, path + "/" + new_basename) + basename = new_basename + except Exception as e: + await ux_show_story("Failed to rename the file. " + str(e), + title="Failure") else: - from auth import write_sig_file + from msgsign import write_sig_file sig_nice = write_sig_file([(digest, fn)]) await ux_show_story("Signature file %s written." % sig_nice) - msg = msg_base + msg_delete return async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, choices=None, none_msg=None, force_vdisk=False, slot_b=None, - allow_batch_sign=False, ux=True): + allow_batch=False, ux=True): # present a menu w/ a list of files... to be read # - optionally, enforce a max size, and provide a "tasting" function - # - if msg==None, don't prompt, just do the search and return list + # - if (not ux), don't prompt, just do the search and return list # - if choices is provided; skip search process # - escape: allow these chars to skip picking process # - slot_b: None=>pick slot w/ card in it, or A if both. + # - allow_batch: adds an "all of the above" choice: ("menu label", menu_handler) + # - suffix argument MUST contain the dot (.txt), if list of suffixes, all MUST + + if suffix: + # actually make it a list of "suffixes" + if not isinstance(suffix, list): + suffix = [suffix] + assert all(s[0] == '.' for s in suffix) if choices is None: choices = [] @@ -1624,13 +1796,13 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, # ignore subdirs continue - if suffix: - if not isinstance(suffix, list): - suffix = [suffix] - if not any([fn.lower().endswith(s) for s in suffix]): - continue + if fn[0] == '.': + # unix-style hidden files + continue - if fn[0] == '.': continue + if suffix and not any(fn.lower().endswith(s) for s in suffix): + # wrong suffix, skip + continue full_fname = path + '/' + fn @@ -1654,7 +1826,7 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, label = fn while label in sofar: # just the file name isn't unique enough sometimes? - # - shouldn't happen anymore now that we dno't support internal FS + # - shouldn't happen anymore now that we don't support internal FS # - unless we do muliple paths label += path.split('/')[-1] + '/' + fn @@ -1676,7 +1848,7 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, if none_msg: msg += none_msg if suffix: - msg += '\n\nThe filename must end in "%s". ' % suffix + msg += '\n\nThe filename must end in: ' + ' OR '.join(suffix) msg += '\n\nMaybe insert (another) SD card and try again?' @@ -1691,10 +1863,10 @@ async def clicked(_1,_2,item): choices.sort() items = [MenuItem(label, f=clicked, arg=(path, fn)) for label, path, fn in choices] - if allow_batch_sign and len(choices) > 1: - # we know that each choices member is psbt as allow_batch_sign is only True - # in Ready To Sign - items.insert(0, MenuItem("[Sign All]", f=batch_sign, arg=choices)) + if allow_batch and len(choices) > 1: + # Allow an "all" selection + label, funct = allow_batch + items.insert(0, MenuItem(label, f=funct, arg=choices)) menu = MenuSystem(items) the_ux.push(menu) @@ -1731,7 +1903,7 @@ async def bless_flash(*a): pa.greenlight_firmware() # redraw our screen - dis.show() + dis.busy_bar(False) # includes dis.show() def is_psbt(filename): @@ -1756,7 +1928,7 @@ async def _batch_sign(choices=None): return assert isinstance(picked, dict) - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt, **picked) if not choices: @@ -1782,20 +1954,22 @@ async def batch_sign(_1, _2, item): import sys await ux_show_story("FAILURE: batch sign failed\n\n" + problem_file_line(e)) - -async def ready2sign(*a): - # Top menu choice of top menu! Signing! - # - check if any signable in SD card, if so do it +async def _ready2sign(intro="", probe=True, miniscript_wallet=None): + # - if probe=True -> check if any signable in SD card (A slot on Q), if so do it + # - if probe=False -> offer all enabled import options via UX # - if no card, check virtual disk for PSBT - # - if still nothing, then talk about USB connection from pincodes import pa from glob import NFC opt = {} + choices = [] + sb_only = False - # just check if we have candidates, no UI - choices = await file_picker(suffix='psbt', min_size=50, ux=False, - max_size=MAX_TXN_LEN, taster=is_psbt) + if probe: + # just check if we have candidates, no UI + sb_only = True + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, + max_size=MAX_TXN_LEN, taster=is_psbt) if pa.tmp_value: title = '[%s]' % xfp2str(settings.get('xfp')) @@ -1803,25 +1977,17 @@ async def ready2sign(*a): title = None if not choices: - msg = '''Coldcard is ready to sign spending transactions! - -Put the proposed transaction onto MicroSD card \ -in PSBT format (Partially Signed Bitcoin Transaction) \ -or upload a transaction to be signed \ -from your desktop wallet software or command line tools.\n\n''' - - footnotes = ("\n\nYou will always be prompted to confirm the details " - "before any signature is performed.") - # if we have only one SD card inserted, at this point, we know no PSBTs on them # as above file_picker already checked # if we have both inserted, A was already checked - so only care about B - picked = await import_export_prompt("PSBT", is_import=True, intro=msg, - footnotes=footnotes, slot_b_only=True, + footnotes = ("You will always be prompted to confirm the details " + "before any signature is performed.") + picked = await import_export_prompt("PSBT", is_import=True, intro=intro, + footnotes=footnotes, slot_b_only=sb_only, title=title) if isinstance(picked, dict): opt = picked # reset options to what was chosen by user - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt, **opt) if not choices: @@ -1830,9 +1996,9 @@ async def ready2sign(*a): return else: if NFC and picked == KEY_NFC: - await NFC.start_psbt_rx() + await NFC.start_psbt_rx(miniscript_wallet) if picked == KEY_QR: - await _scan_any_qr() + await _scan_any_qr(miniscript_wallet=miniscript_wallet) return @@ -1842,16 +2008,29 @@ async def ready2sign(*a): input_psbt = path + '/' + fn else: # multiples - ask which, and offer batch to sign them all - input_psbt = await file_picker(choices=choices, allow_batch_sign=True) + input_psbt = await file_picker(choices=choices, allow_batch=("[Sign All]", batch_sign)) if not input_psbt: return # start the process from auth import sign_psbt_file - + opt["miniscript_wallet"] = miniscript_wallet await sign_psbt_file(input_psbt, **opt) +async def ready2sign(*a): + # Top menu choice of top menu! Signing! + # - check if any signable in SD card, if so do it + # - if no card, check virtual disk for PSBT + + await _ready2sign('''Coldcard is ready to sign spending transactions! + +Put the proposed transaction onto MicroSD card \ +in PSBT format (Partially Signed Bitcoin Transaction) \ +or upload a transaction to be signed \ +from your desktop wallet software or command line tools.''') + + async def sign_message_on_sd(*a): # Menu item: choose a file to be signed (as a short text message) # @@ -1863,10 +2042,11 @@ def is_signable(filename): # min 1 line max 3 lines return 1 <= len(lines) <= 3 - fn = await file_picker(suffix='txt', min_size=2, max_size=500, taster=is_signable, - none_msg=('Must be one line of text, optionally ' + fn = await file_picker(suffix=['.txt', ".json"], min_size=2, max_size=500, taster=is_signable, + none_msg=('Must be txt file with one msg line, optionally ' 'followed by a subkey derivation path on a second line ' - 'and/or address format on third line.')) + 'and/or address format on third line. JSON msg signing ' + 'format also supported')) if not fn: return @@ -1891,7 +2071,7 @@ def is_sig_file(filename): return # start the process - from auth import verify_txt_sig_file + from msgsign import verify_txt_sig_file await verify_txt_sig_file(fn) @@ -1938,7 +2118,7 @@ async def incorrect_pin(): while 1: lll.reset() lll.subtitle = "New " + title - pin = await lll.get_new_pin(title, allow_clear=False) + pin = await lll.get_new_pin() if pin is None: return await ux_aborted() @@ -2174,14 +2354,11 @@ async def change_virtdisk_enable(enable): async def change_seed_vault(is_enabled): # user has changed seed vault enable/disable flag - from glob import settings - if (not is_enabled) and settings.master_get('seeds'): # problem: they still have some seeds... also this path blocks # disable from within a tmp seed settings.set('seedvault', 1) # restore it await ux_show_story("Please remove all seeds from the vault before disabling.") - return goto_top_menu() @@ -2234,10 +2411,11 @@ async def scan_any_qr(menu, label, item): expect_secret, tmp = item.arg await _scan_any_qr(expect_secret, tmp) -async def _scan_any_qr(expect_secret=False, tmp=False): +async def _scan_any_qr(expect_secret=False, tmp=False, miniscript_wallet=None): from ux_q1 import QRScannerInteraction x = QRScannerInteraction() - await x.scan_anything(expect_secret=expect_secret, tmp=tmp) + await x.scan_anything(expect_secret=expect_secret, tmp=tmp, + miniscript_wallet=miniscript_wallet) PUSHTX_SUPPLIERS = [ @@ -2247,6 +2425,21 @@ async def _scan_any_qr(expect_secret=False, tmp=False): ('mempool.space', 'https://mempool.space/pushtx#'), ] +async def feature_requires_nfc(): + # prompt them that it's need (iff not already enabled) + # - return F if they decline + if settings.get('nfc'): + return True + + # force on NFC, so it works... but they can still turn it off later, etc. + if not await ux_confirm("This feature requires NFC to be enabled. %s to enable." % OK): + return False + + settings.set("nfc", 1) + await change_nfc_enable(1) + + return True + async def pushtx_setup_menu(*a): # let them pick a URL from menu to enable "pushtx" feature, and provide # some background, and even let them enter a custom URL. @@ -2265,12 +2458,9 @@ async def pushtx_setup_menu(*a): if ch != "y": return - if not settings.get('nfc'): - # force on NFC, so it works... but they can still turn it off later, etc. - if not await ux_confirm("This feature requires NFC to be enabled. %s to enable." % OK): - return - settings.set("nfc", 1) - await change_nfc_enable(1) + if not await feature_requires_nfc(): + # they don't want to proceed + return async def doit(menu, picked, xx_self): # using stock values, or Disable diff --git a/shared/address_explorer.py b/shared/address_explorer.py index c672e062b..6032c24ff 100644 --- a/shared/address_explorer.py +++ b/shared/address_explorer.py @@ -7,32 +7,23 @@ import chains, stash, version from ux import ux_show_story, the_ux, ux_enter_bip32_index from ux import export_prompt_builder, import_export_prompt_decode -from menu import MenuSystem, MenuItem -from public_constants import AFC_BECH32, AFC_BECH32M, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH -from multisig import MultisigWallet +from menu import MenuSystem, MenuItem, ToggleMenuItem +from public_constants import AFC_BECH32, AFC_BECH32M, AF_P2WPKH, AF_P2TR, AF_CLASSIC +from wallet import MiniScriptWallet from uasyncio import sleep_ms from uhashlib import sha256 -from ubinascii import hexlify as b2a_hex from glob import settings -from auth import write_sig_file -from utils import addr_fmt_label, censor_address +from msgsign import write_sig_file from charcodes import KEY_QR, KEY_NFC, KEY_PAGE_UP, KEY_PAGE_DOWN, KEY_HOME, KEY_LEFT, KEY_RIGHT from charcodes import KEY_CANCEL +from utils import show_single_address, problem_file_line, truncate_address -def truncate_address(addr): - # Truncates address to width of screen, replacing middle chars - if not version.has_qwerty: - # - 16 chars screen width - # - but 2 lost at left (menu arrow, corner arrow) - # - want to show not truncated on right side - return addr[0:6] + '⋯' + addr[-6:] - else: - # tons of space on Q1 - return addr[0:12] + '⋯' + addr[-12:] class KeypathMenu(MenuSystem): - def __init__(self, path=None, nl=0): + def __init__(self, path=None, nl=0, ranged=True, done_fn=None): self.prefix = None + self.done_fn = done_fn + self.ranged = ranged if path is None: # Top level menu; useful shortcuts, and special case just "m" @@ -41,10 +32,14 @@ def __init__(self, path=None, nl=0): MenuItem("m/44h/⋯", f=self.deeper), MenuItem("m/49h/⋯", f=self.deeper), MenuItem("m/84h/⋯", f=self.deeper), - MenuItem("m/0/{idx}", menu=self.done), - MenuItem("m/{idx}", menu=self.done), + MenuItem("m/86h/⋯", f=self.deeper), MenuItem("m", f=self.done), ] + if self.ranged: + items += [ + MenuItem("m/0/{idx}", menu=self.done), + MenuItem("m/{idx}", menu=self.done), + ] else: # drill down one layer: (nl) is the current leaf # - hardened choice first @@ -54,11 +49,14 @@ def __init__(self, path=None, nl=0): MenuItem(p+"/⋯", menu=self.deeper), MenuItem(p+"h", menu=self.done), MenuItem(p, menu=self.done), - MenuItem(p+"h/0/{idx}", menu=self.done), - MenuItem(p+"/0/{idx}", menu=self.done), #useful shortcut? - MenuItem(p+"h/{idx}", menu=self.done), - MenuItem(p+"/{idx}", menu=self.done), ] + if self.ranged: + items += [ + MenuItem(p + "h/0/{idx}", menu=self.done), + MenuItem(p + "/0/{idx}", menu=self.done), # useful shortcut? + MenuItem(p + "h/{idx}", menu=self.done), + MenuItem(p + "/{idx}", menu=self.done), + ] # simple consistent truncation when needed max_wide = max(len(mi.label) for mi in items) @@ -67,7 +65,7 @@ def __init__(self, path=None, nl=0): pl = p[0:p.rfind('/')].rfind('/') else: self.prefix = p # displayed on mk4 only - pl = len(p)-2 + pl = len(p)-2 for mi in items: mi.arg = mi.label mi.label = '⋯'+mi.label[pl:] @@ -96,9 +94,12 @@ async def done(self, _1, menu_idx, item): if isinstance(top, KeypathMenu): the_ux.pop() continue - assert isinstance(top, AddressListMenu) + # assert isinstance(top, AddressListMenu), type(top) break + if self.done_fn: + return await self.done_fn(final_path) + return PickAddrFmtMenu(final_path, top) async def deeper(self, _1, _2, item): @@ -106,15 +107,14 @@ async def deeper(self, _1, _2, item): assert val.endswith('/⋯') cpath = val[:-2] nl = await ux_enter_bip32_index('%s/' % cpath, unlimited=True) - return KeypathMenu(cpath, nl) + return KeypathMenu(cpath, nl, ranged=self.ranged, done_fn=self.done_fn) class PickAddrFmtMenu(MenuSystem): def __init__(self, path, parent): self.parent = parent items = [ - MenuItem(addr_fmt_label(AF_CLASSIC), f=self.done, arg=(path, AF_CLASSIC)), - MenuItem(addr_fmt_label(AF_P2WPKH), f=self.done, arg=(path, AF_P2WPKH)), - MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=self.done, arg=(path, AF_P2WPKH_P2SH)), + MenuItem(chains.addr_fmt_label(af), f=self.done, arg=(path, af)) + for af in chains.SINGLESIG_AF ] super().__init__(items) if path.startswith("m/84h"): @@ -179,8 +179,7 @@ async def render(self): # Create list of choices (address_index_0, path, addr_fmt) choices = [] for name, path, addr_fmt in chains.CommonDerivations: - if '{coin_type}' in path: - path = path.replace('{coin_type}', str(chain.b44_cointype)) + path = path.replace('{coin_type}', str(chain.b44_cointype)) if self.account_num != 0 and '{account}' not in path: # skip derivations that are not affected by account number @@ -189,7 +188,7 @@ async def render(self): deriv = path.format(account=self.account_num, change=0, idx=self.start) node = sv.derive_path(deriv, register=False) address = chain.address(node, addr_fmt) - choices.append( (truncate_address(address), path, addr_fmt) ) + choices.append((truncate_address(address), path, addr_fmt)) dis.progress_sofar(len(choices), len(chains.CommonDerivations)) @@ -199,7 +198,7 @@ async def render(self): indent = ' ↳ ' if version.has_qwerty else '↳' for i, (address, path, addr_fmt) in enumerate(choices): axi = address[-4:] # last 4 address characters - items.append(MenuItem(addr_fmt_label(addr_fmt), f=self.pick_single, + items.append(MenuItem(chains.addr_fmt_label(addr_fmt), f=self.pick_single, arg=(path, addr_fmt, axi))) items.append(MenuItem(indent+address, f=self.pick_single, arg=(path, addr_fmt, axi))) @@ -210,10 +209,15 @@ async def render(self): items.append(MenuItem("Account Number", f=self.change_account)) items.append(MenuItem("Custom Path", menu=self.make_custom)) - # if they have MS wallets, add those next - for ms in MultisigWallet.iter_wallets(): - if not ms.addr_fmt: continue - items.append(MenuItem(ms.name, f=self.pick_multisig, arg=ms)) + # if they have miniscript wallets, add those next + if MiniScriptWallet.exists(): + items.append(ToggleMenuItem('MS Scripts/Derivs', 'aemscsv', + ['Default Off', 'Enable'], story=( + "Enable this option to add script(s) and derivations to the CSV export" + " of Multisig/Miniscript wallets. Default is to only export addresses."))) + + for msc in MiniScriptWallet.iter_wallets(): + items.append(MenuItem(msc.name, f=self.pick_miniscript, arg=msc)) else: items.append(MenuItem("Account: %d" % self.account_num, f=self.change_account)) @@ -245,10 +249,10 @@ async def pick_single(self, _1, _2, item): settings.put('axi', axi) # update last clicked address await self.show_n_addresses(path, addr_fmt, None) - async def pick_multisig(self, _1, _2, item): - ms_wallet = item.arg - settings.put('axi', item.label) # update last clicked address - await self.show_n_addresses(None, None, ms_wallet) + async def pick_miniscript(self, _1, _2, item): + msc_wallet = item.arg + settings.put('axi', item.label) # update last clicked address + await self.show_n_addresses(None, msc_wallet.addr_fmt, msc_wallet) async def make_custom(self, *a): # picking a custom derivation path: makes a tree of menus, with chance @@ -274,13 +278,13 @@ async def got_custom_path(self, path, addr_fmt): async def show_n_addresses(self, path, addr_fmt, ms_wallet, start=0, n=10, allow_change=True): # Displays n addresses by replacing {idx} in path format. # - also for other {account} numbers - # - or multisig case + # - or miniscript case from glob import dis, NFC from wallet import MAX_BIP32_IDX start = self.start - def make_msg(change=0): + def make_msg(change=0, start=start, n=n): # Build message and CTA about export, plus the actual addresses. if n: msg = "Addresses %d⋯%d:\n\n" % (start, min(start + n - 1, MAX_BIP32_IDX)) @@ -293,22 +297,7 @@ def make_msg(change=0): dis.fullscreen('Wait...') if ms_wallet: - # IMPORTANT safety feature: never show complete address - # but show enough they can verify addrs shown elsewhere. - # - makes a redeem script - # - converts into addr - # - assumes 0/0 is first address. - for idx, addr, paths, script in ms_wallet.yield_addresses(start, n, change): - addrs.append(censor_address(addr)) - - if idx == 0 and ms_wallet.N <= 4: - msg += '\n'.join(paths) + '\n =>\n' - else: - msg += '⋯/%d/%d =>\n' % (change, idx) - - msg += truncate_address(addr) + '\n\n' - dis.progress_sofar(idx-start+1, n) - + msg, addrs = ms_wallet.make_addresses_msg(msg, start, n, change) else: # single-signer wallets from wallet import MasterSingleSigWallet @@ -319,16 +308,17 @@ def make_msg(change=0): for idx, addr, deriv in main.yield_addresses(start, n, change if allow_change else None): addrs.append(addr) - msg += "%s =>\n%s\n\n" % (deriv, addr) + msg += "%s =>\n%s\n\n" % (deriv, show_single_address(addr)) dis.progress_sofar(idx-start+1, n or 1) # export options k0 = 'to show change addresses' if allow_change and change == 0 else None - export_msg, escape = export_prompt_builder('address summary file', - no_qr=bool(ms_wallet), key0=k0, - force_prompt=True) + export_msg, escape = export_prompt_builder( + 'address summary file', + key0=k0, force_prompt=True + ) if version.has_qwerty: - escape += KEY_LEFT+KEY_RIGHT+KEY_HOME+KEY_PAGE_UP+KEY_PAGE_DOWN + escape += KEY_LEFT+KEY_RIGHT+KEY_HOME+KEY_PAGE_UP+KEY_PAGE_DOWN+KEY_QR else: escape += "79" @@ -339,11 +329,15 @@ def make_msg(change=0): msg += '\n\n' if n: msg += "Press RIGHT to see next group, LEFT to go back. X to quit." + else: + if addr_fmt != AF_P2TR: + escape += "0" + msg += " Press (0) to sign message with this key." return msg, addrs, escape - msg, addrs, escape = make_msg() change = 0 + msg, addrs, escape = make_msg(change, start) while 1: ch = await ux_show_story(msg, escape=escape) @@ -364,14 +358,10 @@ def make_msg(change=0): # continue on same screen in case they want to write to multiple cards elif choice == KEY_QR: - # switch into a mode that shows them as QR codes - if ms_wallet: - # requires not multisig - continue - from ux import show_qr_codes + addr_fmt = addr_fmt or ms_wallet.addr_fmt is_alnum = bool(addr_fmt & (AFC_BECH32 | AFC_BECH32M)) - await show_qr_codes(addrs, is_alnum, start) + await show_qr_codes(addrs, is_alnum, start, is_addrs=True) continue @@ -384,8 +374,15 @@ def make_msg(change=0): continue - elif choice == '0' and allow_change: - change = 1 + elif choice == '0': + if allow_change: + change = 1 + else: + # only custom path sets allow_change to False + # msg sign + from msgsign import sign_with_own_address + await sign_with_own_address(path, addr_fmt) + elif n is None: # makes no sense to do any of below, showing just single address continue @@ -408,7 +405,7 @@ def make_msg(change=0): else: continue # 3 in non-NFC mode - msg, addrs, escape = make_msg(change) + msg, addrs, escape = make_msg(change, start) def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0, change=0): # Produce CSV file contents as a generator @@ -416,31 +413,14 @@ def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0, cha from ownership import OWNERSHIP if ms_wallet: - # For multisig, include redeem script and derivation for each signer - yield '"' + '","'.join(['Index', 'Payment Address', 'Redeem Script'] - + ['Derivation (%d of %d)' % (i+1, ms_wallet.N) for i in range(ms_wallet.N)] - ) + '"\n' - - if (start == 0) and (n > 100) and change in (0, 1): - saver = OWNERSHIP.saver(ms_wallet, change, start) - else: - saver = None - - for (idx, addr, derivs, script) in ms_wallet.yield_addresses(start, n, change_idx=change): - if saver: - saver(addr) + # saver will be None if we don't think it worth saving these addresses + saver = OWNERSHIP.saver(ms_wallet, change, start, n) - # policy choice: never provide a complete multisig address to user. - addr = censor_address(addr) - - ln = '%d,"%s","%s","' % (idx, addr, b2a_hex(script).decode()) - ln += '","'.join(derivs) - ln += '"\n' - - yield ln + for line in ms_wallet.generate_address_csv(start, n, change, saver=saver): + yield line if saver: - saver(None) # close file + saver(None, 0) # close cache file return @@ -448,26 +428,24 @@ def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0, cha from wallet import MasterSingleSigWallet main = MasterSingleSigWallet(addr_fmt, path, account_num) - if n and (start == 0) and (n > 100) and change in (0, 1): - saver = OWNERSHIP.saver(main, change, start) - else: - saver = None + # saver will be None if we don't think it worth saving these addresses + saver = OWNERSHIP.saver(main, change, start, n) yield '"Index","Payment Address","Derivation"\n' - for (idx, addr, deriv) in main.yield_addresses(start, n, change_idx=change): + for (idx, addr, deriv) in main.yield_addresses(start, n, change): if saver: - saver(addr) + saver(addr, idx) yield '%d,"%s","%s"\n' % (idx, addr, deriv) if saver: - saver(None) # close + saver(None, 0) # close cache file async def make_address_summary_file(path, addr_fmt, ms_wallet, account_num, start=0, count=250, change=0, **save_opts): # write addresses into a text file on the MicroSD/VirtDisk - from glob import dis + from glob import dis, settings from files import CardSlot, CardMissingError, needs_microsd # simple: always set number of addresses. @@ -479,7 +457,6 @@ async def make_address_summary_file(path, addr_fmt, ms_wallet, account_num, # generator function body = generate_address_csv(path, addr_fmt, ms_wallet, account_num, count, start=start, change=change) - # pick filename and write try: with CardSlot(**save_opts) as card: @@ -490,28 +467,32 @@ async def make_address_summary_file(path, addr_fmt, ms_wallet, account_num, for idx, part in enumerate(body): ep = part.encode() fd.write(ep) - if not ms_wallet: - h.update(ep) - + h.update(ep) dis.progress_sofar(idx, count or 1) sig_nice = None - if not ms_wallet: + if ms_wallet: + # sign with my key at the same path as first address of export + addr_fmt = AF_CLASSIC + derive = ms_wallet.get_my_deriv() + derive += "/%d/%d" % (change, start) + else: + addr_fmt = AF_CLASSIC if addr_fmt == AF_P2TR else addr_fmt derive = path.format(account=account_num, change=change, idx=start) # first addr - sig_nice = write_sig_file([(h.digest(), fname)], derive, addr_fmt) + + sig_nice = write_sig_file([(h.digest(), fname)], derive, addr_fmt) + + + msg = '''Address summary file written:\n\n%s''' % nice + if sig_nice: + msg += "\n\nAddress signature file written:\n\n%s" % sig_nice + await ux_show_story(msg) except CardMissingError: await needs_microsd() - return except Exception as e: - from utils import problem_file_line - await ux_show_story('Failed to write!\n\n\n%s\n%s' % (e, problem_file_line(e))) - return + await ux_show_story('Failed to write!\n\n%s\n%s' % (e, problem_file_line(e))) - msg = '''Address summary file written:\n\n%s''' % nice - if sig_nice: - msg += "\n\nAddress signature file written:\n\n%s" % sig_nice - await ux_show_story(msg) async def address_explore(*a): # explore addresses based on derivation path chosen diff --git a/shared/auth.py b/shared/auth.py index 8bf24ad57..cc66df3a2 100644 --- a/shared/auth.py +++ b/shared/auth.py @@ -3,25 +3,24 @@ # Operations that require user authorization, like our core features: signing messages # and signing bitcoin transactions. # -import stash, ure, ux, chains, sys, gc, uio, version, ngu, ujson +import stash, ure, chains, sys, gc, uio, version, ngu, ujson from ubinascii import b2a_base64, a2b_base64 from ubinascii import hexlify as b2a_hex from ubinascii import unhexlify as a2b_hex from uhashlib import sha256 -from public_constants import MSG_SIGNING_MAX_LENGTH, SUPPORTED_ADDR_FORMATS -from public_constants import AFC_SCRIPT, AF_CLASSIC, AFC_BECH32, AF_P2WPKH, AF_P2WPKH_P2SH -from public_constants import STXN_FLAGS_MASK, STXN_FINALIZE, STXN_VISUALIZE, STXN_SIGNED +from public_constants import AFC_SCRIPT, AF_CLASSIC, AFC_BECH32, SUPPORTED_ADDR_FORMATS, AF_P2TR +from public_constants import STXN_FINALIZE, STXN_VISUALIZE, STXN_SIGNED from sffile import SFFile -from ux import ux_aborted, ux_show_story, abort_and_goto, ux_dramatic_pause, ux_clear_keys -from ux import show_qr_code, OK, X +from ux import ux_show_story, abort_and_goto, ux_dramatic_pause, ux_clear_keys, ux_confirm +from ux import show_qr_code, OK, X, abort_and_push, AbortInteraction from usb import CCBusyError -from utils import HexWriter, xfp2str, problem_file_line, cleanup_deriv_path -from utils import B2A, parse_addr_fmt_str, to_ascii_printable +from utils import HexWriter, xfp2str, problem_file_line, cleanup_deriv_path, B2A, show_single_address from psbt import psbtObject, FatalPSBTIssue, FraudulentChangeOutput -from files import CardSlot -from exceptions import HSMDenied +from files import CardSlot, CardMissingError +from exceptions import HSMDenied, QRTooBigError from version import MAX_TXN_LEN from charcodes import KEY_QR, KEY_NFC, KEY_ENTER, KEY_CANCEL, KEY_LEFT, KEY_RIGHT +from msgsign import sign_message_digest # Where in SPI flash/PSRAM the two PSBT files are (in and out) TXN_INPUT_OFFSET = 0 @@ -73,13 +72,12 @@ def check_busy(cls, allowed_cls=None): if allowed_cls and isinstance(cls.active_request, allowed_cls): return - # check if UX actally was cleared, and we're not really doing that anymore; recover + # check if UX actually was cleared, and we're not really doing that anymore; recover # - happens if USB caller never comes back for their final results from ux import the_ux top_ux = the_ux.top_of_stack() if not isinstance(top_ux, cls) and cls.active_request.ux_done: # do cleaup - print('recovery cleanup') cls.cleanup() return @@ -91,8 +89,8 @@ async def failure(self, msg, exc=None, title='Failure'): # show line number and/or simple text about error if exc: - print("%s:" % msg) - sys.print_exception(exc) + #print("%s:" % msg) + #sys.print_exception(exc) msg += '\n\n' em = str(exc) @@ -128,188 +126,28 @@ async def failure(self, msg, exc=None, title='Failure'): Press %s to continue, otherwise %s to cancel.''' % (OK, X) -# RFC2440 style signatures, popular -# since the genesis block, but not really part of any BIP as far as I know. -# -def rfc_signature_template_gen(msg, addr, sig): - template = [ - "-----BEGIN BITCOIN SIGNED MESSAGE-----\n", - "%s\n" % msg, - "-----BEGIN BITCOIN SIGNATURE-----\n", - "%s\n" % addr, - "%s\n" % sig, - "-----END BITCOIN SIGNATURE-----\n" - ] - for part in template: - yield part - -def parse_armored_signature_file(contents): - sep = "-----" - assert contents.count(sep) == 6, "Armor text MUST be surrounded by exactly five (5) dashes." - temp = contents.split(sep) - msg = temp[2].strip() - addr_sig = temp[4].strip() - addr, sig_str = addr_sig.split() - return msg, addr, sig_str - -def sign_message_digest(digest, subpath, prompt, addr_fmt=AF_CLASSIC, pk=None): - # do the signature itself! - from glob import dis - - ch = chains.current_chain() - - if prompt: - dis.fullscreen(prompt, percent=.25) - - if pk is None: - with stash.SensitiveValues() as sv: - # if private key is provided, derivation subpath is ignored - # and provided private key is used for signing - node = sv.derive_path(subpath) - dis.progress_bar_show(.50) - pk = node.privkey() - addr = ch.address(node, addr_fmt) - else: - node = ngu.hdnode.HDNode().from_chaincode_privkey(bytes(32), pk) - dis.progress_bar_show(.50) - addr = ch.address(node, addr_fmt) - - dis.progress_bar_show(.75) - rv = ngu.secp256k1.sign(pk, digest, 0).to_bytes() - # AF_CLASSIC header byte base 31 is returned by default from ngu - NOOP - if addr_fmt != AF_CLASSIC: - header_byte, rs = rv[0], rv[1:] - # ngu only produces header base for compressed p2pkh, anyways get only rec_id - rec_id = (header_byte - 27) & 0x03 - new_header_byte = rec_id + ch.sig_hdr_base(addr_fmt=addr_fmt) - rv = bytes([new_header_byte]) + rs - - dis.progress_bar_show(1) - - return rv, addr - -def make_signature_file_msg(content_list): - # list of tuples consisting of (hash, file_name) - return b"\n".join([ - b2a_hex(h) + b" " + fname.encode() - for h, fname in content_list - ]) - -def parse_signature_file_msg(msg): - # only succeed for our format digest + 2 spaces + fname - try: - res = [] - lines = msg.split('\n') - for ln in lines: - d, fn = ln.split(' ') - # should not need to strip if our file format, so dont - # is hex? is 32 bytes long? - assert len(a2b_hex(d)) == 32 - res.append((d, fn)) - - return res - except: - return - -def sign_export_contents(content_list, deriv, addr_fmt, pk=None): - msg2sign = make_signature_file_msg(content_list) - bitcoin_digest = chains.current_chain().hash_message(msg2sign) - sig_bytes, addr = sign_message_digest(bitcoin_digest, deriv, "Signing...", addr_fmt, pk=pk) - sig = b2a_base64(sig_bytes).decode().strip() - gen = rfc_signature_template_gen(addr=addr, msg=msg2sign.decode(), sig=sig) - return gen - -def verify_signed_file_digest(msg): - from files import CardSlot - - parsed_msg = parse_signature_file_msg(msg) - if not parsed_msg: - # not our format - return - - try: - err, warn = [], [] - with CardSlot() as card: - for digest, fname in parsed_msg: - path = card.abs_path(fname) - if not card.exists(path): - warn.append((fname, None)) - continue - path = card.abs_path(fname) - - md = sha256() - with open(path, "rb") as f: - while True: - chunk = f.read(1024) - if not chunk: - break - md.update(chunk) - - h = b2a_hex(md.digest()).decode().strip() - if h != digest: - err.append((fname, h, digest)) - except: - # fail silently if issues with reading files or SD issues - # no digest checking - return - - return err, warn - -def write_sig_file(content_list, derive=None, addr_fmt=AF_CLASSIC, pk=None, sig_name=None): - from glob import dis - - if derive is None: - ct = chains.current_chain().b44_cointype - derive = "m/44'/%d'/0'/0/0" % ct - - fpath = content_list[0][1] - if len(content_list) > 1: - # we're signing contents of more files - need generic name for sig file - assert sig_name - sig_nice = sig_name + ".sig" - sig_fpath = fpath.rsplit("/", 1)[0] + "/" + sig_nice - else: - sig_fpath = fpath.rsplit(".", 1)[0] + ".sig" - sig_nice = sig_fpath.split("/")[-1] - - sig_gen = sign_export_contents([(h, f.split("/")[-1]) for h, f in content_list], - derive, addr_fmt, pk=pk) - - with open(sig_fpath, 'wt') as fd: - for i, part in enumerate(sig_gen): - fd.write(part) - # rfc template generator has length of 6 - dis.progress_bar_show(i / 6) - return sig_nice - -def validate_text_for_signing(text): - # Check for some UX/UI traps in the message itself. - # - messages must be short and ascii only. Our charset is limited - # - too many spaces, leading/trailing can be an issue - - MSG_MAX_SPACES = 4 # impt. compared to -=- positioning - - result = to_ascii_printable(text) +class ApproveMessageSign(UserAuthorizedAction): + def __init__(self, text, subpath, addr_fmt, approved_cb=None, + msg_sign_request=None, only_printable=True): + super().__init__() + is_json = False - length = len(result) - assert length >= 2, "msg too short (min. 2)" - assert length <= MSG_SIGNING_MAX_LENGTH, "msg too long (max. %d)" % MSG_SIGNING_MAX_LENGTH - assert " " not in result, 'too many spaces together in msg(max. 3)' - # other confusion w/ whitepace - assert result[0] != ' ', 'leading space(s) in msg' - assert result[-1] != ' ', 'trailing space(s) in msg' + from msgsign import validate_text_for_signing, parse_msg_sign_request - # looks ok - return result + if msg_sign_request: + text, subpath, addr_fmt, is_json = parse_msg_sign_request(msg_sign_request) -class ApproveMessageSign(UserAuthorizedAction): - def __init__(self, text, subpath, addr_fmt, approved_cb=None): - super().__init__() - self.text = validate_text_for_signing(text) + self.text = validate_text_for_signing( + text, only_printable=not is_json and only_printable + ) self.subpath = cleanup_deriv_path(subpath) - self.addr_fmt = parse_addr_fmt_str(addr_fmt) + self.addr_fmt = chains.parse_addr_fmt_str(addr_fmt) self.approved_cb = approved_cb + # temporary - no p2tr support + if self.addr_fmt == AF_P2TR: + raise ValueError("Unsupported address format: 'p2tr'") + from glob import dis dis.fullscreen('Wait...') @@ -321,22 +159,22 @@ def __init__(self, text, subpath, addr_fmt, approved_cb=None): async def interact(self): # Prompt user w/ details and get approval - from glob import dis, hsm_active + from glob import hsm_active if hsm_active: ch = await hsm_active.approve_msg_sign(self.text, self.address, self.subpath) else: - story = MSG_SIG_TEMPLATE.format(msg=self.text, addr=self.address, subpath=self.subpath) + story = MSG_SIG_TEMPLATE.format(msg=self.text, addr=show_single_address(self.address), + subpath=self.subpath) ch = await ux_show_story(story) if ch != 'y': # they don't want to! self.refused = True else: - # perform signing (progress bar shown) digest = chains.current_chain().hash_message(self.text.encode()) - self.result = sign_message_digest(digest, self.subpath, "Signing...", self.addr_fmt)[0] + self.result, _ = sign_message_digest(digest, self.subpath, "Signing...", self.addr_fmt) if self.approved_cb: # for micro sd case @@ -351,35 +189,43 @@ async def interact(self): def sign_msg(text, subpath, addr_fmt): - subpath = cleanup_deriv_path(subpath) + # Start the approval process for message signing. UserAuthorizedAction.check_busy() UserAuthorizedAction.active_request = ApproveMessageSign(text, subpath, addr_fmt) + # kill any menu stack, and put our thing at the top abort_and_goto(UserAuthorizedAction.active_request) +async def approve_msg_sign(text, subpath, addr_fmt, approved_cb=None, + msg_sign_request=None, kill_menu=False, + only_printable=True): + + # Ask user if they want to sign some short text message. + UserAuthorizedAction.cleanup() + UserAuthorizedAction.check_busy(ApproveMessageSign) + try: + UserAuthorizedAction.active_request = ApproveMessageSign( + text, subpath, addr_fmt, + approved_cb=approved_cb, + msg_sign_request=msg_sign_request, + only_printable=only_printable, + ) + + if kill_menu: + abort_and_goto(UserAuthorizedAction.active_request) + else: + # do not kill the menu stack! just push + from ux import the_ux + the_ux.push(UserAuthorizedAction.active_request) + + except (AssertionError, ValueError) as exc: + await ux_show_story("Problem: %s\n\nMessage to be signed must be a single line of ASCII text." % exc) async def sign_txt_file(filename): # sign a one-line text file found on a MicroSD card # - not yet clear how to do address types other than 'classic' - from files import CardSlot, CardMissingError - from ux import the_ux - - UserAuthorizedAction.cleanup() - - # copy message into memory - with CardSlot() as card: - with card.open(filename, 'rt') as fd: - text = fd.readline().strip() - subpath = fd.readline().strip() - addr_fmt = fd.readline().strip() - - if not subpath: - # default: top of wallet. - subpath = 'm' - - if not addr_fmt: - addr_fmt = AF_CLASSIC + from msgsign import sd_sign_msg_done async def done(signature, address, text): # complete. write out result @@ -388,203 +234,24 @@ async def done(signature, address, text): orig_path, basename = filename.rsplit('/', 1) orig_path += '/' base = basename.rsplit('.', 1)[0] - out_fn = None - sig = b2a_base64(signature).decode('ascii').strip() - - while 1: - # try to put back into same spot - # add -signed to end. - target_fname = base+'-signed.txt' - - for path in [orig_path, None]: - try: - with CardSlot(readonly=True) as card: - out_full, out_fn = card.pick_filename(target_fname, path) - out_path = path - if out_full: break - except CardMissingError: - prob = 'Missing card.\n\n' - out_fn = None - - if not out_fn: - # need them to insert a card - prob = '' - else: - # attempt write-out - try: - dis.fullscreen("Saving...") - with CardSlot() as card: - with card.open(out_full, 'wt') as fd: - # save in full RFC style - # gen length is 6 - gen = rfc_signature_template_gen(addr=address, msg=text, sig=sig) - for i, part in enumerate(gen): - fd.write(part) - dis.progress_bar_show(i / 6) - - # success and done! - break - - except OSError as exc: - prob = 'Failed to write!\n\n%s\n\n' % exc - sys.print_exception(exc) - # fall through to try again - - # prompt them to input another card? - ch = await ux_show_story(prob+"Please insert an SDCard to receive signed message, " - "and press %s." % OK, title="Need Card") - if ch == 'x': - await ux_aborted() - return - - # done. - msg = "Created new file:\n\n%s" % out_fn - await ux_show_story(msg, title='File Signed') + await sd_sign_msg_done(signature, address, text, base, orig_path) + UserAuthorizedAction.cleanup() UserAuthorizedAction.check_busy() - try: - UserAuthorizedAction.active_request = ApproveMessageSign(text, subpath, addr_fmt, approved_cb=done) - # do not kill the menu stack! - the_ux.push(UserAuthorizedAction.active_request) - except AssertionError as exc: - await ux_show_story("Problem: %s\n\nMessage to be signed must be a single line of ASCII text." % exc) - return - -def verify_signature(msg, addr, sig_str): - warnings = "" - script = None - hash160 = None - invalid_addr_fmt_msg = "Invalid address format - must be one of p2pkh, p2sh-p2wpkh, or p2wpkh." - invalid_addr = "Invalid signature for message." - - if addr[0] in "1mn": - addr_fmt = AF_CLASSIC - decoded_addr = ngu.codecs.b58_decode(addr) - hash160 = decoded_addr[1:] # remove prefix - elif addr.startswith("bc1q") or addr.startswith("tb1q") or addr.startswith("bcrt1q"): - if len(addr) > 44: # testnet/mainnet max singlesig len 42, regtest 44 - # p2wsh - raise ValueError(invalid_addr_fmt_msg) - addr_fmt = AF_P2WPKH - _, _, hash160 = ngu.codecs.segwit_decode(addr) - elif addr[0] in "32": - addr_fmt = AF_P2WPKH_P2SH - decoded_addr = ngu.codecs.b58_decode(addr) - script = decoded_addr[1:] # remove prefix - else: - raise ValueError(invalid_addr_fmt_msg) - - try: - sig_bytes = a2b_base64(sig_str) - if not sig_bytes or len(sig_bytes) != 65: - # can return b'' in case of wrong, can also raise - raise ValueError("invalid encoding") - - header_byte = sig_bytes[0] - header_base = chains.current_chain().sig_hdr_base(addr_fmt) - if (header_byte - header_base) not in (0, 1, 2, 3): - # wrong header value only - this can still verify OK - warnings += "Specified address format does not match signature header byte format." - - # least two significant bits - rec_id = (header_byte - 27) & 0x03 - # need to normalize it to 31 base for ngu - new_header_byte = 31 + rec_id - sig = ngu.secp256k1.signature(bytes([new_header_byte]) + sig_bytes[1:]) - except ValueError as e: - raise ValueError("Parsing signature failed - %s." % str(e)) - - digest = chains.current_chain().hash_message(msg.encode('ascii')) - try: - rec_pubkey = sig.verify_recover(digest) - except ValueError as e: - raise ValueError("Invalid signature for msg - %s." % str(e)) - - rec_pubkey_bytes = rec_pubkey.to_bytes() - rec_hash160 = ngu.hash.hash160(rec_pubkey_bytes) - - if script: - target = bytes([0, 20]) + rec_hash160 - target = ngu.hash.hash160(target) - if target != script: - raise ValueError(invalid_addr) - else: - if rec_hash160 != hash160: - raise ValueError(invalid_addr) - return warnings - -async def verify_armored_signed_msg(contents, digest_check=True): - # digest_check=False for NFC cases, where we do not have filesystem - from glob import dis - - dis.fullscreen("Verifying...") - - try: - msg, addr, sig_str = parse_armored_signature_file(contents) - except Exception as e: - e_line = problem_file_line(e) - await ux_show_story("Malformed signature file. %s %s" % (str(e), e_line), title="FAILURE") - return - - try: - sig_warn = verify_signature(msg, addr, sig_str) - except Exception as e: - await ux_show_story(str(e), title="ERROR") - return - - title = "CORRECT" - warn_msg = "" - err_msg = "" - story = "Good signature by address:\n %s" % addr - - if digest_check: - digest_prob = verify_signed_file_digest(msg) - if digest_prob: - err, digest_warn = digest_prob - if digest_warn: - title = "WARNING" - wmsg_base = "not present. Contents verification not possible." - if len(digest_warn) == 1: - fname = digest_warn[0][0] - warn_msg += "'%s' is %s" % (fname, wmsg_base) - else: - warn_msg += "Files:\n" + "\n".join("> %s" % fname for fname, _ in digest_warn) - warn_msg += "\nare %s" % wmsg_base - - if err: - title = "ERROR" - for fname, calc, got in err: - err_msg += ("Referenced file '%s' has wrong contents.\n" - "Got:\n%s\n\nExpected:\n%s" % (fname, got, calc)) - - if sig_warn: - # we know not ours only because wrong recid header used & not BIP-137 compliant - story = "Correctly signed, but not by this Coldcard. %s" % sig_warn - - await ux_show_story('\n\n'.join(m for m in [err_msg, story, warn_msg] if m), title=title) - -async def verify_txt_sig_file(filename): - from files import CardSlot, CardMissingError, needs_microsd # copy message into memory - try: - with CardSlot() as card: - with card.open(filename, 'rt') as fd: - text = fd.read() - except CardMissingError: - await needs_microsd() - return - except Exception as e: - await ux_show_story('Error: ' + str(e)) - return - - await verify_armored_signed_msg(text) + with CardSlot() as card: + with card.open(filename, 'rt') as fd: + res = fd.read() + await approve_msg_sign(None, None, None, approved_cb=done, + msg_sign_request=res) async def try_push_tx(data, txid, txn_sha=None): - from glob import settings, PSRAM, NFC # if NFC PushTx is enabled, do that w/o questions. + from glob import settings, PSRAM, NFC + url = settings.get('ptxurl', False) if NFC and url: try: @@ -595,55 +262,87 @@ async def try_push_tx(data, txid, txn_sha=None): await NFC.share_push_tx(url, txid, data, txn_sha) return True except: pass # continue normally if it fails, perhaps too big? - return False + return False class ApproveTransaction(UserAuthorizedAction): - def __init__(self, psbt_len, flags=0x0, approved_cb=None, psbt_sha=None, is_sd=None): + def __init__(self, psbt_len, flags=None, psbt_sha=None, input_method=None, + output_encoder=None, filename=None, miniscript_wallet=None): super().__init__() self.psbt_len = psbt_len - self.do_finalize = bool(flags & STXN_FINALIZE) - self.do_visualize = bool(flags & STXN_VISUALIZE) + + # do finalize is None if not USB, None = decide based on is_complete + if flags is None: + self.do_finalize = self.do_visualize = None + else: + self.do_finalize = bool(flags & STXN_FINALIZE) + self.do_visualize = bool(flags & STXN_VISUALIZE) + self.stxn_flags = flags self.psbt = None self.psbt_sha = psbt_sha - self.approved_cb = approved_cb + self.input_method = input_method + self.output_encoder = output_encoder + self.filename = filename self.result = None # will be (len, sha256) of the resulting PSBT - self.is_sd = is_sd self.chain = chains.current_chain() + self.miniscript_wallet = miniscript_wallet def render_output(self, o): # Pretty-print a transactions output. # - expects CTxOut object # - gives user-visible string + # returns: tuple(ux_output_rendition, address_or_script_str_for_qr_display) # val = ' '.join(self.chain.render_value(o.nValue)) try: dest = self.chain.render_address(o.scriptPubKey) - - return '%s\n - to address -\n%s\n' % (val, dest) + # known script types are short enough that we can display QR on both hw versions + return '%s\n - to address -\n%s\n' % (val, show_single_address(dest)), dest except ValueError: pass + # Handle future things better: allow them to happen at least. + # sending to some unknown script, possibly very long + # but full-show required for verification + # OP_RETURN dest contains also OP_RETURN itself (for PSBT qr explorer) + dest = B2A(o.scriptPubKey) + # check for OP_RETURN data = self.chain.op_return(o.scriptPubKey) - if data: - data_hex, data_ascii = data - to_ret = '%s\n - OP_RETURN -\n%s' % (val, data_hex) - if data_ascii: - return to_ret + " (ascii: %s)\n" % data_ascii - return to_ret + "\n" + # In UX story only data are shown as OP_RETURN is part of base msg + if data is None: + rv = '%s\n - to script -\n%s\n' % (val, dest) + else: + base = '%s\n - OP_RETURN -\n%s' + if not data: + dest = "" + rv = base % (val, "null-data\n") + else: + data_ascii = None + if len(data) > 160: + # completely arbitrary limit, prevents huge stories + # anchor data are not relevant for verification - can be hidden + ss = b2a_hex(data[:80]).decode() + "\n ⋯\n" + b2a_hex(data[-80:]).decode() + # but we show empty QR in txn explorer for these big, modified data + else: + ss = b2a_hex(data).decode() + if (min(data) >= 32) and (max(data) < 127): # printable & not huge + try: + data_ascii = data.decode("ascii") + except: pass - # Handle future things better: allow them to happen at least. - self.psbt.warnings.append( - ('Output?', 'Sending to a script that is not well understood.')) - dest = B2A(o.scriptPubKey) + rv = base % (val, ss) + if data_ascii: + rv += " (ascii: %s)" % data_ascii + rv += "\n" - return '%s\n - to script -\n%s\n' % (val, dest) + return rv, dest async def interact(self): # Prompt user w/ details and get approval from glob import dis, hsm_active + from ccc import CCCFeature, SSSPFeature # step 1: parse PSBT from PSRAM into in-memory objects. @@ -652,6 +351,7 @@ async def interact(self): # NOTE: psbtObject captures the file descriptor and uses it later self.psbt = psbtObject.read_psbt(fd) except BaseException as exc: + # sys.print_exception(exc) if isinstance(exc, MemoryError): msg = "Transaction is too complex" exc = None @@ -661,28 +361,29 @@ async def interact(self): return await self.failure(msg, exc) dis.fullscreen("Validating...") + self.psbt.active_miniscript = self.miniscript_wallet # Do some analysis/ validation try: - await self.psbt.validate() # might do UX: accept multisig import - dis.progress_bar_show(0.10) - self.psbt.consider_inputs() - - dis.progress_bar_show(0.33) - self.psbt.consider_keys() - - dis.progress_bar_show(0.66) - self.psbt.consider_outputs() + await self.psbt.validate() # might do UX: accept multisig import + + ccc_c_xfp = CCCFeature.get_xfp() # can be None + args = self.psbt.consider_inputs(cosign_xfp=ccc_c_xfp) + self.psbt.consider_outputs(*args, cosign_xfp=ccc_c_xfp) + del args # not needed anymore + # we can properly assess sighash only after we know + # which outputs are change self.psbt.consider_dangerous_sighash() - dis.progress_bar_show(0.85) except FraudulentChangeOutput as exc: - print('FraudulentChangeOutput: ' + exc.args[0]) + # sys.print_exception(exc) + #print('FraudulentChangeOutput: ' + exc.args[0]) return await self.failure(exc.args[0], title='Change Fraud') except FatalPSBTIssue as exc: - print('FatalPSBTIssue: ' + exc.args[0]) + #print('FatalPSBTIssue: ' + exc.args[0]) return await self.failure(exc.args[0]) except BaseException as exc: + # sys.print_exception(exc) del self.psbt gc.collect() @@ -694,6 +395,16 @@ async def interact(self): return await self.failure(msg, exc) + # early test for spending policy; not an error if violates policy + # - might add warnings + could_ccc_sign, ccc_needs_2fa = CCCFeature.could_cosign(self.psbt) + + # test for allowing any signature when in single-signer mode + # - but CCC will override it. + should_block, ss_needs_2fa = SSSPFeature.can_allow(self.psbt) + if should_block and not could_ccc_sign: + return await self.failure('Spending Policy violation.') + # step 2: figure out what we are approving, so we can get sign-off # - outputs, amounts # - fee @@ -715,6 +426,10 @@ async def interact(self): elif wl >= 2: msg.write('(%d warnings below)\n\n' % wl) + if self.psbt.active_miniscript: + # show name of the multisig/miniscript wallet that we signed with + msg.write("Wallet: " + self.psbt.active_miniscript.name + "\n\n") + if self.psbt.consolidation_tx: # consolidating txn that doesn't change balance of account. msg.write("Consolidating %s %s\nwithin wallet.\n\n" % @@ -735,7 +450,7 @@ async def interact(self): )) # outputs + change story created here - needs_txn_explorer = self.output_summary_text(msg) + self.output_summary_text(msg) gc.collect() if self.psbt.ux_notes: @@ -744,7 +459,6 @@ async def interact(self): for label, m in self.psbt.ux_notes: msg.write('- %s: %s\n' % (label, m)) - msg.write("\n") if self.psbt.warnings: msg.write('---WARNING---\n\n') @@ -761,16 +475,19 @@ async def interact(self): ux_clear_keys(True) dis.progress_bar_show(1) # finish the Validating... + if not hsm_active: - msg.write("\nPress %s to approve and sign transaction." % OK) - if needs_txn_explorer: - msg.write(" Press (2) to explore txn.") - if self.is_sd and CardSlot.both_inserted(): + esc = "2" + msg.write("Press %s to approve and sign transaction." + " Press (2) to explore txn outputs." % OK) + if (self.input_method == "sd") and CardSlot.both_inserted(): + esc += "b" msg.write(" (B) to write to lower SD slot.") - msg.write(" X to abort.") + msg.write(" %s to abort." % X) + while True: - ch = await ux_show_story(msg, title="OK TO SEND?", escape="2b") - if ch == "2" and needs_txn_explorer: + ch = await ux_show_story(msg, title="OK TO SEND?", escape=esc) + if ch == "2": await self.txn_explorer() continue else: @@ -778,8 +495,8 @@ async def interact(self): del msg break else: + # get approval (maybe) from the HSM ch = await hsm_active.approve_transaction(self.psbt, self.psbt_sha, msg.getvalue()) - dis.progress_bar_show(1) # finish the Validating... except MemoryError: # recovery? maybe. @@ -793,7 +510,7 @@ async def interact(self): return await self.failure(msg) if ch not in 'yb': - # they don't want to! + # they don't want to sign! self.refused = True await ux_dramatic_pause("Refused.", 1) @@ -803,73 +520,59 @@ async def interact(self): self.done() return + if ccc_needs_2fa and could_ccc_sign: + # They still need to pass web2fa challenge (but it meets other specs ok) + try: + await CCCFeature.web2fa_challenge() + except: + could_ccc_sign = False + ch2 = await ux_show_story("Will not add CCC signature. Proceed anyway?") + if ch2 != 'y': + return await self.failure("2FA Failed") + + elif ss_needs_2fa: + # Need 2FA for single-sig case .. refuse to sign if it fails. + try: + await SSSPFeature.web2fa_challenge() + except: + return await self.failure("2FA Failed") + # do the actual signing. try: dis.fullscreen('Wait...') gc.collect() # visible delay caused by this but also sign_it() below self.psbt.sign_it() + + if could_ccc_sign: + # this is where the CCC co-signing happens. + dis.fullscreen('Co-Signing...') + gc.collect() + CCCFeature.sign_psbt(self.psbt) + else: + # maybe capture new min-height for velocity limit + SSSPFeature.update_last_signed(self.psbt) + except FraudulentChangeOutput as exc: return await self.failure(exc.args[0], title='Change Fraud') except MemoryError: msg = "Transaction is too complex" return await self.failure(msg) except BaseException as exc: + # sys.print_exception(exc) return await self.failure("Signing failed late", exc) - if self.approved_cb: - # for NFC, micro SD cases - kws = dict(psbt=self.psbt) - if self.is_sd and (ch == "b"): - kws["slot_b"] = True - await self.approved_cb(**kws) - self.done() - return - - txid = None try: - # re-serialize the PSBT back out - with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as fd: - if self.do_finalize: - txid = self.psbt.finalize(fd) - else: - self.psbt.serialize(fd) - - self.result = (fd.tell(), fd.checksum.digest()) - - self.done(redraw=(not txid)) - + await done_signing(self.psbt, self, self.input_method, + self.filename, self.output_encoder, + slot_b=(ch == "b"), finalize=self.do_finalize) + self.done() + except AbortInteraction: + # user might have sent new sign cmd, while we still at export prompt + pass except BaseException as exc: + # sys.print_exception(exc) return await self.failure("PSBT output failed", exc) - from glob import NFC - - if self.do_finalize and txid and not hsm_active: - - if await try_push_tx(self.result[0], txid, self.result[1]): - return # success, exit - - kq, kn = "(1)", "(3)" - if version.has_qwerty: - kq, kn = KEY_QR, KEY_NFC - while 1: - # Show txid when we can; advisory - # - maybe even as QR, hex-encoded in alnum mode - tmsg = txid + '\n\nPress %s for QR Code of TXID. ' % kq - - if NFC: - tmsg += 'Press %s to share signed txn via NFC.' % kn - - ch = await ux_show_story(tmsg, "Final TXID", escape='13'+KEY_NFC+KEY_QR) - - if ch in '1'+KEY_QR: - await show_qr_code(txid, True) - continue - - if ch in KEY_NFC+"3" and NFC: - await NFC.share_signed_txn(txid, TXN_OUTPUT_OFFSET, - self.result[0], self.result[1]) - continue - break async def txn_explorer(self): # Page through unlimited-sized transaction details @@ -880,11 +583,16 @@ def make_msg(offset, count): dis.fullscreen('Wait...') rv = "" end = min(offset + count, self.psbt.num_outputs) - - for idx, out in self.psbt.output_iter(offset, end): + addrs = [] + change = [] + for i, (idx, out) in enumerate(self.psbt.output_iter(offset, end)): outp = self.psbt.outputs[idx] item = "Output %d%s:\n\n" % (idx, " (change)" if outp.is_change else "") - item += self.render_output(out) + msg, addr_or_script = self.render_output(out) + item += msg + addrs.append(addr_or_script) + if outp.is_change: + change.append(i) item += "\n" rv += item dis.progress_sofar(idx-offset+1, count) @@ -892,18 +600,31 @@ def make_msg(offset, count): rv += 'Press RIGHT to see next group' if offset: rv += ', LEFT to go back' - rv += '. X to quit.' - return rv + if not version.has_qwerty: + # Q has hint key + rv += ", (4) to show QR code" + rv += ('. %s to quit.' % X) + + return rv, addrs, change, end start = 0 n = 10 - msg = make_msg(start, n) + msg, addrs, change, end = make_msg(start, n) while True: - ch = await ux_show_story(msg, escape='79'+KEY_RIGHT+KEY_LEFT) + ch = await ux_show_story(msg, title="%d-%d" % (start, end-1), + escape='479'+KEY_RIGHT+KEY_LEFT+KEY_QR, + hint_icons=KEY_QR) if ch == 'x': del msg return + elif ch in "4"+KEY_QR: + from ux import show_qr_codes + # showing addresses from PSBT, no idea what is in there + # handle QR code failures gracefully + await show_qr_codes(addrs, False, start, is_addrs=True, + change_idxs=change, can_raise=False) + continue elif (ch in KEY_LEFT+"7"): if (start - n) < 0: continue @@ -920,11 +641,13 @@ def make_msg(offset, count): # nothing changed - do not recalc msg continue - msg = make_msg(start, n) + msg, addrs, change, end = make_msg(start, n) async def save_visualization(self, msg, sign_text=False): - # write text into spi flash, maybe signing it as we go + # write story text out, maybe signing it as we go # - return length and checksum + from charcodes import OUT_CTRL_ADDRESS + txt_len = msg.seek(0, 2) msg.seek(0) @@ -932,7 +655,8 @@ async def save_visualization(self, msg, sign_text=False): with SFFile(TXN_OUTPUT_OFFSET, max_size=txt_len+300, message="Visualizing...") as fd: while 1: - blk = msg.read(256).encode('ascii') + # replace with empty space, to keep correct txt_len - already hashed + blk = msg.read(256).replace(OUT_CTRL_ADDRESS, ' ').encode('ascii') if not blk: break if chk: chk.update(blk) @@ -941,11 +665,11 @@ async def save_visualization(self, msg, sign_text=False): if chk: # append the signature digest = ngu.hash.sha256s(chk.digest()) - sig = sign_message_digest(digest, 'm', None, AF_CLASSIC)[0] + sig, _ = sign_message_digest(digest, 'm', None, AF_CLASSIC) fd.write(b2a_base64(sig).decode('ascii').strip()) fd.write('\n') - return (fd.tell(), fd.checksum.digest()) + return fd.tell(), fd.checksum.digest() def output_summary_text(self, msg): # Produce text report of where their cash is going. This is what @@ -959,14 +683,15 @@ def output_summary_text(self, msg): MAX_VISIBLE_OUTPUTS = const(10) MAX_VISIBLE_CHANGE = const(20) - needs_txn_explorer = False largest_outs = [] largest_change = [] total_change = 0 + has_change = False for idx, tx_out in self.psbt.output_iter(): outp = self.psbt.outputs[idx] if outp.is_change: + has_change = True total_change += tx_out.nValue if len(largest_change) < MAX_VISIBLE_CHANGE: largest_change.append((tx_out.nValue, self.chain.render_address(tx_out.scriptPubKey))) @@ -976,7 +701,8 @@ def output_summary_text(self, msg): else: if len(largest_outs) < MAX_VISIBLE_OUTPUTS: - largest_outs.append((tx_out.nValue, self.render_output(tx_out))) + rendered, _ = self.render_output(tx_out) + largest_outs.append((tx_out.nValue, rendered)) if len(largest_outs) == MAX_VISIBLE_OUTPUTS: # descending sort from the biggest value to lowest (sort on out.nValue) largest_outs = sorted(largest_outs, key=lambda x: x[0], reverse=True) @@ -996,7 +722,8 @@ def output_summary_text(self, msg): if outp.is_change: ret = (here, self.chain.render_address(tx_out.scriptPubKey)) else: - ret = (here, self.render_output(tx_out)) + rendered, _ = self.render_output(tx_out) + ret = (here, rendered) largest.insert(keep, ret) # foreign outputs (soon to be other people's coins) @@ -1008,7 +735,6 @@ def output_summary_text(self, msg): left = self.psbt.num_outputs - len(largest_outs) - self.psbt.num_change_outputs if left > 0: - needs_txn_explorer = True msg.write('.. plus %d smaller output(s), not shown here, which total: ' % left) # calculate left over value @@ -1018,36 +744,33 @@ def output_summary_text(self, msg): msg.write("\n") # change outputs - verified to be coming back to our wallet - if total_change > 0: + if has_change: msg.write("Change back:\n%s %s\n" % self.chain.render_value(total_change)) visible_change_sum = 0 if len(largest_change) == 1: visible_change_sum += largest_change[0][0] - msg.write(' - to address -\n%s\n' % largest_change[0][1]) + msg.write(' - to address -\n%s\n\n' % show_single_address(largest_change[0][1])) else: msg.write(' - to addresses -\n') for val, addr in largest_change: visible_change_sum += val - msg.write(addr) - msg.write('\n') + msg.write(show_single_address(addr)) + msg.write('\n\n') left_c = self.psbt.num_change_outputs - len(largest_change) if left_c: - needs_txn_explorer = True msg.write('.. plus %d smaller change output(s), not shown here, which total: ' % left_c) - msg.write('%s %s\n' % self.chain.render_value(total_change - visible_change_sum)) - - msg.write("\n") - - # if we didn't already show all outputs, then give user a chance to - # view them individually - return needs_txn_explorer + msg.write('%s %s\n\n' % self.chain.render_value(total_change - visible_change_sum)) -def sign_transaction(psbt_len, flags=0x0, psbt_sha=None): +def sign_transaction(psbt_len, flags=0x0, psbt_sha=None, miniscript_wallet=None): # transaction (binary) loaded into PSRAM already, checksum checked + # optional miniscript_wallet arg, choose particular enrolled wallet by name to sign UserAuthorizedAction.check_busy(ApproveTransaction) - UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, flags, psbt_sha=psbt_sha) + UserAuthorizedAction.active_request = ApproveTransaction( + psbt_len, flags, psbt_sha=psbt_sha, input_method="usb", + miniscript_wallet=miniscript_wallet, + ) # kill any menu stack, and put our thing at the top abort_and_goto(UserAuthorizedAction.active_request) @@ -1072,22 +795,295 @@ def psbt_encoding_taster(taste, psbt_len): raise ValueError("not psbt") return decoder, output_encoder, psbt_len - -async def sign_psbt_file(filename, force_vdisk=False, slot_b=None): + + +async def done_signing(psbt, tx_req, input_method=None, filename=None, + output_encoder=None, slot_b=False, finalize=None): + # User authorized PSBT for signing, and we added signatures. + # - allow PushTX if enabled (first thing) + # - can save final TXN out to SD card/VirtDisk, share by NFC, QR. + + from glob import PSRAM, hsm_active + from sffile import SFFile + from ux import show_qr_code, import_export_prompt + + first_time = True + msg = None + title = None + + is_complete = psbt.is_complete() + if finalize is not None: + # USB case - user can choose whether to attempt finalization + is_complete = finalize + + with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as psram: + if is_complete: + txid = psbt.finalize(psram) + noun = "Finalized TX ready for broadcast" + else: + psbt.serialize(psram) + noun = "Partly Signed PSBT" + txid = None + + data_len = psram.tell() + data_sha2 = psram.checksum.digest() + + # BBQR is at TMP_OUTPUT_OFFSET + 1MB - allowing it in this case would overwrite txn + # allow_qr = data_len < (1024*1024) + # actual more reasonable limit - as BBQR has some overhead and only 1Mbit of space + allow_qr = data_len < (671*1024) + + if input_method == "usb": + # return result over USB before going to all options + tx_req.result = data_len, data_sha2 + if hsm_active: + # it is enough to just return back via USB, other options + # are pointless + return + + first_time = False + msg = noun + " shared via USB." + title = "PSBT Signed" + + if txid and await try_push_tx(data_len, txid, data_sha2): + # go directly to reexport menu after pushTX + first_time = False + title = "TX Pushed" + + # for specific cases, key teleport is an option + offer_kt = False + if not is_complete and version.has_qwerty and psbt.active_miniscript: + offer_kt = 'use Key Teleport to send PSBT to other co-signers' + + while True: + ch = None + if first_time: + # first time, assume they want to send out same way it came in -- don't prompt + if input_method == "qr": + if allow_qr: + ch = KEY_QR + elif input_method == "nfc": + ch = KEY_NFC + elif input_method == "kt": + ch = 't' + else: + # SD/VDisk + ch = {"force_vdisk": input_method == "vdisk", "slot_b": slot_b} + + if not ch: + # show all possible export options (based on hardware enabled, features) + intro = [] + if msg: + intro.append(msg) + if txid: + intro.append('TXID:\n' + txid) + + # "force_prompt" is needed after first iteration as we can be Mk4, with NFC,Vdisk off, + # no QR support & not finalizing (no option to show txid provided). + # In that case this would just return dict and keep producing signed + # files on SD infinitely (would never actually prompt). + ch = await import_export_prompt(noun, intro="\n\n".join(intro), offer_kt=offer_kt, + txid=txid, title=title, force_prompt=not first_time, + no_qr=not version.has_qwerty or not allow_qr) + if ch == KEY_CANCEL: + UserAuthorizedAction.cleanup() + break + + elif txid and (ch == '6'): + await show_qr_code(txid, is_alnum=True, force_msg=True) + continue + + elif ch == KEY_QR: + here = PSRAM.read_at(TXN_OUTPUT_OFFSET, data_len) + msg = txid or 'Partly Signed PSBT' + try: + if len(here) > 920: + # too big for simple QR - use BBQr instead + raise QRTooBigError + hex_here = b2a_hex(here).upper().decode() + await show_qr_code(hex_here, is_alnum=True, msg=msg) + except QRTooBigError: + from ux_q1 import show_bbqr_codes + await show_bbqr_codes('T' if txid else 'P', here, msg) + + msg = noun + " shared via QR." + del here + + elif ch == KEY_NFC: + from glob import NFC + if is_complete: + await NFC.share_signed_txn(txid, TXN_OUTPUT_OFFSET, data_len, data_sha2) + else: + await NFC.share_psbt(TXN_OUTPUT_OFFSET, data_len, data_sha2) + + msg = noun + " shared via NFC." + + elif (ch == 't') and not is_complete: + # they might want to teleport it, but only if we have PSBT + # there is no need to teleport PSBT if txn is already complete & ready to be broadcast + from teleport import kt_send_psbt + ok = await kt_send_psbt(psbt, data_len) + if ok: + title = "Sent by Teleport" + else: + title = "Failed to Teleport" + + continue + + else: + # typical case: save to SD card, show filenames we used + assert isinstance(ch, dict) + msg = await _save_to_disk(psbt, txid, ch, is_complete, data_len, + output_encoder, filename) + + input_method = None + first_time = False + title = "PSBT Signed" + +async def _save_to_disk(psbt, txid, save_options, is_complete, data_len, output_encoder, filename=None): + # Saving a PSBT from PSRAM to something disk-like. + # - handle save-to-SD/VirtDisk cases. With re-attempt when no card, etc. + assert isinstance(save_options, dict) # from import_export_prompt + + from glob import dis, settings, PSRAM + import os + + dis.fullscreen("Wait...") + + if filename: + _, basename = filename.rsplit('/', 1) + base = basename.rsplit('.', 1)[0] + else: + base = 'recent-txn' + + # default encoding is binary + output_encoder = output_encoder or (lambda x:x) + + out2_fn = None + out_fn = None + + del_after = settings.get('del', 0) + + def _chunk_write(file_d, ofs, chunk=2048): + written = 0 + while written < data_len: + if (written + chunk) > data_len: + chunk = data_len - written + + file_d.write(PSRAM.read_at(ofs, chunk)) + written += chunk + ofs += chunk + + while 1: + # try to put back into same spot, but also do top-of-card + if not is_complete: + # keep the filename under control during multiple passes + target_fname = base.replace('-part', '') + '-part.psbt' + else: + # add -signed to end. We won't offer to sign again. + target_fname = base + '-signed.psbt' + + # attempt write-out + try: + with CardSlot(**save_options) as card: + out_full, out_fn = card.pick_filename(target_fname) + out_path = out_full.rsplit("/", 1)[0] + "/" + + if is_complete and del_after: + # don't write signed PSBT if we'd just delete it anyway + out_fn = None + else: + with output_encoder(card.open(out_full, 'wb')) as fd: + # save as updated PSBT + if not is_complete: + _chunk_write(fd, TXN_OUTPUT_OFFSET) + else: + psbt.serialize(fd) + + if is_complete: + # write out as hex too, if it's final + out2_full, out2_fn = card.pick_filename( + base + '-final.txn' if not del_after else 'tmp.txn', + out_path) + + if out2_full: + with HexWriter(card.open(out2_full, 'w+t')) as fd: + # save transaction, in hex + if is_complete: + _chunk_write(fd, TXN_OUTPUT_OFFSET) + else: + txid = psbt.finalize(fd) + + if del_after: + # rename it now that we know the txid + after_full, out2_fn = card.pick_filename( + txid + '.txn', out_path, overwrite=True) + os.rename(out2_full, after_full) + + if del_after and filename: + # this can do nothing if they swapped SDCard between steps, which is ok, + # but if the original file is still there, this blows it away. + # - if not yet final, the foo-part.psbt file stays + try: + card.securely_blank_file(filename) + except: pass + + # success and done! + break + + except CardMissingError: + prob = 'Need a card!\n\n' + + except OSError as exc: + prob = 'Failed to write!\n\n%s\n\n' % exc + # sys.print_exception(exc) + # fall through to try again + + # If this point reached, some problem, we could not write. + + if save_options.get('force_vdisk'): + await ux_show_story(prob, title='Error') + # they can't fix here, so give up + return + + # prompt them to input another card? + ch = await ux_show_story( + prob + "Please insert a card to receive signed transaction, " + "and press OK.", title="Need Card") + if ch == 'x': + return + + # Done, show the filenames we used. + if out_fn: + msg = "Updated PSBT is:\n\n%s" % out_fn + if out2_fn: + msg += '\n\n' + else: + # del_after is probably set + msg = '' + + if out2_fn: + msg += 'Finalized transaction (ready for broadcast):\n\n%s' % out2_fn + + return msg + + +async def sign_psbt_file(filename, force_vdisk=False, slot_b=None, just_read=False, ux_abort=False, + miniscript_wallet=None): # sign a PSBT file found on a MicroSD card # - or from VirtualDisk (mk4) - from files import CardSlot, CardMissingError + # - to re-use reading/decoding logic, pass just_read from glob import dis from ux import the_ux - tmp_buf = bytearray(1024) + tmp_buf = bytearray(4096) # copy file into PSRAM # - can't work in-place on the card because we want to support writing out to different card - # - accepts hex or base64 encoding, but binary prefered + # - accepts hex or base64 encoding, but binary preferred with CardSlot(force_vdisk, readonly=True, slot_b=slot_b) as card: with card.open(filename, 'rb') as fd: - dis.fullscreen('Reading...') + dis.fullscreen('Reading...', 0) # see how long it is psbt_len = fd.seek(0, 2) @@ -1118,138 +1114,26 @@ async def sign_psbt_file(filename, force_vdisk=False, slot_b=None): out.write(here) total += len(here) - dis.progress_bar_show(total / psbt_len) + dis.progress_sofar(total, psbt_len) # might have been whitespace inflating initial estimate of PSBT size assert total <= psbt_len psbt_len = total - async def done(psbt, slot_b=None): - dis.fullscreen("Wait...") - orig_path, basename = filename.rsplit('/', 1) - orig_path += '/' - base = basename.rsplit('.', 1)[0] - out2_fn = None - out_fn = None - txid = None - - from glob import settings - import os - del_after = settings.get('del', 0) - - while 1: - # try to put back into same spot, but also do top-of-card - is_comp = psbt.is_complete() - if not is_comp: - # keep the filename under control during multiple passes - target_fname = base.replace('-part', '')+'-part.psbt' - else: - # add -signed to end. We won't offer to sign again. - target_fname = base+'-signed.psbt' - - for path in [orig_path, None]: - try: - with CardSlot(force_vdisk, readonly=True, slot_b=slot_b) as card: - out_full, out_fn = card.pick_filename(target_fname, path) - out_path = path - if out_full: break - except CardMissingError: - prob = 'Missing card.\n\n' - out_fn = None - - if not out_fn: - # need them to insert a card - prob = '' - else: - # attempt write-out - try: - with CardSlot(force_vdisk, slot_b=slot_b) as card: - if is_comp and del_after: - # don't write signed PSBT if we'd just delete it anyway - out_fn = None - else: - with output_encoder(card.open(out_full, 'wb')) as fd: - # save as updated PSBT - psbt.serialize(fd) - - if is_comp: - # write out as hex too, if it's final - out2_full, out2_fn = card.pick_filename( - base+'-final.txn' if not del_after else 'tmp.txn', out_path) - - with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as fd0: - txid = psbt.finalize(fd0) - fd0.flush_out() # need to flush here as we are probably not gona call .read( again - tx_len, tx_sha = fd0.tell(), fd0.checksum.digest() - if txid and await try_push_tx(tx_len, txid, tx_sha): - return # success, exit - - if out2_full: - fd0.seek(0) - - with HexWriter(card.open(out2_full, 'w+t')) as fd: - # save transaction, in hex - tmp_buf = bytearray(4096) - while True: - rv = fd0.readinto(tmp_buf) - if not rv: break - fd.write(memoryview(tmp_buf)[:rv]) - - if del_after: - # rename it now that we know the txid - after_full, out2_fn = card.pick_filename( - txid+'.txn', out_path, overwrite=True) - os.rename(out2_full, after_full) - - if del_after: - # this can do nothing if they swapped SDCard between steps, which is ok, - # but if the original file is still there, this blows it away. - # - if not yet final, the foo-part.psbt file stays - try: - card.securely_blank_file(filename) - except: pass - - # success and done! - break - - except OSError as exc: - prob = 'Failed to write!\n\n%s\n\n' % exc - sys.print_exception(exc) - # fall thru to try again - - if force_vdisk: - await ux_show_story(prob, title='Error') - return - - # prompt them to input another card? - ch = await ux_show_story(prob+"Please insert an SDCard to receive signed transaction, " - "and press %s." % OK, title="Need Card") - if ch == 'x': - await ux_aborted() - return - - # done. - if out_fn: - msg = "Updated PSBT is:\n\n%s" % out_fn - if out2_fn: - msg += '\n\n' - else: - # del_after is probably set - msg = '' - - if out2_fn: - msg += 'Finalized transaction (ready for broadcast):\n\n%s' % out2_fn - if txid and not del_after: - msg += '\n\nFinal TXID:\n'+txid - - await ux_show_story(msg, title='PSBT Signed') - - UserAuthorizedAction.cleanup() + if just_read: + return psbt_len UserAuthorizedAction.cleanup() - UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, approved_cb=done, - is_sd=not force_vdisk) - the_ux.push(UserAuthorizedAction.active_request) + UserAuthorizedAction.active_request = ApproveTransaction( + psbt_len, input_method="vdisk" if force_vdisk else "sd", + filename=filename, output_encoder=output_encoder, + miniscript_wallet=miniscript_wallet, + ) + if ux_abort: + # needed for auto vdisk mode + abort_and_push(UserAuthorizedAction.active_request) + else: + the_ux.push(UserAuthorizedAction.active_request) class RemoteBackup(UserAuthorizedAction): def __init__(self): @@ -1271,8 +1155,53 @@ async def interact(self): except BaseException as exc: self.failed = "Error during backup process." - print("Backup failure: ") - sys.print_exception(exc) + #print("Backup failure: ") + #sys.print_exception(exc) + finally: + self.done() + + +class RemoteRestoreBackup(UserAuthorizedAction): + def __init__(self, file_len, bitflag): + super().__init__() + self.file_len = file_len + self.custom_pwd = bitflag & 1 + self.plaintext = bitflag & 2 + self.force_tmp = bitflag & 4 + + def to_words(self): + # conversion to "words" argument of "restore_complete" function + if self.plaintext: + return None + elif self.custom_pwd: + return False + return True + + def to_tmp(self): + # conversion to "temporary" argument of "restore_complete" function + from pincodes import pa + if pa.is_secret_blank() and not self.force_tmp: + # no master secret & not forcing tmp + # will load backup as master seed + return False, "master" + + # has master secret --> load backup as tmp + # secret is blank but user forcing tmp + return True, "temporary" + + async def interact(self): + try: + # requires confirm from user + tmp, noun = self.to_tmp() + if await ux_confirm("Restore uploaded backup as a %s seed?" % noun): + from backups import restore_complete + await restore_complete(self.file_len, tmp, self.to_words(), usb=True) + else: + self.refused = True + + except BaseException as exc: + self.failed = "Error during backup restore." + # sys.print_exception(exc) finally: self.done() @@ -1287,6 +1216,12 @@ def start_remote_backup(): # kill any menu stack, and put our thing at the top abort_and_goto(UserAuthorizedAction.active_request) +def start_remote_restore_backup(file_len, bitflag): + UserAuthorizedAction.cleanup() + UserAuthorizedAction.active_request = RemoteRestoreBackup(file_len, bitflag) + # kill any menu stack, and put our thing at the top + abort_and_goto(UserAuthorizedAction.active_request) + class NewPassphrase(UserAuthorizedAction): def __init__(self, pw): @@ -1333,7 +1268,7 @@ async def interact(self): except BaseException as exc: self.failed = "Exception" - sys.print_exception(exc) + # sys.print_exception(exc) finally: self.done() @@ -1375,17 +1310,19 @@ async def interact(self): msg = self.get_msg() msg += '\n\nCompare this payment address to the one shown on your other, less-trusted, software.' + esc = "4" if not version.has_qwerty: if NFC: - msg += ' Press %s to share via NFC.' % (KEY_NFC if version.has_qwerty else "(3)") + msg += ' Press (3) to share via NFC.' + esc += "3" msg += ' Press (4) to view QR Code.' while 1: - ch = await ux_show_story(msg, title=self.title, escape='34', - hint_icons=KEY_QR+(KEY_NFC if NFC else '')) + ch = await ux_show_story(msg, title=self.title, escape=esc, + hint_icons=KEY_QR+(KEY_NFC if NFC else '')) if ch in '4'+KEY_QR: - await show_qr_code(self.address, (self.addr_fmt & AFC_BECH32)) + await show_qr_code(self.address, (self.addr_fmt & AFC_BECH32), is_addrs=True) continue if NFC and (ch in '3'+KEY_NFC): @@ -1396,7 +1333,7 @@ async def interact(self): else: # finish the Wait... - dis.progress_bar_show(1) + dis.progress_bar_show(1) if self.restore_menu: self.pop_menu() @@ -1417,60 +1354,39 @@ def setup(self, addr_fmt, subpath): self.address = sv.chain.address(node, addr_fmt) def get_msg(self): - return '''{addr}\n\n= {sp}''' .format(addr=self.address, sp=self.subpath) - - -class ShowP2SHAddress(ShowAddressBase): + return '''{addr}\n\n= {sp}''' .format(addr=show_single_address(self.address), + sp=self.subpath) - def setup(self, ms, addr_fmt, xfp_paths, witdeem_script): - self.witdeem_script = witdeem_script - self.addr_fmt = addr_fmt - self.ms = ms +class ShowMiniscriptAddress(ShowAddressBase): - # calculate all the pubkeys involved. - self.subpath_help = ms.validate_script(witdeem_script, xfp_paths=xfp_paths) + def setup(self, msc, change, idx): + self.msc = msc + self.change = change + self.idx = idx - self.address = ms.chain.p2sh_address(addr_fmt, witdeem_script) + d = self.msc.to_descriptor().derive(None, change=change).derive(idx) + self.address = self.msc.chain.render_address(d.script_pubkey()) + self.addr_fmt = self.msc.addr_fmt def get_msg(self): return '''\ {addr} Wallet: - {name} - {M} of {N} - -Paths: - -{sp}'''.format(addr=self.address, name=self.ms.name, - M=self.ms.M, N=self.ms.N, sp='\n\n'.join(self.subpath_help)) - -def start_show_p2sh_address(M, N, addr_format, xfp_paths, witdeem_script): - # Show P2SH address to user, also returns it. - # - first need to find appropriate multisig wallet associated - # - they must provide full redeem script, and we will re-verify it and check pubkeys inside it - from multisig import MultisigWallet - - try: - assert addr_format in SUPPORTED_ADDR_FORMATS - assert addr_format & AFC_SCRIPT - except: - raise AssertionError('Unknown/unsupported addr format') +Index: + {idx} - # Search for matching multisig wallet that we must already know about - xs = list(xfp_paths) - xs.sort() +Change: + {change}'''.format(addr=show_single_address(self.address), name=self.msc.name, + idx=self.idx, change=bool(self.change)) - ms = MultisigWallet.find_match(M, N, xs) - assert ms, 'Multisig wallet with those fingerprints not found' - assert ms.M == M - assert ms.N == N +def start_show_miniscript_address(msc, change, index): UserAuthorizedAction.check_busy(ShowAddressBase) - UserAuthorizedAction.active_request = ShowP2SHAddress(ms, addr_format, xfp_paths, witdeem_script) + UserAuthorizedAction.active_request = ShowMiniscriptAddress(msc, change, index) # kill any menu stack, and put our thing at the top abort_and_goto(UserAuthorizedAction.active_request) @@ -1478,6 +1394,7 @@ def start_show_p2sh_address(M, N, addr_format, xfp_paths, witdeem_script): # provide the value back to attached desktop return UserAuthorizedAction.active_request.address + def show_address(addr_format, subpath, restore_menu=False): try: assert addr_format in SUPPORTED_ADDR_FORMATS @@ -1505,64 +1422,112 @@ def usb_show_address(addr_format, subpath): return active_request.address -class NewEnrollRequest(UserAuthorizedAction): - def __init__(self, ms): +class MiniscriptDeleteRequest(UserAuthorizedAction): + def __init__(self, msc): super().__init__() - self.wallet = ms - # self.result ... will be re-serialized xpub + self.wallet = msc async def interact(self): - from multisig import MultisigOutOfSpace + from wallet import miniscript_delete + await miniscript_delete(self.wallet) + self.done() + + +def maybe_delete_miniscript(msc): + UserAuthorizedAction.cleanup() + UserAuthorizedAction.active_request = MiniscriptDeleteRequest(msc) + + # kill any menu stack, and put our thing at the top + abort_and_goto(UserAuthorizedAction.active_request) + +class NewMiniscriptEnrollRequest(UserAuthorizedAction): + def __init__(self, msc, bsms_index=None): + super().__init__() + self.wallet = msc + self.bsms_index = bsms_index + + async def interact(self): + from wallet import WalletOutOfSpace ms = self.wallet try: - ch = await ms.confirm_import() - - if ch != 'y': + approved = await ms.confirm_import() + if not approved: # they don't want to! self.refused = True await ux_dramatic_pause("Refused.", 2) - except MultisigOutOfSpace: + elif self.bsms_index is not None: + # remove signer round 2 from settings after multisig import is approved by user + from bsms import BSMSSettings + BSMSSettings.signer_delete(self.bsms_index) + + except WalletOutOfSpace: return await self.failure('No space left') except BaseException as exc: self.failed = "Exception" - sys.print_exception(exc) + # sys.print_exception(exc) finally: - UserAuthorizedAction.cleanup() # because no results to store - self.pop_menu() + UserAuthorizedAction.cleanup() # because no results to store + if self.bsms_index is not None: + # bsms special case, get him back to multisig menu + from ux import the_ux, restore_menu + from wallet import MiniscriptMenu + while 1: + top = the_ux.top_of_stack() + if not top: break + if not isinstance(top, MiniscriptMenu): + the_ux.pop() + continue + break + restore_menu() + else: + self.pop_menu() -def maybe_enroll_xpub(sf_len=None, config=None, name=None, ux_reset=False): - # Offer to import (enroll) a new multisig wallet. Allow reject by user. + +def maybe_enroll_xpub(sf_len=None, config=None, name=None, ux_reset=False, + bsms_index=None, desc_obj=None): + # Offer to import (enroll) a new multisig/miniscript wallet. Allow reject by user. from glob import dis - from multisig import MultisigWallet + from wallet import MiniScriptWallet UserAuthorizedAction.cleanup() - dis.fullscreen('Wait...') # needed + dis.fullscreen('Wait...') dis.busy_bar(True) + bip388 = False try: - if sf_len: - with SFFile(TXN_INPUT_OFFSET, length=sf_len) as fd: - config = fd.read(sf_len).decode() + if desc_obj: + # caller is sending us already validated descriptor object + assert name + msc = MiniScriptWallet.from_descriptor_obj(name, desc_obj) + else: + if sf_len: + with SFFile(TXN_INPUT_OFFSET, length=sf_len) as fd: + config = fd.read(sf_len).decode() - try: - j_conf = ujson.loads(config) - assert "desc" in j_conf, "'desc' key required" - config = j_conf["desc"] - assert config, "'desc' empty" + try: + j_conf = ujson.loads(config) + if "desc_template" in j_conf and "keys_info" in j_conf: + assert "name" in j_conf + config = j_conf + bip388 = True + else: + assert "desc" in j_conf, "'desc' key required" + config = j_conf["desc"] + assert config, "'desc' empty" - if "name" in j_conf: - # name from json has preference over filenames and desc checksum - name = j_conf["name"] - assert 2 <= len(name) <= 40, "'name' length" - except ValueError: pass + if "name" in j_conf: + # name from json has preference over filenames and desc checksum + name = j_conf["name"] + assert 2 <= len(name) <= 40, "'name' length" + except ValueError: pass - # this call will raise on parsing errors, so let them rise up - # and be shown on screen/over usb - ms = MultisigWallet.from_file(config, name=name) + # this call will raise on parsing errors, so let them rise up + # and be shown on screen/over usb + msc = MiniScriptWallet.from_file(config, name=name, bip388=bip388) - UserAuthorizedAction.active_request = NewEnrollRequest(ms) + UserAuthorizedAction.active_request = NewMiniscriptEnrollRequest(msc, bsms_index=bsms_index) if ux_reset: # for USB case, and import from PSBT @@ -1573,9 +1538,9 @@ def maybe_enroll_xpub(sf_len=None, config=None, name=None, ux_reset=False): from ux import the_ux the_ux.push(UserAuthorizedAction.active_request) finally: - # always finish busy bar dis.busy_bar(False) + class FirmwareUpgradeRequest(UserAuthorizedAction): def __init__(self, hdr, length, hdr_check=False, psram_offset=None): super().__init__() @@ -1636,7 +1601,7 @@ async def interact(self): except BaseException as exc: self.failed = "Exception" - sys.print_exception(exc) + # sys.print_exception(exc) finally: UserAuthorizedAction.cleanup() # because no results to store self.pop_menu() diff --git a/shared/backups.py b/shared/backups.py index f83a2b41c..5bc441233 100644 --- a/shared/backups.py +++ b/shared/backups.py @@ -5,16 +5,18 @@ import compat7z, stash, ckcc, chains, gc, sys, bip39, uos, ngu from ubinascii import hexlify as b2a_hex from ubinascii import unhexlify as a2b_hex -from utils import pad_raw_secret -from ux import ux_show_story, ux_confirm, ux_dramatic_pause, OK, X +from utils import deserialize_secret, swab32, xfp2str +from sffile import SFFile +from ux import ux_show_story, ux_confirm, ux_dramatic_pause, OK, X, ux_input_text import version, ujson -from uio import StringIO +from uio import StringIO, BytesIO import seed from glob import settings from pincodes import pa # we make passwords with this number of words num_pw_words = const(12) +bkpw_min_len = const(32) # max size we expect for a backup data file (encrypted or cleartext) # - limited by size of LFS area of flash, since all settings are held there @@ -43,12 +45,7 @@ def ADD(key, val): COMMENT('Private key details: ' + chain.name) - with stash.SensitiveValues(bypass_tmp=bypass_tmp) as sv: - if sv.deltamode: - # die rather than give up our secrets - import callgate - callgate.fast_wipe() - + with stash.SensitiveValues(bypass_tmp=bypass_tmp, enforce_delta=True) as sv: if sv.mode == 'words': ADD('mnemonic', bip39.b2a_words(sv.raw)) @@ -103,6 +100,9 @@ def ADD(key, val): if k == 'bkpw': continue # confusing/circular if k == 'sd2fa': continue # do NOT backup SD 2FA (card can be lost or damaged) if k == 'words': continue # words length is recalculated from secret + if k == 'ccc': continue # not supported, security issue + if k == 'ktrx': continue # not useful after the fact + if k == 'lfr': continue # temporary error msg value if k == 'seedvault' and not v: continue if k == 'seeds' and not v: continue ADD('setting.' + k, v) @@ -123,14 +123,14 @@ def ADD(key, val): return rv.getvalue() -def extract_raw_secret(chain, vals): +def extract_raw_secret(vals): # step1: the private key # - prefer raw_secret over other values # - TODO: fail back to other values assert 'raw_secret' in vals rs = vals.pop('raw_secret') - raw = pad_raw_secret(rs) + raw = deserialize_secret(rs) # check we can decode this right (might be different firmare) opmode, bits, node = stash.SecretStash.decode(raw) @@ -138,22 +138,23 @@ def extract_raw_secret(chain, vals): # verify against xprv value (if we have it) if 'xprv' in vals: - check_xprv = chain.serialize_private(node) + check_xprv = chains.get_chain(vals.get('chain', 'BTC')).serialize_private(node) assert check_xprv == vals['xprv'], 'xprv mismatch' - return raw + return raw, node def extract_long_secret(vals): ls = None if ('long_secret' in vals) and version.has_608: try: ls = a2b_hex(vals.pop('long_secret')) - except Exception as exc: - sys.print_exception(exc) + except: + # sys.print_exception(exc) # but keep going. + pass return ls -def restore_from_dict_ll(vals): +def restore_from_dict_ll(vals, raw): # Restore from a dict of values. Already JSON decoded. # Need a Reboot on success, return string on failure # - low-level version, factored out for better testing @@ -164,12 +165,6 @@ def restore_from_dict_ll(vals): #print("Restoring from: %r" % vals) chain = chains.get_chain(vals.get('chain', 'BTC')) - try: - raw = extract_raw_secret(chain, vals) - except Exception as e: - return ('Unable to decode raw_secret and ' - 'restore the seed value!\n\n\n'+str(e)), None - dis.fullscreen("Saving...") dis.progress_bar_show(.1) @@ -188,9 +183,7 @@ def restore_from_dict_ll(vals): if ls is not None: try: pa.ls_change(ls) - except Exception as exc: - sys.print_exception(exc) - # but keep going + except: pass # but keep going pb = .70 dis.progress_bar_show(pb) @@ -214,13 +207,17 @@ def restore_from_dict_ll(vals): # old backups need this to function properly continue + if k == 'ccc': + # CCC feature cannot be backed-up nor restored for security reasons + # (would allow replay attacks) + continue + if k == 'tp': # restore trick pins, which may involve many ops from trick_pins import tp try: tp.restore_backup(vals[key]) - except Exception as exc: - sys.print_exception(exc) + except: pass # continue as `tp.restore_backup` handles # saving into settings @@ -261,36 +258,50 @@ def restore_from_dict_ll(vals): return None, need_ftux -async def restore_tmp_from_dict_ll(vals): +def text_bk_parser(contents): + # given a (binary encoded) text file, decode into a dict of values + # - use json rules to decode the "value" sides + vals = {} + for line in contents.decode().split('\n'): + if not line: continue + if line[0] == '#': continue + + try: + k,v = line.split(' = ', 1) + #print("%s = %s" % (k, v)) + + vals[k] = ujson.loads(v) + except: + print("unable to decode line: %r" % line) + # but keep going! + + return vals + +async def restore_tmp_from_dict_ll(vals, raw): from glob import dis chain = chains.get_chain(vals.get('chain', 'BTC')) - try: - raw = extract_raw_secret(chain, vals) - except Exception as e: - return ('Unable to decode raw_secret and ' - 'restore the seed value!\n\n\n' + str(e)) dis.fullscreen("Applying...") from seed import set_ephemeral_seed from actions import goto_top_menu - await set_ephemeral_seed(raw, chain, meta="Coldcard Backup") + await set_ephemeral_seed(raw, chain, origin="Coldcard Backup") for k, v in vals.items(): if not k[:8] == "setting.": continue key = k[8:] - if key in ["multisig"]: + if key == "miniscript": # whitelist - settings.set(k, v) + settings.set(key, v) goto_top_menu() -async def restore_from_dict(vals): +async def restore_from_dict(vals, raw): # Restore from a dict of values. Already JSON decoded (ie. dict object). # Need a Reboot on success, return string on failure - prob, need_ftux = restore_from_dict_ll(vals) + prob, need_ftux = restore_from_dict_ll(vals, raw) if prob: return prob if need_ftux: @@ -309,7 +320,7 @@ async def restore_from_dict(vals): async def make_complete_backup(fname_pattern='backup.7z', write_sflash=False): from stash import bip39_passphrase - words = None + pwd = None skip_quiz = False bypass_tmp = False @@ -329,35 +340,49 @@ async def make_complete_backup(fname_pattern='backup.7z', write_sflash=False): "so backup will be of that seed."): return - stored_words = settings.get('bkpw', None) + # first check if bkpw already defined on tmp seed settings + stored_pwd = None + master_pwd = settings.master_get("bkpw", None) + if pa.tmp_value: + stored_pwd = settings.get('bkpw', None) - if stored_words: - stored_words = stored_words.split() - ch = await ux_show_story("Use same backup file password as last time?\n\n" - " 1: %s\n ...\n%d: %s" - % (stored_words[0], len(stored_words), stored_words[-1]), sensitive=True) + if not stored_pwd and master_pwd: + stored_pwd = master_pwd + + if stored_pwd: + # we can have words or other type of password here + split_pwd = stored_pwd.split() + if len(split_pwd) == num_pw_words: # weak + hint = " 1: %s\n ...\n%d: %s" % (split_pwd[0], len(split_pwd), split_pwd[-1]) + else: + hint = " %s...%s" % (stored_pwd[0], stored_pwd[-1]) + + ch = await ux_show_story("Use same backup file password as last time?\n\n" + hint, + sensitive=True) if ch == 'y': - words = stored_words + pwd = stored_pwd # string, not list skip_quiz = True - if not words: + if not pwd: # Pick a password: like bip39 but no checksum word # b = bytearray(32) while 1: ckcc.rng_bytes(b) - words = bip39.b2a_words(b).split(' ')[0:num_pw_words] + pwd = bip39.b2a_words(b).rsplit(' ', num_pw_words)[0] - ch = await seed.show_words(words, - prompt="Record this (%d word) backup file password:\n", escape='6') + ch = await seed.show_words( + prompt="Record this (%d word) backup file password:\n" % num_pw_words, + words=pwd.split(" "), escape='6' + ) - if ch == '6' and not write_sflash: + if (ch == '6') and not write_sflash: # Secret feature: plaintext mode # - only safe for people living in faraday cages inside locked vaults. if await ux_confirm("The file will **NOT** be encrypted and " "anyone who finds the file will get all of your money for free!"): - words = [] + pwd = [] fname_pattern = 'backup.txt' break continue @@ -367,43 +392,43 @@ async def make_complete_backup(fname_pattern='backup.7z', write_sflash=False): break - if words and not skip_quiz: + if pwd and not skip_quiz: # quiz them, but be nice and do a shorter test. - ch = await seed.word_quiz(words, limited=(num_pw_words//3)) + ch = await seed.word_quiz(pwd.split(" "), limited=(num_pw_words//3)) if ch == 'x': return - if words and words != stored_words: + if pwd and pwd != stored_pwd: ch = await ux_show_story("Would you like to use these same words next time you perform a backup?" " Press (1) to save them into this Coldcard for next time.", escape='1') if ch == '1': - settings.put('bkpw', ' '.join(words)) - settings.save() - elif stored_words: - settings.remove_key('bkpw') + settings.set('bkpw', pwd) # if on tmp save to tmp, do not update master settings.save() + # stop droping bkpw just because someone decided to use differrent password + # elif stored_words: + # settings.remove_key('bkpw') + # settings.save() - return await write_complete_backup(words, fname_pattern, write_sflash=write_sflash, + return await write_complete_backup(pwd, fname_pattern, write_sflash=write_sflash, bypass_tmp=bypass_tmp) -async def write_complete_backup(words, fname_pattern, write_sflash=False, +async def write_complete_backup(pwd, fname_pattern, write_sflash=False, allow_copies=True, bypass_tmp=False): # Just do the writing from glob import dis from files import CardSlot # Show progress: - dis.fullscreen('Encrypting...' if words else 'Generating...') + dis.fullscreen('Encrypting...' if pwd else 'Generating...') body = render_backup_contents(bypass_tmp=bypass_tmp).encode() gc.collect() - if words: + if pwd: # NOTE: Takes a few seconds to do the key-streching, but little actual # time to do the encryption. - pw = ' '.join(words) - zz = compat7z.Builder(password=pw, progress_fcn=dis.progress_bar_show) + zz = compat7z.Builder(password=pwd, progress_fcn=dis.progress_bar_show) zz.add_data(body) # pick random filename, but ending in .txt @@ -422,8 +447,6 @@ async def write_complete_backup(words, fname_pattern, write_sflash=False, if write_sflash: # for use over USB and unit testing: commit file into PSRAM - from sffile import SFFile - with SFFile(0, max_size=MAX_BACKUP_FILE_SIZE, message='Saving...') as fd: if zz: fd.write(hdr) @@ -452,11 +475,9 @@ async def write_complete_backup(words, fname_pattern, write_sflash=False, except Exception as e: # includes CardMissingError - import sys - sys.print_exception(e) # catch any error ch = await ux_show_story('Failed to write! Please insert formated MicroSD card, ' - 'and press %s to try again.\n\nX to cancel.\n\n\n' % OK +str(e)) + 'and press %s to try again.\n\n%s to cancel.\n\n\n%s' % (OK, X, e)) if ch == 'x': break continue @@ -522,103 +543,157 @@ async def verify_backup_file(fname): await ux_show_story("Backup file CRC checks out okay.\n\nPlease note this is only a check against accidental truncation and similar. Targeted modifications can still pass this test.") -async def restore_complete(fname_or_fd, temporary=False): +async def restore_complete(fname_or_fd, temporary=False, words=True, usb=False): from ux import the_ux async def done(words): # remove all pw-picking from menu stack - seed.WordNestMenu.pop_all() + if not version.has_qwerty and words: + seed.WordNestMenu.pop_all() prob = await restore_complete_doit(fname_or_fd, words, temporary=temporary) - if prob: await ux_show_story(prob, title='FAILED') - if version.has_qwerty: - from ux_q1 import seed_word_entry - return await seed_word_entry('Enter Password:', num_pw_words, - done_cb=done, has_checksum=False) - # give them a menu to pick from, and start picking - m = seed.WordNestMenu(num_words=num_pw_words, has_checksum=False, done_cb=done) + if words: + if version.has_qwerty: + from ux_q1 import seed_word_entry, CHARS_W + + basename = None + if isinstance(fname_or_fd, str): + basename = fname_or_fd.split('/')[-1] + if len(basename) > CHARS_W: + basename = basename[:16] + "⋯" + basename[-16:] + + return await seed_word_entry("Enter Password%s:" % (" for" if basename else ""), + num_pw_words, done_cb=done, has_checksum=False, + line2=basename) + + # give them a menu to pick from, and start picking + if usb: + # we're not originating from a menu + words = await seed.WordNestMenu.get_n_words(12) + await done(words) + else: + m = seed.WordNestMenu(num_words=num_pw_words, has_checksum=False, done_cb=done) + the_ux.push(m) + + else: + pwd = [] # cleartext if words=None + if words is False: + ipw = await ux_input_text("", prompt="Your Backup Password", + min_len=bkpw_min_len, max_len=128) + if not ipw: return + pwd.append(ipw) + + await done(pwd) + + +def check_and_decrypt(fd, password): + try: + compat7z.check_file_headers(fd) + except Exception as e: + raise RuntimeError('Unable to read backup file.' + ' Has it been touched?\n\nError: '+str(e)) + + from glob import dis + dis.fullscreen("Decrypting...") + try: + zz = compat7z.Builder() + fname, contents = zz.read_file(fd, password, MAX_BACKUP_FILE_SIZE, + progress_fcn=dis.progress_bar_show) + + # simple quick sanity checks + assert fname.endswith('.txt') # was == 'ckcc-backup.txt' + assert contents[0:1] == b'#' and contents[-1:] == b'\n' + return contents + + except Exception as e: + # assume everything here is "password wrong" errors + raise RuntimeError('Unable to decrypt backup file. Incorrect password?' + '\n\nTried:\n\n' + password) - the_ux.push(m) -async def restore_complete_doit(fname_or_fd, words, file_cleanup=None, temporary=False): +async def restore_complete_doit(fname_or_fd, words, file_cleanup=None, temporary=False, + ux_confirm=True): # Open file, read it, maybe decrypt it; return string if any error # - some errors will be shown, None return in that case # - no return if successful (due to reboot) - from glob import dis from files import CardSlot, CardMissingError, needs_microsd # build password password = ' '.join(words) - prob = None - try: - with CardSlot(readonly=True) as card: - # filename already picked, taste it and maybe consider using its data. - try: - fd = open(fname_or_fd, 'rb') if isinstance(fname_or_fd, str) else fname_or_fd - except: - return 'Unable to open backup file.\n\n' + str(fname_or_fd) + if isinstance(fname_or_fd, int): + # USB restore - backup is already in PSRAM, fname of fd is length + # TXN_INPUT_OFFSET = 0 + with SFFile(0, length=fname_or_fd) as fd: + if not words: + contents = fd.read(fname_or_fd) + else: + # read full size, then decrypt + fd = BytesIO(fd.read(fname_or_fd)) + try: + contents = check_and_decrypt(fd, password) + except RuntimeError as e: + return str(e) + else: + try: + with CardSlot(readonly=True) as card: + # filename already picked, taste it and maybe consider using its data. + try: + fd = open(fname_or_fd, 'rb') + except: + return 'Unable to open backup file.\n\n' + str(fname_or_fd) + + try: + if words: + contents = check_and_decrypt(fd, password) + else: + contents = fd.read() - try: - if not words: - contents = fd.read() - else: - try: - compat7z.check_file_headers(fd) - except Exception as e: - return 'Unable to read backup file. Has it been touched?\n\nError: ' \ - + str(e) - - dis.fullscreen("Decrypting...") - try: - zz = compat7z.Builder() - fname, contents = zz.read_file(fd, password, MAX_BACKUP_FILE_SIZE, - progress_fcn=dis.progress_bar_show) - - # simple quick sanity checks - assert fname.endswith('.txt') # was == 'ckcc-backup.txt' - assert contents[0:1] == b'#' and contents[-1:] == b'\n' - - except Exception as e: - # assume everything here is "password wrong" errors - #print("pw wrong? %s" % e) - - return ('Unable to decrypt backup file. Incorrect password?' - '\n\nTried:\n\n' + password) - finally: - fd.close() + except RuntimeError as e: + return str(e) + finally: + fd.close() if file_cleanup: file_cleanup(fname_or_fd) - except CardMissingError: - await needs_microsd() - return - - vals = {} - for line in contents.decode().split('\n'): - if not line: continue - if line[0] == '#': continue + except CardMissingError: + await needs_microsd() + return - try: - k,v = line.split(' = ', 1) - #print("%s = %s" % (k, v)) + try: + vals = text_bk_parser(contents) + except: + return "Invalid backup file." - vals[k] = ujson.loads(v) - except: - print("unable to decode line: %r" % line) - # but keep going! + try: + raw, node = extract_raw_secret(vals) + except Exception as e: + return ('Unable to decode raw_secret and ' + 'restore the seed value!\n\n\n'+str(e)) + + if ux_confirm: + # check master fingerprint from raw secret that is actually being loaded + # master extended public keys can be wrong & is unverified + xfp_str = xfp2str(swab32(node.my_fp())) + ch = await ux_show_story("Above is the master fingerprint of the seed stored in the backup." + " Press %s to continue, and load backup as %s seed. Press %s" + " to abort." % (OK, "temporary" if temporary else "master", X), + title="["+xfp_str+"]") + if ch != "y": + await ux_dramatic_pause('Aborted.', 2) + return # this leads to reboot if it works, else errors shown, etc. if temporary: - return await restore_tmp_from_dict_ll(vals) + return await restore_tmp_from_dict_ll(vals, raw) else: - return await restore_from_dict(vals) + return await restore_from_dict(vals, raw) async def clone_start(*a): # Begins cloning process, on target device. @@ -701,8 +776,9 @@ def delme(xfn): uos.remove(fname) # ccbk-start.json # this will reset in successful case, no return (but delme is called) - prob = await restore_complete_doit(incoming, words, file_cleanup=delme) - + # no need to ask for UX confirmation during clone - as user can see what is loaded on source CC + prob = await restore_complete_doit(incoming, words, file_cleanup=delme, + ux_confirm=False) if prob: await ux_show_story(prob, title='FAILED') @@ -742,11 +818,9 @@ async def clone_write_data(*a): my_pubkey = pair.pubkey().to_bytes(False) session_key = pair.ecdh_multiply(his_pubkey) - words = [b2a_hex(session_key).decode()] - fname = b2a_hex(my_pubkey).decode() + '-ccbk.7z' - await write_complete_backup(words, fname, allow_copies=False, bypass_tmp=True) + await write_complete_backup(b2a_hex(session_key).decode(), fname, allow_copies=False, bypass_tmp=True) await ux_show_story("Done.\n\nTake this MicroSD card back to other Coldcard and continue from there.") diff --git a/shared/bbqr.py b/shared/bbqr.py index 61a90fb9d..c684e0b86 100644 --- a/shared/bbqr.py +++ b/shared/bbqr.py @@ -6,12 +6,14 @@ from utils import problem_file_line from exceptions import QRDecodeExplained from ubinascii import unhexlify as a2b_hex +from version import MAX_TXN_LEN b32encode = ngu.codecs.b32_encode b32decode = ngu.codecs.b32_decode TYPE_LABELS = dict(P='PSBT File', T='Transaction', J='JSON', C='CBOR', U='Unicode Text', - X='Executable', B='Binary') + X='Executable', B='Binary', + R='KT Rx', S='KT Tx', E='KT PSBT') def int2base36(n): # convert an integer to two digits of base 36 string. 00 thu ZZ as bytes @@ -212,7 +214,7 @@ def collect(self, scan): # can happen if QR got corrupted between scanner and us (overlap) # or back BBQr implementation #print("corrupt QR: %s" % scan) - import sys; sys.print_exception(exc) + # import sys; sys.print_exception(exc) dis.draw_bbqr_progress(hdr, self.parts, corrupt=True) return True @@ -241,7 +243,7 @@ def collect(self, scan): # provide UX -- even if we didn't use it dis.draw_bbqr_progress(hdr, self.parts) - # do we need more still? + # return T if we need more parts still return (len(self.parts) < hdr.num_parts) or self.runt class BBQrStorage: @@ -328,14 +330,12 @@ def __init__(self): def alloc_buf(self, upper_bound): # using first part of PSRAM - from public_constants import MAX_TXN_LEN_MK4 - - if upper_bound >= MAX_TXN_LEN_MK4: + if upper_bound >= MAX_TXN_LEN: raise QRDecodeExplained("Too big") # If data is compressed, write tmp (compressed) copy into top half of PSRAM # and we'll put final, decompressed copy at zero offset (later) - self.psr_offset = MAX_TXN_LEN_MK4 if self.hdr.encoding == 'Z' else 0 + self.psr_offset = MAX_TXN_LEN if self.hdr.encoding == 'Z' else 0 self.buf = True @@ -394,7 +394,6 @@ def zlib_decompress(self): from glob import PSRAM, dis from uzlib import DecompIO from io import BytesIO - from public_constants import MAX_TXN_LEN_MK4 dis.fullscreen('Decompressing...') @@ -414,7 +413,7 @@ def zlib_decompress(self): buf += here ln = len(buf) & ~3 - if off+ln > MAX_TXN_LEN_MK4: + if off+ln > MAX_TXN_LEN: # test with: `yes | dd bs=1000 count=2700 | bbqr make - | pbcopy` raise QRDecodeExplained("Too big") diff --git a/shared/bsms.py b/shared/bsms.py new file mode 100644 index 000000000..2e788fc44 --- /dev/null +++ b/shared/bsms.py @@ -0,0 +1,1062 @@ + +# (c) Copyright 2022 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# bsms.py - Bitcoin Secure Multisig Setup: BIP-129 +# +# For faster testing... +# ./simulator.py --seq 99y3y4y +# +import ngu, os, stash, chains, aes256ctr, version +from ubinascii import b2a_base64, a2b_base64 +from ubinascii import unhexlify as a2b_hex +from ubinascii import hexlify as b2a_hex + +from public_constants import AF_P2WSH, AF_P2WSH_P2SH, AF_CLASSIC, MAX_SIGNERS +from utils import xfp2str, problem_file_line +from menu import MenuSystem, MenuItem +from files import CardSlot, CardMissingError, needs_microsd +from ux import ux_show_story, ux_enter_number, restore_menu, ux_input_text +from ux import the_ux, _import_prompt_builder, export_prompt_builder +from descriptor import Descriptor, Key, append_checksum +from miniscript import Sortedmulti, Number +from charcodes import KEY_NFC, KEY_QR + + +BSMS_VERSION = "BSMS 1.0" +ALLOWED_PATH_RESTRICTIONS = "/0/*,/1/*" + +ENCRYPTION_TYPES = { + "1": "STANDARD", + "2": "EXTENDED", + "3": "NO ENCRYPTION" +} + +class RejectAutoCollection(BaseException): + pass + +class BSMSOutOfSpace(RuntimeError): + # should not be a concern on Mk4 and later; just in case, handle well. + pass + +def exceptions_handler(f): + nice_name = " ".join(f.__name__.split("_")).replace("bsms", "BSMS") + async def new_func(*args): + try: + await f(*args) + except BaseException as e: + await ux_show_story(title="FAILURE", msg='%s\n\n%s failed\n%s' % (e, nice_name, problem_file_line(e))) + return new_func + + +def normalize_token(token_hex): + if token_hex[:2] in ["0x", "0X"]: + token_hex = token_hex[2:] # remove 0x prefix + return token_hex + + +def validate_token(token_hex): + if token_hex == "00": + return + try: + int(token_hex, 16) + except: + raise ValueError("Invalid token: %s" % token_hex) + if len(token_hex) not in [16, 32]: + raise ValueError("Invalid token length. Expected 64 or 128 bits (16 or 32 hex characters)") + + +def key_derivation_function(token_hex): + if token_hex == "00": + return + return ngu.hash.pbkdf2_sha512("No SPOF", a2b_hex(token_hex), 2048)[:32] + + +def hmac_key(key): + return ngu.hash.sha256s(key) + + +def msg_auth_code(key, token_hex, data): + msg_str = token_hex + data + msg_bytes = bytes(msg_str, "utf-8") + return ngu.hmac.hmac_sha256(key, msg_bytes) + + +def bsms_decrypt(key, data_bytes): + mac, ciphertext = data_bytes[:32], data_bytes[32:] + iv = mac[:16] + decrypt = aes256ctr.new(key, iv) + decrypted = decrypt.cipher(ciphertext) + try: + plaintext = decrypted.decode() + if not plaintext.startswith("BSMS"): + raise ValueError + return plaintext + except: + # failed decryption + return "" + + +def bsms_encrypt(key, token_hex, data_str): + hmac_k = hmac_key(key) + mac = msg_auth_code(hmac_k, token_hex, data_str) + iv = mac[:16] + encrypt = aes256ctr.new(key, iv) + ciphertext = encrypt.cipher(data_str) + + return mac + ciphertext + + +def signer_data_round1(token_hex, desc_type_key, key_description, sig_bytes=None): + result = "%s\n" % BSMS_VERSION + result += "%s\n" % token_hex + result += "%s\n" % desc_type_key + result += "%s" % key_description + + if sig_bytes: + sig = b2a_base64(sig_bytes).decode().strip() + result += "\n" + sig + + return result + + +def coordinator_data_round2(desc_template, addr, path_restrictions=ALLOWED_PATH_RESTRICTIONS): + result = "%s\n" % BSMS_VERSION + result += "%s\n" % desc_template + result += "%s\n" % path_restrictions + result += "%s" % addr + + return result + + +def token_summary(tokens): + if len(tokens) == 1: + return tokens[0] + + numbered_tokens = ["%d. %s" % (i, token) for i, token in enumerate(tokens, start=1)] + return "\n\n".join(numbered_tokens) + + +def coordinator_summary(M, N, addr_fmt, et, tokens): + addr_fmt_str = "p2wsh" if addr_fmt == AF_P2WSH else "p2sh-p2wsh" + summary = "%d of %d\n\n" % (M, N) + summary += "Address format:\n%s\n\n" % addr_fmt_str + summary += "Encryption type:\n%s\n\n" % ENCRYPTION_TYPES[et] + + if tokens: + summary += "Tokens:\n" + token_summary(tokens) + "\n\n" + + return summary + + +class BSMSSettings: + # keys in settings object + BSMS_SETTINGS = "bsms" + BSMS_SIGNER_SETTINGS = "s" + BSMS_COORD_SETTINGS = "c" + + @classmethod + def save(cls, updated_settings, orig): + try: + updated_settings.save() + except: + # back out change; no longer sure of NVRAM state + try: + updated_settings.set(cls.BSMS_SETTINGS, orig) + updated_settings.save() + except: + pass # give up on recovery + raise BSMSOutOfSpace + + @classmethod + def add(cls, who, value): + from glob import settings + + settings_bsms = settings.get(cls.BSMS_SETTINGS, {}) + orig = settings_bsms.copy() + if who in settings_bsms: + settings_bsms[who].append(value) + else: + settings_bsms[who] = [value] + + settings.set(cls.BSMS_SETTINGS, settings_bsms) + cls.save(settings, orig) + + @classmethod + def delete(cls, who, index): + from glob import settings + + settings_bsms = settings.get(cls.BSMS_SETTINGS, {}) + orig = settings_bsms.copy() + if who in settings_bsms: + try: + settings_bsms[who].pop(index) + settings.set(cls.BSMS_SETTINGS, settings_bsms) + cls.save(settings, orig) + except IndexError: + pass + + @classmethod + def signer_add(cls, token_hex): + cls.add(cls.BSMS_SIGNER_SETTINGS, token_hex) + + @classmethod + def coordinator_add(cls, config_tuple): + cls.add(cls.BSMS_COORD_SETTINGS, config_tuple) + + @classmethod + def signer_delete(cls, index): + cls.delete(cls.BSMS_SIGNER_SETTINGS, index) + + @classmethod + def coordinator_delete(cls, index): + cls.delete(cls.BSMS_COORD_SETTINGS, index) + + @classmethod + def get(cls): + from glob import settings + return settings.get(cls.BSMS_SETTINGS, {}) + + @classmethod + def get_signers(cls): + bsms = cls.get() + return bsms.get(cls.BSMS_SIGNER_SETTINGS, []) + + @classmethod + def get_coordinators(cls): + bsms = cls.get() + return bsms.get(cls.BSMS_COORD_SETTINGS, []) + + +class BSMSMenu(MenuSystem): + @classmethod + def construct(cls): + raise NotImplementedError + + def update_contents(self): + tmp = self.construct() + self.replace_items(tmp) + + +async def user_delete_signer_settings(menu, label, item): + index = item.arg + BSMSSettings.signer_delete(index) + the_ux.pop() + restore_menu() + +async def bsms_signer_detail(menu, label, item): + token_hex = BSMSSettings.get_signers()[item.arg] + # shoulf not raise here, as token is only saved if properly validated + token_dec = str(int(token_hex, 16)) + await ux_show_story("Token HEX:\n%s\n\nToken decimal:\n%s" % (token_hex, token_dec)) + + +async def bsms_coordinator_detail(menu, label, item): + M, N, addr_fmt, et, tokens = BSMSSettings.get_coordinators()[item.arg] + summary = coordinator_summary(M, N, addr_fmt, et, tokens) + await ux_show_story(title="SUMMARY", msg=summary) + + +async def make_bsms_signer_r2_menu(menu, label, item): + index = item.arg + rv = [ + MenuItem('Round 2', f=bsms_signer_round2, arg=index), + MenuItem('Detail', f=bsms_signer_detail, arg=index), + MenuItem('Delete', f=user_delete_signer_settings, arg=index), + ] + return rv + + +class BSMSSignerMenu(BSMSMenu): + @classmethod + def construct(cls): + # Dynamic + rv = [] + signers = BSMSSettings.get_signers() + if signers: + for i, token_hex in enumerate(signers): + label = "%d %s" % (i+1, token_hex[:4]) + rv.append(MenuItem('%s' % label, menu=make_bsms_signer_r2_menu, arg=i)) + rv.append(MenuItem('Round 1', f=bsms_signer_round1)) + + return rv + + +async def user_delete_coordinator_settings(menu, label, item): + index = item.arg + BSMSSettings.coordinator_delete(index) + the_ux.pop() + restore_menu() + + +async def make_bsms_coord_r2_menu(menu, label, item): + index = item.arg + rv = [ + MenuItem('Round 2', f=bsms_coordinator_round2, arg=index), + MenuItem('Detail', f=bsms_coordinator_detail, arg=index), + MenuItem('Delete', f=user_delete_coordinator_settings, arg=index), + ] + return rv + + +class BSMSCoordinatorMenu(BSMSMenu): + @classmethod + def construct(cls): + # Dynamic + rv = [] + coordinators = BSMSSettings.get_coordinators() + if coordinators: + for i, (M, N, addr_fmt, et, tokens) in enumerate(coordinators): + # only p2wsh and p2sh-p2wsh are allowed + if addr_fmt == AF_P2WSH: + af_str = "native" + else: + af_str = "nested" + label = "%d %dof%d_%s_%s" % (i+1, M, N, af_str, et) + rv.append(MenuItem('%s' % label, menu=make_bsms_coord_r2_menu, arg=i)) + rv.append(MenuItem('Create BSMS', f=bsms_coordinator_start)) + + return rv + + +async def make_ms_wallet_bsms_menu(menu, label, item): + from pincodes import pa + + if pa.is_secret_blank(): + await ux_show_story("You must have wallet seed before creating multisig wallets.") + return + + await ux_show_story( +"Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets. " +"On the next screen you choose your role in this process.\n\n" +"WARNING: BSMS is an EXPERIMENTAL and BETA feature which requires supporting implementations " +"on other signing devices to work properly. Please test the final wallet carefully " +"and report any problems to appropriate vendor. Deposit only small test amounts and verify " +"all co-signers can sign transactions before use.") + rv = [ + MenuItem('Signer', menu=make_bsms_signer_menu), + MenuItem('Coordinator', menu=make_bsms_coordinator_menu), + ] + return rv + + +async def make_bsms_signer_menu(menu, label, item): + rv = BSMSSignerMenu.construct() + return BSMSSignerMenu(rv) + + +async def make_bsms_coordinator_menu(menu, label, item): + rv = BSMSCoordinatorMenu.construct() + return BSMSCoordinatorMenu(rv) + + +async def decrypt_nfc_data(key, data): + try: + data_bytes = a2b_hex(data) + data = bsms_decrypt(key, data_bytes) + return data + except: + # will be offered another chance + return + +@exceptions_handler +async def bsms_coordinator_start(*a): + from glob import NFC, dis, settings + xfp = xfp2str(settings.get('xfp', 0)) + # M/N + N = await ux_enter_number('No. of signers?(N)', 15) + assert 2 <= N <= MAX_SIGNERS, "Number of co-signers must be 2-15" + + M = await ux_enter_number("Threshold? (M)", 15) + assert 1 <= M <= N, "M cannot be bigger than N (N=%d)" % N + + ch = await ux_show_story("Default address format is P2WSH.\n\n" + "Press (2) for P2SH-P2WSH instead.", escape='2') + if ch == 'y': + addr_fmt = AF_P2WSH + elif ch == '2': + addr_fmt = AF_P2WSH_P2SH + else: + return + + while 1: + encryption_type = await ux_show_story( + "Choose encryption type. Press (1) for STANDARD encryption, (2) for EXTENDED," + " and (3) for no encryption", escape="123") + + if encryption_type == 'x': return + if encryption_type in "123": + break + + tokens = [] + if encryption_type == "2": + dis.fullscreen('Generating...') + for i in range(N): # each signer different 16 bytes (128bits) nonce/token + tokens.append(b2a_hex(ngu.random.bytes(16)).decode()) + dis.progress_bar_show(i / N) + elif encryption_type == "1": + tokens.append(b2a_hex(ngu.random.bytes(8)).decode()) # all signers same token + + summary = coordinator_summary(M, N, addr_fmt, encryption_type, tokens) + summary += "Press OK to continue, or X to cancel" + ch = await ux_show_story(title="SUMMARY", msg=summary) + if ch != "y": + return + + token_hex = "00" if not tokens else tokens[0] + ch = await ux_show_story("Press (1) to participate as co-signer in this BSMS " + "with current active key [%s] and token '%s'. " + "Press OK to continue normally." % (xfp, token_hex), escape="1") + export_tokens = tokens[:] + if ch == "1": + b4 = len(BSMSSettings.get_signers()) + await bsms_signer_round1(token_hex) + current = BSMSSettings.get_signers() + if len(current) > b4 and token_hex in current: + if encryption_type == "2": + # remove 0th token from the list as we already used that for self + # we do not need this token for export, but still need to store it in settings + export_tokens = tokens[1:] + + force_vdisk = False + title = "BSMS token file(s)" + prompt, escape = export_prompt_builder(title) + if tokens and prompt: + ch = await ux_show_story(prompt, escape=escape) + if ch == (KEY_NFC if version.has_qwerty else '3') and tokens: + force_vdisk = None + await NFC.share_text(token_summary(export_tokens)) + elif ch == "2": + force_vdisk = True + elif ch == '1': + force_vdisk = False + else: + return + + msg = "Success. Coordinator round 1 saved." + if tokens and force_vdisk is not None: + dis.fullscreen("Saving...") + f_pattern = "bsms" + f_names = [] + try: + with CardSlot(force_vdisk=force_vdisk) as card: + for i, token in enumerate(export_tokens, start=1): + f_name = "%s_%s.token" % (f_pattern, token[:4]) + fname, nice = card.pick_filename(f_name) + with open(fname, 'wt') as fd: + fd.write(token) + f_names.append(nice) + dis.progress_bar_show(i / len(tokens)) + except CardMissingError: + await needs_microsd() + return + except Exception as e: + await ux_show_story('Failed to write!\n\n\n' + str(e)) + return + msg = '''%s written.\n\nFiles:\n\n%s''' % (title, "\n\n".join(f_names)) + + BSMSSettings.coordinator_add((M, N, addr_fmt, encryption_type, tokens)) + await ux_show_story(msg) + restore_menu() + + +async def nfc_import_signer_round1_data(N, tkm, et, get_token_func): + from glob import NFC + + all_data = [] + for i in range(N): + token = get_token_func(i) + for attempt in range(2): + prompt = "Share co-signer #%d round-1 data" % (i + 1) + if et == "2": + prompt += " for token starting with %s" % token[:4] + ch = await ux_show_story(prompt) + if ch != "y": + return + + data = await NFC.read_bsms_data() + if et in "12": + encryption_key = key_derivation_function(token) + data = await decrypt_nfc_data(encryption_key, data) + if not data: + fail_msg = "Decryption failed for co-signer #%d" % (i + 1) + if et == "2": + fail_msg += " with token %s" % token[:4] + ch = await ux_show_story( + title="FAILURE", + msg=fail_msg + ". Try again?" if attempt == 0 else fail_msg) # second chance + if ch == "y" and attempt == 0: + continue + else: + return + tkm[token] = encryption_key + + all_data.append(data) + break # exit "second chance" loop + return all_data + +@exceptions_handler +async def bsms_coordinator_round2(menu, label, item): + import version as version_mod + from glob import NFC, dis + from actions import file_picker + + bsms_settings_index = item.arg + chain = chains.current_chain() + + force_vdisk = False + + # this can be RAM intensive (max 15 F mapped to keys) + # => ((32 + 16) * 15) roughly (actually more with python overhead) + token_key_map = {} + + # choose correct values based on label (index in coordinator bsms settings) + M, N, addr_fmt, et, tokens = BSMSSettings.get_coordinators()[bsms_settings_index] + + def get_token(index): + if len(tokens) == 1 and et == "1": + token = tokens[0] + elif len(tokens) == N and et == "2": + token = tokens[index] + else: + token = "00" + return token + + is_encrypted = et in "12" and tokens + suffix = ".dat" if is_encrypted else ".txt" + mode = "rb" if is_encrypted else "rt" + prompt, escape = _import_prompt_builder("co-signer round 1 files", False, False) + if prompt: + ch = await ux_show_story(prompt, escape=escape) + if ch == (KEY_NFC if version_mod.has_qwerty else '3'): + force_vdisk = None + r1_data = await nfc_import_signer_round1_data(N, token_key_map, et, get_token) + else: + if ch == "1": + force_vdisk = False + else: + force_vdisk = True + + if force_vdisk is not None: + # auto-collection attempt + r1_data = [] + try: + f_pattern = "bsms_sr1" + auto_msg = "Press OK to pick co-signer round 1 files manually, or press (1) to attempt auto-collection." + auto_msg += " For auto-collection to succeed all filenames have to start with '%s'" % f_pattern + auto_msg += " and end with extension '%s'." % suffix + if et == "2": # EXTENDED + auto_msg += (" In addition for EXTENDED encryption all files must contain first four characters of" + " respective token. For example '%s_af9f%s'." % (f_pattern, suffix)) + elif et == "3": # NO_ENCRYPTION + auto_msg += (" In addition for NO ENCRYPTION cases, number of files with above mentioned" + " pattern and suffix must equal number of signers (N).") + auto_msg += " If above is not respected auto-collection fails and defaults to manual selection of files." + ch = await ux_show_story(auto_msg, escape="1") + if ch == "x": return # exit + if ch == "y": raise RejectAutoCollection + # try autodiscovery first - if failed - default to manual input + dis.fullscreen("Collecting...") + file_names = [] + with CardSlot(force_vdisk=force_vdisk) as card: + f_list = os.listdir(card.mountpt) + f_list_len = len(f_list) + for i, name in enumerate(f_list, start=1): + if not card.is_dir(name) and f_pattern in name and name.endswith(suffix): + file_names.append(name) + dis.progress_bar_show(i / f_list_len) + file_names_len = len(file_names) + dis.fullscreen("Validating...") + if et == "1": + # can have multiple of these files - we will try to decrypt all that + # have above pattern. Those that fail will be ignored and at the end + # we check if we have correct num of files (num==N) + token = get_token(0) # STANDARD encryption has just one token + encryption_key = key_derivation_function(token) + token_key_map[token] = encryption_key + + with CardSlot(force_vdisk=force_vdisk) as card: + for i, fname in enumerate(file_names, start=1): + with open(card.abs_path(fname), mode) as f: + data = f.read() + data = bsms_decrypt(encryption_key, data) + if not data: + continue + + assert data.startswith("BSMS"), "Failure - not BSMS file?" + r1_data.append(data) + dis.progress_bar_show(i / file_names_len) + + elif et == "2": + with CardSlot(force_vdisk=force_vdisk) as card: + for i in range(N): + token = get_token(i) + for fname in file_names: + if token[:4] in fname: + with open(card.abs_path(fname), mode) as f: + data = f.read() + encryption_key = key_derivation_function(token) + data = bsms_decrypt(encryption_key, data) + + assert data, "Failed to decrypt %s with token %s" % (fname, token) + assert data.startswith("BSMS"), "Failure - not BSMS file?" + token_key_map[token] = encryption_key + r1_data.append(data) + + break + else: + assert False, "haven't find file for token %s" % token + + dis.progress_bar_show(i / N) + else: + assert file_names_len == N, "Need same number of files (%d) as co-signers(N=%d)"\ + % (file_names_len, N) + + with CardSlot(force_vdisk=force_vdisk) as card: + for i, fname in enumerate(file_names, start=1): + with open(card.abs_path(fname), mode) as f: + data = f.read() + assert data.startswith("BSMS"), "Failure - not BSMS file?" + r1_data.append(data) + dis.progress_bar_show(i / file_names_len) + + assert len(r1_data) == N, "No. of signer round 1 data auto-collected "\ + "does not equal number of signers (N)" + except BaseException as e: + if isinstance(e, RejectAutoCollection): + # raised when user manually chooses not to use auto-collection + msg_prefix = "" + else: + msg_prefix = "Auto-collection failed. Defaulting to manual selection of files. " + + # iterate over N and prompt user to choose correct files + for i in range(N): + token = get_token(i) + f_pick_msg = msg_prefix + f_pick_msg += 'Select co-signer #%d file containing round 1 data' % (i + 1) + if et == "2": + f_pick_msg += " for token starting with %s" % token[:4] + f_pick_msg += '. File extension has to be "%s"' % suffix + for attempt in range(2): # two chances to succeed + await ux_show_story(f_pick_msg) + fn = await file_picker(suffix=suffix, min_size=220, max_size=500, + force_vdisk=force_vdisk) + if not fn: return + + dis.fullscreen("Wait...") + with CardSlot(force_vdisk=force_vdisk) as card: + dis.progress_bar_show(0.1) + with open(fn, mode) as fd: + data = fd.read() + dis.progress_bar_show(0.3) + if is_encrypted: + encryption_key = key_derivation_function(token) + dis.progress_bar_show(0.6) + data = bsms_decrypt(encryption_key, data) + if not data: + fail_msg = "Decryption failed for co-signer #%d" % (i + 1) + if et == "2": + fail_msg += " with token %s" % token[:4] + ch = await ux_show_story(title="FAILURE", msg=fail_msg + + (" Try again?" if attempt == 0 else fail_msg)) + + if ch == "y" and attempt == 0: + continue + else: + return + + dis.progress_bar_show(0.9) + token_key_map[token] = encryption_key + + r1_data.append(data) + dis.progress_bar_show(1) + + break # break from "second chance loop" + + if not r1_data: + return + + keys = [] + dis.fullscreen("Validating...") + for i, data in enumerate(r1_data): + # divided in the loop with number of in-loop occurences of 'dis.progress_bar_show' (currently 5) + i_div_N = (i+1) / N + token = get_token(i) + assert data.startswith(BSMS_VERSION), "Incompatible BSMS version. Need %s got %s" % ( + BSMS_VERSION, data[:9] + ) + version, tok, key_exp, description, sig = data.strip().split("\n") + assert tok == token, "Token mismatch saved %s, received from signer %s" % (token, tok) + key = Key.from_string(key_exp) + dis.progress_bar_show(i_div_N / 4) + msg = signer_data_round1(token, key_exp, description) + digest = chain.hash_message(msg.encode()) + dis.progress_bar_show(i_div_N / 3) + _, recovered_pk = chains.verify_recover_pubkey(a2b_base64(sig), digest) + assert key.node.pubkey() == recovered_pk, "Recovered key from signature does not equal key provided. Wrong signature?" + dis.progress_bar_show(i_div_N / 2) + keys.append(key) + dis.progress_bar_show(i_div_N / 1) + + dis.fullscreen("Generating...") + try: + dis.busy_bar(True) + miniscript = Sortedmulti(Number(M), *keys) + desc_obj = Descriptor(miniscript=miniscript, addr_fmt=addr_fmt) + desc = desc_obj.to_string(checksum=False) + desc = desc.replace("<0;1>/*", "**") + if not is_encrypted: + # append checksum for unencrypted BSMS + desc = append_checksum(desc) + # external address at index 0 -> 0/0 + derived_desc = desc_obj.derive(0).derive(0) + addr = chain.render_address(derived_desc.script_pubkey()) + # == + r2_data = coordinator_data_round2(desc, addr) + + finally: + dis.busy_bar(False) + + force_vdisk = False + title = "BSMS descriptor template file(s)" + prompt, escape = export_prompt_builder(title) + if prompt: + ch = await ux_show_story(prompt, escape=escape) + if ch == (KEY_NFC if version_mod.has_qwerty else '3'): + if et == "2": + for i, token in enumerate(tokens): + ch = await ux_show_story("Exporting data for co-signer #%d with token %s" + % (i+1, token[:4])) + if ch != "y": + return + data = bsms_encrypt(token_key_map[token], token, r2_data) + await NFC.share_text(b2a_hex(data).decode()) + elif et == "1": + token = get_token(0) + data = bsms_encrypt(token_key_map[token], token, r2_data) + await NFC.share_text(b2a_hex(data).decode()) + else: + await NFC.share_text(r2_data) + await ux_show_story("All done.") + return + elif ch == "2": + force_vdisk = True + elif ch == '1': + force_vdisk = False + else: + return + + def to_export_generator(): + # save memory + if et == "3": # NO_ENCRYPTION + yield None, r2_data + elif et == "1": # STANDARD + token = get_token(0) + yield token, bsms_encrypt(token_key_map[token], token, r2_data) + else: + # EXTENDED + for token in tokens: + yield token, bsms_encrypt(token_key_map[token], token, r2_data) + + dis.fullscreen("Saving...") + mode = "wb" if is_encrypted else "wt" + f_pattern = "bsms_cr2" + f_names = [] + try: + with CardSlot(force_vdisk=force_vdisk) as card: + for i, (token, data) in enumerate(to_export_generator(), start=1): + f_name = "%s%s%s" % (f_pattern, "_" + token[:4] if et == "2" else "", suffix) + fname, nice = card.pick_filename(f_name) + with open(fname, mode) as fd: + fd.write(data) + f_names.append(nice) + dis.progress_bar_show(i / (len(token_key_map) or 1)) + except CardMissingError: + await needs_microsd() + return + except Exception as e: + await ux_show_story('Failed to write!\n\n\n' + str(e)) + return + msg = '''%s written. Files:\n\n%s''' % (title, "\n\n".join(f_names)) + await ux_show_story(msg) + + +@exceptions_handler +async def bsms_signer_round1(*a): + from glob import dis, NFC, VD, settings + + shortcut = len(a) == 1 + token_int = None + if not shortcut: + prompt = "Press (1) to import token file from SD Card, (2) to input token manually" + prompt += ", (3) for unencrypted BSMS." + escape = "123" + if version.has_qwerty: + prompt += "%s to scan QR. " % KEY_QR + escape += KEY_QR + if NFC is not None: + prompt += " %s to import via NFC" % (KEY_NFC if version.has_qwerty else "(4)") + escape += KEY_NFC if version.has_qwerty else "4" + if VD is not None: + prompt += ", (6) to import from Virtual Disk" + escape += "6" + prompt += "." + + ch = await ux_show_story(prompt, escape=escape) + + if ch == '3': + token_hex = "00" + elif ch in "4"+KEY_NFC: + token_hex = await NFC.read_bsms_token() + elif ch == "2": + prompt = "To input token as hex press (1), as decimal press (2)" + escape = "12" + ch = await ux_show_story(prompt, escape=escape) + if ch == "1": + token_hex = await ux_input_text("", hex_only=True, scan_ok=True, + prompt="Hex Token") + elif ch == "2": + if version.has_qwerty: + token_int = await ux_input_text("", scan_ok=True, prompt="Decimal Token") + else: + from ux_mk4 import ux_input_digits + token_int = await ux_input_digits("", prompt="Decimal Token") + token_hex = hex(int(token_int)) + else: + return + elif ch in "16": + from actions import file_picker + force_vdisk = (ch == '6') + + # pick a likely-looking file. + fn = await file_picker(suffix=".token", min_size=15, max_size=35, + force_vdisk=force_vdisk) + if not fn: return + + with CardSlot(force_vdisk=force_vdisk) as card: + with open(fn, 'rt') as fd: + token_hex = fd.read().strip() + else: + return + else: + token_hex = a[0] + + # will raise, exc catched in decorator, FAILURE msg provided + validate_token(token_hex) + token_hex = normalize_token(token_hex) + is_extended = (len(token_hex) == 32) + entered_msg = "%s\n\nhex:\n%s" % (token_int, token_hex) if token_int else token_hex + + if not shortcut: + ch = await ux_show_story("You have entered token:\n" + entered_msg + "\n\nIs token correct?") + if ch != "y": + return + + xfp = xfp2str(settings.get('xfp', 0)) + chain = chains.current_chain() + ch = await ux_show_story( +"Choose co-signer address format for correct SLIP derivation path. Default is 'unknown' as this " +"information may not be known at this point in BSMS. SLIP agnostic path will be chosen. " +"Press (1) for P2WSH. Press (2) for P2SH-P2WSH. " +"Correct SLIP path is completely unnecessary as descriptors (BIP-0380) are used.", + escape='12') + if ch == 'y': + pth_template = "m/129'/{coin}'/{acct_num}'" + af_str = "" + elif ch == '1': + pth_template = "m/48'/{coin}'/{acct_num}'/2'" + af_str = " P2WSH" + elif ch == '2': + pth_template = "m/48'/{coin}'/{acct_num}'/1'" + af_str = " P2SH-P2WSH" + else: + return + + acct_num = await ux_enter_number('Account Number:', 9999) or 0 + + # textual key description + key_description = "Coldcard signer%s account %d" % (af_str, acct_num) + ch = await ux_show_story( +"Choose key description. To continue with default, generated description: '%s' press OK." +"\n\nPress (1) for custom key description." % key_description, escape="1") + + if ch == "1": + key_description = await ux_input_text("", confirm_exit=False) or "" + + key_description_len = len(key_description) + assert key_description_len <= 80, "Key Description: 80 char max (was %d)" % key_description_len + + dis.fullscreen("Wait...") + + with stash.SensitiveValues() as sv: + dis.progress_bar_show(0.1) + + dd = pth_template.format(coin=chain.b44_cointype, acct_num=acct_num) + node = sv.derive_path(dd) + ext_key = chain.serialize_public(node) + + dis.progress_bar_show(0.25) + + desc_type_key = "[%s%s]%s" % (xfp, dd[1:], ext_key) + msg = signer_data_round1(token_hex, desc_type_key, key_description) + digest = chain.hash_message(msg.encode()) + sk = node.privkey() + sv.register(sk) + + dis.progress_bar_show(0.5) + + sig = ngu.secp256k1.sign(sk, digest, 0).to_bytes() + result_data = signer_data_round1(token_hex, desc_type_key, key_description, sig_bytes=sig) + + dis.progress_bar_show(.75) + + encryption_key = key_derivation_function(token_hex) + if encryption_key: + result_data = bsms_encrypt(encryption_key, token_hex, result_data) + + dis.progress_bar_show(1) + + # export round 1 file + force_vdisk = False + title = "BSMS signer round 1 file" + prompt, escape = export_prompt_builder(title) + if prompt: + ch = await ux_show_story(prompt, escape=escape) + if ch == (KEY_NFC if version.has_qwerty else '3'): + force_vdisk = None + if isinstance(result_data, bytes): + result_data = b2a_hex(result_data).decode() + await NFC.share_text(result_data) + elif ch == "2": + force_vdisk = True + elif ch == '1': + force_vdisk = False + else: + return + + msg = "Success. Signer round 1 saved." + if force_vdisk is not None: + basename = "bsms_sr1%s" % "_" + token_hex[:4] if is_extended else "bsms_sr1" + f_pattern = basename + ".txt" if encryption_key is None else basename + ".dat" + # choose a filename + try: + with CardSlot(force_vdisk=force_vdisk) as card: + fname, nice = card.pick_filename(f_pattern) + with open(fname, 'wb') as fd: + if isinstance(result_data, str): + result_data = result_data.encode() + fd.write(result_data) + except CardMissingError: + await needs_microsd() + return + except Exception as e: + await ux_show_story('Failed to write!\n\n\n' + str(e)) + return + msg = '''%s written:\n\n%s''' % (title, nice) + BSMSSettings.signer_add(token_hex) + await ux_show_story(msg) + if not shortcut: + restore_menu() + + +@exceptions_handler +async def bsms_signer_round2(menu, label, item): + import version + from glob import NFC, dis, settings + from actions import file_picker + from auth import maybe_enroll_xpub + + chain = chains.current_chain() + force_vdisk = False + + # choose correct values based on label (index in signer bsms settings) + bsms_settings_index = item.arg + token = BSMSSettings.get_signers()[bsms_settings_index] + + decrypt_fail_msg = "Decryption with token %s failed." % token[:4] + is_encrypted = False if token == "00" else True + suffix = ".dat" if is_encrypted else ".txt" + mode = "rb" if is_encrypted else "rt" + + prompt, escape = _import_prompt_builder("descriptor template file", False, False) + if prompt: + ch = await ux_show_story(prompt, escape=escape) + + if ch == (KEY_NFC if version.has_qwerty else '3'): + force_vdisk = None + desc_template_data = await NFC.read_bsms_data() + + if desc_template_data is None: + return + + if is_encrypted: + data_bytes = a2b_hex(desc_template_data) + encryption_key = key_derivation_function(token) + desc_template_data = bsms_decrypt(encryption_key, data_bytes) + assert desc_template_data, decrypt_fail_msg + else: + if ch == "1": + force_vdisk = False + else: + force_vdisk = True + + if force_vdisk is not None: + fn = await file_picker(suffix=suffix, min_size=200, max_size=10000, + force_vdisk=force_vdisk) + if not fn: return + + with CardSlot(force_vdisk=force_vdisk) as card: + with open(fn, mode) as fd: + desc_template_data = fd.read() + if is_encrypted: + encryption_key = key_derivation_function(token) + desc_template_data = bsms_decrypt(encryption_key, desc_template_data) + assert desc_template_data, decrypt_fail_msg + + dis.fullscreen("Validating...") + try: + dis.busy_bar(True) + assert desc_template_data.startswith(BSMS_VERSION), \ + "Incompatible BSMS version. Need %s got %s" % (BSMS_VERSION, desc_template_data[:9]) + + version, desc_template, pth_restrictions, addr = desc_template_data.split("\n") + assert pth_restrictions == ALLOWED_PATH_RESTRICTIONS, \ + "Only '%s' allowed as path restrictions. Got %s" % ( + ALLOWED_PATH_RESTRICTIONS, pth_restrictions) + + # if checksum is provided we better verify it before descriptor modification /** + # remove checksum as we need to replace /** + desc_template, csum = Descriptor.checksum_check(desc_template) + desc = desc_template.replace("/**", "/<0;1>/*") + + desc_obj = Descriptor.from_string(desc) + desc_obj.validate() + assert desc_obj.is_sortedmulti, "sortedmulti required" + + my_xfp = settings.get('xfp') + my_keys = 0 + + for key in desc_obj.keys: + if key.origin.cc_fp == my_xfp: + my_keys += 1 + + assert my_keys <= 1, "Multiple %s keys in descriptor (%d)" % (xfp2str(my_xfp), my_keys) + + # check address is correct + calc_addr = chain.render_address(desc_obj.derive(0).derive(0).script_pubkey()) + assert calc_addr == addr, "Address mismatch! Calculated %s, got %s" % (calc_addr, addr) + + # name consists last 4 characters of the address at /0/0 + ms_name = "bsms_" + addr[-4:] + + try: + # at this point we have properly validated descriptor + maybe_enroll_xpub(desc_obj=desc_obj, name=ms_name, bsms_index=bsms_settings_index) + # bsms_settings_signer_delete(bsms_settings_index) + # moved to auth.py to only be done if actually approved + except Exception as e: + await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) + + finally: + dis.busy_bar(False) + +# EOF \ No newline at end of file diff --git a/shared/calc.py b/shared/calc.py index f746e4a8e..3649634e1 100644 --- a/shared/calc.py +++ b/shared/calc.py @@ -1,6 +1,6 @@ # (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -# calc.py - Simple python REPL before login +# calc.py - Simple TOY calculator, before login. Not meant to be useful, just fun! # # Test with: ./simulator.py --q1 --eff -g --set calc=1 # @@ -9,7 +9,7 @@ from ux_q1 import ux_input_text async def login_repl(): - from glob import dis, settings + from glob import dis from pincodes import pa NUM_LINES = 7 # 10 - title - 2 for prompt @@ -19,22 +19,25 @@ async def login_repl(): re_pin = re.compile(r'^(\d\d+)[-_ ](\d\d+)$') # in decreasing order of hazard... - blacklist = ['import', '__', 'exec', 'locals', 'globals', 'eval', 'input'] + # - find these with: import builtins; help(builtins) + blacklist = ['import', '__', 'exec', 'locals', 'globals', 'eval', 'input', + 'getattr', 'setattr', 'delattr', 'open', 'execfile', 'compile' ] lines = '''\ Example Commands: >> 23 + 55 / 22 ->> a = 4; b = 3; ->> a*b ->> sha256('123456123456') ->> cls() # clear screen\ +>> 1.020 * 45.88 +>> sha256('some message') +>> cls # clear screen +>> help\ '''.split('\n') state = dict() state['sha256'] = lambda x: B2A(ngu.hash.sha256s(x)) state['sha512'] = lambda x: B2A(ngu.hash.sha512(x).digest()) state['ripemd'] = lambda x: B2A(ngu.hash.ripemd160(x)) + state['rand'] = lambda x=32: B2A(ngu.random.bytes(x)) state['cls'] = lambda: lines.clear() state['help'] = lambda: 'Commands: ' + (', '.join(state)) @@ -56,17 +59,17 @@ async def login_repl(): try: dis.busy_bar(1) - if ln == None : + if ln is None : # Cancel key - do nothing ans = None - elif ln in state and callable(state[ln]): - # no needs for () in my world + elif ln in ('help', 'cls', 'rand'): + # no need for () for these commands ans = state[ln]() - elif re_pin.match(ln) and len(ln) <= 13: + elif pa.attempts_left and re_pin.match(ln) and (len(ln) <= 13): # try login m = re_pin.match(ln) ln = m.group(1)+ '-' + m.group(2) - print(ln) + try: pa.setup(ln) ok = pa.login() @@ -80,16 +83,14 @@ async def login_repl(): else: ans = 'Error: ' + repr(exc.args) - elif re_prefix.match(ln) and len(ln) <= 7: + elif re_prefix.match(ln) and (len(ln) <= 7): # show words ans = pa.prefix_words(ln[:-1].encode()) else: if any((b in ln) for b in blacklist): ans = None - elif '=' in ln: - ans = exec(ln, state) else: - ans = eval(ln, state) + ans = eval(ln, state.copy()) except Exception as exc: lines.extend(word_wrap(str(exc), 34)) diff --git a/shared/ccc.py b/shared/ccc.py new file mode 100644 index 000000000..644edaff9 --- /dev/null +++ b/shared/ccc.py @@ -0,0 +1,1285 @@ +# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# ccc.py - ColdCard Co-sign feature. Be a leg in a 2-of-3 that is signed based on a policy. +# +# Rebranding/single-signer additions: +# +# - "CCC" (was "ColdCard Cosigning") will now be branded as "Spending Policy: Multisig" +# - single singer policies will be called "Spending Policy: Single Sig" +# - internally: CCC is the multisig stuff, vs SSSP: Single Signer Spending Policy +# - "hobbled" refers to less-than full control over Coldcard, even though you have main PIN +# +import gc, chains, version, ngu, web2fa, bip39, re +from chains import NLOCK_IS_TIME +from utils import swab32, xfp2str, truncate_address, deserialize_secret, show_single_address +from glob import settings, dis +from ux import ux_confirm, ux_show_story, the_ux, OK, ux_dramatic_pause, ux_enter_number, ux_aborted +from menu import MenuSystem, MenuItem, start_chooser +from seed import seed_words_to_encoded_secret +from stash import SecretStash +from charcodes import KEY_QR, KEY_CANCEL, KEY_NFC +from exceptions import SpendPolicyViolation + + +# limit to number of addresses in list +MAX_WHITELIST = const(25) + +class LastFailReason: + # We don't show the user the reason for policy fail (by design, so attacker + # cannot maximize their take against the policy), but during setup/experiments + # we offer to show the reason in the menu. Includes both SS and MS cases. + # - now holding this in a setting so they can power-cycle and bypass to view + + @classmethod + def record(cls, msg): + settings.put('lfr', msg) + + @classmethod + def get(cls): + return settings.get('lfr', None) + + @classmethod + def clear(cls): + settings.remove_key('lfr') + +class SpendingPolicy(dict): + # Details of what is allowed or not. Same for single vs. multisig signing. + # - a dict() but with write-thru to setting value + + def __init__(self, nvkey, pol_dict=None): + # deserialize and construct + #assert nvkey in { 'ccc', 'sssp' } + self.nvkey = nvkey + super().__init__() + + if pol_dict is not None: + self.clear() + self.update(pol_dict.items()) + else: + v = dict(settings.master_get(self.nvkey, {})).get('pol', None) + if v is not None: + self.update(v.items()) # mpy bugfix, when called with SpendingPolicy + + + def _save_policy(self): + # serialize the spending policy, save it + v = dict(settings.master_get(self.nvkey, {})) + v['pol'] = self.copy() + settings.master_set(self.nvkey, v, master_only=True) + + def update_policy_key(self, _quiet=False, **kws): + # Update a few elements of the spending policy + # - all changes are saved immediately (which is a little slow/visible) + if not _quiet: + dis.fullscreen("Saving...") + self.update(kws) + self._save_policy() + + def meets_policy(self, psbt): + # Does policy allow signing this? Else raise why. Return T if web2fa required. + pol = self + + # not safe to sign any txn w/ warnings: might be complaining about + # massive miner fees, or weird OP_RETURN stuff + if psbt.warnings: + raise SpendPolicyViolation("has warnings") + + # Magnitude: size limits for output side (non change) + magnitude = pol.get("mag", None) + if magnitude is not None: + if magnitude < 1000: + # it is a BTC, convert to sats + magnitude = magnitude * 100000000 + + outgoing = psbt.total_value_out - psbt.total_change_value + if outgoing > magnitude: + raise SpendPolicyViolation("magnitude") + + # Velocity: if zero => no velocity checks + velocity = pol.get("vel", None) + if velocity: + if not psbt.lock_time: + raise SpendPolicyViolation("no nLockTime") + + if psbt.lock_time >= NLOCK_IS_TIME: + # this is unix timestamp - not allowed - fail + raise SpendPolicyViolation("nLockTime not height") + + block_h = pol.get("block_h", chains.current_chain().ccc_min_block) + if psbt.lock_time <= block_h: + raise SpendPolicyViolation("rewound (%d)" % psbt.lock_time) + + # we won't sign txn unless old height + velocity >= new height + if psbt.lock_time < (block_h + velocity): + raise SpendPolicyViolation("velocity (%d)" % psbt.lock_time) + + # Whitelist of outputs addresses + wl = pol.get("addrs", None) + if wl: + c = chains.current_chain() + wl = set(wl) + for idx, txo in psbt.output_iter(): + out = psbt.outputs[idx] + if not out.is_change: # ignore change + addr = c.render_address(txo.scriptPubKey) + if addr not in wl: + raise SpendPolicyViolation("whitelist: " + addr) + + # Web 2FA + # - slow, requires UX, and they might not achieve it... + # - wait until about to do signature + if pol.get('web2fa', False): + psbt.warnings.append((pol.nvkey.upper(), 'Web 2FA required.')) + return True + + async def web2fa_challenge(self, msg): + # they are trying to sign something, so make them get out their phone + # - at this point they have already ok'ed the details of the txn + # - and we have approved other elements of the spending policy. + # - could show MS wallet name, or txn details but will not because that is + # an info leak to Coinkite... and we just don't want to know. + assert self.get('web2fa') + + ok = await web2fa.perform_web2fa(msg, self.get('web2fa')) + if not ok: + LastFailReason.record('2FA Fail') + raise SpendPolicyViolation + + def update_last_signed(self, psbt): + # Call after successfully signing a PSBT ... notes the height involved. + # - might add other things besides height here someday + LastFailReason.clear() + + old_h = self.get('block_h', 1) + + if old_h < psbt.lock_time < NLOCK_IS_TIME: + # always update last block height, even if velocity isn't enabled yet + # - attacker might have changed to testnet, but there is no + # reason to ever lower block height. strictly ascending + self.update_policy_key(_quiet=True, block_h=psbt.lock_time) + +class SSSPFeature: + # Using setting value "sssp" + + @classmethod + def is_enabled(cls): + # can be test drive, or is feature enabled? + from pincodes import pa + return (pa.hobbled_mode == 2) or sssp_spending_policy('en') + + @classmethod + def update_last_signed(cls, psbt): + # new PSBT has been completely signed successfully. + if not cls.is_enabled(): + return + pol = cls.get_policy() + pol.update_last_signed(psbt) + + @classmethod + def default_policy(cls): + # a very basic and permissive policy, but non-zero too. + # - 1BTC per day + chain = chains.current_chain() + return SpendingPolicy('sssp', dict(mag=1, vel=144, + block_h=chain.ccc_min_block, web2fa='', addrs=[])) + + @classmethod + def get_policy(cls): + # de-serialize just the spending policy + return SpendingPolicy('sssp') + + @classmethod + def can_allow(cls, psbt): + # We are looking at a PSBT: should we let user sign it, or block? + # - return (block_signing, needs_2fa_step) + if not cls.is_enabled(): + exists = bool(settings.master_get('sssp', False)) + if exists: + # this will not block CCC co-signing, because that test is already + # done before this call. + psbt.warnings.append(('SP', "Spending Policy defined but disabled.")) + return False, False + + try: + # check policy + pol = cls.get_policy() + needs_2fa = pol.meets_policy(psbt) + except SpendPolicyViolation as e: + LastFailReason.record(str(e)) + # caller will show msg + return True, False + + return False, needs_2fa + + @classmethod + async def web2fa_challenge(cls): + # they are trying to sign something, so make them get out their phone + # - at this point they have already ok'ed the details of the txn + # - and we have approved other elements of the spending policy. + # - could show MS wallet name, or txn details but will not because that is + # an info leak to Coinkite... and we just don't want to know. + await cls.get_policy().web2fa_challenge('Approve Transaction') + + +class CCCFeature: + # Using setting value "ccc" + + @classmethod + def is_enabled(cls): + # Is the feature enabled right now? + return bool(settings.get('ccc', False)) + + @classmethod + def words_check(cls, words): + # Test if words provided are right + enc = seed_words_to_encoded_secret(words) + exp = cls.get_encoded_secret() + return enc == exp + + @classmethod + def get_num_words(cls): + # return 12 or 24 + return SecretStash.is_words(cls.get_encoded_secret()) + + @classmethod + def get_encoded_secret(cls): + # Gets the key C as encoded binary secret, compatible w/ + # encodings used in stash. + return deserialize_secret(settings.get('ccc')['secret']) + + @classmethod + def get_xfp(cls): + # Just the XFP value for our key C + ccc = settings.get('ccc') + return ccc['c_xfp'] if ccc else None + + @classmethod + def get_master_xpub(cls): + ccc = settings.get('ccc') + return ccc['c_xpub'] if ccc else None + + @classmethod + def init_setup(cls, words): + # Encode 12 or 24 words into the secret to held as key C. + # - also capture XFP and XPUB for key C + # TODO: move to "storage locker"? + assert len(words) in (12, 24) + enc = seed_words_to_encoded_secret(words) + _,_,node = SecretStash.decode(enc) + + chain = chains.current_chain() + xfp = swab32(node.my_fp()) + xpub = chain.serialize_public(node) # fully useless value tho + + # NOTE: b_xfp and b_xpub still needed, but that's another step, not yet. + + v = dict(secret=SecretStash.storage_serialize(enc), + c_xfp=xfp, c_xpub=xpub, + pol=CCCFeature.default_policy()) + + settings.put('ccc', v) + settings.save() + + @classmethod + def default_policy(cls): + # a very basic and permissive policy, but non-zero too. + # - 1BTC per day + chain = chains.current_chain() + return SpendingPolicy('ccc', dict(mag=1, vel=144, + block_h=chain.ccc_min_block, web2fa='', addrs=[])) + + @classmethod + def get_policy(cls): + # de-serialize just the spending policy + return SpendingPolicy('ccc') + + @classmethod + def remove_ccc(cls): + # delete our settings complete; lose key C .. already confirmed + # - leave MS in place + settings.remove_key('ccc') + settings.save() + + @classmethod + def could_cosign(cls, psbt): + # We are looking at a PSBT: can we sign it, and would we? + # - if we **could** but will not, due to policy, add warning msg + # - return (we could sign, needs 2fa step) + if not cls.is_enabled(): + return False, False + + ms = psbt.active_miniscript + if not ms: + # not multisig, so ignore/permit + return False, False + + # TODO: if key B has already signed the PSBT, and so we don't need key C, + # don't try to sign; maybe show warning? + + xfp = cls.get_xfp() + if xfp not in [i[0] for i in ms.to_descriptor().xfp_paths()]: + # does not involve us + return False, False + + try: + # check policy + pol = cls.get_policy() + needs_2fa = pol.meets_policy(psbt) + except SpendPolicyViolation as e: + LastFailReason.record(str(e)) + psbt.warnings.append(('CCC', "Violates spending policy. Won't sign.")) + return False, False + + return True, needs_2fa + + @classmethod + def sign_psbt(cls, psbt): + # do the math + psbt.sign_it(cls.get_encoded_secret(), cls.get_xfp()) + LastFailReason.clear() + + pol = cls.get_policy() + pol.update_last_signed(psbt) + + @classmethod + async def web2fa_challenge(cls): + # do UX for web2fa; user is given option to proceed even if it fails + # (without the co-signing) + await cls.get_policy().web2fa_challenge('Approve Transaction: Co-Sign') + + +def render_mag_value(mag): + # handle integer bitcoins, and satoshis in same value + if mag < 1000: + return '%d BTC' % mag + else: + return '%d SATS' % mag + + +class CCCConfigMenu(MenuSystem): + def __init__(self): + items = self.construct() + super().__init__(items) + + def update_contents(self): + tmp = self.construct() + self.replace_items(tmp) + + def construct(self): + from wallet import MiniScriptWallet, make_miniscript_wallet_menu + + my_xfp = CCCFeature.get_xfp() + items = [ + MenuItem(('[%s] Co-Signing' if version.has_qwerty else '[%s]') + % xfp2str(my_xfp), f=self.show_ident), + MenuItem('Spending Policy', + menu=lambda *a: SpendingPolicyMenu.be_a_submenu(CCCFeature.get_policy())), + MenuItem('Export CCC XPUBs', f=self.export_xpub_c), + MenuItem('Multisig Wallets'), + ] + + # look for wallets that are defined related to CCC feature, shortcut to them + count = 0 + for i, ms in enumerate(MiniScriptWallet.iter_wallets()): + if not ms.m_n: # basic multisig check + continue + if my_xfp in [i[0] for i in ms.xfp_paths()]: + M, N = ms.m_n + items.append(MenuItem('↳ %d/%d: %s' % (M, N, ms.name), + menu=make_miniscript_wallet_menu, arg=(i,ms))) + count += 1 + + items.append(MenuItem('↳ Build 2-of-N', f=self.build_2ofN, arg=count)) + + if LastFailReason.get(): + # xxxxxxxxxxxxxxxx + items.insert(1, MenuItem('Last Violation', f=self.debug_last_fail)) + + items.append(MenuItem('Load Key C', f=self.enter_temp_mode)) + items.append(MenuItem('Remove CCC', f=self.remove_ccc)) + + return items + + async def debug_last_fail(self, *a): + # debug for customers: why did we reject that last txn? + pol = CCCFeature.get_policy() + bh = pol.get('block_h', None) + msg = '' + if bh: + msg += "CCC height:\n\n%s\n\n" % bh + + lfr = LastFailReason.get() + msg += 'The most recent policy check failed because of:\n\n%s\n\nPress (4) to clear.' \ + % lfr + ch = await ux_show_story(msg, escape='4') + + if ch == '4': + LastFailReason.clear() + self.update_contents() + + async def remove_ccc(self, *a): + # disable and remove feature + if not await ux_confirm('Key C will be lost, and policy settings forgotten.' + ' This unit will only be able to partly sign transactions.' + ' To completely remove this wallet, proceed to the multisig' + ' menu and remove related wallet entries.'): + return + + if not await ux_confirm("Funds in related wallet/s may be impacted.", confirm_key='4'): + return await ux_aborted() + + CCCFeature.remove_ccc() + the_ux.pop() + + async def on_cancel(self): + # trying to exit from CCCConfigMenu + from seed import in_seed_vault + + enc = CCCFeature.get_encoded_secret() + + if in_seed_vault(enc): + # remind them to clear the seed-vault copy of Key C because it defeats feature + await ux_show_story("Key C is in your Seed Vault. If you are done with setup, " + "you MUST delete it from the Vault!", title='REMINDER') + + the_ux.pop() + + async def export_xpub_c(self, *a): + # do standard Coldcard export for multisig setups + xfp = CCCFeature.get_xfp() + enc = CCCFeature.get_encoded_secret() + + from wallet import export_miniscript_xpubs + await export_miniscript_xpubs(xfp=xfp, alt_secret=enc, skip_prompt=True) + + async def build_2ofN(self, m, l, i): + count = i.arg + # ask for a key B, assume A and C are defined => export MS config and import into self. + # - like the airgap setup, but assume A and C are this Coldcard + m = '''Builds simple 2-of-N multisig wallet, with this Coldcard's main secret (key A), \ +the CCC policy-controlled key C, and at least one other device, as key B. \ +\nYou will need to export the XPUB from another Coldcard and place it on an SD Card, or \ +be ready to show it as a QR, before proceeding.''' + if await ux_show_story(m) != 'y': + return + + from multisig import create_ms_step1 + + # picks addr fmt, QR or not, gets at least one file, then... + await create_ms_step1(for_ccc=(CCCFeature.get_encoded_secret(), count)) + + # prompt for file, prompt for our acct number, unless already exported to this card? + + async def show_ident(self, *a): + # give some background? or just KISS for now? + xfp = xfp2str(CCCFeature.get_xfp()) + xpub = CCCFeature.get_master_xpub() + await ux_show_story( + "Key C:\n\n" + "XFP (Master Fingerprint):\n\n %s\n\n" + "Master Extended Public Key:\n\n %s " % (xfp, xpub)) + + async def enter_temp_mode(self, *a): + # apply key C as temp seed, so you can do anything with it + # - just a shortcut, since they have the words, and could enter them + # - one-way trip because the CCC feature won't be enabled inside the temp seed settings + if await ux_show_story( + 'Loads the CCC controlled seed (key C) as a Temporary Seed and allows ' + 'easy use of all Coldcard features on that key.\n\nIf you save into Seed Vault, ' + 'access to CCC Config menu is quick and easy.') != 'y': + return + + from seed import set_ephemeral_seed + from actions import goto_top_menu + + enc = CCCFeature.get_encoded_secret() + await set_ephemeral_seed(enc, origin='Key C from CCC') + + goto_top_menu() + + +class SPAddrWhitelist(MenuSystem): + # simulator arg: --seq tcENTERENTERsENTERwENTER + def __init__(self, pol): + self.policy = pol + items = self.construct() + super().__init__(items) + + def update_contents(self): + tmp = self.construct() + self.replace_items(tmp) + + @classmethod + async def be_a_submenu(cls, pol, *a): + return cls(pol) + + def construct(self): + # list of addresses + addrs = self.policy.get('addrs', []) + maxxed = (len(addrs) >= MAX_WHITELIST) + + items = [] + # better to show usability options at the top, as we can have up to 25 addresses in the menu + if version.has_qr: + items.append(MenuItem('Scan QR', f=(self.maxed_out if maxxed else self.scan_qr), + shortcut=KEY_QR)) + + items.append(MenuItem('Import from File', + f=(self.maxed_out if maxxed else self.import_file))) + + # show most recent added addresses at the top of the menu list + a_items = [MenuItem(truncate_address(a), f=self.edit_addr, arg=a) for a in addrs[::-1]] + + if a_items: + items += a_items + if len(a_items) > 1: + items.append(MenuItem("Clear Whitelist", f=self.clear_all)) + else: + items.append(MenuItem("(none yet)")) + + return items + + async def edit_addr(self, menu, idx, item): + # show detail and offer delete + addr = item.arg + msg = ('Spends to this address will be permitted:\n\n%s' + '\n\nPress (4) to delete.' % show_single_address(addr)) + ch = await ux_show_story(msg, escape='4') + if ch == '4': + self.delete_addr(addr) + + def delete_addr(self, addr): + # no confirm, stakes are low + addrs = self.policy.get('addrs', []) + addrs.remove(addr) + self.policy.update_policy_key(addrs=addrs) + self.update_contents() + + async def clear_all(self, *a): + if await ux_confirm("Remove all addresses from the whitelist?", confirm_key='4'): + self.policy.update_policy_key(addrs=[]) + self.update_contents() + + async def import_file(self, *a): + # Import from a file, or NFC. + # - simulator: --seq tcENTERENTERsENTERwENTERiENTER1 + # - very forgiving, does not care about file format + # - but also silent on all errors + from ux import import_export_prompt + from glob import NFC + from actions import file_picker + from files import CardSlot + from utils import cleanup_payment_address + + choice = await import_export_prompt("List of addresses", is_import=True, no_qr=True) + + if choice == KEY_CANCEL: + return + elif choice == KEY_NFC: + addr = await NFC.read_address() + if not addr: + # error already displayed in nfc.py + return + + await self.add_addresses([addr]) + return + + # loose RE to match any group of chars that could be addresses + # - really just removing whitespace and punctuation + # - lacking re.findall(), so using re.split() on negatives + pat = re.compile(r'[^A-Za-z0-9]') + + # pick a likely-looking file: just looking at size and extension + fn = await file_picker(suffix=['.csv', '.txt'], + min_size=20, max_size=20000, + none_msg="Must contain payment addresses", **choice) + + if not fn: return + + results = [] + with CardSlot(readonly=True, **choice) as card: + with open(fn, 'rt') as fd: + for ln in fd.readlines(): + if len(results) >= MAX_WHITELIST: + # no need to clog memory and parse more, we're done + break + for here in pat.split(ln): + if len(here) >= 4: + try: + addr = cleanup_payment_address(here) + results.append(addr) + except: pass + + if not results: + await ux_show_story("Unable to find any payment addresses in that file.") + else: + # silently limit to first 25 results; lets them use addresses.csv easily + await self.add_addresses(results[:MAX_WHITELIST]) + + + async def scan_qr(self, *a): + # Scan and return a text string. For things like BIP-39 passphrase + # and perhaps they are re-using a QR from something else. Don't act on contents. + from ux_q1 import QRScannerInteraction + q = QRScannerInteraction() + + got = [] + ln = '' + while 1: + here = await q.scan_for_addresses("Bitcoin Address(es) to Whitelist", line2=ln) + if not here: break + for addr in here: + if addr not in got: + got.append(addr) + ln = 'Got %d so far. ENTER to apply.' % len(got) + + if got: + # import them + await self.add_addresses(got) + + async def maxed_out(self, *a): + await ux_show_story("Max %d items in whitelist. Please make room first." % MAX_WHITELIST) + + async def add_addresses(self, more_addrs): + # add new entries, if unique; preserve ordering + addrs = self.policy.get('addrs', []) + new = [] + for a in more_addrs: + if a not in addrs: + addrs.append(a) + new.append(a) + + if not new: + await ux_show_story("Already in whitelist:\n\n" + + '\n\n'.join(show_single_address(a) for a in more_addrs)) + return + + if len(addrs) > MAX_WHITELIST: + return await self.maxed_out() + + self.policy.update_policy_key(addrs=addrs) + self.update_contents() + + if len(new) > 1: + await ux_show_story("Added %d new addresses to whitelist:\n\n%s" % + (len(new), '\n\n'.join(show_single_address(a) for a in new))) + else: + await ux_show_story("Added new address to whitelist:\n\n%s" % + show_single_address(new[0])) + +class SPCheckedMenuItem(MenuItem): + # Show a checkmark if **policy** setting is defined and not the default + # - only works inside SpendingPolicyMenu + def __init__(self, label, polkey, **kws): + super().__init__(label, **kws) + self.polkey = polkey + + def is_chosen(self): + # should we show a check in parent menu? check the policy + m = the_ux.top_of_stack() + #assert isinstance(m, SpendingPolicyMenu) + return bool(m.policy.get(self.polkey, False)) + +class SpendingPolicyMenu(MenuSystem): + # Build menu stack that allows edit of all features of the spending + # policy. + # - supports both CCC and SSSP modes w/ same policies + # - Key C is set already at this point. + # - and delete/cancel CCC (clears setting?) + # - be a sticky menu that's hard to exit (ie. SAVE choice and no cancel out) + + def __init__(self, pol): + self.policy = pol + items = self.construct() + super().__init__(items) + + def update_contents(self): + tmp = self.construct() + self.replace_items(tmp) + + @classmethod + async def be_a_submenu(cls, pol, *a): + return cls(pol) + + def construct(self): + items = [ + # xxxxxxxxxxxxxxxx + SPCheckedMenuItem('Max Magnitude', 'mag', f=self.set_magnitude), + SPCheckedMenuItem('Limit Velocity', 'vel', f=self.set_velocity), + SPCheckedMenuItem('Whitelist' + (' Addresses' if version.has_qr else ''), + 'addrs', + menu=lambda *a: SPAddrWhitelist.be_a_submenu(self.policy)), + SPCheckedMenuItem('Web 2FA', 'web2fa', f=self.toggle_2fa), + ] + + if self.policy.get('web2fa'): + items.extend([ + MenuItem('↳ Test 2FA', f=self.test_2fa), + MenuItem('↳ Enroll More', f=self.enroll_more_2fa), + ]) + + return items + + async def test_2fa(self, *a): + ss = self.policy.get('web2fa') + assert ss + ok = await web2fa.perform_web2fa('Testing Only', ss) + + await ux_show_story('Correct code was given.' if ok else 'Failed or aborted.') + + async def enroll_more_2fa(self, *a): + # let more phones in on the party, but they get same shared secret + ss = self.policy.get('web2fa') + assert ss + await web2fa.web2fa_enroll(ss) + + async def set_magnitude(self, *a): + # Looks decent on both Q and Mk4... + was = self.policy.get('mag', 0) + val = await ux_enter_number('Transaction Max:', max_value=int(1e8), + can_cancel=True, value=(was or '')) + + args = dict(mag=val) + if (val is None) or (val == was): + msg = "Did not change" + val = was + else: + msg = "You have set the" + unchanged = False + + if not val: + msg = "No check for maximum transaction size will be done. " + if self.policy.get('vel', 0): + msg += 'Velocity check also disabled. ' + args['vel'] = 0 + else: + msg += " maximum per-transaction: \n\n %s" % render_mag_value(val) + + self.policy.update_policy_key(**args) + + await ux_show_story(msg, title="TX Magnitude") + + async def set_velocity(self, *a): + mag = self.policy.get('mag', 0) or 0 + + if not mag: + msg = 'Velocity limit requires a per-transaction magnitude to be set.'\ + ' This has been set to 1BTC as a starting value.' + self.policy.update_policy_key(mag=1) + + await ux_show_story(msg) + + start_chooser(self.velocity_chooser) + + + def velocity_chooser(self): + # offer some useful values from a menu + vel = self.policy.get('vel', 0) # in blocks + + # xxxxxxxxxxxxxxxx + ch = [ 'Unlimited', + '6 blocks (hour)', + '24 blocks (4h)', + '48 blocks (8h)', + '72 blocks (12h)', + '144 blocks (day)', + '288 blocks (2d)', + '432 blocks (3d)', + '720 blocks (5d)', + '1008 blocks (1w)', + '2016 blocks (2w)', + '3024 blocks (3w)', + '4032 blocks (4w)', + ] + va = [0] + [int(x.split()[0]) for x in ch[1:]] + + try: + which = va.index(vel) + except ValueError: + which = 0 + + def set(idx, text): + self.policy.update_policy_key(vel=va[idx]) + + return which, ch, set + + async def toggle_2fa(self, *a): + if self.policy.get('web2fa'): + # enabled already + + if not await ux_confirm("Disable web 2FA check? Effect is immediate."): + return + + self.policy.update_policy_key(web2fa='') + self.update_contents() + + await ux_show_story("Web 2FA has been disabled. If you re-enable it, a new " + "secret will be generated, so it is safe to remove it from your " + "phone at this point.") + + return + + ch = await ux_show_story('''When enabled, any spend (signing) requires \ +use of mobile 2FA application (TOTP RFC-6238). Shared-secret is picked now, \ +and loaded on your phone via QR code. + +WARNING: You will not be able to sign transactions if you do not have an NFC-enabled \ +phone with Internet access and 2FA app holding correct shared-secret.''', + title="Web 2FA") + if ch != 'y': + return + + # challenge them, and don't set unless it works + ss = await web2fa.web2fa_enroll() + if not ss: + return + + # update state + self.policy.update_policy_key(web2fa=ss) + self.update_contents() + +async def gen_or_import(): + # returns 12 words, or None to abort + from seed import WordNestMenu, generate_seed, approve_word_list, SeedVaultChooserMenu + + msg = "Press %s to generate a new 12-word seed phrase to be used "\ + "as the Coldcard Co-Signing Secret (key C).\n\nOr press (1) to import existing "\ + "12-words or (2) for 24-words import." % OK + + if settings.master_get("seedvault", False): + msg += ' Press (6) to import from Seed Vault.' + + ch = await ux_show_story(msg, escape='126', title="CCC Key C") + + if ch in '12': + nwords = 24 if ch == '2' else 12 + + async def done_key_C_import(words): + if not version.has_qwerty: + WordNestMenu.pop_all() + await enable_step1(words) + + if version.has_qwerty: + from ux_q1 import seed_word_entry + await seed_word_entry('Key C Seed Words', nwords, done_cb=done_key_C_import) + else: + nxt = WordNestMenu(nwords, done_cb=done_key_C_import) + the_ux.push(nxt) + + return None # will call parent again + + elif ch == '6': + # pick existing from Seed Vault + picked = await SeedVaultChooserMenu.pick(words_only=True) + if picked: + words = SecretStash.decode_words(deserialize_secret(picked.encoded)) + await enable_step1(words) + + return None + + elif ch == 'y': + # normal path: pick 12 words, quiz them + await ux_dramatic_pause('Generating...', 3) + seed = generate_seed() + words = await approve_word_list(seed, 12) + else: + return None + + return words + + +async def toggle_ccc_feature(*a): + # The only menu item show to user! + if settings.get('ccc'): + return await modify_ccc_settings() + + # enable the feature -- not simple! + # - create C key (maybe import?) + # - collect a policy setup, maybe 2FA enrol too + # - lock that down + # - TODO copy + ch = await ux_show_story('''\ +Adds an additional seed to your Coldcard, and enforces a "spending policy" whenever \ +it signs with that key. Spending policies can restrict: magnitude (BTC out), \ +velocity (blocks between txn), address whitelisting, and/or require confirmation by 2FA phone app. + +Assuming the use of a 2-of-3 multisig wallet, keys are as follows:\n +A=Coldcard (master seed), B=Backup Key (offline/recovery), C=Spending Policy Key. + +Spending policy cannot be viewed or changed without knowledge of key C.\ +''', + title="Coldcard Co-Signing" if version.has_qwerty else 'CC Co-Sign') + + if ch != 'y': + # just a tourist + return + + await enable_step1(None) + +async def enable_step1(words): + if not words: + words = await gen_or_import() + if not words: return + + dis.fullscreen("Wait...") + dis.busy_bar(True) + try: + # do BIP-32 basics: capture XFP and XPUB and encoded version of the secret + CCCFeature.init_setup(words) + finally: + dis.busy_bar(False) + + # continue into config menu + m = CCCConfigMenu() + + the_ux.push(m) + +async def modify_ccc_settings(): + # Generally not expecting changes to policy on the fly because + # that's the whole point. Use the B key to override individual spends + # but if you can prove you have C key, then it's harmless to allow changes + # since you could just spend as needed. + + enc = CCCFeature.get_encoded_secret() + bypass = False + + from seed import in_seed_vault + if in_seed_vault(enc): + # If seed vault enabled and they have the key C in there already, just go + # directly into menu (super helpful for debug/setup/testing time). We do warn tho. + await ux_show_story('''You have a copy of the CCC key C in the Seed Vault, so \ +you may proceed to change settings now.\n\nYou must delete that key from the vault once \ +setup and debug is finished, or all benefit of this feature is lost!''', title='REMINDER') + + bypass = True + + else: + ch = await ux_show_story( + "Spending policy cannot be viewed, changed nor disabled, " + "unless you have the seed words for key C.", + title="CCC Enabled") + + if ch != 'y': return + + if bypass: + # doing full decode cycle here for better testing + chk, raw, _ = SecretStash.decode(enc) + assert chk == 'words' + words = bip39.b2a_words(raw).split(' ') + await key_c_challenge(words) + return + + # small info-leak here: exposing 12 vs 24 words, but we expect most to be 12 anyway + nwords = CCCFeature.get_num_words() + + import seed + if version.has_qwerty: + from ux_q1 import seed_word_entry + await seed_word_entry('Enter Seed Words', nwords, done_cb=key_c_challenge) + else: + return seed.WordNestMenu(nwords, done_cb=key_c_challenge) + +NUM_CHALLENGE_FAILS = 0 + +async def key_c_challenge(words): + # They entered some words, if they match our key C then allow edit of policy + + if not version.has_qwerty: + from seed import WordNestMenu + WordNestMenu.pop_all() + + dis.fullscreen('Verifying...') + + if not CCCFeature.words_check(words): + # keep an in-memory counter, and after 3 fails, reboot + global NUM_CHALLENGE_FAILS + NUM_CHALLENGE_FAILS += 1 + if NUM_CHALLENGE_FAILS >= 3: + from utils import clean_shutdown + clean_shutdown() + + await ux_show_story("Sorry, those words are incorrect.") + return + + # success. they are in. + + # got to config menu + m = CCCConfigMenu() + the_ux.push(m) + +def sssp_spending_policy(key, default=False, set_value=None): + # This function can be used to check if feature(s) are enabled in + # the single-signer policy settings. Might be used while hobbled. + # keys: + # 'en' = feature enabled; hobble on next boot + # 'notes' = allow access to knows + # 'words' = add first/last seed words to challenge to unlock + # 'okeys' = allow BIP-39 and/or seed vault + + v = settings.master_get('sssp', dict()) + + if key in { 'en', 'notes', 'words', 'okeys' }: + # booleans: present or removed from dict + if set_value is not None: + if set_value: + v[key] = True + else: + v.pop(key, None) + + settings.master_set('sssp', v, master_only=True) + + return (key in v) or default + + raise KeyError(key) + + +async def sssp_feature_menu(*a): + # Show the top menu for SSSP feature, or enable access first time. + from pincodes import pa + from actions import goto_top_menu + + if pa.hobbled_mode == 2: + # allow exit from test-drive mode, directly into editing settings + pa.hobbled_mode = False + goto_top_menu() + elif settings.master_get('sssp'): + # normal entry into menu system, after the first time + assert not pa.hobbled_mode + else: + # tell them a story, and maybe enable feature + en = await sssp_enable() + if not en: return + + m = SSSPConfigMenu() + the_ux.push(m) + +async def sssp_enable(): + # enabling the feature + # - collect and setup a new trick pin + # - set sssp settings w/ something non-empty but still disabled. + # - return T if they completed enabling process + + from login import LoginUX + from trick_pins import tp + from pincodes import pa + + # enable the feature -- not simple! + # - pick new (trick pin) that lets you back here. + # - collect a policy setup, maybe 2FA enrol too + # - lock that down + ch = await ux_show_story('''\ +You can define a "spending policy" which stops you from signing \ +transactions unless conditions are met. +Spending policies can restrict: magnitude (BTC out), \ +velocity (blocks between txn), address whitelisting, \ +and/or require confirmation by 2FA phone app. + +When active, your COLDCARD \ +is locked into a special mode that restricts seed access, backups, settings and other features. + +First step is to define a new PIN code that is used when you want to bypass or \ +disable this feature. +''', + title="Spending Policy") + + if ch != 'y': + # just a tourist + return + + # re-use existing PIN if there for some reason + new_pin = tp.has_sp_unlock() + + if not new_pin: + have = tp.all_tricks() + main_pin = pa.pin.decode() + while 1: + lll = LoginUX() + lll.is_setting = True + lll.subtitle = "Spending Policy" + (" Unlock" if version.has_qwerty else '') + + new_pin = await lll.get_new_pin() + if new_pin is None: + return + + dis.fullscreen("Saving...") + + # quick checks - does not spot hidden trick pins + if (new_pin != main_pin) and (new_pin not in have): + # verify uniqueness within SE2 + b, slot = tp.get_by_pin(new_pin) + if slot is None: + tp.define_unlock_pin(new_pin) + break + + await tp.err_unique_pin(new_pin) + + # all features disabled to start + settings.master_set('sssp', dict(en=False, pol={}), master_only=True) + + # continue into config menu + return True + +async def sssp_word_challenge(*a): + # Ask for first/last seed word and verify. Return if correct answers given. + # Reboots on failure. + from stash import SensitiveValues + + with SensitiveValues() as sv: + if sv.mode != 'words': + # they are using XPRV or something, skip test entirely + return + + words = bip39.b2a_words(sv.raw).split(' ') + want_words = words[:1] + words[-1:] + assert len(want_words) == 2 + + for retry in range(2): + if version.has_qwerty: + # see special rendering code for this case in ux_q1.py:ux_draw_words(num_words=2) + from ux_q1 import seed_word_entry + got_words = await seed_word_entry('First and Last Seed Words', 2, has_checksum=False) + else: + from seed import WordNestMenu + got_words = await WordNestMenu.get_n_words(2) + + if got_words == want_words: + # success - done + return + + await ux_show_story("Sorry, those words are incorrect.") + + # they failed; log them out ... they can just try login again + from actions import login_now + await login_now() + + # NOT-REACHED + +class SSSPCheckedMenuItem(MenuItem): + # Show a checkmark if **top level** security setting is defined and not the default + # - only works inside SSSPPolicyMenu? + # - similar to menu.py:ToggleMenuItem + + def __init__(self, label, polkey, story, **kws): + super().__init__(label, **kws) + self.polkey = polkey + self.story = story + + def is_chosen(self): + # should we show a check in menu? check the current SSSP settings + return sssp_spending_policy(self.polkey) + + async def activate(self, menu, idx): + # do simple toggle on request + was = sssp_spending_policy(self.polkey) + + msg = self.story + "\n\n%s?" % ('Disable' if was else 'Enable') + + ch = await ux_show_story(msg) + if ch == 'x': return + + # this can be slow, so show something + dis.fullscreen("Saving...") + sssp_spending_policy(self.polkey, set_value=(not was)) + + +class SSSPConfigMenu(MenuSystem): + def __init__(self): + items = self.construct() + super().__init__(items) + + def update_contents(self): + tmp = self.construct() + self.replace_items(tmp) + + def construct(self): + items = [ + # xxxxxxxxxxxxxxxx + MenuItem('Edit Policy...', + menu=lambda *a: SpendingPolicyMenu.be_a_submenu(SSSPFeature.get_policy())), + SSSPCheckedMenuItem('Word Check', 'words', 'To change Spending Policy, in addition to special PIN, you must provide the first and last seed words.'), + SSSPCheckedMenuItem('Allow Notes', 'notes', 'Allow (read-only) access to secure notes and passwords? Otherwise, they are inaccessible.', predicate=version.has_qwerty), + SSSPCheckedMenuItem('Related Keys', 'okeys', 'Allow access to BIP-39 passphrase wallets based on master seed, and Seed Vault (read-only). Single Spending Policy applies to all.'), + #MenuItem('Test Word Challenge', f=sssp_word_challenge), # XXX test only? + ] + + if LastFailReason.get(): + # xxxxxxxxxxxxxxxx + items.insert(1, MenuItem('Last Violation', f=self.debug_last_fail)) + + items.append(MenuItem('Remove Policy', f=self.remove_sssp)) + items.append(MenuItem('Test Drive', f=self.test_drive)) + items.append(MenuItem('ACTIVATE', f=self.activate_feature)) + + return items + + async def activate_feature(self, *a): + # Policy is being set in stone now; confirm and switch to hobble mode, etc. + from trick_pins import tp + + bypass_pin = tp.has_sp_unlock() + + if not bypass_pin: + msg = "You have no Spending Policy bypass PIN defined, so changes to this COLDCARD cannot be made past this point. Only option will be to destroy seed and reload everything." + else: + msg = "To return to normal unlimited spending mode, you will need to enter the special pin (%s), then the Main PIN" % bypass_pin + if sssp_spending_policy('words'): + msg += ', followed by the first and last seed words' + msg += '.' + + if not await ux_confirm(msg, 'CONTINUE?'): + return + + # set it for next login + dis.fullscreen("Saving...") + sssp_spending_policy('en', set_value=True) + + # make it real ... could reboot here instead, but no need. + from pincodes import pa + from actions import goto_top_menu + + pa.hobbled_mode = True + goto_top_menu() + + async def test_drive(self, *a): + # allow test drive of feature + if not await ux_confirm("See what COLDCARD operation will look like with Spending Policy enabled.", 'CONTINUE?'): + return + + from pincodes import pa + from actions import goto_top_menu + + pa.hobbled_mode = 2 # Truthy value to indicate they can escape easily + goto_top_menu() + + async def debug_last_fail(self, *a): + # debug for customers: why did we reject that last txn? + pol = SSSPFeature.get_policy() + bh = pol.get('block_h', None) + msg = '' + if bh: + msg += "Last height:\n\n%s\n\n" % bh + + lfr = LastFailReason.get() + msg += 'The most recent policy check failed because of:\n\n%s\n\nPress (4) to clear.' \ + % lfr + ch = await ux_show_story(msg, escape='4') + + if ch == '4': + LastFailReason.clear() + self.update_contents() + + async def remove_sssp(self, *a): + # disable and remove feature + if not await ux_confirm('Bypass PIN will be removed, and all spending policy settings forgotten.'): + return + + settings.remove_key('sssp') + settings.save() + + from trick_pins import tp + tp.delete_sp_unlock_pins() + + the_ux.pop() + + +# EOF diff --git a/shared/chains.py b/shared/chains.py index 26af1410f..10d65d025 100644 --- a/shared/chains.py +++ b/shared/chains.py @@ -5,12 +5,17 @@ import ngu from uhashlib import sha256 from ubinascii import hexlify as b2a_hex -from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR, AF_BARE_PK from public_constants import AF_P2SH, AF_P2WSH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH -from public_constants import AFC_PUBKEY, AFC_SEGWIT, AFC_BECH32, AFC_SCRIPT -from serializations import hash160, ser_compact_size, disassemble +from public_constants import AFC_PUBKEY, AFC_BECH32, AFC_SCRIPT +from public_constants import TAPROOT_LEAF_TAPSCRIPT, TAPROOT_LEAF_MASK +from serializations import hash160, ser_compact_size, disassemble, ser_string from ucollections import namedtuple from opcodes import OP_RETURN, OP_1, OP_16 +from precomp_tag_hash import TAP_TWEAK_H, TAP_LEAF_H + + +SINGLESIG_AF = (AF_P2WPKH, AF_CLASSIC, AF_P2TR, AF_P2WPKH_P2SH) # See SLIP 132 # for background on these version bytes. Not to be confused with SLIP-32 which involves Bech32. @@ -19,18 +24,40 @@ # See also: # - # - defines ypub/zpub/Xprc variants -# - -# - nice bech32 encoded scheme for going forward # - # - mailing list post proposed ypub, etc. # - from # - also electrum source: electrum/lib/constants.py +# nLockTime in transaction equal or above this value is a unix timestamp (time_t) not block height. +NLOCK_IS_TIME = const(500000000) + + +def taptweak(internal_key, tweak=None): + # BIP 341 states: "If the spending conditions do not require a script path, + # the output key should commit to an unspendable script path instead of having no script path. + # This can be achieved by computing the output key point as: + # Q = P + int(hashTapTweak(bytes(P)))G." + actual_tweak = internal_key if tweak is None else internal_key + tweak + tweak = ngu.hash.sha256t(TAP_TWEAK_H, actual_tweak, True) + xo_pubkey = ngu.secp256k1.xonly_pubkey(internal_key) + xo_pubkey_tweaked = xo_pubkey.tweak_add(tweak) + return xo_pubkey_tweaked.to_bytes() + +def tapscript_serialize(script, leaf_version=TAPROOT_LEAF_TAPSCRIPT): + # leaf version is only 7 msb + lv = leaf_version % TAPROOT_LEAF_MASK + return bytes([lv]) + ser_string(script) + +def tapleaf_hash(script, leaf_version=TAPROOT_LEAF_TAPSCRIPT): + return ngu.hash.sha256t(TAP_LEAF_H, tapscript_serialize(script, leaf_version), True) + + class ChainsBase: curve = 'secp256k1' menu_name = None # use 'name' if this isn't defined - core_name = None # name of chain's "core" p2p software + ccc_min_block = 0 # b44_cointype comes from # @@ -65,68 +92,57 @@ def serialize_public(cls, node, addr_fmt=AF_CLASSIC): return node.serialize(cls.slip132[addr_fmt].pub, False) @classmethod - def deserialize_node(cls, text, addr_fmt): - # xpub/xprv to object - addr_fmt = AF_CLASSIC if addr_fmt == AF_P2SH else addr_fmt - node = ngu.hdnode.HDNode() - version = node.deserialize(text) - assert (version == cls.slip132[addr_fmt].pub) \ - or (version == cls.slip132[addr_fmt].priv) - return node + def script_pubkey(cls, addr_fmt, pubkey=None, script=None): + digest = None + if addr_fmt & AFC_SCRIPT: + assert script, "need witness/redeem script" - @classmethod - def p2sh_address(cls, addr_fmt, witdeem_script): - # Multisig and general P2SH support - # - witdeem => witness script for segwit, or redeem script otherwise - # - redeem script can be generated from witness script if needed. - # - this function needs a witdeem script to be provided, not simple to make - # - more verification needed to prove it's change/included address (NOT HERE) - # - reference: - # - returns: str(address) - - assert addr_fmt & AFC_SCRIPT, 'for p2sh only' - assert witdeem_script, "need witness/redeem script" - - if addr_fmt & AFC_SEGWIT: - digest = ngu.hash.sha256s(witdeem_script) - else: - digest = hash160(witdeem_script) + if addr_fmt in [AF_P2WSH, AF_P2WSH_P2SH]: + digest = ngu.hash.sha256s(script) + # bech32 encoded segwit p2sh + spk = b'\x00\x20' + digest + if addr_fmt == AF_P2WSH_P2SH: + # segwit p2wsh encoded as classic P2SH + digest = hash160(spk) + spk = b'\xA9\x14' + digest + b'\x87' + + else: + assert addr_fmt == AF_P2SH + digest = hash160(script) + spk = b'\xA9\x14' + digest + b'\x87' - if addr_fmt & AFC_BECH32: - # bech32 encoded segwit p2sh - addr = ngu.codecs.segwit_encode(cls.bech32_hrp, 0, digest) - elif addr_fmt == AF_P2WSH_P2SH: - # segwit p2wsh encoded as classic P2SH - addr = ngu.codecs.b58_encode(cls.b58_script + hash160(b'\x00\x20' + digest)) else: - # P2SH classic - addr = ngu.codecs.b58_encode(cls.b58_script + digest) + assert pubkey + keyhash = ngu.hash.hash160(pubkey) + if addr_fmt == AF_P2TR: + assert len(pubkey) == 32 # internal + spk = b'\x51\x20' + taptweak(pubkey) + elif addr_fmt == AF_CLASSIC: + spk = b'\x76\xA9\x14' + keyhash + b'\x88\xAC' + elif addr_fmt == AF_P2WPKH_P2SH: + redeem_script = b'\x00\x14' + keyhash + spk = b'\xA9\x14' + ngu.hash.hash160(redeem_script) + b'\x87' + elif addr_fmt == AF_P2WPKH: + spk = b'\x00\x14' + keyhash + else: + raise ValueError('bad address template: %s' % addr_fmt) - return addr + return spk, digest @classmethod def pubkey_to_address(cls, pubkey, addr_fmt): # - renders a pubkey to an address # - works only with single-key addresses assert not addr_fmt & AFC_SCRIPT - - keyhash = ngu.hash.hash160(pubkey) - if addr_fmt == AF_CLASSIC: - script = b'\x76\xA9\x14' + keyhash + b'\x88\xAC' - elif addr_fmt == AF_P2WPKH_P2SH: - redeem_script = b'\x00\x14' + keyhash - scripthash = ngu.hash.hash160(redeem_script) - script = b'\xA9\x14' + scripthash + b'\x87' - elif addr_fmt == AF_P2WPKH: - script = b'\x00\x14' + keyhash - else: - raise ValueError('bad address template: %s' % addr_fmt) - - return cls.render_address(script) + spk, _ = cls.script_pubkey(addr_fmt, pubkey=pubkey) + return cls.render_address(spk) @classmethod def address(cls, node, addr_fmt): # return a human-readable, properly formatted address + if addr_fmt == AF_P2TR: + xo_pk = node.pubkey()[1:] + return ngu.codecs.segwit_encode(cls.bech32_hrp, 1, taptweak(xo_pk)) if addr_fmt == AF_CLASSIC: # olde fashioned P2PKH @@ -134,7 +150,7 @@ def address(cls, node, addr_fmt): return node.addr_help(cls.b58_addr[0]) if addr_fmt & AFC_SCRIPT: - # use p2sh_address() instead. + # use chain.render_address raise ValueError(hex(addr_fmt)) # so must be P2PKH, fetch it. @@ -161,7 +177,7 @@ def privkey(cls, node): @classmethod def hash_message(cls, msg=None, msg_len=0): # Perform sha256 for message-signing purposes (only) - # - or get setup for that, if msg == None + # - or get setup for that, if msg is None s = sha256() s.update(cls.msg_signing_prefix()) @@ -242,37 +258,37 @@ def render_address(cls, script): @classmethod def op_return(cls, script): - """Returns decoded string op return data if script is op return otherwise None""" + # returns decoded string op return data if script is op return otherwise None gen = disassemble(script) script_type = next(gen) - if OP_RETURN in script_type: - try: - data = next(gen)[0] - if data is None: raise RuntimeError - except (RuntimeError, StopIteration): - return "null-data", "" - data_hex = b2a_hex(data).decode() - data_ascii = None - if min(data) >= 32 and max(data) < 127: # printable - try: - data_ascii = data.decode("ascii") - except: - pass - return data_hex, data_ascii - return None + if OP_RETURN not in script_type: + return + + try: + data = next(gen)[0] + if data: + return data + except StopIteration: + pass + + return b"" @classmethod def possible_address_fmt(cls, addr): # Given a text (serialized) address, return what # address format applies to the address, but # for AF_P2SH case, could be: AF_P2SH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH. .. we don't know - if addr.startswith(cls.bech32_hrp): - if addr.startswith(cls.bech32_hrp+'1p'): - # really any ver=1 script or address, but for now... + hrp = cls.bech32_hrp + "1" + if addr.startswith(hrp): + if addr.startswith(hrp+'p'): + # segwit v1 (any ver=1 script or address, but for now just taproot...) return AF_P2TR - else: + elif addr.startswith(hrp+'q'): + # segwit v0 return AF_P2WPKH if len(addr) < 55 else AF_P2WSH + return 0 + try: raw = ngu.codecs.b58_decode(addr) except ValueError: @@ -290,8 +306,8 @@ def possible_address_fmt(cls, addr): class BitcoinMain(ChainsBase): # see ctype = 'BTC' - name = 'Bitcoin' - core_name = 'Bitcoin Core' + name = 'Bitcoin Mainnet' + ccc_min_block = 922061 # Nov 3/2025 slip132 = { AF_CLASSIC: Slip132Version(0x0488B21E, 0x0488ADE4, 'x'), @@ -299,6 +315,7 @@ class BitcoinMain(ChainsBase): AF_P2WPKH: Slip132Version(0x04b24746, 0x04b2430c, 'z'), AF_P2WSH_P2SH: Slip132Version(0x0295b43f, 0x0295b005, 'Y'), AF_P2WSH: Slip132Version(0x02aa7ed3, 0x02aa7a99, 'Z'), + AF_P2TR: Slip132Version(0x0488B21E, 0x0488ADE4, 'x'), } bech32_hrp = 'bc' @@ -309,10 +326,10 @@ class BitcoinMain(ChainsBase): b44_cointype = 0 -class BitcoinTestnet(BitcoinMain): +class BitcoinTestnet(ChainsBase): + # testnet4 (was testnet3 up until 2025 but all parameters are the same) ctype = 'XTN' - name = 'Bitcoin Testnet' - menu_name = 'Testnet: BTC' + name = 'Bitcoin Testnet 4' slip132 = { AF_CLASSIC: Slip132Version(0x043587cf, 0x04358394, 't'), @@ -320,6 +337,7 @@ class BitcoinTestnet(BitcoinMain): AF_P2WPKH: Slip132Version(0x045f1cf6, 0x045f18bc, 'v'), AF_P2WSH_P2SH: Slip132Version(0x024289ef, 0x024285b5, 'U'), AF_P2WSH: Slip132Version(0x02575483, 0x02575048, 'V'), + AF_P2TR: Slip132Version(0x043587cf, 0x04358394, 't'), } bech32_hrp = 'tb' @@ -331,10 +349,9 @@ class BitcoinTestnet(BitcoinMain): b44_cointype = 1 -class BitcoinRegtest(BitcoinMain): +class BitcoinRegtest(ChainsBase): ctype = 'XRT' name = 'Bitcoin Regtest' - menu_name = 'Regtest: BTC' slip132 = { AF_CLASSIC: Slip132Version(0x043587cf, 0x04358394, 't'), @@ -342,6 +359,7 @@ class BitcoinRegtest(BitcoinMain): AF_P2WPKH: Slip132Version(0x045f1cf6, 0x045f18bc, 'v'), AF_P2WSH_P2SH: Slip132Version(0x024289ef, 0x024285b5, 'U'), AF_P2WSH: Slip132Version(0x02575483, 0x02575048, 'V'), + AF_P2TR: Slip132Version(0x043587cf, 0x04358394, 't'), } bech32_hrp = 'bcrt' @@ -376,10 +394,17 @@ def current_chain(): return get_chain(chain) +def current_key_chain(): + c = current_chain() + if c == BitcoinRegtest: + # regtest has same extended keys as testnet + c = BitcoinTestnet + return c + # Overbuilt: will only be testnet and mainchain. AllChains = [BitcoinMain, BitcoinTestnet, BitcoinRegtest] -def slip32_deserialize(xp): +def slip132_deserialize(xp): # .. and classify chain and addr-type, as implied by prefix node = ngu.hdnode.HDNode() version = node.deserialize(xp) @@ -403,8 +428,82 @@ def slip32_deserialize(xp): AF_P2WPKH_P2SH ), # generates 3xxx/2xxx p2sh-looking addresses ( 'BIP-84 (Native Segwit P2WPKH)', "m/84h/{coin_type}h/{account}h/{change}/{idx}", AF_P2WPKH ), # generates bc1 bech32 addresses + ('BIP-86 (Taproot Segwit P2TR)', "m/86h/{coin_type}h/{account}h/{change}/{idx}", + AF_P2TR), # generates bc1p bech32m addresses ] +STD_DERIVATIONS = { + "p2pkh": CommonDerivations[0][1], + "p2sh-p2wpkh": CommonDerivations[1][1], + "p2wpkh-p2sh": CommonDerivations[1][1], + "p2wpkh": CommonDerivations[2][1], + "p2tr": CommonDerivations[3][1], +} + +MS_STD_DERIVATIONS = { + ("p2sh", "m/45h", AF_P2SH), + ("p2sh_p2wsh", "m/48h/{coin}h/{acct_num}h/1h", AF_P2WSH_P2SH), + ("p2wsh", "m/48h/{coin}h/{acct_num}h/2h", AF_P2WSH), + ('p2tr', "m/48h/{coin}h/{acct_num}h/3h", AF_P2TR), +} + +AF_TO_STR_AF = { + AF_BARE_PK: "p2pk", + AF_CLASSIC: "p2pkh", + AF_P2TR: "p2tr", + AF_P2WPKH: "p2wpkh", + AF_P2WPKH_P2SH: "p2sh-p2wpkh", + AF_P2SH: "p2sh", + AF_P2WSH: "p2wsh", + AF_P2WSH_P2SH: "p2sh-p2wsh", +} + +def parse_addr_fmt_str(addr_fmt): + # accepts strings and also integers if already parsed + # integers are coming from USB + try: + if isinstance(addr_fmt, int): + if addr_fmt in [AF_P2WPKH_P2SH, AF_P2WPKH, AF_CLASSIC]: + return addr_fmt + else: + try: + addr_fmt = AF_TO_STR_AF[addr_fmt] # just for error msg + except: pass + raise ValueError + + addr_fmt = addr_fmt.lower() + if addr_fmt in ("p2sh-p2wpkh", "p2wpkh-p2sh"): + return AF_P2WPKH_P2SH + elif addr_fmt == "p2pkh": + return AF_CLASSIC + elif addr_fmt == "p2wpkh": + return AF_P2WPKH + elif addr_fmt == "p2tr": + return AF_P2TR + else: + raise ValueError + except ValueError: + raise ValueError("Unsupported address format: '%s'" % addr_fmt) + + +def af_to_bip44_purpose(addr_fmt): + # Address format to BIP-44 "purpose" number + # - single signature only + return {AF_CLASSIC: 44, + AF_P2WPKH_P2SH: 49, + AF_P2WPKH: 84, + AF_P2TR: 86}[addr_fmt] + +def addr_fmt_label(addr_fmt): + return { + AF_CLASSIC: "Classic P2PKH", + AF_P2WPKH_P2SH: "P2SH-Segwit", + AF_P2WPKH: "Segwit P2WPKH", + AF_P2TR: "Taproot P2TR", + AF_P2WSH: "Segwit P2WSH", + AF_P2WSH_P2SH: "P2SH-P2WSH", + AF_P2SH: "Legacy P2SH", + }[addr_fmt] def verify_recover_pubkey(sig, digest): # verifies a message digest against a signature and recovers diff --git a/shared/charcodes.py b/shared/charcodes.py index 06d829c0e..f1242afd5 100644 --- a/shared/charcodes.py +++ b/shared/charcodes.py @@ -107,4 +107,11 @@ assert DECODER[KEYNUM_SYMBOL] == KEY_SYMBOL assert DECODER[KEYNUM_LAMP] == KEY_LAMP +# These affect how 'ux stories' are rendered; they are control +# characters on the output side of things, not input. +# - must be first char in line +OUT_CTRL_TITLE = '\x01' # be a title line +OUT_CTRL_ADDRESS = '\x02' # it's a payment address +OUT_CTRL_NOWRAP = '\x03' # do not word wrap this line + # EOF diff --git a/shared/compat7z.py b/shared/compat7z.py index e475c4f6f..3034d1ce3 100644 --- a/shared/compat7z.py +++ b/shared/compat7z.py @@ -198,7 +198,7 @@ def read_iter(cls, f, expect_crc=None): # read only next one; ftell has to be on first byte already rv = cls.read(f) - if expect_crc != None: + if expect_crc is not None: assert rv # read past end assert masked_crc(rv.bits) == expect_crc @@ -315,7 +315,7 @@ def add_data(self, raw): padded_len = (here + 15) & ~15 if padded_len != here: - if self.padding != None: + if self.padding is not None: raise ValueError() # "can't do less than a block except at end" self.padding = (padded_len - here) raw += bytes(self.padding) diff --git a/shared/decoders.py b/shared/decoders.py index 6903e37c7..a71483fb9 100644 --- a/shared/decoders.py +++ b/shared/decoders.py @@ -4,7 +4,7 @@ # # included in Q builds only, not Mk4 --> manifest_q1.py # -import ngu, bip39, ure, stash +import ngu, bip39, ure, stash, json from ubinascii import unhexlify as a2b_hex from exceptions import QRDecodeExplained from bbqr import TYPE_LABELS @@ -101,7 +101,7 @@ def decode_qr_result(got, expect_secret=False, expect_text=False, expect_bbqr=Fa try: ty, final_size, got = got.storage.finalize() except BaseException as exc: - import sys; sys.print_exception(exc) + #import sys; sys.print_exception(exc) raise QRDecodeExplained("BBQr decode failed: " + str(exc)) if expect_bbqr: @@ -131,7 +131,23 @@ def decode_qr_result(got, expect_secret=False, expect_text=False, expect_bbqr=Fa pass elif ty == 'J': - return 'json', (got,) + what = "json" + if "msg" in got: + what = "smsg" + + return what, (got,) + + elif ty in 'RSE': + # key-teleport related + + from pincodes import pa + if pa.hobbled_mode and ty != 'E': + raise QRDecodeExplained("KT Blocked") + + if ty == 'R' and len(got) != 33: + raise QRDecodeExplained("Truncated KT RX") + + return 'teleport', (ty, got) else: msg = TYPE_LABELS.get(ty, 'Unknown FileType') raise QRDecodeExplained("Sorry, %s not useful." % msg) @@ -159,6 +175,16 @@ def decode_qr_result(got, expect_secret=False, expect_text=False, expect_bbqr=Fa if expect_secret: raise QRDecodeExplained("Not a secret?") + try: + dct = json.loads(got) + if "msg" in dct: + return "smsg", (got,) + except: pass + + # Sparrow compat + if "signmessage" in got: + return "smsg", (got,) + # try to recognize various bitcoin-related text strings... return decode_short_text(got) @@ -178,6 +204,9 @@ def decode_short_text(got): # might be a PSBT? if len(got) > 100: + if got.lstrip().startswith("-----BEGIN BITCOIN SIGNED MESSAGE-----"): + return "vmsg", (got,) + from auth import psbt_encoding_taster try: decoder, _, psbt_len = psbt_encoding_taster(got[0:10].encode(), len(got)) @@ -194,25 +223,9 @@ def decode_short_text(got): # was something else. pass - # multisig descriptor - # multi( catches both multi( and sortedmulti( - if ("multi(" in got): - return 'multi', (got,) - - if ("\n" in got) and ('pub' in got): - # legacy multisig import/export format - # [0-9a-fA-F]{8}\s*:\s*[xtyYzZuUvV]pub[1-9A-HJ-NP-Za-km-z]{107} - # above is more precise BUT counted repetitions not supported in mpy - cc_ms_pat = r"[0-9a-fA-F]+\s*:\s*[xtyYzZuUvV]pub[1-9A-HJ-NP-Za-km-z]+" - rgx = ure.compile(cc_ms_pat) - # go line by line and match above, once 2 matches observed - considered multisig - # important to not use ure.search for big strings (can run out of stack) - c = 0 # match count - for l in got.split("\n"): - if rgx.search(l): - c += 1 - if c > 1: - return 'multi', (got,) + from descriptor import Descriptor + if Descriptor.is_descriptor(got): + return 'minisc', (got,) # Things with newlines in them are not URL's # - working URLs are not >4k diff --git a/shared/desc_utils.py b/shared/desc_utils.py new file mode 100644 index 000000000..e2f7c870b --- /dev/null +++ b/shared/desc_utils.py @@ -0,0 +1,477 @@ +# (c) Copyright 2020 by Stepan Snigirev, see +# +# Changes (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +import ngu, chains, ustruct, stash +from io import BytesIO +from public_constants import MAX_PATH_DEPTH +from binascii import unhexlify as a2b_hex +from binascii import hexlify as b2a_hex +from utils import keypath_to_str, str_to_keypath, swab32, xfp2str +from serializations import ser_compact_size + + +WILDCARD = "*" +PROVABLY_UNSPENDABLE = b'\x02P\x92\x9bt\xc1\xa0IT\xb7\x8bK`5\xe9z^\x07\x8aZ\x0f(\xec\x96\xd5G\xbf\xee\x9a\xce\x80:\xc0' + +INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ " +CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + +def polymod(c, val): + c0 = c >> 35 + c = ((c & 0x7ffffffff) << 5) ^ val + if (c0 & 1): + c ^= 0xf5dee51989 + if (c0 & 2): + c ^= 0xa9fdca3312 + if (c0 & 4): + c ^= 0x1bab10e32d + if (c0 & 8): + c ^= 0x3706b1677a + if (c0 & 16): + c ^= 0x644d626ffd + + return c + +def descriptor_checksum(desc): + c = 1 + cls = 0 + clscount = 0 + for ch in desc: + pos = INPUT_CHARSET.find(ch) + if pos == -1: + raise ValueError(ch) + + c = polymod(c, pos & 31) + cls = cls * 3 + (pos >> 5) + clscount += 1 + if clscount == 3: + c = polymod(c, cls) + cls = 0 + clscount = 0 + + if clscount > 0: + c = polymod(c, cls) + for j in range(0, 8): + c = polymod(c, 0) + c ^= 1 + + rv = '' + for j in range(0, 8): + rv += CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31] + + return rv + +def append_checksum(desc): + return desc + "#" + descriptor_checksum(desc) + + +def parse_desc_str(string): + """Remove comments, empty lines and strip line. Produce single line string""" + res = "" + for l in string.split("\n"): + strip_l = l.strip() + if not strip_l: + continue + if strip_l.startswith("#"): + continue + res += strip_l + return res + + +def read_until(s, chars=b",)(#"): + res = b"" + while True: + chunk = s.read(1) + if len(chunk) == 0: + return res, None + if chunk in chars: + return res, chunk + res += chunk + + +class KeyOriginInfo: + def __init__(self, fingerprint: bytes, derivation: list, cc_fp=None): + self.fingerprint = fingerprint + self.derivation = derivation + self._cc_fp = cc_fp + + def __eq__(self, other): + return self.psbt_derivation() == other.psbt_derivation() + + def __hash__(self): + return hash(tuple(self.psbt_derivation())) + + @property + def cc_fp(self): + if self._cc_fp is None: + self._cc_fp = ustruct.unpack('") + assert char, err + assert b";" not in int_num, "Solved cardinality > 2" + cls.not_hardened(int_num) + + assert int_num != ext_num # cannot be the same + multi_i = len(idxs) + idxs.append((int(ext_num.decode()), int(int_num.decode()))) + + else: + # char in "/)," + if got == b"*": + # every derivation has to end with wildcard (only ranged keys allowed) + idxs.append(WILDCARD) + break + elif got: + cls.not_hardened(got) + idxs.append(int(got.decode())) + + # comma and parenthesis not allowed in subderivation, marker of the end + if char in b",)": break + + assert idxs[-1] == WILDCARD, "All keys must be ranged" + if idxs == [0, WILDCARD]: + # normalize and instead save as <0;1> as change derivation was not provided + obj = cls() + else: + + assert multi_i is not None, "need multipath" + assert len(idxs[multi_i]) == 2, "wrong multipath" + + obj = cls(tuple(idxs)) + obj.multi_path_index = multi_i + + return obj + + def to_string(self, external=True, internal=True): + res = [] + for i in self.indexes: + if isinstance(i, tuple): + if internal is True and external is False: + i = str(i[1]) + elif internal is False and external is True: + i = str(i[0]) + else: + i = "<%d;%d>" % (i[0], i[1]) + else: + i = str(i) + res.append(i) + return "/".join(res) + + +class Key: + def __init__(self, node, origin, derivation=None, taproot=False, chain_type=None): + self.origin = origin + self.node = node + self.derivation = derivation or KeyDerivationInfo() + self.taproot = taproot + self.chain_type = chain_type + + def __eq__(self, other): + return hash(self) == hash(other) + + def __hash__(self): + # return hash(self.to_string()) + return hash(self.node.pubkey()) + hash(self.derivation) + + def __len__(self): + return 34 - int(self.taproot) # <33:sec> or <32:xonly> + + @property + def fingerprint(self): + return self.origin.fingerprint + + def serialize(self): + return self.key_bytes() + + def compile(self): + d = self.serialize() + return ser_compact_size(len(d)) + d + + @classmethod + def parse(cls, s): + first = s.read(1) + origin = None + + if first == b"[": + prefix, char = read_until(s, b"]") + if char != b"]": + raise ValueError("Invalid key - missing ] in key origin info") + origin = KeyOriginInfo.from_string(prefix.decode()) + else: + s.seek(-1, 1) + + k, char = read_until(s, b",)/") + der = None + if char == b"/": + der = KeyDerivationInfo.parse(s) + if char is not None: + s.seek(-1, 1) + + # parse key + node, chain_type = cls.parse_key(k) + if origin is None: + cc_fp = swab32(node.my_fp()) + origin = KeyOriginInfo(ustruct.pack('/*") + + +def bip388_validate_policy(desc_tmplt, keys_info): + from uio import BytesIO + + s = BytesIO(desc_tmplt) + r = [] + while True: + got, char = read_until(s, b"@") + if not char: + # no more - done + break + + # key derivation info required for policy + got, char = read_until(s, b"/") + assert char, "key derivation missing" + num = int(got.decode()) + if num not in r: + r.append(num) + + assert s.read(1) in b"<*", "need multipath" + + + assert len(r) == len(keys_info), "Invalid policy" + assert r == list(range(len(r))), "Out of order" diff --git a/shared/descriptor.py b/shared/descriptor.py index e8cf6835c..9ab0c1a1b 100644 --- a/shared/descriptor.py +++ b/shared/descriptor.py @@ -1,252 +1,319 @@ -# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# (c) Copyright 2020 by Stepan Snigirev, see # -# descriptor.py - Bitcoin Core's descriptors and their specialized checksums. +# Changes (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -# Based on: https://github.com/bitcoin/bitcoin/blob/master/src/script/descriptor.cpp -# -from public_constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH - -MULTI_FMT_TO_SCRIPT = { - AF_P2SH: "sh(%s)", - AF_P2WSH_P2SH: "sh(wsh(%s))", - AF_P2WSH: "wsh(%s)", - None: "wsh(%s)", - # hack for tests - "p2sh": "sh(%s)", - "p2sh-p2wsh": "sh(wsh(%s))", - "p2wsh-p2sh": "sh(wsh(%s))", - "p2wsh": "wsh(%s)", -} - -SINGLE_FMT_TO_SCRIPT = { - AF_P2WPKH: "wpkh(%s)", - AF_CLASSIC: "pkh(%s)", - AF_P2WPKH_P2SH: "sh(wpkh(%s))", - None: "wpkh(%s)", - "p2pkh": "pkh(%s)", - "p2wpkh": "wpkh(%s)", - "p2sh-p2wpkh": "sh(wpkh(%s))", - "p2wpkh-p2sh": "sh(wpkh(%s))", -} - -INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ " -CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" - -try: - from utils import xfp2str, str2xfp -except ModuleNotFoundError: - import struct - from binascii import unhexlify as a2b_hex - from binascii import hexlify as b2a_hex - # assuming not micro python - def xfp2str(xfp): - # Standardized way to show an xpub's fingerprint... it's a 4-byte string - # and not really an integer. Used to show as '0x%08x' but that's wrong endian. - return b2a_hex(struct.pack('> 35 - c = ((c & 0x7ffffffff) << 5) ^ val - if (c0 & 1): - c ^= 0xf5dee51989 - if (c0 & 2): - c ^= 0xa9fdca3312 - if (c0 & 4): - c ^= 0x1bab10e32d - if (c0 & 8): - c ^= 0x3706b1677a - if (c0 & 16): - c ^= 0x644d626ffd - - return c - -def descriptor_checksum(desc): - c = 1 - cls = 0 - clscount = 0 - for ch in desc: - pos = INPUT_CHARSET.find(ch) - if pos == -1: - raise ValueError(ch) - - c = polymod(c, pos & 31) - cls = cls * 3 + (pos >> 5) - clscount += 1 - if clscount == 3: - c = polymod(c, cls) - cls = 0 - clscount = 0 - - if clscount > 0: - c = polymod(c, cls) - for j in range(0, 8): - c = polymod(c, 0) - c ^= 1 - - rv = '' - for j in range(0, 8): - rv += CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31] - - return rv - -def append_checksum(desc): - return desc + "#" + descriptor_checksum(desc) - - -def parse_desc_str(string): - """Remove comments, empty lines and strip line. Produce single line string""" - res = "" - for l in string.split("\n"): - strip_l = l.strip() - if not strip_l: - continue - if strip_l.startswith("#"): - continue - res += strip_l - return res - - -def multisig_descriptor_template(xpub, path, xfp, addr_fmt): - key_exp = "[%s%s]%s/0/*" % (xfp.lower(), path.replace("m", ''), xpub) - if addr_fmt == AF_P2WSH_P2SH: - descriptor_template = "sh(wsh(sortedmulti(M,%s,...)))" - elif addr_fmt == AF_P2WSH: - descriptor_template = "wsh(sortedmulti(M,%s,...))" - elif addr_fmt == AF_P2SH: - descriptor_template = "sh(sortedmulti(M,%s,...))" - else: - return None - descriptor_template = descriptor_template % key_exp - return descriptor_template +import ngu, chains +from io import BytesIO +from collections import OrderedDict +from binascii import hexlify as b2a_hex +from utils import xfp2str +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR +from public_constants import AF_P2WSH, AF_P2WSH_P2SH, AF_P2SH, MAX_TR_SIGNERS +from desc_utils import parse_desc_str, append_checksum, descriptor_checksum, Key +from miniscript import Miniscript +from precomp_tag_hash import TAP_BRANCH_H + + +class Tapscript: + def __init__(self, tree): + self.tree = tree # miniscript or (tapscript, tapscript) + self._merkle_root = None + self._processed_tree = None + + def iter_leaves(self): + if isinstance(self.tree, Miniscript): + yield self.tree + else: + for ts in self.tree: + yield from ts.iter_leaves() + + @property + def merkle_root(self): + if not self._merkle_root: + self._processed_tree, self._merkle_root = self.process_tree() + return self._merkle_root + + def derive(self, idx, key_map, change=False): + if isinstance(self.tree, Miniscript): + tree = self.tree.derive(idx, key_map, change=change) + else: + l, r = self.tree + tree = [l.derive(idx, key_map, change=change), + r.derive(idx, key_map, change=change)] + + return type(self)(tree) + + def process_tree(self): + if isinstance(self.tree, Miniscript): + script = self.tree.compile() + h = chains.tapleaf_hash(script) + return [(chains.TAPROOT_LEAF_TAPSCRIPT, script, bytes())], h + + l, r = self.tree + left, left_h = l.process_tree() + right, right_h = r.process_tree() + left = [(version, script, control + right_h) for version, script, control in left] + right = [(version, script, control + left_h) for version, script, control in right] + if right_h < left_h: + right_h, left_h = left_h, right_h + + h = ngu.hash.sha256t(TAP_BRANCH_H, left_h + right_h, True) + return left + right, h + + # UNUSED - using above proces tree cached result to dump scripts to CSV + # def script_tree(self): + # if isinstance(self.tree, Miniscript): + # return b2a_hex(chains.tapscript_serialize(self.tree.compile())).decode() + # + # l, r = self.tree + # return "{" + l.script_tree() + "," +r.script_tree() + "}" + + @classmethod + def read_from(cls, s): + c = s.read(1) + assert len(c) + if c == b"{": # more than one miniscript + left = cls.read_from(s) + c = s.read(1) + if c == b"}": + return left + if c != b",": + raise ValueError("Invalid tapscript: expected ','") + + right = cls.read_from(s) + if s.read(1) != b"}": + raise ValueError("Invalid tapscript: expected '}'") + + return cls((left, right)) + + s.seek(-1, 1) + ms = Miniscript.read_from(s, taproot=True) + return cls(ms) + + def to_string(self, external=True, internal=True): + if isinstance(self.tree, Miniscript): + return self.tree.to_string(external, internal) + + l, r = self.tree + return ("{" + l.to_string(external,internal) + "," + + r.to_string(external, internal) + "}") class Descriptor: - __slots__ = ( - "keys", - "addr_fmt", - ) + def __init__(self, key=None, miniscript=None, tapscript=None, addr_fmt=None, keys=None): + if addr_fmt in [AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH]: + assert miniscript + assert not key + else: + # single-sig + taproot/tapscript + assert miniscript is None + assert key - def __init__(self, keys, addr_fmt): - self.keys = keys + self.key = key + self.miniscript = miniscript + self.tapscript = tapscript self.addr_fmt = addr_fmt + # cached keys + self._keys = keys + + def validate(self, disable_checks=False): + # should only be run once while importing wallet + from glob import settings + + c = 0 + has_mine = 0 + err_top_B = "Top level miniscript should be 'B'" + max_signers = 20 + + if self.tapscript: + assert self.key # internal key (would fail during parse) + max_signers = MAX_TR_SIGNERS + for l in self.tapscript.iter_leaves(): + assert l.type == "B", err_top_B + l.verify() + l.is_sane(taproot=True) + # cannot have same keys in single miniscript + # provably unspendable taproot internal key is not covered here + assert len(l.keys) == len(set(l.keys)), "Insane" + + elif self.miniscript: + assert self.key is None + assert self.miniscript.type == "B", err_top_B + self.miniscript.verify() + self.miniscript.is_sane(taproot=False) + # cannot have same keys in single miniscript + assert len(self.miniscript.keys) == len(set(self.miniscript.keys)), "Insane" + + my_xfp = settings.get('xfp', 0) + ext_nums = set() + int_nums = set() + for k in self.keys: + has_mine += k.validate(my_xfp, disable_checks) + ext, int = k.derivation.get_ext_int() + ext_nums.add(ext) + int_nums.add(int) + c += 1 + + if not self.tapscript and not self.is_basic_multisig: + # this is non-taproot Miniscript + # Miniscript expressions can only be used in wsh or tr. + assert self.addr_fmt != AF_P2SH, "Miniscript in legacy P2SH not allowed" + + assert ext_nums.isdisjoint(int_nums), "Non-disjoint multipath" + assert c <= max_signers, "max signers" + + assert has_mine > 0, 'My key %s missing in descriptor.' % xfp2str(my_xfp).upper() + + def bip388_wallet_policy(self): + # Return compact descriptor (BIP-388 style) template and key info + # - only same origin keys + keys_info = OrderedDict() + + for k in self.keys: + pk = k.node.pubkey() + if pk not in keys_info: + keys_info[pk] = k.to_string(external=False, internal=False) + + desc_tmplt = self.to_string(checksum=False).replace("/<0;1>/*", "/**") + + keys_info = list(keys_info.values()) + for i, k_str in enumerate(keys_info): + desc_tmplt = desc_tmplt.replace(k_str, '@%d' % i) + + return desc_tmplt, keys_info + + @property + def script_len(self): + if self.is_taproot: + return 34 # OP_1 <32:xonly> + if self.miniscript: + return len(self.miniscript) + if self.addr_fmt == AF_P2WPKH: + return 22 # 00 <20:pkh> + return 25 # OP_DUP OP_HASH160 <20:pkh> OP_EQUALVERIFY OP_CHECKSIG + + def xfp_paths(self, skip_unspend_ik=False): + res = [] + for k in self.keys: + if self.is_taproot and k.is_provably_unspendable and skip_unspend_ik: + continue - @staticmethod - def checksum_check(desc_w_checksum , csum_required=False): - try: - desc, checksum = desc_w_checksum.split("#") - except ValueError: - if csum_required: - raise ValueError("Missing descriptor checksum") - return desc_w_checksum, None - calc_checksum = descriptor_checksum(desc) - if calc_checksum != checksum: - raise WrongCheckSumError("Wrong checksum %s, expected %s" % (checksum, calc_checksum)) - return desc, checksum + res.append(k.origin.psbt_derivation()) - @staticmethod - def parse_key_orig_info(key): - # key origin info is required for our MultisigWallet - close_index = key.find("]") - if key[0] != "[" or close_index == -1: - raise ValueError("Key origin info is required for %s" % (key)) - key_orig_info = key[1:close_index] # remove brackets - key = key[close_index + 1:] - assert "/" in key_orig_info, "Malformed key derivation info" - return key_orig_info, key + return res - @staticmethod - def parse_key_derivation_info(key): - invalid_subderiv_msg = "Invalid subderivation path - only 0/* or <0;1>/* allowed" - slash_split = key.split("/") - assert len(slash_split) > 1, invalid_subderiv_msg - if all(["h" not in elem and "'" not in elem for elem in slash_split[1:]]): - assert slash_split[-1] == "*", invalid_subderiv_msg - assert slash_split[-2] in ["0", "<0;1>", "<1;0>"], invalid_subderiv_msg - assert len(slash_split[1:]) == 2, invalid_subderiv_msg - return slash_split[0] - else: - raise ValueError("Cannot use hardened sub derivation path") - - def checksum(self): - return descriptor_checksum(self._serialize()) - - def serialize_keys(self, internal=False, int_ext=False): - result = [] - for xfp, deriv, xpub in self.keys: - if deriv[0] == "m": - # get rid of 'm' - deriv = deriv[1:] - elif deriv[0] != "/": - # input "84'/0'/0'" would lack slash separtor with xfp - deriv = "/" + deriv - if not isinstance(xfp, str): - xfp = xfp2str(xfp) - koi = xfp + deriv - # normalize xpub to use h for hardened instead of ' - key_str = "[%s]%s" % (koi.lower(), xpub) - if int_ext: - key_str = key_str + "/" + "<0;1>" + "/" + "*" - else: - key_str = key_str + "/" + "/".join(["1", "*"] if internal else ["0", "*"]) - result.append(key_str.replace("'", "h")) - return result + @property + def is_segwit_v0(self): + return self.addr_fmt in [AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2WSH, AF_P2WSH_P2SH] - def _serialize(self, internal=False, int_ext=False): - """Serialize without checksum""" - assert len(self.keys) == 1 # "Multiple keys for single signature script" - desc_base = SINGLE_FMT_TO_SCRIPT[self.addr_fmt] - inner = self.serialize_keys(internal=internal, int_ext=int_ext)[0] - return desc_base % (inner) + @property + def is_segwit(self): + return self.is_taproot or self.is_segwit_v0 - def serialize(self, internal=False, int_ext=False): - """Serialize with checksum""" - return append_checksum(self._serialize(internal=internal, int_ext=int_ext)) + @property + def is_taproot(self): + return self.addr_fmt == AF_P2TR - @classmethod - def parse(cls, desc_w_checksum): - # remove garbage - desc_w_checksum = parse_desc_str(desc_w_checksum) - # check correct checksum - desc, checksum = cls.checksum_check(desc_w_checksum) - # legacy - if desc.startswith("pkh("): - addr_fmt = AF_CLASSIC - tmp_desc = desc.replace("pkh(", "") - tmp_desc = tmp_desc.rstrip(")") - - # native segwit - elif desc.startswith("wpkh("): - addr_fmt = AF_P2WPKH - tmp_desc = desc.replace("wpkh(", "") - tmp_desc = tmp_desc.rstrip(")") - - # wrapped segwit - elif desc.startswith("sh(wpkh("): - addr_fmt = AF_P2WPKH_P2SH - tmp_desc = desc.replace("sh(wpkh(", "") - tmp_desc = tmp_desc.rstrip("))") + @property + def is_legacy_sh(self): + return self.addr_fmt in [AF_P2SH, AF_P2WSH_P2SH, AF_P2WPKH_P2SH] + + @property + def is_basic_multisig(self): + return self.miniscript and self.miniscript.NAME in ["multi", "sortedmulti"] + + @property + def is_sortedmulti(self): + return self.is_basic_multisig and self.miniscript.NAME == "sortedmulti" + + @property + def keys(self): + if self._keys: + return self._keys + + if self.tapscript: + # internal is always first + # use ordered dict as order preserving set + keys = OrderedDict() + # add internal key + keys[self.key] = None + # taptree keys + for lv in self.tapscript.iter_leaves(): + for k in lv.keys: + keys[k] = None + + self._keys = list(keys) + + elif self.miniscript: + self._keys = self.miniscript.keys else: - raise ValueError("Unsupported descriptor. Supported: pkh(), wpkh(), sh(wpkh()).") + # single-sig + self._keys = [self.key] + + return self._keys + + def derive(self, idx=None, change=False): + if self.is_taproot: + # derive keys first + # duplicate keys can be may be found in different leaves + # use map to derive each key just once + derived_keys = OrderedDict() + ikd = None + for i, k in enumerate(self.keys): + dk = k.derive(idx, change=change) + dk.taproot = self.is_taproot + derived_keys[k] = dk + if not i: + # internal key is always at index 0 in self.keys + ikd = dk + + return type(self)( + ikd, + tapscript=self.tapscript.derive(idx, derived_keys, change=change), + addr_fmt=self.addr_fmt, + keys=list(derived_keys.values()), + ) + if self.miniscript: + return type(self)( + None, + self.miniscript.derive(idx, change=change), + addr_fmt=self.addr_fmt, + ) + + # single-sig + return type(self)(self.key.derive(idx, change=change)) + + def script_pubkey(self, compiled_scr=None): + if self.is_taproot: + tweak = None + if self.tapscript: + tweak = self.tapscript.merkle_root + output_pubkey = chains.taptweak(self.key.serialize(), tweak) + return b"\x51\x20" + output_pubkey + + if self.is_legacy_sh: + if self.miniscript: + # caller may have already built a script + scr = compiled_scr or self.miniscript.compile() + redeem_scr = scr + if self.addr_fmt == AF_P2WSH_P2SH: + redeem_scr = b"\x00\x20" + ngu.hash.sha256s(scr) + else: + redeem_scr = b"\x00\x14" + ngu.hash.hash160(self.key.node.pubkey()) + + return b"\xa9\x14" + ngu.hash.hash160(redeem_scr) + b"\x87" - koi, key = cls.parse_key_orig_info(tmp_desc) - if key[0:4] not in ["tpub", "xpub"]: - raise ValueError("Only extended public keys are supported") + if self.addr_fmt == AF_P2WSH: + # witness script p2wsh only + return b"\x00\x20" + ngu.hash.sha256s(compiled_scr or self.miniscript.compile()) - xpub = cls.parse_key_derivation_info(key) - xfp = str2xfp(koi[:8]) - origin_deriv = "m" + koi[8:] + if self.addr_fmt == AF_P2WPKH: + return b"\x00\x14" + ngu.hash.hash160(self.key.serialize()) - return cls(keys=[(xfp, origin_deriv, xpub)], addr_fmt=addr_fmt) + # p2pkh + assert self.addr_fmt == AF_CLASSIC + return b"\x76\xa9\x14" + ngu.hash.hash160(self.key.serialize()) + b"\x88\xac" @classmethod def is_descriptor(cls, desc_str): @@ -267,142 +334,132 @@ def is_descriptor(cls, desc_str): return True return False - def bitcoin_core_serialize(self, external_label=None): + @staticmethod + def checksum_check(desc_w_checksum, csum_required=False): + try: + desc, checksum = desc_w_checksum.split("#") + except ValueError: + if csum_required: + raise ValueError("Missing descriptor checksum") + return desc_w_checksum, None + calc_checksum = descriptor_checksum(desc) + if calc_checksum != checksum: + raise ValueError("Wrong checksum %s, expected %s" % (checksum, calc_checksum)) + return desc, checksum + + @classmethod + def from_string(cls, desc, checksum=False): + desc = parse_desc_str(desc) + desc, cs = cls.checksum_check(desc) + s = BytesIO(desc.encode()) + res = cls.read_from(s) + left = s.read() + if len(left) > 0: + raise ValueError("Unexpected characters after descriptor: %r" % left) + if checksum: + if cs is None: + _, cs = res.to_string().split("#") + return res, cs + return res + + @classmethod + def read_from(cls, s): + start = s.read(8) + af = AF_CLASSIC + internal_key = None + tapscript = None + if start.startswith(b"tr("): + af = AF_P2TR + s.seek(-5, 1) + internal_key = Key.parse(s) + internal_key.taproot = True + sep = s.read(1) + if sep == b")": + s.seek(-1, 1) + else: + assert sep == b"," + tapscript = Tapscript.read_from(s) + + elif start.startswith(b"sh(wsh("): + af = AF_P2WSH_P2SH + s.seek(-1, 1) + elif start.startswith(b"wsh("): + af = AF_P2WSH + s.seek(-4, 1) + elif start.startswith(b"sh(wpkh("): + af = AF_P2WPKH_P2SH + elif start.startswith(b"wpkh("): + af = AF_P2WPKH + s.seek(-3, 1) + elif start.startswith(b"pkh("): + s.seek(-4, 1) + elif start.startswith(b"sh("): + af = AF_P2SH + s.seek(-5, 1) + else: + raise ValueError("Invalid descriptor") + + miniscript = None + if af == AF_P2TR: + key = internal_key + nbrackets = 1 + elif af in [AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH]: + miniscript = Miniscript.read_from(s) + key = internal_key + nbrackets = 1 + int(af == AF_P2WSH_P2SH) + else: + key = Key.parse(s) + nbrackets = 1 + int(af == AF_P2WPKH_P2SH) + + end = s.read(nbrackets) + if end != b")" * nbrackets: + raise ValueError("Invalid descriptor") + + desc = cls(key, miniscript, tapscript, af) + return desc + + def to_string(self, external=True, internal=True, checksum=True): + if self.is_taproot: + desc = "tr(%s" % self.key.to_string(external, internal) + if self.tapscript: + desc += "," + tree = self.tapscript.to_string(external, internal) + desc += tree + + res = desc + ")" + + else: + if self.miniscript is not None: + res = self.miniscript.to_string(external, internal) + if self.addr_fmt in [AF_P2WSH, AF_P2WSH_P2SH]: + res = "wsh(%s)" % res + else: + if self.addr_fmt in [AF_P2WPKH, AF_P2WPKH_P2SH]: + res = "wpkh(%s)" % self.key.to_string(external, internal) + else: + res = "pkh(%s)" % self.key.to_string(external, internal) + + if self.is_legacy_sh: + res = "sh(%s)" % res + + if checksum: + res = append_checksum(res) + return res + + def bitcoin_core_serialize(self): # this will become legacy one day # instead use <0;1> descriptor format res = [] - for internal in [False, True]: + for external in (True, False): desc_obj = { - "desc": self.serialize(internal=internal), + "desc": self.to_string(external, not external), "active": True, "timestamp": "now", - "internal": internal, + "internal": not external, "range": [0, 100], } - if internal is False and external_label: - desc_obj["label"] = external_label res.append(desc_obj) return res - -class MultisigDescriptor(Descriptor): - # only supprt with key derivation info - # only xpubs - # can be extended when needed - __slots__ = ( - "M", - "N", - "keys", - "addr_fmt", - "is_sorted" # whether to use sortedmulti() or multi() - ) - - def __init__(self, M, N, keys, addr_fmt, is_sorted=True): - self.M = M - self.N = N - self.is_sorted = is_sorted - super().__init__(keys, addr_fmt) - - @classmethod - def parse(cls, desc_w_checksum): - # remove garbage - desc_w_checksum = parse_desc_str(desc_w_checksum) - # check correct checksum - desc, checksum = cls.checksum_check(desc_w_checksum) - is_sorted = "sortedmulti(" in desc - rplc = "sortedmulti(" if is_sorted else "multi(" - - # wrapped segwit - if desc.startswith("sh(wsh("+rplc): - addr_fmt = AF_P2WSH_P2SH - tmp_desc = desc.replace("sh(wsh("+rplc, "") - tmp_desc = tmp_desc.rstrip(")))") - - # native segwit - elif desc.startswith("wsh("+rplc): - addr_fmt = AF_P2WSH - tmp_desc = desc.replace("wsh("+rplc, "") - tmp_desc = tmp_desc.rstrip("))") - - # legacy - elif desc.startswith("sh("+rplc): - addr_fmt = AF_P2SH - tmp_desc = desc.replace("sh("+rplc, "") - tmp_desc = tmp_desc.rstrip("))") - - else: - raise ValueError("Unsupported descriptor. Supported: sh(), sh(wsh()), wsh().") - - splitted = tmp_desc.split(",") - M, keys = int(splitted[0]), splitted[1:] - N = int(len(keys)) - if M > N: - raise ValueError("M must be <= N: got M=%d and N=%d" % (M, N)) - - res_keys = [] - for key in keys: - koi, key = cls.parse_key_orig_info(key) - if key[0:4] not in ["tpub", "xpub"]: - raise ValueError("Only extended public keys are supported") - - xpub = cls.parse_key_derivation_info(key) - xfp = str2xfp(koi[:8]) - origin_deriv = "m" + koi[8:] - res_keys.append((xfp, origin_deriv, xpub)) - - return cls(M=M, N=N, keys=res_keys, addr_fmt=addr_fmt, is_sorted=is_sorted) - - def _serialize(self, internal=False, int_ext=False): - """Serialize without checksum""" - desc_base = MULTI_FMT_TO_SCRIPT[self.addr_fmt] - _type = "sortedmulti" if self.is_sorted else "multi" - _type += "(%s)" - desc_base = desc_base % _type - assert len(self.keys) == self.N - inner = str(self.M) + "," + ",".join( - self.serialize_keys(internal=internal, int_ext=int_ext)) - - return desc_base % (inner) - - def pretty_serialize(self): - """Serialize in pretty and human-readable format""" - _type = "sortedmulti" if self.is_sorted else "multi" - res = "# Coldcard descriptor export\n" - if self.is_sorted: - res += "# order of keys in the descriptor does not matter, will be sorted before creating script (BIP-67)\n" - else: - res += ("# !!! DANGER: order of keys in descriptor MUST be preserved. " - "Correct order of keys is required to compose valid redeem/witness script.\n") - if self.addr_fmt == AF_P2SH: - res += "# bare multisig - p2sh\n" - res += "sh("+_type+"(\n%s\n))" - # native segwit - elif self.addr_fmt == AF_P2WSH: - res += "# native segwit - p2wsh\n" - res += "wsh("+_type+"(\n%s\n))" - - # wrapped segwit - elif self.addr_fmt == AF_P2WSH_P2SH: - res += "# wrapped segwit - p2sh-p2wsh\n" - res += "sh(wsh(" + _type + "(\n%s\n)))" - else: - raise ValueError("Malformed descriptor") - - assert len(self.keys) == self.N - inner = "\t" + "# %d of %d (%s)\n" % ( - self.M, self.N, - "requires all participants to sign" if self.M == self.N else "threshold") - inner += "\t" + str(self.M) + ",\n" - ser_keys = self.serialize_keys() - for i, key_str in enumerate(ser_keys, start=1): - if i == self.N: - inner += "\t" + key_str - else: - inner += "\t" + key_str + ",\n" - - checksum = self.serialize().split("#")[1] - - return (res % inner) + "#" + checksum - # EOF diff --git a/shared/display.py b/shared/display.py index bf8196834..787e117d9 100644 --- a/shared/display.py +++ b/shared/display.py @@ -4,9 +4,10 @@ # import machine, uzlib, ckcc, utime from ssd1306 import SSD1306_SPI -from version import is_devmode +from version import is_devmode, is_edge import framebuf from graphics_mk4 import Graphics +from charcodes import OUT_CTRL_TITLE, OUT_CTRL_ADDRESS # we support 4 fonts from zevvpeep import FontSmall, FontLarge, FontTiny @@ -75,7 +76,7 @@ def text(self, x,y, msg, font=FontSmall, invert=0): if x is None or x < 0: # center/rjust w = self.width(msg, font) - if x == None: + if x is None: x = max(0, (self.WIDTH - w) // 2) else: # measure from right edge (right justify) @@ -146,14 +147,23 @@ def scroll_bar(self, offset, count, per_page): self.text(-2, 21, 'D', font=FontTiny, invert=1) self.text(-2, 28, 'E', font=FontTiny, invert=1) self.text(-2, 35, 'V', font=FontTiny, invert=1) + elif is_edge: + self.dis.fill_rect(128 - 6, 19, 5, 26, 1) + self.text(-2, 20, 'E', font=FontTiny, invert=1) + self.text(-2, 27, 'D', font=FontTiny, invert=1) + self.text(-2, 33, 'G', font=FontTiny, invert=1) + self.text(-2, 39, 'E', font=FontTiny, invert=1) def fullscreen(self, msg, percent=None, line2=None): # show a simple message "fullscreen". - # - 'line2' not supported on smaller screen sizes, ignore self.clear() y = 14 self.text(None, y, msg, font=FontLarge) + if line2: + # 21 + 6 ie. FontLarge.height of above text + FontTiny.height as space between + self.text(None, y + 27, line2, font=FontSmall) + if percent is not None: self.progress_bar(percent) self.show() @@ -265,17 +275,18 @@ def menu_draw(self, ry, msg, is_sel, is_checked, space_indicators): if is_sel: self.dis.fill_rect(0, y, Display.WIDTH, h-1, 1) self.icon(2, y, 'wedge', invert=1) - self.text(x, y, msg, invert=1) + nx = self.text(x, y, msg, invert=1) else: - self.text(x, y, msg) + nx = self.text(x, y, msg) # LATER: removed because caused confusion w/ underscore #if msg[0] == ' ' and space_indicators: # see also graphics/mono/space.txt #self.icon(x-2, y+9, 'space', invert=is_sel) - if is_checked: - self.icon(108, y, 'selected', invert=is_sel) + if is_checked and nx <= 113: + # omit checkmark if it doesn't fit + self.icon(113, y, 'selected', invert=is_sel) def menu_show(self, *a): self.show() @@ -304,9 +315,14 @@ def draw_story(self, lines, top, num_lines, is_sensitive, **ignored): for ln in lines: if ln == 'EOT': self.hline(y+3) - elif ln and ln[0] == '\x01': + elif ln and ln[0] == OUT_CTRL_TITLE: self.text(0, y, ln[1:], FontLarge) y += 21 + elif ln and ln[0] == OUT_CTRL_ADDRESS: + from utils import chunk_address + fmt = '\u2009'.join(chunk_address(ln[1:])) + self.text(14, y, fmt) # fixed indent, to be centered + y += 15 # a bit extra vertical line height else: self.text(0, y, ln) @@ -322,13 +338,25 @@ def draw_status(self, **k): # no status bar on Mk4 return - def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert): + def draw_qr_error(self, idx_hint, msg): + self.clear() + lm = 4 + bw = 54 + y = (self.HEIGHT - bw) // 2 + # empty rectangle + self.dis.fill_rect(lm, y, bw, bw, 1) + self.dis.fill_rect(lm+1, y+1, bw-2, bw-2, 0) + # error in rectangle - handpicked position + self.text(lm+5,y+10, "QR too") + self.text(lm+16,y+24, "big") + self._draw_qr_display(bw, lm, msg, False, None, idx_hint, False) + + def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, + is_addr=False, force_msg=False, is_change=False): # 'sidebar' is a pre-formated obj to show to right of QR -- oled life # - 'msg' will appear to right if very short, else under in tiny - from utils import word_wrap - + # - ignores "is_addr" because exactly zero space to do anything special self.clear() - w = qr_data.width() if w <= 29: # version 1,2,3 => we can double-up the pixels @@ -368,13 +396,23 @@ def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert): gly = framebuf.FrameBuffer(bytearray(packed), w, w, framebuf.MONO_HLSB) self.dis.blit(gly, XO, YO, 1) + self._draw_qr_display(bw, lm, msg, is_alnum, sidebar, idx_hint, invert, is_addr, is_change) + + def _draw_qr_display(self, bw, lm, msg, is_alnum, sidebar, idx_hint, invert, + is_addr=False, is_change=False): + # does not draw actual QR, but all other things in the screen + from utils import word_wrap + if not sidebar and not msg: pass - elif not sidebar and len(msg) > (5*7): + elif not sidebar and ((len(msg) > (5*7)) or is_change): # use FontTiny and word wrap (will just split if no spaces) + # native segwit addresses and taproot + # if is_change=True also p2pkh and p2sh fall into this category as space is needed for "CHANGE" x = bw + lm + 4 ww = ((128 - x)//4) - 1 # char width avail y = 1 + parts = list(word_wrap(msg, ww)) if len(parts) > 8: parts = parts[:8] @@ -385,9 +423,13 @@ def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert): for line in parts: self.text(x, y, line, FontTiny) y += 8 + + if is_addr and is_change: + self.text(x+4, y+8, "CHANGE BACK", FontTiny) else: # hand-positioned for known cases # - sidebar = (text, #of char per line) + # p2pkh and p2sh addresses (if is_change=False) x, y = 73, (0 if is_alnum else 2) dy = 10 if is_alnum else 12 sidebar, ll = sidebar if sidebar else (msg, 7) diff --git a/shared/drv_entro.py b/shared/drv_entro.py index 3fad9dae4..8f52d94e7 100644 --- a/shared/drv_entro.py +++ b/shared/drv_entro.py @@ -11,8 +11,8 @@ from menu import MenuItem, MenuSystem from ubinascii import hexlify as b2a_hex from ubinascii import b2a_base64 -from auth import write_sig_file -from utils import chunk_writer, xfp2str, swab32 +from msgsign import write_sig_file +from utils import xfp2str, swab32 from charcodes import KEY_QR, KEY_NFC, KEY_CANCEL BIP85_PWD_LEN = 21 @@ -56,32 +56,32 @@ async def drv_entro_start(*a): def bip85_derive(picked, index): # implement the core step of BIP85 from our master secret - + path = "m/83696968h/" if picked in (0,1,2): # BIP-39 seed phrases (we only support English) num_words = stash.SEED_LEN_OPTS[picked] width = (16, 24, 32)[picked] # of bytes - path = "m/83696968h/39h/0h/{num_words}h/{index}h".format(num_words=num_words, index=index) + path += "39h/0h/%dh/%dh" % (num_words, index) s_mode = 'words' elif picked == 3: - # HDSeed for Bitcoin Core: but really a WIF of a private key, can be used anywhere + # HDSeed for Bitcoin Core: but really a WIF of a private key s_mode = 'wif' - path = "m/83696968h/2h/{index}h".format(index=index) + path += "2h/%dh" % index width = 32 elif picked == 4: # New XPRV - path = "m/83696968h/32h/{index}h".format(index=index) + path += "32h/%dh" % index s_mode = 'xprv' width = 64 elif picked in (5, 6): width = 32 if picked == 5 else 64 - path = "m/83696968h/128169h/{width}h/{index}h".format(width=width, index=index) + path += "128169h/%dh/%dh" % (width, index) s_mode = 'hex' elif picked == 7: width = 64 # hardcoded width for now # b"pwd".hex() --> 707764 - path = "m/83696968h/707764h/{pwd_len}h/{index}h".format(pwd_len=BIP85_PWD_LEN, index=index) + path += "707764h/%dh/%dh" % (BIP85_PWD_LEN, index) s_mode = 'pw' else: raise ValueError(picked) @@ -161,7 +161,7 @@ async def drv_entro_step2(_1, picked, _2, just_pick=False): qr_alnum = True msg = 'Seed words (%d):\n' % len(words) - msg += ux_render_words(words) + msg += ux_render_words(words, leading_blanks=1) encoded = stash.SecretStash.encode(seed_phrase=new_secret) @@ -226,12 +226,13 @@ async def drv_entro_step2(_1, picked, _2, just_pick=False): choice = import_export_prompt_decode(ch) if isinstance(choice, dict): # write to SD card or Virtual Disk: simple text file + dis.fullscreen("Saving...") try: with CardSlot(**choice) as card: fname, out_fn = card.pick_filename('drv-%s-idx%d.txt' % (s_mode, index)) body = msg + "\n" with open(fname, 'wt') as fp: - chunk_writer(fp, body) + fp.write(body) h = ngu.hash.sha256s(body.encode()) sig_nice = write_sig_file([(h, fname)], derive=path) @@ -240,7 +241,7 @@ async def drv_entro_step2(_1, picked, _2, just_pick=False): await needs_microsd() continue except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) + await ux_show_story('Failed to write!\n\n'+str(e)) continue story = "Filename is:\n\n%s" % out_fn @@ -250,7 +251,7 @@ async def drv_entro_step2(_1, picked, _2, just_pick=False): break elif choice == KEY_QR: from ux import show_qr_code - await show_qr_code(qr, qr_alnum) + await show_qr_code(qr, qr_alnum, is_secret=True) elif choice == '0': if s_mode == 'pw': # gets confirmation then types it @@ -263,14 +264,14 @@ async def drv_entro_step2(_1, picked, _2, just_pick=False): xfp_str = xfp2str(settings.get("xfp", 0)) await seed.set_ephemeral_seed( encoded, - meta='BIP85 Derived from [%s], index=%d' % (xfp_str, index) + origin='BIP85 Derived from [%s], index=%d' % (xfp_str, index) ) goto_top_menu() break elif NFC and choice == KEY_NFC: # Share any of these over NFC - await NFC.share_text(qr) + await NFC.share_text(qr, is_secret=True) stash.blank_object(msg) stash.blank_object(new_secret) diff --git a/shared/exceptions.py b/shared/exceptions.py index 2d92d1366..6f84242dd 100644 --- a/shared/exceptions.py +++ b/shared/exceptions.py @@ -19,10 +19,10 @@ class CCBusyError(RuntimeError): # HSM is blocking your action class HSMDenied(RuntimeError): pass - class HSMCMDDisabled(RuntimeError): pass + # PSBT / transaction related class FatalPSBTIssue(RuntimeError): pass @@ -51,4 +51,12 @@ class QRDecodeExplained(ValueError): class UnknownAddressExplained(ValueError): pass +# We're not going to (co-)sign using spending policy features +class SpendPolicyViolation(RuntimeError): + pass + +# data too big for simple QR +class QRTooBigError(ValueError): + pass + # EOF diff --git a/shared/export.py b/shared/export.py index 5760cc9ff..74e466dad 100644 --- a/shared/export.py +++ b/shared/export.py @@ -5,23 +5,25 @@ import stash, chains, version, ujson, ngu from uio import StringIO from ucollections import OrderedDict -from utils import xfp2str, swab32, chunk_writer -from ux import ux_show_story +from utils import xfp2str, swab32, problem_file_line +from ux import ux_show_story, import_export_prompt from glob import settings -from auth import write_sig_file -from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2WSH, AF_P2WSH_P2SH, AF_P2SH +from msgsign import write_sig_file +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2WSH, AF_P2WSH_P2SH, AF_P2SH, AF_P2TR from charcodes import KEY_NFC, KEY_CANCEL, KEY_QR from ownership import OWNERSHIP +from exceptions import QRTooBigError -async def export_by_qr(body, label, type_code): +async def export_by_qr(body, label, type_code, force_bbqr=False): # render as QR and show on-screen from ux import show_qr_code try: - # ignore label/title - provides no useful info - # makes qr smaller and harder to read + if force_bbqr or len(body) > 2000: + raise QRTooBigError + await show_qr_code(body) - except (ValueError, RuntimeError, TypeError): + except QRTooBigError: if version.has_qwerty: # do BBQr on Q from ux_q1 import show_bbqr_codes @@ -31,6 +33,81 @@ async def export_by_qr(body, label, type_code): return + +async def export_contents(title, contents, fname_pattern, derive=None, addr_fmt=None, + is_json=False, force_bbqr=False, force_prompt=False, direct_way=None): + # export text and json files while offering NFC, QR & Vdisk + # produces signed export in case of SD/Vdisk (signed with key at deriv and addr_fmt) + # checks if suitable to offer QR export on Mk4 + # argument contents can support function that generates content + # argument direct way can be KEY_{NFC,QR}, any other truth value is SD/Vdisk, + # if None ask for way via UX story + from glob import dis, NFC, VD + from files import CardSlot, CardMissingError, needs_microsd + from qrs import MAX_V11_CHAR_LIMIT + + if callable(contents): + dis.fullscreen('Generating...') + contents, derive, addr_fmt = contents() + + # figure out if offering QR code export make sense given HW + # len() is O(1) + no_qr = not version.has_qwerty and (len(contents) >= MAX_V11_CHAR_LIMIT) + + if addr_fmt == AF_P2TR: + sig = None + else: + sig = not (derive is None and addr_fmt is None) + + ch = direct_way # set it to direct way only once, outside the loop + while True: + if direct_way is None: + ch = await import_export_prompt("%s file" % title, + force_prompt=force_prompt, no_qr=no_qr) + if ch == KEY_CANCEL: + break + elif ch == KEY_QR: + await export_by_qr(contents, title, "J" if is_json else "U", force_bbqr=force_bbqr) + elif ch == KEY_NFC: + if is_json: + await NFC.share_json(contents) + else: + await NFC.share_text(contents) + else: + # SD/VDisk + # choose a filename + try: + dis.fullscreen("Saving...") + with CardSlot(**ch) as card: + fname, nice = card.pick_filename(fname_pattern) + + # do actual write + with open(fname, 'wt' if is_json else 'wb') as fd: + fd.write(contents) + + if sig: + h = ngu.hash.sha256s(contents.encode()) + sig_nice = write_sig_file([(h, fname)], derive, addr_fmt) + + msg = '%s file written:\n\n%s' % (title, nice) + if sig: + msg += "\n\n%s signature file written:\n\n%s" % (title, sig_nice) + + await ux_show_story(msg) + + except CardMissingError: + await needs_microsd() + except Exception as e: + await ux_show_story('Failed to write!\n\n%s\n%s' % (e, problem_file_line(e))) + + # both exceptions & success gets here + if no_qr and (NFC is None) and (VD is None) and not force_prompt: + # user has no other ways enabled, we already exported to SD - done + return + + if direct_way: + return + def generate_public_contents(): # Generate public details about wallet. # @@ -73,14 +150,7 @@ def generate_public_contents(): sym=chain.ctype, ct=chain.b44_cointype, xfp=xfp)) for name, path, addr_fmt in chains.CommonDerivations: - - if '{coin_type}' in path: - path = path.replace('{coin_type}', str(chain.b44_cointype)) - - if '{' in name: - name = name.format(core_name=chain.core_name) - - show_slip132 = ('Core' not in name) + path = path.replace('{coin_type}', str(chain.b44_cointype)) yield ('''## For {name}: {path}\n\n'''.format(name=name, path=path)) yield ('''First %d receive addresses (account=0, change=0):\n\n''' % num_rx) @@ -103,7 +173,7 @@ def generate_public_contents(): node = sv.derive_path(hard_sub, register=False) yield ("%s => %s\n" % (hard_sub, chain.serialize_public(node))) - if show_slip132 and addr_fmt != AF_CLASSIC and (addr_fmt in chain.slip132): + if addr_fmt not in (AF_CLASSIC, AF_P2TR) and (addr_fmt in chain.slip132): yield ("%s => %s ##SLIP-132##\n" % ( hard_sub, chain.serialize_public(node, addr_fmt))) @@ -120,59 +190,13 @@ def generate_public_contents(): yield ('\n\n') - from multisig import MultisigWallet - if MultisigWallet.exists(): - yield '\n# Your Multisig Wallets\n\n' - - for ms in MultisigWallet.get_all(): - fp = StringIO() - - ms.render_export(fp) - print("\n---\n", file=fp) - - yield fp.getvalue() - del fp - -async def write_text_file(fname_pattern, body, title, derive, addr_fmt): - # Export data as a text file. - from glob import dis, NFC - from files import CardSlot, CardMissingError, needs_microsd - from ux import import_export_prompt - - choice = await import_export_prompt("%s file" % title, is_import=False, - no_qr=(not version.has_qwerty)) - if choice == KEY_CANCEL: - return - elif choice == KEY_QR: - await export_by_qr(body, title, "U") - return - elif choice == KEY_NFC: - await NFC.share_text(body) - return - - # choose a filename - try: - dis.fullscreen("Saving...") - with CardSlot(**choice) as card: - fname, nice = card.pick_filename(fname_pattern) - - # do actual write - with open(fname, 'wb') as fd: - chunk_writer(fd, body) - - h = ngu.hash.sha256s(body.encode()) - sig_nice = write_sig_file([(h, fname)], derive, addr_fmt) + from wallet import MiniScriptWallet + if MiniScriptWallet.exists(): + yield '\n# Your Multisig/Miniscript Wallets\n\n' - except CardMissingError: - await needs_microsd() - return - except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) - return + for msc in MiniScriptWallet.iter_wallets(): + yield msc.to_string() + "\n---\n" - msg = '%s file written:\n\n%s\n\n%s signature file written:\n\n%s' % (title, nice, title, - sig_nice) - await ux_show_story(msg) async def make_summary_file(fname_pattern='public.txt'): from glob import dis @@ -183,7 +207,7 @@ async def make_summary_file(fname_pattern='public.txt'): # generator function: body = "".join(list(generate_public_contents())) ch = chains.current_chain() - await write_text_file(fname_pattern, body, 'Summary', + await export_contents('Summary', body, fname_pattern, "m/44h/%dh/0h/0/0" % ch.b44_cointype, AF_CLASSIC) @@ -195,10 +219,11 @@ async def make_bitcoin_core_wallet(account_num=0, fname_pattern='bitcoin-core.tx # make the data examples = [] - imp_multi, imp_desc = generate_bitcoin_core_wallet(account_num, examples) + imp_multi, imp_desc, imp_desc_tr = generate_bitcoin_core_wallet(account_num, examples) imp_multi = ujson.dumps(imp_multi) imp_desc = ujson.dumps(imp_desc) + imp_desc_tr = ujson.dumps(imp_desc_tr) body = '''\ # Bitcoin Core Wallet Import File @@ -214,7 +239,10 @@ async def make_bitcoin_core_wallet(account_num=0, fname_pattern='bitcoin-core.tx The following command can be entered after opening Window -> Console in Bitcoin Core, or using bitcoin-cli: -importdescriptors '{imp_desc}' +p2wpkh: + importdescriptors '{imp_desc}' +p2tr: + importdescriptors '{imp_desc_tr}' > **NOTE** If your UTXO was created before generating `importdescriptors` command, you should adjust the value of `timestamp` before executing command in bitcoin core. By default it is set to `now` meaning do not rescan the blockchain. If approximate time of UTXO creation is known - adjust `timestamp` from `now` to UNIX epoch time. @@ -229,59 +257,80 @@ async def make_bitcoin_core_wallet(account_num=0, fname_pattern='bitcoin-core.tx ## Resulting Addresses (first 3) -'''.format(imp_multi=imp_multi, imp_desc=imp_desc, xfp=xfp, nb=chains.current_chain().name) +'''.format(imp_multi=imp_multi, imp_desc=imp_desc, imp_desc_tr=imp_desc_tr, + xfp=xfp, nb=chains.current_chain().name) body += '\n'.join('%s => %s' % t for t in examples) body += '\n' OWNERSHIP.note_wallet_used(AF_P2WPKH, account_num) + OWNERSHIP.note_wallet_used(AF_P2TR, account_num) ch = chains.current_chain() derive = "84h/{coin_type}h/{account}h".format(account=account_num, coin_type=ch.b44_cointype) - await write_text_file(fname_pattern, body, 'Bitcoin Core', derive + "/0/0", AF_P2WPKH) + await export_contents('Bitcoin Core', body, fname_pattern, derive + "/0/0", AF_P2WPKH) def generate_bitcoin_core_wallet(account_num, example_addrs): # Generate the data for an RPC command to import keys into Bitcoin Core # - yields dicts for json purposes - from descriptor import Descriptor + from descriptor import Descriptor, Key chain = chains.current_chain() - derive = "84h/{coin_type}h/{account}h".format(account=account_num, - coin_type=chain.b44_cointype) - + derive_v0 = "84h/{coin_type}h/{account}h".format( + account=account_num, coin_type=chain.b44_cointype + ) + derive_v1 = "86h/{coin_type}h/{account}h".format( + account=account_num, coin_type=chain.b44_cointype + ) with stash.SensitiveValues() as sv: - prefix = sv.derive_path(derive) - xpub = chain.serialize_public(prefix) + prefix = sv.derive_path(derive_v0) + xpub_v0 = chain.serialize_public(prefix) for i in range(3): sp = '0/%d' % i node = sv.derive_path(sp, master=prefix) a = chain.address(node, AF_P2WPKH) - example_addrs.append( ('m/%s/%s' % (derive, sp), a) ) + example_addrs.append(('m/%s/%s' % (derive_v0, sp), a)) + + with stash.SensitiveValues() as sv: + prefix = sv.derive_path(derive_v1) + xpub_v1 = chain.serialize_public(prefix) + + for i in range(3): + sp = '0/%d' % i + node = sv.derive_path(sp, master=prefix) + a = chain.address(node, AF_P2TR) + example_addrs.append(('m/%s/%s' % (derive_v1, sp), a)) xfp = settings.get('xfp') - _, vers, _ = version.get_mpy_version() + key0 = Key.from_cc_data(xfp, derive_v0, xpub_v0) + desc_v0 = Descriptor(key=key0, addr_fmt=AF_P2WPKH) + + key1 = Key.from_cc_data(xfp, derive_v1, xpub_v1) + desc_v1 = Descriptor(key=key1, addr_fmt=AF_P2TR) OWNERSHIP.note_wallet_used(AF_P2WPKH, account_num) + OWNERSHIP.note_wallet_used(AF_P2TR, account_num) - desc_obj = Descriptor(keys=[(xfp, derive, xpub)], addr_fmt=AF_P2WPKH) # for importmulti imm_list = [ { - 'desc': desc_obj.serialize(internal=internal), + 'desc': desc_v0.to_string(external, internal), 'range': [0, 1000], 'timestamp': 'now', 'internal': internal, 'keypool': True, 'watchonly': True } - for internal in [False, True] + for external, internal in [(True, False), (False, True)] ] # for importdescriptors - imd_list = desc_obj.bitcoin_core_serialize() - return imm_list, imd_list + imd_list = desc_v0.bitcoin_core_serialize() + imd_list_v1 = desc_v1.bitcoin_core_serialize() + return imm_list, imd_list, imd_list_v1 + def generate_wasabi_wallet(): # Generate the data for a JSON file which Wasabi can open directly as a new wallet. @@ -319,20 +368,16 @@ def generate_unchained_export(account_num=0): # - no account numbers (at this level) chain = chains.current_chain() - todo = [ - ( "m/48h/{coin}h/{acct_num}h/2h", 'p2wsh', AF_P2WSH ), - ( "m/48h/{coin}h/{acct_num}h/1h", 'p2sh_p2wsh', AF_P2WSH_P2SH), - ( "m/45h", 'p2sh', AF_P2SH), # if acct_num == 0 - ] - xfp = xfp2str(settings.get('xfp', 0)) rv = OrderedDict(xfp=xfp, account=account_num) - + sign_der = None with stash.SensitiveValues() as sv: - for deriv, name, fmt in todo: + for name, deriv, fmt in chains.MS_STD_DERIVATIONS: if fmt == AF_P2SH and account_num: continue dd = deriv.format(coin=chain.b44_cointype, acct_num=account_num) + if fmt == AF_P2WSH: + sign_der = dd + "/0/0" node = sv.derive_path(dd) xp = chain.serialize_public(node, fmt) @@ -341,13 +386,11 @@ def generate_unchained_export(account_num=0): rv['%s_deriv' % name] = dd rv[name] = xp - # sig_deriv = "m/44'/{ct}'/{acc}'".format(ct=chain.b44_cointype, acc=account_num) + "/0/0" - # return ujson.dumps(rv), sig_deriv, AF_CLASSIC - return ujson.dumps(rv), False, False + return ujson.dumps(rv), sign_der, AF_CLASSIC def generate_generic_export(account_num=0): # Generate data that other programers will use to import Coldcard (single-signer) - from descriptor import Descriptor, multisig_descriptor_template + from descriptor import Descriptor, Key chain = chains.current_chain() master_xfp = settings.get("xfp") @@ -361,12 +404,14 @@ def generate_generic_export(account_num=0): with stash.SensitiveValues() as sv: # each of these paths would have /{change}/{idx} in usage (not hardened) for name, deriv, fmt, atype, is_ms in [ - ( 'bip44', "m/44h/{ct}h/{acc}h", AF_CLASSIC, 'p2pkh', False ), - ( 'bip49', "m/49h/{ct}h/{acc}h", AF_P2WPKH_P2SH, 'p2sh-p2wpkh', False ), # was "p2wpkh-p2sh" - ( 'bip84', "m/84h/{ct}h/{acc}h", AF_P2WPKH, 'p2wpkh', False ), - ( 'bip48_1', "m/48h/{ct}h/{acc}h/1h", AF_P2WSH_P2SH, 'p2sh-p2wsh', True ), - ( 'bip48_2', "m/48h/{ct}h/{acc}h/2h", AF_P2WSH, 'p2wsh', True ), - ( 'bip45', "m/45h", AF_P2SH, 'p2sh', True ), + ('bip44', "m/44h/{ct}h/{acc}h", AF_CLASSIC, 'p2pkh', False), + ('bip49', "m/49h/{ct}h/{acc}h", AF_P2WPKH_P2SH, 'p2sh-p2wpkh', False), # was "p2wpkh-p2sh" + ('bip84', "m/84h/{ct}h/{acc}h", AF_P2WPKH, 'p2wpkh', False), + ('bip86', "m/86h/{ct}h/{acc}h", AF_P2TR, 'p2tr', False), + ('bip48_1', "m/48h/{ct}h/{acc}h/1h", AF_P2WSH_P2SH, 'p2sh-p2wsh', True), + ('bip48_2', "m/48h/{ct}h/{acc}h/2h", AF_P2WSH, 'p2wsh', True), + ('bip48_3', "m/48h/{ct}h/{acc}h/3h", AF_P2TR, 'p2tr', True), + ('bip45', "m/45h", AF_P2SH, 'p2sh', True), ]: if fmt == AF_P2SH and account_num: continue @@ -375,24 +420,25 @@ def generate_generic_export(account_num=0): node = sv.derive_path(dd) xfp = xfp2str(swab32(node.my_fp())) xp = chain.serialize_public(node, AF_CLASSIC) - zp = chain.serialize_public(node, fmt) if fmt != AF_CLASSIC else None - if is_ms: - desc = multisig_descriptor_template(xp, dd, master_xfp_str, fmt) - else: - desc = Descriptor(keys=[(master_xfp, dd, xp)], addr_fmt=fmt).serialize(int_ext=True) - - OWNERSHIP.note_wallet_used(fmt, account_num) + zp = chain.serialize_public(node, fmt) if fmt not in (AF_CLASSIC, AF_P2TR) else None + key = Key.from_cc_data(master_xfp, dd, xp) + key_exp = key.to_string(external=False, internal=False) rv[name] = OrderedDict(name=atype, xfp=xfp, deriv=dd, xpub=xp, - desc=desc) + key_exp=key_exp) if zp and zp != xp: rv[name]['_pub'] = zp if not is_ms: + desc_obj = Descriptor(key=key, addr_fmt=fmt) + rv[name]['desc'] = desc_obj.to_string() + + OWNERSHIP.note_wallet_used(fmt, account_num) + # bonus/check: first non-change address: 0/0 node.derive(0, False).derive(0, False) rv[name]['first'] = chain.address(node, fmt) @@ -403,22 +449,15 @@ def generate_generic_export(account_num=0): def generate_electrum_wallet(addr_type, account_num): # Generate line-by-line JSON details about wallet. # - # Much reverse enginerring of Electrum here. It's a complex + # Much reverse engineering of Electrum here. It's a complex # legacy file format. chain = chains.current_chain() xfp = settings.get('xfp') - # Must get the derivation path, and the SLIP32 version bytes right! - if addr_type == AF_CLASSIC: - mode = 44 - elif addr_type == AF_P2WPKH: - mode = 84 - elif addr_type == AF_P2WPKH_P2SH: - mode = 49 - else: - raise ValueError(addr_type) + # Must get the derivation path, and the SLIP132 version bytes right! + mode = chains.af_to_bip44_purpose(addr_type) OWNERSHIP.note_wallet_used(addr_type, account_num) @@ -448,106 +487,65 @@ def generate_electrum_wallet(addr_type, account_num): return ujson.dumps(rv), derive + "/0/0", addr_type -async def make_json_wallet(label, func, fname_pattern='new-wallet.json'): - # Record **public** values and helpful data into a JSON file - # - OWNERSHIP.note_wallet_used(..) should be called already by our caller or func - - from glob import dis, NFC - from files import CardSlot, CardMissingError, needs_microsd - from ux import import_export_prompt - from qrs import MAX_V11_CHAR_LIMIT - - dis.fullscreen('Generating...') - json_str, derive, addr_fmt = func() - skip_sig = derive is False and addr_fmt is False - - choice = await import_export_prompt("%s file" % label, is_import=False, - no_qr=(not version.has_qwerty and len(json_str) >= MAX_V11_CHAR_LIMIT)) - - if choice == KEY_CANCEL: - return - elif choice == KEY_NFC: - await NFC.share_json(json_str) - return - elif choice == KEY_QR: - # render as QR and show on-screen - # - on mk4, this isn't offered if more than about 300 bytes because we can't - # show that as a single QR - await export_by_qr(json_str, label, "J") - return - - # choose a filename and save - try: - with CardSlot(**choice) as card: - fname, nice = card.pick_filename(fname_pattern) - - # do actual write - with open(fname, 'wt') as fd: - chunk_writer(fd, json_str) - - if not skip_sig: - h = ngu.hash.sha256s(json_str.encode()) - sig_nice = write_sig_file([(h, fname)], derive, addr_fmt) - - except CardMissingError: - await needs_microsd() - return - except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) - return - - msg = '%s file written:\n\n%s' % (label, nice) - if not skip_sig: - msg += '\n\n%s signature file written:\n\n%s' % (label, sig_nice) - - await ux_show_story(msg) - async def make_descriptor_wallet_export(addr_type, account_num=0, mode=None, int_ext=True, - fname_pattern="descriptor.txt"): - from descriptor import Descriptor + fname_pattern="descriptor.txt", direct_way=None): + from descriptor import Descriptor, Key from glob import dis dis.fullscreen('Generating...') chain = chains.current_chain() - xfp = settings.get('xfp') + xfp = settings.get('xfp', 0) dis.progress_bar_show(0.1) if mode is None: - if addr_type == AF_CLASSIC: - mode = 44 - elif addr_type == AF_P2WPKH: - mode = 84 - elif addr_type == AF_P2WPKH_P2SH: - mode = 49 - else: - raise ValueError(addr_type) + mode = chains.af_to_bip44_purpose(addr_type) OWNERSHIP.note_wallet_used(addr_type, account_num) - derive = "m/{mode}h/{coin_type}h/{account}h".format(mode=mode, - account=account_num, coin_type=chain.b44_cointype) + derive = "m/{mode}h/{coin_type}h/{account}h".format( + mode=mode, account=account_num, coin_type=chain.b44_cointype + ) dis.progress_bar_show(0.2) with stash.SensitiveValues() as sv: dis.progress_bar_show(0.3) xpub = chain.serialize_public(sv.derive_path(derive)) dis.progress_bar_show(0.7) - desc = Descriptor(keys=[(xfp, derive, xpub)], addr_fmt=addr_type) + + key = Key.from_cc_data(xfp, derive, xpub) + desc = Descriptor(key=key, addr_fmt=addr_type) dis.progress_bar_show(0.8) if int_ext: # with <0;1> notation - body = desc.serialize(int_ext=True) + body = desc.to_string() else: # external descriptor # internal descriptor body = "%s\n%s" % ( - desc.serialize(internal=False), - desc.serialize(internal=True), + desc.to_string(internal=False), + desc.to_string(external=False), ) dis.progress_bar_show(1) - await write_text_file(fname_pattern, body, "Descriptor", derive + "/0/0", addr_type) + await export_contents("Descriptor", body, fname_pattern, derive + "/0/0", + addr_type, force_prompt=True, direct_way=direct_way) + + +async def make_key_expression_export(orig_der, fname_pattern="key_expr.txt"): + from glob import dis + + dis.fullscreen('Generating...') + + xfp = xfp2str(settings.get('xfp', 0)).lower() + + with stash.SensitiveValues() as sv: + ek = chains.current_chain().serialize_public(sv.derive_path(orig_der)) + + body = "[%s/%s]%s" % (xfp, orig_der.replace("m/", ""), ek) + + await export_contents("Key Expression", body, fname_pattern, + None, None, force_prompt=True) # EOF diff --git a/shared/files.py b/shared/files.py index 321a2aaad..5b0f3e20f 100644 --- a/shared/files.py +++ b/shared/files.py @@ -264,7 +264,7 @@ def __init__(self, force_vdisk=False, readonly=False, slot_b=None): self.active_led = self.active_led2 if use_b_slot else self.active_led1 def __enter__(self): - # Mk4: maybe use our virtual disk in preference to SD Card + # maybe use our virtual disk in preference to SD Card if glob.VD and (self.force_vdisk or not self.is_inserted()): self.mountpt = glob.VD.mount(self.readonly) return self diff --git a/shared/flow.py b/shared/flow.py index e14745a0f..f39e9a5c3 100644 --- a/shared/flow.py +++ b/shared/flow.py @@ -9,7 +9,7 @@ from actions import * from choosers import * from mk4 import dev_enable_repl -from multisig import make_multisig_menu, import_multisig_nfc +from wallet import make_miniscript_menu, import_miniscript_nfc from seed import make_ephemeral_seed_menu, make_seed_vault_menu, start_b39_pw from address_explorer import address_explore from drv_entro import drv_entro_start, password_entry @@ -19,9 +19,11 @@ from paper import make_paper_wallet from trick_pins import TrickPinMenu from tapsigner import import_tapsigner_backup_file +from ccc import toggle_ccc_feature, sssp_spending_policy, sssp_feature_menu # useful shortcut keys from charcodes import KEY_QR, KEY_NFC +from public_constants import AF_P2WPKH_P2SH, AF_P2WPKH # Optional feature: HSM, depends on hardware @@ -38,12 +40,14 @@ from battery import battery_idle_timeout_chooser, brightness_chooser from q1 import scan_and_bag from notes import make_notes_menu + from teleport import kt_start_rx, kt_send_file_psbt else: battery_idle_timeout_chooser = None brightness_chooser = None scan_and_bag = None make_notes_menu = None - + kt_start_rx = None + kt_send_file_psbt = None # # NOTE: "Always In Title Case" @@ -69,6 +73,8 @@ def has_secrets(): from pincodes import pa return pa.has_secrets() +qr_and_has_secrets = has_secrets if version.has_qr else False + def nfc_enabled(): from glob import NFC return bool(NFC) @@ -95,6 +101,33 @@ def hsm_available(): # contains hsm feature + can it be used (needs se2 secret and no tmp active) return version.supports_hsm and has_real_secret() +def qr_and_ms(): + # has QR scanner, and at least one MS wallet + if not version.has_qr: return False + return bool(settings.get('miniscript', False)) + +def has_pushtx_url(): + # they want to use PushTX feature + return bool(settings.get("ptxurl", False)) + +# Spending Policy (Hobbled mode) predicates. +# +def is_hobble_testdrive(): + from pincodes import pa + return (pa.hobbled_mode == 2) + +def sssp_related_keys(): + return sssp_spending_policy('okeys') + +def sssp_allow_passphrase(): + return word_based_seed() and sssp_related_keys() + +def sssp_allow_notes(): + return settings.get("secnap", False) and sssp_spending_policy('notes') + +def sssp_allow_vault(): + return settings.master_get('seedvault') and sssp_related_keys() + async def goto_home(*a): goto_top_menu() @@ -136,8 +169,8 @@ async def goto_home(*a): # xxxxxxxxxxxxxxxx MenuItem('Login Settings', menu=LoginPrefsMenu), MenuItem('Hardware On/Off', menu=HWTogglesMenu), - NonDefaultMenuItem('Multisig Wallets', 'multisig', - menu=make_multisig_menu, predicate=has_secrets), + NonDefaultMenuItem('Multisig/Miniscript', 'miniscript', + menu=make_miniscript_menu, predicate=has_secrets, shortcut="m"), NonDefaultMenuItem('NFC Push Tx', 'ptxurl', menu=pushtx_setup_menu), MenuItem('Display Units', chooser=value_resolution_chooser), MenuItem('Max Network Fee', chooser=max_fee_chooser), @@ -154,9 +187,9 @@ async def goto_home(*a): MS-DOS tools should not be able to find the PSBT data (ie. undelete), but forensic tools \ which take apart the flash chips of the SDCard may still be able to find the \ data or filenames.'''), - ToggleMenuItem('Menu Wrapping', 'wa', ['Default Off', 'Enable'], + ToggleMenuItem('Menu Wrapping', 'wa', ['Default', 'Always Wrap'], story='''When enabled, allows scrolling past menu top/bottom \ -(wrap around). By default, this is only happens in very large menus.'''), +(wrap around). By default, this only happens in menus whose length is greater than 10.'''), ToggleMenuItem('Home Menu XFP', 'hmx', ['Only Tmp', 'Always Show'], story=('Forces display of XFP (seed fingerprint) ' 'at top of main menu. Normally, XFP is shown only when ' @@ -176,30 +209,35 @@ async def goto_home(*a): # xxxxxxxxxxxxxxxx MenuItem("Segwit (BIP-84)", f=export_xpub, arg=84), MenuItem("Classic (BIP-44)", f=export_xpub, arg=44), - MenuItem("P2WPKH/P2SH (49)", f=export_xpub, arg=49), + MenuItem("Taproot/P2TR"+("(BIP-86)" if version.has_qwerty else "(86)"), f=export_xpub, arg=86), + MenuItem("P2WPKH/P2SH "+("(BIP-49)"if version.has_qwerty else "(49)"), f=export_xpub, arg=49), MenuItem("Master XPUB", f=export_xpub, arg=0), MenuItem("Current XFP", f=export_xpub, arg=-1), ] WalletExportMenu = [ # xxxxxxxxxxxxxxxx + MenuItem("Sparrow", f=named_generic_skeleton, arg="Sparrow"), + MenuItem("Cove", f=named_generic_skeleton, arg="Cove"), MenuItem("Bitcoin Core", f=bitcoin_core_skeleton), - MenuItem("Fully Noded", f=named_generic_skeleton, arg="Fully Noded"), - MenuItem("Sparrow Wallet", f=named_generic_skeleton, arg="Sparrow"), MenuItem("Nunchuk", f=named_generic_skeleton, arg="Nunchuk"), + MenuItem("Bull Bitcoin", f=ss_descriptor_skeleton, + arg=(True, [AF_P2WPKH], "", "bull-bitcoin.txt", KEY_QR)), MenuItem("Zeus", f=ss_descriptor_skeleton, - arg=(True, [AF_P2WPKH, AF_P2WPKH_P2SH], "Zeus Wallet", "zeus-export.txt")), + arg=(True, [AF_P2WPKH, AF_P2WPKH_P2SH], "Zeus Wallet", "zeus-export.txt", None)), MenuItem("Electrum Wallet", f=electrum_skeleton), - MenuItem("Theya", f=named_generic_skeleton, arg="Theya"), MenuItem("Wasabi Wallet", f=wasabi_skeleton), + MenuItem("Fully Noded", f=named_generic_skeleton, arg="Fully Noded"), MenuItem("Unchained", f=unchained_capital_export), - MenuItem("Lily Wallet", f=named_generic_skeleton, arg="Lily"), + MenuItem("Theya", f=named_generic_skeleton, arg="Theya"), + MenuItem("Bitcoin Safe", f=named_generic_skeleton, arg="Bitcoin Safe"), MenuItem("Samourai Postmix", f=samourai_post_mix_descriptor_export), MenuItem("Samourai Premix", f=samourai_pre_mix_descriptor_export), # MenuItem("Samourai BadBank", f=samourai_bad_bank_descriptor_export), # not released yet MenuItem("Descriptor", f=ss_descriptor_skeleton), MenuItem("Generic JSON", f=generic_skeleton), MenuItem("Export XPUB", menu=XpubExportMenu), + MenuItem("Key Expression", f=key_expression_skeleton), MenuItem("Dump Summary", predicate=has_secrets, f=dump_summary), ] @@ -211,9 +249,11 @@ async def goto_home(*a): MenuItem('Export Wallet', predicate=has_secrets, menu=WalletExportMenu), #dup elsewhere MenuItem('Sign Text File', predicate=has_secrets, f=sign_message_on_sd), MenuItem('Batch Sign PSBT', predicate=has_secrets, f=batch_sign), + MenuItem('Teleport Multisig/Miniscript PSBT', predicate=qr_and_has_secrets, f=kt_send_file_psbt), MenuItem('List Files', f=list_files), MenuItem('Verify Sig File', f=verify_sig_file), MenuItem('NFC File Share', predicate=nfc_enabled, f=nfc_share_file, shortcut=KEY_NFC), + MenuItem('BBQr File Share', predicate=version.has_qr, f=qr_share_file, arg=True), MenuItem('QR File Share', predicate=version.has_qr, f=qr_share_file, shortcut=KEY_QR), MenuItem('Clone Coldcard', predicate=has_secrets, f=clone_write_data), MenuItem('Format SD Card', f=wipe_sd_card), @@ -231,7 +271,9 @@ async def goto_home(*a): # xxxxxxxxxxxxxxxx MenuItem("Serial REPL", f=dev_enable_repl), MenuItem('Warm Reset', f=reset_self), - MenuItem("Restore Txt Bkup", f=restore_everything_cleartext), + MenuItem("Restore Bkup", f=restore_backup_dev), + MenuItem("BKPW Override", menu=bkpw_override, predicate=has_secrets), + MenuItem('Reflash GPU', f=reflash_gpu, predicate=version.has_qwerty), ] AdvancedVirginMenu = [ # No PIN, no secrets yet (factory fresh) @@ -248,6 +290,7 @@ async def goto_home(*a): MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu), MenuItem("Upgrade Firmware", menu=UpgradeMenu, predicate=is_not_tmp), MenuItem("File Management", menu=FileMgmtMenu), + MenuItem("Key Teleport (start)", f=kt_start_rx, predicate=version.has_qr), MenuItem('Paper Wallets', f=make_paper_wallet), MenuItem('Perform Selftest', f=start_selftest), MenuItem("I Am Developer.", menu=maybe_dev_menu), @@ -291,7 +334,7 @@ async def goto_home(*a): "WARNING: Seed Vault is encrypted (AES-256-CTR) by your seed," " but not held directly inside secure elements. Backups are required" " after any change to vault! Recommended for experiments or temporary use."), - predicate=has_se_secrets), + predicate=has_real_secret), MenuItem('Perform Selftest', f=start_selftest), # little harmful MenuItem("Set High-Water", f=set_highwater), MenuItem('Wipe HSM Policy', f=wipe_hsm_policy, predicate=hsm_policy_available), @@ -304,7 +347,7 @@ async def goto_home(*a): warnings, funds can be stolen by specially crafted PSBT or MitM. Keep blocked unless you intend to sign special transactions.'''), - ToggleMenuItem('Testnet Mode', 'chain', ['Bitcoin', 'Testnet3', 'Regtest'], + ToggleMenuItem('Testnet Mode', 'chain', ['Bitcoin', 'Testnet4', 'Regtest'], value_map=['BTC', 'XTN', 'XRT'], on_change=change_which_chain, story="Testnet must only be used by developers because \ @@ -323,7 +366,6 @@ async def goto_home(*a): MenuItem('Settings Space', f=show_settings_space), MenuItem('MCU Key Slots', f=show_mcu_keys_left), MenuItem('Bless Firmware', f=bless_flash), # no need for this anymore? - MenuItem('Reflash GPU', f=reflash_gpu, predicate=version.has_qwerty), MenuItem("Wipe LFS", f=wipe_filesystem), # kills other-seed settings, HSM stuff, addr cache ] @@ -331,7 +373,7 @@ async def goto_home(*a): # xxxxxxxxxxxxxxxx MenuItem("Backup System", f=backup_everything), MenuItem("Verify Backup", f=verify_backup), - MenuItem("Restore Backup", f=restore_everything), # just a redirect really + MenuItem("Restore Backup", f=need_clear_seed), # just a UX msg really MenuItem('Clone Coldcard', predicate=has_secrets, f=clone_write_data), ] @@ -342,8 +384,21 @@ async def goto_home(*a): MenuItem('Verify Sig File', f=nfc_sign_verify), MenuItem('Verify Address', f=nfc_address_verify), MenuItem('File Share', f=nfc_share_file), - MenuItem('Import Multisig', f=import_multisig_nfc), - MenuItem('Push Transaction', f=nfc_pushtx_file, predicate=lambda: settings.get("ptxurl", False)), + MenuItem('Import Miniscript', f=import_miniscript_nfc), + MenuItem('Push Transaction', f=nfc_pushtx_file, predicate=has_pushtx_url), +] + + +SpendingPolicySubMenu = [ + NonDefaultMenuItem('Single-Signer', 'sssp', f=sssp_feature_menu, predicate=has_real_secret), + NonDefaultMenuItem('Co-Sign Multi.' if not version.has_qwerty else 'Co-Sign Multisig (CCC)', + 'ccc', f=toggle_ccc_feature, predicate=is_not_tmp), + ToggleMenuItem('HSM Mode', 'hsmcmd', ['Default Off', 'Enable'], + story=("Enable HSM? Enables all user management commands, and other HSM-only USB commands. " + "By default these commands are disabled."), + predicate=hsm_available), + MenuItem('User Management', menu=make_users_menu, + predicate=lambda: hsm_available() and settings.get('hsmcmd', False)), ] AdvancedNormalMenu = [ @@ -352,19 +407,15 @@ async def goto_home(*a): MenuItem('Export Wallet', predicate=has_secrets, menu=WalletExportMenu, shortcut='x'), # also inside FileMgmt MenuItem("Upgrade Firmware", menu=UpgradeMenu, predicate=is_not_tmp), MenuItem("File Management", menu=FileMgmtMenu), - NonDefaultMenuItem('Secure Notes & Passwords', 'notes', menu=make_notes_menu, + NonDefaultMenuItem('Secure Notes & Passwords', 'secnap', menu=make_notes_menu, predicate=version.has_qwerty), MenuItem('Derive Seed B85' if not version.has_qwerty else 'Derive Seeds (BIP-85)', f=drv_entro_start), MenuItem("View Identity", f=view_ident), MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu), + MenuItem("Key Teleport (start)", f=kt_start_rx, predicate=version.has_qr), + MenuItem("Spending Policy", menu=SpendingPolicySubMenu,shortcut='s',predicate=has_real_secret), MenuItem('Paper Wallets', f=make_paper_wallet), - ToggleMenuItem('Enable HSM', 'hsmcmd', ['Default Off', 'Enable'], - story=("Enable HSM? Enables all user management commands, and other HSM-only USB commands. " - "By default these commands are disabled."), - predicate=hsm_available), - MenuItem('User Management', menu=make_users_menu, - predicate=hsm_available), MenuItem('NFC Tools', predicate=nfc_enabled, menu=NFCToolsMenu, shortcut=KEY_NFC), MenuItem("Danger Zone", menu=DangerZoneMenu, shortcut='z'), ] @@ -373,7 +424,7 @@ async def goto_home(*a): VirginSystem = [ # xxxxxxxxxxxxxxxx MenuItem('Choose PIN Code', f=initial_pin_setup), - MenuItem('Advanced/Tools', menu=AdvancedVirginMenu), + MenuItem('Advanced/Tools', menu=AdvancedVirginMenu, shortcut='t'), MenuItem('Bag Number', f=show_bag_number), MenuItem('Help', f=virgin_help, predicate=not version.has_qwerty), ] @@ -384,7 +435,7 @@ async def goto_home(*a): MenuItem("24 Words", menu=start_seed_import, arg=24), MenuItem('Scan QR Code', predicate=version.has_qr, shortcut=KEY_QR, f=scan_any_qr, arg=(True, False)), - MenuItem("Restore Backup", f=restore_everything), + MenuItem("Restore Backup", f=restore_backup, arg=False), # tmp=False MenuItem("Clone Coldcard", menu=clone_start), MenuItem("Import XPRV", f=import_xprv, arg=False), # ephemeral=False MenuItem("Tapsigner Backup", f=import_tapsigner_backup_file, arg=False), @@ -408,9 +459,11 @@ async def goto_home(*a): MenuItem('New Seed Words', menu=NewSeedMenu), MenuItem('Import Existing', menu=ImportWallet), MenuItem("Migrate Coldcard", menu=clone_start), + MenuItem("Key Teleport (start)", f=kt_start_rx, predicate=version.has_qr), MenuItem('Help', f=virgin_help, predicate=not version.has_qwerty), - MenuItem('Advanced/Tools', menu=AdvancedPinnedVirginMenu), + MenuItem('Advanced/Tools', menu=AdvancedPinnedVirginMenu, shortcut='t'), MenuItem('Settings', menu=SettingsMenu), + ShortcutItem(KEY_QR, predicate=version.has_qr, f=scan_any_qr, arg=(True, False)), ] # In operation, normal system, after a good PIN received. @@ -424,7 +477,7 @@ async def goto_home(*a): MenuItem('Start HSM Mode', f=start_hsm_menu_item, predicate=hsm_policy_available), MenuItem("Address Explorer", menu=address_explore, shortcut='x'), MenuItem('Secure Notes & Passwords', menu=make_notes_menu, shortcut='n', - predicate=lambda: version.has_qwerty and (settings.get("notes", False) != False)), + predicate=lambda: version.has_qwerty and settings.get("secnap", False)), MenuItem('Type Passwords', f=password_entry, shortcut='t', predicate=lambda: settings.get("emu", False) and has_secrets()), MenuItem('Seed Vault', menu=make_seed_vault_menu, shortcut='v', @@ -437,10 +490,78 @@ async def goto_home(*a): # Shown until unit is put into a numbered bag FactoryMenu = [ - MenuItem('Version: ' + version.get_mpy_version()[1], f=show_version), MenuItem('Bag Me Now', f=scan_and_bag), + MenuItem('Version: ' + version.get_mpy_version()[1], f=show_version), MenuItem('DFU Upgrade', f=start_dfu, shortcut='u'), MenuItem('Ship W/O Bag', f=ship_wo_bag), MenuItem("Debug Functions", menu=DebugFunctionsMenu, shortcut='f'), MenuItem("Perform Selftest", f=start_selftest, shortcut='s'), ] + +# Special menus for hobbled mode where we have a (single signer) spending policy in effect. +# - no access to secrets, backups, firmware up/downgrades. +# - secure notes, but readonly; can be disabled completely. +# - key teleport, but only for PSBT & multisig purposes. +# - can only be enabled after we have secrets, so no need for has_secrets tests here +# + +# Slightly limited file menu when hobbled. +# - no backup/restore +HobbledFileMgmtMenu = [ + # xxxxxxxxxxxxxxxx + MenuItem('Sign Text File', f=sign_message_on_sd), + MenuItem('Batch Sign PSBT', f=batch_sign), + MenuItem('List Files', f=list_files), + MenuItem('Export Wallet', menu=WalletExportMenu), # dup under Adv/Tools + MenuItem('Verify Sig File', f=verify_sig_file), + MenuItem('NFC File Share', predicate=nfc_enabled, f=nfc_share_file, shortcut=KEY_NFC), + MenuItem('BBQr File Share', predicate=version.has_qr, f=qr_share_file, arg=True), + MenuItem('QR File Share', predicate=version.has_qr, f=qr_share_file, shortcut=KEY_QR), + MenuItem('Format SD Card', f=wipe_sd_card), + MenuItem('Format RAM Disk', predicate=vdisk_enabled, f=wipe_vdisk), +] + +# NFC tools when hobbled: not much different. +HobbledNFCToolsMenu = [ + MenuItem('Sign PSBT', f=nfc_sign_psbt), + MenuItem('Show Address', f=nfc_show_address), + MenuItem('Sign Message', f=nfc_sign_msg), + MenuItem('Verify Sig File', f=nfc_sign_verify), + MenuItem('Verify Address', f=nfc_address_verify), + MenuItem('File Share', f=nfc_share_file), + MenuItem('Push Transaction', f=nfc_pushtx_file, predicate=has_pushtx_url), +] + +# Very limited advanced menu when hobbled. +HobbledAdvancedMenu = [ + # xxxxxxxxxxxxxxxx + MenuItem("File Management", menu=HobbledFileMgmtMenu), + MenuItem('Export Wallet', menu=WalletExportMenu, shortcut='x'), # also inside FileMgmt + MenuItem('Teleport Multisig/Miniscript PSBT', predicate=qr_and_ms, f=kt_send_file_psbt), + MenuItem("View Identity", f=view_ident), + MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu, predicate=sssp_related_keys), + MenuItem('Paper Wallets', f=make_paper_wallet), + MenuItem('NFC Tools', predicate=nfc_enabled, menu=HobbledNFCToolsMenu, shortcut=KEY_NFC), + MenuItem('Show %s Version' % ("Firmware" if version.has_qwerty else "FW"), f=show_version), + MenuItem("Destroy Seed", f=clear_seed, predicate=has_real_secret), +] + +# Main menu when a spending policy (hobbled) is in effect. +HobbledTopMenu = [ + # xxxxxxxxxxxxxxxx + MenuItem('Ready To Sign', f=ready2sign, shortcut='r'), + MenuItem('Passphrase', menu=start_b39_pw, predicate=sssp_allow_passphrase, shortcut='p'), + MenuItem('Scan Any QR Code', predicate=version.has_qr, f=scan_any_qr, arg=(False, True), + shortcut=KEY_QR), + MenuItem("Address Explorer", menu=address_explore, shortcut='x'), + MenuItem('Secure Notes & Passwords', menu=make_notes_menu, predicate=sssp_allow_notes, + shortcut='n'), + MenuItem('Type Passwords', f=password_entry, shortcut='t', + predicate=lambda: settings.get("emu", False) and sssp_related_keys()), + MenuItem('Seed Vault', menu=make_seed_vault_menu, predicate=sssp_allow_vault, + shortcut='v'), + MenuItem('Advanced/Tools', menu=HobbledAdvancedMenu, shortcut='t'), + MenuItem('Secure Logout', f=logout_now, predicate=not version.has_battery), + MenuItem('EXIT TEST DRIVE', f=sssp_feature_menu, predicate=is_hobble_testdrive), + ShortcutItem(KEY_NFC, predicate=nfc_enabled, menu=HobbledNFCToolsMenu), +] diff --git a/shared/glob.py b/shared/glob.py index 0c30e97df..44d9ed524 100644 --- a/shared/glob.py +++ b/shared/glob.py @@ -29,4 +29,9 @@ # QR scanner (Q1 only) SCAN = None +# Multisig/Miniscript descriptor cache +# mapping from unique wallet name to Descriptor object +# cache size = 1 +DESC_CACHE = {} + # EOF diff --git a/shared/gpu.py b/shared/gpu.py index 4e0924683..35da5b1ce 100644 --- a/shared/gpu.py +++ b/shared/gpu.py @@ -8,7 +8,6 @@ # import utime, struct import uasyncio as asyncio -from utils import B2A from machine import Pin from ustruct import pack diff --git a/shared/history.py b/shared/history.py index bccccde4f..479d25807 100644 --- a/shared/history.py +++ b/shared/history.py @@ -18,7 +18,7 @@ # - 8 bytes exact satoshi value => base64 (pad trimmed) => 11 chars # - stored satoshi value is XOR'ed with LSB from prevout txn hash, which isn't stored # - result is a 31 character string for each history entry, plus 4 overhead => 35 each -# - if we store 30 of those it's about 25% of total setting space +# - if we store 30 of those it's about 25% of total setting space (Mk3) # HISTORY_SAVED = const(30) HISTORY_MAX_MEM = const(128) @@ -132,7 +132,7 @@ def add(cls, prevout, amount): # save new addition assert len(key) == ENCKEY_LEN - assert amount > 0 + # assert amount > 0 entry = key + cls.encode_value(prevout, amount) cls.runtime_cache.append(entry) diff --git a/shared/hsm.py b/shared/hsm.py index db538668c..4f153f5b5 100644 --- a/shared/hsm.py +++ b/shared/hsm.py @@ -4,16 +4,15 @@ # # Unattended signing of transactions and messages, subject to a set of rules. # -import stash, ustruct, chains, sys, gc, uio, ujson, uos, utime, ckcc, ngu, version -from sffile import SFFile +import ustruct, chains, sys, gc, uio, ujson, uos, utime, ckcc, ngu from utils import problem_file_line, cleanup_deriv_path, match_deriv_path +from utils import cleanup_payment_address from pincodes import AE_LONG_SECRET_LEN from stash import blank_object from users import Users, MAX_NUMBER_USERS, calc_local_pincode from public_constants import MAX_USERNAME_LEN -from multisig import MultisigWallet +from wallet import MiniScriptWallet from ubinascii import hexlify as b2a_hex -from ubinascii import unhexlify as a2b_hex from uhashlib import sha256 from ucollections import OrderedDict from files import CardSlot, CardMissingError @@ -70,9 +69,9 @@ def restore_backup(s): with open(POLICY_FNAME, 'wt') as f: f.write(s) - except BaseException as exc: + except: # keep going, we don't want to brick - sys.print_exception(exc) + # sys.print_exception(exc) pass def pop_list(j, fld_name, cleanup_fcn=None): @@ -88,13 +87,13 @@ def pop_list(j, fld_name, cleanup_fcn=None): else: return [] -def pop_deriv_list(j, fld_name, extra_val=None): +def pop_deriv_list(j, fld_name, extra_vals=None): # expect a list of derivation paths, but also 'any' meaning accept all # - maybe also 'p2sh' as special value # - also, path can have n def cu(s): - if s.lower() == 'any': return s.lower() - if extra_val and s.lower() == extra_val: return s.lower() + if extra_vals and s.lower() in extra_vals: + return s.lower() try: return cleanup_deriv_path(s, allow_star=True) except: @@ -149,22 +148,6 @@ def assert_empty_dict(j): if extra: raise ValueError("Unknown item: " + ', '.join(extra)) -def cleanup_whitelist_value(s): - # one element in a list of addresses or paths or descriptors? - # - later matching is string-based, so just doing basic syntax check here - # - must be checksumed-base58 or bech32 - try: - ngu.codecs.b58_decode(s) - return s - except: pass - - try: - ngu.codecs.segwit_decode(s) - return s - except: pass - - raise ValueError('bad whitelist value: ' + s) - class WhitelistOpts: # contains various options related to whitelisting @@ -195,7 +178,7 @@ class ApprovalRule: # - users: list of authorized users # - min_users: how many of those are needed to approve # - local_conf: local user must also confirm w/ code - # - wallet: which multisig wallet to restrict to, or '1' for single signer only + # - wallet: which miniscript wallet to restrict to, or '1' for single signer only # - min_pct_self_transfer: minimum percentage of own input value that must go back to self # - patterns: list of transaction patterns to check for. Valid values: # * EQ_NUM_INS_OUTS: the number of inputs and outputs must be equal @@ -215,7 +198,7 @@ def check_user(u): self.per_period = pop_int(j, 'per_period', 0, MAX_SATS) self.max_amount = pop_int(j, 'max_amount', 0, MAX_SATS) self.users = pop_list(j, 'users', check_user) - self.whitelist = pop_list(j, 'whitelist', cleanup_whitelist_value) + self.whitelist = pop_list(j, 'whitelist', cleanup_payment_address) self.whitelist_opts = pop_dict(j, 'whitelist_opts', False, WhitelistOpts) self.min_users = pop_int(j, 'min_users', 1, len(self.users)) self.local_conf = pop_bool(j, 'local_conf') @@ -236,10 +219,10 @@ def check_user(u): # redundant w/ code in pop_int() above assert 1 <= self.min_users <= len(self.users), "range" - # if specified, 'wallet' must be an existing multisig wallet's name + # if specified, 'wallet' must be an existing miniscript wallet's name if self.wallet and self.wallet != '1': - names = [ms.name for ms in MultisigWallet.get_all()] - assert self.wallet in names, "unknown MS wallet: "+self.wallet + msc_names = [msc.name for msc in MiniScriptWallet.iter_wallets()] + assert self.wallet in msc_names, "unknown wallet: " + self.wallet # patterns must be valid for p in self.patterns: @@ -283,9 +266,9 @@ def render(n): rv = 'Any amount' if self.wallet == '1': - rv += ' (non multisig)' + rv += ' (singlesig only)' elif self.wallet: - rv += ' from multisig wallet "%s"' % self.wallet + rv += ' from miniscript wallet "%s"' % self.wallet if self.users: rv += ' may be authorized by ' @@ -326,12 +309,11 @@ def matches_transaction(self, psbt, users, total_out, local_oked, chain): # Does this rule apply to this PSBT file? if self.wallet: # rule limited to one wallet - if psbt.active_multisig: - # if multisig signing, might need to match specific wallet name - assert self.wallet == psbt.active_multisig.name, 'wrong wallet' + if psbt.active_miniscript: + assert self.wallet == psbt.active_miniscript.name, 'wrong miniscript wallet' else: - # non multisig, but does this rule apply to all wallets or single-singers - assert self.wallet == '1', 'not multisig' + # not miniscript, but does this rule apply to all wallets or single-singers + assert self.wallet == '1', 'singlesig only' if self.max_amount is not None: assert total_out <= self.max_amount, 'amount exceeded' @@ -367,7 +349,7 @@ def matches_transaction(self, psbt, users, total_out, local_oked, chain): # we are verifying the whole consensus-encoded txout txo_bytes = CTxOut(txo.nValue, txo.scriptPubKey).serialize() digest = chain.hash_message(txo_bytes) - addr_fmt, pubkey = chains.verify_recover_pubkey(o.attestation, digest) + addr_fmt, pubkey = chains.verify_recover_pubkey(psbt.get(o.attestation), digest) # we have extracted a valid pubkey from the sig, but is it # a whitelisted pubkey or something else? ver_addr = chain.pubkey_to_address(pubkey, addr_fmt) @@ -390,11 +372,11 @@ def matches_transaction(self, psbt, users, total_out, local_oked, chain): # check the self-transfer percentage if self.min_pct_self_transfer: - own_in_value = sum([i.amount for i in psbt.inputs if i.num_our_keys]) + own_in_value = sum([i.amount for i in psbt.inputs if i.sp_idxs]) own_out_value = 0 for idx, txo in psbt.output_iter(): o = psbt.outputs[idx] - if o.num_our_keys: + if o.sp_idxs: own_out_value += txo.nValue percentage = (float(own_out_value) / own_in_value) * 100.0 assert percentage >= self.min_pct_self_transfer, 'does not meet self transfer threshold, expected: %.2f, actual: %.2f' % (self.min_pct_self_transfer, percentage) @@ -405,8 +387,8 @@ def matches_transaction(self, psbt, users, total_out, local_oked, chain): assert len(psbt.inputs) == len(psbt.outputs), 'unequal number of inputs and outputs' if "EQ_NUM_OWN_INS_OUTS" in self.patterns: - own_ins = sum([1 for i in psbt.inputs if i.num_our_keys]) - own_outs = sum([1 for o in psbt.outputs if o.num_our_keys]) + own_ins = sum([1 for i in psbt.inputs if i.sp_idxs]) + own_outs = sum([1 for o in psbt.outputs if o.sp_idxs]) assert own_ins == own_outs, 'unequal number of own inputs and outputs' if "EQ_OUT_AMOUNTS" in self.patterns: @@ -504,9 +486,9 @@ def load(self, j): self.warnings_ok = pop_bool(j, 'warnings_ok') # a list of paths we can accept for signing - self.msg_paths = pop_deriv_list(j, 'msg_paths') - self.share_xpubs = pop_deriv_list(j, 'share_xpubs') - self.share_addrs = pop_deriv_list(j, 'share_addrs', 'p2sh') + self.msg_paths = pop_deriv_list(j, 'msg_paths', ['any']) + self.share_xpubs = pop_deriv_list(j, 'share_xpubs', ['any']) + self.share_addrs = pop_deriv_list(j, 'share_addrs', ['any', 'msas']) # free text shown at top self.notes = pop_string(j, 'notes', 1, 80) @@ -591,7 +573,7 @@ def explain(self, fd): fd.write('\n') def plist(pl): - remap = {'any': '(any path)', 'p2sh': '(any P2SH)' } + remap = {'any': '(any path)', 'msas': '(any miniscript)' } return ' OR '.join(remap.get(i, i) for i in pl) fd.write('\nMessage signing:\n') @@ -621,7 +603,7 @@ def plist(pl): fd.write('- XPUB values will be shared, if path matches: m OR %s.\n' % plist(self.share_xpubs)) if self.share_addrs: - fd.write('- Address values values will be shared, if path matches: %s.\n' + fd.write('- Address values will be shared, if path matches: %s.\n' % plist(self.share_addrs)) if self.priv_over_ux: fd.write('- Status responses optimized for privacy.\n') @@ -814,14 +796,14 @@ def approve_xpub_share(self, subpath): return match_deriv_path(self.share_xpubs, subpath) - def approve_address_share(self, subpath=None, is_p2sh=False): + def approve_address_share(self, subpath=None, miniscript=False): # Are we allowing "show address" requests over USB? if not self.share_addrs: return False - if is_p2sh: - return ('p2sh' in self.share_addrs) + if miniscript: + return ('msas' in self.share_addrs) return match_deriv_path(self.share_addrs, subpath) @@ -894,6 +876,7 @@ async def approve_transaction(self, psbt, psbt_sha, story): # reject anything with warning, probably if psbt.warnings: + print(psbt.warnings) if self.warnings_ok: log.info("Txn has warnings, but policy is to accept anyway.") else: @@ -951,7 +934,7 @@ async def approve_transaction(self, psbt, psbt_sha, story): return 'y' except BaseException as exc: - sys.print_exception(exc) + # sys.print_exception(exc) err = "Rejected: " + (str(exc) or problem_file_line(exc)) self.refuse(log, err) @@ -994,7 +977,7 @@ def hsm_status_report(): rv['approval_wait'] = True rv['users'] = Users.list() - rv['wallets'] = [ms.name for ms in MultisigWallet.get_all()] + rv['wallets'] = [msc.name for msc in MiniScriptWallet.iter_wallets()] rv['chain'] = settings.get('chain', 'BTC') diff --git a/shared/hsm_ux.py b/shared/hsm_ux.py index 5cc4fb912..f34f43707 100644 --- a/shared/hsm_ux.py +++ b/shared/hsm_ux.py @@ -67,7 +67,7 @@ async def interact(self): except BaseException as exc: self.failed = "Exception" - sys.print_exception(exc) + # sys.print_exception(exc) self.refused = True self.ux_done = True @@ -297,7 +297,7 @@ def draw_busy(self, msg, percent): # replacements for display.py:Display functions - def hack_fullscreen(self, msg, percent=None): + def hack_fullscreen(self, msg, percent=None, **kwargs): self.draw_busy(msg, percent) def hack_progress_bar(self, percent): self.draw_busy(None, percent) @@ -354,7 +354,7 @@ async def interact(self): await sleep_ms(100) except BaseException as exc: # just in case, keep going - sys.print_exception(exc) + # sys.print_exception(exc) continue # do the interactions, but don't let user actually press anything diff --git a/shared/imptask.py b/shared/imptask.py index 6979854c6..297415ebd 100644 --- a/shared/imptask.py +++ b/shared/imptask.py @@ -58,8 +58,8 @@ def handle_exc(self, loop, context): else: # uncaught exception in an unnamed (and unimportant) task print("UNNAMED: " + context["message"]) - sys.print_exception(context["exception"]) - print("... future: %r" % context.get("future", '?')) + sys.print_exception(context["exception"]) # VERY USEFUL on sim + #print("... future: %r" % context.get("future", '?')) def start_task(self, name, awaitable): # start a critical task and watch for it to never die diff --git a/shared/lcd_display.py b/shared/lcd_display.py index 0e3722a1e..de601c23a 100644 --- a/shared/lcd_display.py +++ b/shared/lcd_display.py @@ -3,11 +3,11 @@ # lcd_display.py - LCD rendering for Q1's 320x240 pixel *colour* display! # import machine, uzlib, utime, array -from uasyncio import sleep_ms from graphics_q1 import Graphics from st7788 import ST7788 -from utils import xfp2str, word_wrap +from utils import xfp2str, word_wrap, chunk_address from ucollections import namedtuple +from charcodes import OUT_CTRL_TITLE, OUT_CTRL_ADDRESS # the one font: fixed-width (except for a few double-width chars) from font_iosevka import CELL_W, CELL_H, TEXT_PALETTES, COL_TEXT, COL_DARK_TEXT, COL_SCROLL_DARK @@ -154,7 +154,7 @@ def set_lcd_brightness(self, on_battery=None, tmp_override=None): # otherwise: respect setting if on_battery is None: - on_battery = (get_batt_threshold() != None) + on_battery = (get_batt_threshold() is not None) if on_battery: # user-defined brightness when running on batteries. @@ -190,7 +190,7 @@ def draw_status(self, full=False, **kws): self.image(165, 0, 'tmp_%d' % kws['tmp']) xfp = kws.get('xfp', None) # expects an integer - if xfp != None: + if xfp is not None: x = 217 for ch in xfp2str(xfp).lower(): self.image(x, 0, 'ch_'+ch) @@ -268,7 +268,7 @@ def text(self, x,y, msg, font=None, invert=False, dark=False): if x is None or x < 0: w = self.width(msg) - if x == None: + if x is None: # center: also blanks rest of line x = max(0, (CHARS_W - w) // 2) end_x = x + w @@ -612,25 +612,105 @@ def draw_story(self, lines, top, num_lines, is_sensitive, hint_icons=''): self.clear() y=0 + prev_x = None for ln in lines: if ln == 'EOT': self.text(0, y, '┅'*CHARS_W, dark=True) continue - elif ln and ln[0] == '\x01': + + elif ln and ln[0] == OUT_CTRL_TITLE: # title ... but we have no special font? Inverse! self.text(0, y, ' '+ln[1:]+' ', invert=True) if hint_icons: - # maybe show that [QR] can do something + # hint_icons not shown if is story without title + # maybe show that [QR,NFC] can do something self.text(-1, y, hint_icons, dark=True) + + elif ln and ln[0] == OUT_CTRL_ADDRESS: + # we can assume this will be a single line for our display + # thanks to code in utils.word_wrap + prev_x = self._draw_addr(y, ln[1:], prev_x=prev_x) + else: self.text(0, y, ln) + prev_x = None y += 1 self.scroll_bar(top, num_lines, CHARS_H) self.show() - def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, partial_bar=None): + def _draw_addr(self, y, addr, prev_x=None): + # Draw a single-line of an address + # - use prev_x=0 to start centered + if prev_x is None: + # left justify (for stories) + prev_x = x = 1 + elif prev_x == 0: + # center first line, following line(s) will be left-justified to match that + prev_x = x = max(((CHARS_W - (len(addr) * 5) // 4) // 2), 0) + else: + x = prev_x + + self.text(x, y, ' '+' '.join(chunk_address(addr))+' ', invert=True) + + return prev_x + + @staticmethod + def handle_qr_msg(msg, max_lines=False): + if len(msg) <= CHARS_W: + parts = [msg] + elif ' ' not in msg and (len(msg) <= (CHARS_W * 2)): + # fits in two lines, but has no spaces + hh = len(msg) // 2 + parts = [msg[0:hh], msg[hh:]] + else: + if not max_lines: + # do word wrap + parts = list(word_wrap(msg, CHARS_W)) + else: + # 2 lines max + parts = [msg[:30] + "⋯", "⋯" + msg[-30:]] + + return parts + + def draw_qr_lines(self, lines, is_addr): + y = CHARS_H - len(lines) + prev_x = 0 + for line in lines: + if not is_addr: + self.text(None, y, line) + else: + prev_x = self._draw_addr(y, line, prev_x=prev_x) + y += 1 + + def draw_qr_idx_hint(self, str_idx): + lh = len(str_idx) + assert lh <= 10 + if lh > 5: + # needs 2 lines + self.text(-1, 0, str_idx[:5]) + self.text(-1, 1, str_idx[5:]) + else: + self.text(-1, 0, str_idx) + + def draw_qr_error(self, idx_hint, msg=None): + x = 85 + y = 30 + w = 150 + self.clear() + self.dis.fill_rect(x, y, w, w, COL_TEXT) + self.dis.fill_rect(x + 1, y + 1, w - 2, w - 2) # Black + self.text(12, 3, "QR too big") + if msg: + lines = self.handle_qr_msg(msg, max_lines=True) + self.draw_qr_lines(lines, False) + + self.draw_qr_idx_hint(idx_hint) + self.show() + + def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, partial_bar=None, + is_addr=False, force_msg=False, is_change=False): # Show a QR code on screen w/ some text under it # - invert not supported on Q1 # - sidebar not supported here (see users.py) @@ -638,18 +718,19 @@ def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, par assert not sidebar # maybe show something other than QR contents under it - if msg: - if len(msg) <= CHARS_W: - parts = [msg] - elif ' ' not in msg and (len(msg) <= CHARS_W*2): - # fits in two lines, but has no spaces (ie. payment addr) - # so split nicely, and shift off center - hh = len(msg) // 2 - parts = [msg[0:hh] + ' ', ' '+msg[hh:]] + if is_addr: + # With fancy display, no address, even classic can fit in single line, + # so always split nicely in middle and at mod4 + hh = len(msg) // 2 + if hh <= 20: + hh = (hh + 3) & ~0x3 + parts = [msg[0:hh], msg[hh:]] + num_lines = 2 else: - # do word wrap - parts = list(word_wrap(msg, CHARS_W)) - + # p2wsh address would need 3 lines to show, so we won't + num_lines = 0 + elif msg: + parts = self.handle_qr_msg(msg) num_lines = len(parts) else: num_lines = 0 @@ -670,25 +751,21 @@ def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, par fullscreen = False trim_lines = 0 - if w == 77: - # v15 => 77px x 3: 77*3 = 231px - expand = 3 - num_lines = 0 - fullscreen = True - elif w in (109, 113, 117): - # v23 => 109px x 2 = 218px - # v24 => 113px x 2 = 226px - # v25 => 117px x 2 = 234px - expand = 2 - num_lines = 0 - fullscreen = True - elif expand == 1 and num_lines: - # Maybe loose the text lines? - expand2 = max(1, ACTIVE_H // (w+2)) - if expand2 > expand: - # v18,v19,v20,v21,v22 + # always try to show the biggest possible QR code if not force_msg + if not force_msg: + if num_lines: + # better with text dropped? + e2 = max(1, ACTIVE_H // (w + 2)) + if e2 > expand: + num_lines = 0 + expand = e2 + + # fullscreen ? + e3 = (ACTIVE_H + 20) // (w + 2) + if expand < e3: + expand = e3 + fullscreen = True num_lines = 0 - expand = expand2 # vert center in available space qw = (w+2) * expand @@ -722,20 +799,17 @@ def draw_qr_display(self, qr_data, msg, is_alnum, sidebar, idx_hint, invert, par if num_lines: # centered text under that - y = CHARS_H - num_lines - for line in parts: - self.text(None, y, line) - y += 1 + self.draw_qr_lines(parts, is_addr) if idx_hint: - lh = len(idx_hint) - assert lh <= 10 - if lh > 6: - # needs 2 lines - self.text(-1, 0, idx_hint[:6]) - self.text(-1, 1, idx_hint[6:]) - else: - self.text(-1, 0, idx_hint) + self.draw_qr_idx_hint(idx_hint) + + if is_addr and is_change: + for i, c in enumerate("CHANGE", start=4): + self.text(1, i, c) + + for i, c in enumerate("BACK", start=6): + self.text(-1, i, c) # pass a max brightness flag here, which will be cleared after next show self.show(max_bright=True) @@ -770,8 +844,12 @@ def draw_bbqr_progress(self, hdr, got_parts, corrupt=False): else: pat = '' # clear line - self.text(None, -3, pat) + if count == hdr.num_parts and count == 1: + # skip the BS, it's a simple one + self.progress_bar_show(1) + return + self.text(None, -3, pat) self.text(None, -2, 'Keep scanning more...' if count < hdr.num_parts else 'Got all parts!') self.text(None, -1, '%s: %d of %d parts' % (hdr.file_label(), count, hdr.num_parts), dark=True) diff --git a/shared/login.py b/shared/login.py index ed6b64a82..97da5b25d 100644 --- a/shared/login.py +++ b/shared/login.py @@ -181,14 +181,22 @@ async def interact(self): async def we_are_ewaste(self, num_fails): msg = '''After %d failed PIN attempts this Coldcard is locked forever. \ By design, there is no way to reset or recover the secure element, and its contents \ -are now forever inaccessible. +are now forever inaccessible.\n\n''' % num_fails -Restore your seed words onto a new Coldcard.''' % num_fails + if has_qwerty: + msg += 'Calculator mode starts now.' + else: + msg += 'Restore your seed words onto a new Coldcard.' while 1: ch = await ux_show_story(msg, title='I Am Brick!', escape='6') if ch == '6': break + if has_qwerty: + from calc import login_repl + await login_repl() + + async def confirm_attempt(self, attempts_left, value): ch = await ux_show_story('''You have %d attempts left before this Coldcard BRICKS \ @@ -270,7 +278,7 @@ async def prompt_pin(self): return await self.interact() - async def get_new_pin(self, title, story=None, allow_clear=False): + async def get_new_pin(self, title=None, story=None): # Do UX flow to get new (or change) PIN. Always does the double-entry thing self.is_setting = True @@ -283,10 +291,6 @@ async def get_new_pin(self, title, story=None, allow_clear=False): first_pin = await self.interact() if first_pin is None: return None - if allow_clear and first_pin == '999999-999999': - # don't make them repeat the 'clear pin' value - return first_pin - self.is_repeat = True while 1: diff --git a/shared/main.py b/shared/main.py index 870bd7e15..afacb7e20 100644 --- a/shared/main.py +++ b/shared/main.py @@ -61,9 +61,7 @@ from psram import PSRAMWrapper glob.PSRAM = PSRAMWrapper() -except BaseException as exc: - sys.print_exception(exc) - # continue tho +except: pass # continue tho # Setup keypad/keyboard if version.has_qwerty: @@ -83,7 +81,6 @@ async def more_setup(): # Boot up code; splash screen is being shown - try: from files import CardSlot CardSlot.setup() @@ -91,6 +88,10 @@ async def more_setup(): # This "pa" object holds some state shared w/ bootloader about the PIN try: from pincodes import pa + # check for bricked system early + # bricked CC not going past this point + await pa.enforce_brick() + pa.setup(b'') # just to see where we stand. is_blank = pa.is_blank() except RuntimeError as e: diff --git a/shared/manifest.py b/shared/manifest.py index df9165ac9..6699aa78c 100644 --- a/shared/manifest.py +++ b/shared/manifest.py @@ -1,17 +1,20 @@ # Freeze everything in this list. # - not optimized because we need asserts to work -# - for specific boards, see manifest_mk[34].py and manifest_q1.py +# - for specific boards, see manifest_{mk4,q1}.py freeze_as_mpy('', [ 'actions.py', 'address_explorer.py', 'auth.py', 'backups.py', + 'bsms.py', 'callgate.py', + 'ccc.py', 'chains.py', 'choosers.py', 'compat7z.py', 'countdowns.py', 'descriptor.py', + 'desc_utils.py', 'dev_helper.py', 'display.py', 'drv_entro.py', @@ -26,16 +29,24 @@ 'login.py', 'main.py', 'menu.py', + "miniscript.py", + 'mk4.py', + 'msgsign.py', 'multisig.py', + 'ndef.py', + 'nfc.py', 'numpad.py', 'nvstore.py', 'opcodes.py', + 'ownership.py', 'paper.py', 'pincodes.py', + 'precomp_tag_hash.py', 'psbt.py', + 'psram.py', 'pwsave.py', - 'queues.py', 'qrs.py', + 'queues.py', 'random.py', 'seed.py', 'selftest.py', @@ -43,31 +54,33 @@ 'sffile.py', 'ssd1306.py', 'stash.py', + 'tapsigner.py', + 'trick_pins.py', 'usb.py', 'utils.py', 'ux.py', + 'vdisk.py', 'version.py', - 'xor_seed.py', - 'tapsigner.py', 'wallet.py', - 'ownership.py', + 'web2fa.py', + 'xor_seed.py' ], opt=0) # Optimize data-like files, since no need to debug them. freeze_as_mpy('', [ - 'sigheader.py', - 'public_constants.py', 'charcodes.py', + 'public_constants.py', + 'sigheader.py', ], opt=3) # Maybe include test code. import os if int(os.environ.get('DEBUG_BUILD', 0)): freeze_as_mpy('', [ + 'dev_helper.py', 'h.py', - 'dev_helper.py', - 'usb_test_commands.py', 'sim_display.py', + 'usb_test_commands.py', ], opt=0) include("$(MPY_DIR)/extmod/uasyncio/manifest.py") diff --git a/shared/manifest_mk4.py b/shared/manifest_mk4.py index 9b062d466..b7ac0c1eb 100644 --- a/shared/manifest_mk4.py +++ b/shared/manifest_mk4.py @@ -1,17 +1,11 @@ # Mk4 only files; would not be needed on Mk3 or earlier. freeze_as_mpy('', [ - 'ssd1306.py', - 'mempad.py', - 'psram.py', - 'mk4.py', - 'vdisk.py', - 'nfc.py', - 'ndef.py', - 'trick_pins.py', - 'ux_mk4.py', 'hsm.py', 'hsm_ux.py', + 'mempad.py', + 'ssd1306.py', 'users.py', + 'ux_mk4.py' ], opt=0) # Optimize data-like files, since no need to debug them. diff --git a/shared/manifest_q1.py b/shared/manifest_q1.py index 6d624b801..02370ac07 100644 --- a/shared/manifest_q1.py +++ b/shared/manifest_q1.py @@ -1,29 +1,24 @@ -# Q1/Mk4 only files; would not be needed on Mk3 or earlier. +# Q1 only files; would not be needed on Mk4 freeze_as_mpy('', [ - 'psram.py', - 'mk4.py', - 'q1.py', - 'keyboard.py', - 'scanner.py', + 'battery.py', 'bbqr.py', - 'decoders.py', - 'lcd_display.py', - 'st7788.py', + 'calc.py', + 'decoders.py', 'gpu.py', - 'vdisk.py', - 'nfc.py', - 'ndef.py', - 'trick_pins.py', - 'ux_q1.py', - 'battery.py', + 'keyboard.py', + 'lcd_display.py', 'notes.py', - 'calc.py', + 'q1.py', + 'scanner.py', + 'st7788.py', + 'teleport.py', + 'ux_q1.py' ], opt=0) # Optimize data-like files, since no need to debug them. freeze_as_mpy('', [ - 'graphics_q1.py', 'font_iosevka.py', 'gpu_binary.py', # remove someday? + 'graphics_q1.py', ], opt=3) diff --git a/shared/menu.py b/shared/menu.py index 4b14ee825..ccaa9e50a 100644 --- a/shared/menu.py +++ b/shared/menu.py @@ -119,7 +119,7 @@ def __init__(self, key, **kws): super().__init__('SHORTCUT', shortcut=key, **kws) class NonDefaultMenuItem(MenuItem): - # Show a checkmark if setting is defined and not the default ... so know know it's set + # Show a checkmark if setting is defined and not the default def __init__(self, label, nvkey, prelogin=False, default_value=None, **kws): super().__init__(label, **kws) self.nvkey = nvkey @@ -182,7 +182,7 @@ async def activate(self, menu, idx): if self.nvkey == "chain": default = (self.get() == "BTC") else: - default = (self.get(None) == None) + default = (self.get(None) is None) if self.story and default: ch = await ux_show_story(self.story) if ch == 'x': return @@ -306,10 +306,6 @@ def show(self): if fcn and fcn(): checked = True - if not has_qwerty and checked and (len(msg) > 14): - # on mk4 every label longer than 14 will overlap with checkmark - checked = False - if self.multi_selected is not None and (real_idx in self.multi_selected): # ignore length constraint above, we need to visually show that # smthg is selected - in any case @@ -335,9 +331,8 @@ def should_wrap_menu(self): if wrap: return True # Do wrap-around (by request from NVK) if longer than the screen itself (on Q), - # for mk4, limit is 16 which hits mostly the seed word menus. - limit = 10 if has_qwerty else 16 - return self.count > limit + # Mk4: same limit + return self.count > 10 def down(self): if self.cursor < self.count-1: @@ -362,12 +357,6 @@ def top(self): self.cursor = 0 self.ypos = 0 - def goto_n(self, n): - # goto N from top of (current) screen - # change scroll only if needed to make it visible - self.cursor = max(min(n + self.ypos, self.count-1), 0) - self.ypos = max(self.cursor - n, 0) - def goto_idx(self, n): # skip to any item, force cusor near middle of screen n = self.count-1 if n >= self.count else n @@ -388,7 +377,7 @@ def page(self, n): self.up() # events - def on_cancel(self): + async def on_cancel(self): # override me if the_ux.pop(): # top of stack (main top-level menu) @@ -399,7 +388,7 @@ async def activate(self, picked): # if picked is None: # "go back" or cancel or something - self.on_cancel() + await self.on_cancel() else: await picked.activate(self, self.cursor) @@ -412,7 +401,7 @@ async def interact(self): gc.collect() if self.multi_selected is not None: # multichoice - self.on_cancel() + await self.on_cancel() return ch await self.activate(ch) @@ -474,7 +463,7 @@ async def wait_choice(self): self.ypos = 0 elif '1' <= key <= '9': # jump down, based on screen postion - self.goto_n(ord(key)-ord('1')) + self.goto_idx(ord(key)-ord('1')) elif key in self.shortcuts: # run the function, if predicate allows m = self.shortcuts[key] @@ -489,7 +478,7 @@ async def wait_choice(self): return self.items[self.cursor] # search downwards for a menu item that starts with indicated letter - # if found, select it but dont drill down + # if found, select it but don't drill down lst = list(range(self.cursor+1, self.count)) + list(range(0, self.cursor)) for n in lst: if self.items[n].label[0].upper() == key.upper(): diff --git a/shared/miniscript.py b/shared/miniscript.py new file mode 100644 index 000000000..4bbdbb4c8 --- /dev/null +++ b/shared/miniscript.py @@ -0,0 +1,1156 @@ +# (c) Copyright 2020 by Stepan Snigirev, see +# +# Changes (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +import ngu +from binascii import unhexlify as a2b_hex +from binascii import hexlify as b2a_hex +from serializations import ser_compact_size +from desc_utils import Key, read_until +from public_constants import MAX_TR_SIGNERS + + +class Number: + def __init__(self, num): + self.num = num + + @classmethod + def read_from(cls, s, taproot=False): + num = 0 + char = s.read(1) + while char in b"0123456789": + num = 10 * num + int(char.decode()) + char = s.read(1) + s.seek(-1, 1) + return cls(num) + + def compile(self): + if self.num == 0: + return b"\x00" + if self.num <= 16: + return bytes([80 + self.num]) + b = self.num.to_bytes(32, "little").rstrip(b"\x00") + if b[-1] >= 128: + b += b"\x00" + return bytes([len(b)]) + b + + def __len__(self): + return len(self.compile()) + + def to_string(self, *args, **kwargs): + return "%d" % self.num + + +class KeyHash(Key): + @classmethod + def parse_key(cls, k: bytes, *args, **kwargs): + # convert to string + kd = k.decode() + # raw 20-byte hash + if len(kd) == 40: + return kd, None + return super().parse_key(k, *args, **kwargs) + + def serialize(self, *args, **kwargs): + start = 1 if self.taproot else 0 + return ngu.hash.hash160(self.node.pubkey()[start:33]) + + def __len__(self): + return 21 # <20:pkh> + + def compile(self): + d = self.serialize() + return ser_compact_size(len(d)) + d + + +class Raw: + def __init__(self, raw): + if len(raw) != self.LEN * 2: + raise ValueError("Invalid raw element length: %d" % len(raw)) + self.raw = a2b_hex(raw) + + @classmethod + def read_from(cls, s, taproot=False): + return cls(s.read(2 * cls.LEN).decode()) + + def to_string(self, *args, **kwargs): + return b2a_hex(self.raw).decode() + + def compile(self): + return ser_compact_size(len(self.raw)) + self.raw + + def __len__(self): + return len(ser_compact_size(self.LEN)) + self.LEN + + +class Raw32(Raw): + LEN = 32 + def __len__(self): + return 33 + + +class Raw20(Raw): + LEN = 20 + def __len__(self): + return 21 + + +class Miniscript: + def __init__(self, *args, **kwargs): + self.args = args + self.taproot = kwargs.get("taproot", False) + + def compile(self): + return self.inner_compile() + + def verify(self): + for arg in self.args: + if isinstance(arg, Miniscript): + arg.verify() + + @property + def keys(self): + res = [] + for arg in self.args: + if isinstance(arg, Miniscript): + res += arg.keys + elif isinstance(arg, Key): # KeyHash is subclass of Key + res.append(arg) + return res + + def is_sane(self, taproot=False): + err = "multi mixin" + forbiden = (Sortedmulti, Multi) if taproot else (Sortedmulti_a, Multi_a) + assert type(self) not in forbiden, err + + for arg in self.args: + assert type(arg) not in forbiden, err + if isinstance(arg, Miniscript): + arg.is_sane(taproot=taproot) + + @staticmethod + def key_derive(key, idx, key_map=None, change=False): + if key_map and key in key_map: + kd = key_map[key] + else: + kd = key.derive(idx, change=change) + return kd + + def derive(self, idx, key_map=None, change=False): + args = [] + for arg in self.args: + if isinstance(arg, Key): # KeyHash is subclass of Key + arg = self.key_derive(arg, idx, key_map, change=change) + elif hasattr(arg, "derive"): + arg = arg.derive(idx, key_map, change) + + args.append(arg) + return type(self)(*args) + + @property + def properties(self): + return self.PROPS + + @property + def type(self): + return self.TYPE + + @classmethod + def read_from(cls, s, taproot=False): + op, char = read_until(s, b"(") + op = op.decode() + wrappers = "" + if ":" in op: + wrappers, op = op.split(":") + if char != b"(": + raise ValueError("Missing operator") + if op not in OPERATOR_NAMES: + raise ValueError("Unknown operator '%s'" % op) + # number of arguments, classes of arguments, compile function, type, validity checker + MiniscriptCls = OPERATORS[OPERATOR_NAMES.index(op)] + args = MiniscriptCls.read_arguments(s, taproot=taproot) + miniscript = MiniscriptCls(*args, taproot=taproot) + for w in reversed(wrappers): + if w not in WRAPPER_NAMES: + raise ValueError("Unknown wrapper %s" % w) + WrapperCls = WRAPPERS[WRAPPER_NAMES.index(w)] + miniscript = WrapperCls(miniscript, taproot=taproot) + return miniscript + + @classmethod + def read_arguments(cls, s, taproot=False): + args = [] + if cls.NARGS is None: + if type(cls.ARGCLS) == tuple: + firstcls, nextcls = cls.ARGCLS + else: + firstcls, nextcls = cls.ARGCLS, cls.ARGCLS + + args.append(firstcls.read_from(s, taproot=taproot)) + while True: + char = s.read(1) + if char == b",": + args.append(nextcls.read_from(s, taproot=taproot)) + elif char == b")": + break + else: + raise ValueError( + "Expected , or ), got: %s" % (char + s.read()) + ) + else: + for i in range(cls.NARGS): + args.append(cls.ARGCLS.read_from(s, taproot=taproot)) + if i < cls.NARGS - 1: + char = s.read(1) + if char != b",": + raise ValueError("Missing arguments, %s" % char) + char = s.read(1) + if char != b")": + raise ValueError("Expected ) got %s" % (char + s.read())) + return args + + def to_string(self, external=True, internal=True): + # meh + res = type(self).NAME + "(" + res += ",".join([ + arg.to_string(external, internal) + for arg in self.args + ]) + res += ")" + return res + + def __len__(self): + """Length of the compiled script, override this if you know the length""" + return len(self.compile()) + + def len_args(self): + return sum([len(arg) for arg in self.args]) + +########### Known fragments (miniscript operators) ############## + + +class OneArg(Miniscript): + NARGS = 1 + # small handy functions + @property + def arg(self): + return self.args[0] + + @property + def carg(self): + return self.arg.compile() + + +class PkK(OneArg): + # + NAME = "pk_k" + ARGCLS = Key + TYPE = "K" + PROPS = "ondu" + + def inner_compile(self): + return self.carg + + def __len__(self): + return self.len_args() + + +class PkH(OneArg): + # DUP HASH160 EQUALVERIFY + NAME = "pk_h" + ARGCLS = KeyHash + TYPE = "K" + PROPS = "ndu" + + def inner_compile(self): + return b"\x76\xa9" + self.carg + b"\x88" + + def __len__(self): + return self.len_args() + 3 + +class After(OneArg): + # CHECKLOCKTIMEVERIFY + NAME = "after" + ARGCLS = Number + TYPE = "B" + PROPS = "z" + + def inner_compile(self): + return self.carg + b"\xb1" + + def verify(self): + super().verify() + assert 0 < self.arg.num < 0x80000000, "%s out of range [1, 2147483647]" % self.NAME + + def __len__(self): + return self.len_args() + 1 + +class Older(OneArg): + # CHECKSEQUENCEVERIFY + NAME = "older" + ARGCLS = Number + TYPE = "B" + PROPS = "z" + + def inner_compile(self): + return self.carg + b"\xb2" + + def verify(self): + super().verify() + # not consensus valid + # https://github.com/bitcoin/bitcoin/pull/33135 older(65536) is equivalent to older(1) + if self.arg.num & (1 << 22): + # time-based + assert 0x400000 < self.arg.num < 0x410000, "Time-based %s out of range [4194305, 4259839]" % self.NAME + else: + # block-based + assert 0 < self.arg.num < 0x10000, "Block-based %s out of range [1, 65535]" % self.NAME + + def __len__(self): + return self.len_args() + 1 + +class Sha256(OneArg): + # SIZE <32> EQUALVERIFY SHA256 EQUAL + NAME = "sha256" + ARGCLS = Raw32 + TYPE = "B" + PROPS = "ondu" + + def inner_compile(self): + # Number(32).compile() --> b"\x01\x20" + return b"\x82\x01\x20\x88\xa8" + self.carg + b"\x87" + + def __len__(self): + return self.len_args() + 6 + +class Hash256(Sha256): + # SIZE <32> EQUALVERIFY HASH256 EQUAL + NAME = "hash256" + + def inner_compile(self): + # Number(32).compile() --> b"\x01\x20" + return b"\x82\x01\x20\x88\xaa" + self.carg + b"\x87" + + +class Ripemd160(Sha256): + # SIZE <32> EQUALVERIFY RIPEMD160 EQUAL + NAME = "ripemd160" + ARGCLS = Raw20 + + def inner_compile(self): + # Number(32).compile() --> b"\x01\x20" + return b"\x82\x01\x20\x88\xa6" + self.carg + b"\x87" + + +class Hash160(Ripemd160): + # SIZE <32> EQUALVERIFY HASH160 EQUAL + NAME = "hash160" + + def inner_compile(self): + # Number(32).compile() --> b"\x01\x20" + return b"\x82\x01\x20\x88\xa9" + self.carg + b"\x87" + + +class AndOr(Miniscript): + # [X] NOTIF [Z] ELSE [Y] ENDIF + NAME = "andor" + NARGS = 3 + ARGCLS = Miniscript + + @property + def type(self): + # type same as Y/Z + return self.args[1].type + + def verify(self): + # requires: X is Bdu; Y and Z are both B, K, or V + super().verify() + if self.args[0].type != "B": + raise ValueError("andor: X should be 'B'") + px = self.args[0].properties + if "d" not in px and "u" not in px: + raise ValueError("andor: X should be 'du'") + if self.args[1].type != self.args[2].type: + raise ValueError("andor: Y and Z should have the same types") + if self.args[1].type not in "BKV": + raise ValueError("andor: Y and Z should be B K or V") + + @property + def properties(self): + # props: z=zXzYzZ; o=zXoYoZ or oXzYzZ; u=uYuZ; d=dZ + props = "" + px, py, pz = [arg.properties for arg in self.args] + if "z" in px and "z" in py and "z" in pz: + props += "z" + if ("z" in px and "o" in py and "o" in pz) or ( + "o" in px and "z" in py and "z" in pz + ): + props += "o" + if "u" in py and "u" in pz: + props += "u" + if "d" in pz: + props += "d" + return props + + def inner_compile(self): + return ( + self.args[0].compile() + + b"\x64" + + self.args[2].compile() + + b"\x67" + + self.args[1].compile() + + b"\x68" + ) + + def __len__(self): + return self.len_args() + 3 + +class AndV(Miniscript): + # [X] [Y] + NAME = "and_v" + NARGS = 2 + ARGCLS = Miniscript + + def inner_compile(self): + return self.args[0].compile() + self.args[1].compile() + + def __len__(self): + return self.len_args() + + def verify(self): + # X is V; Y is B, K, or V + super().verify() + if self.args[0].type != "V": + raise ValueError("and_v: X should be 'V'") + if self.args[1].type not in "BKV": + raise ValueError("and_v: Y should be B K or V") + + @property + def type(self): + # same as Y + return self.args[1].type + + @property + def properties(self): + # z=zXzY; o=zXoY or zYoX; n=nX or zXnY; u=uY + px, py = [arg.properties for arg in self.args] + props = "" + if "z" in px and "z" in py: + props += "z" + if ("z" in px and "o" in py) or ("z" in py and "o" in px): + props += "o" + if "n" in px or ("z" in px and "n" in py): + props += "n" + if "u" in py: + props += "u" + return props + + +class AndB(Miniscript): + # [X] [Y] BOOLAND + NAME = "and_b" + NARGS = 2 + ARGCLS = Miniscript + TYPE = "B" + + def inner_compile(self): + return self.args[0].compile() + self.args[1].compile() + b"\x9a" + + def __len__(self): + return self.len_args() + 1 + + def verify(self): + # X is B; Y is W + super().verify() + if self.args[0].type != "B": + raise ValueError("and_b: X should be B") + if self.args[1].type != "W": + raise ValueError("and_b: Y should be W") + + @property + def properties(self): + # z=zXzY; o=zXoY or zYoX; n=nX or zXnY; d=dXdY; u + px, py = [arg.properties for arg in self.args] + props = "" + if "z" in px and "z" in py: + props += "z" + if ("z" in px and "o" in py) or ("z" in py and "o" in px): + props += "o" + if "n" in px or ("z" in px and "n" in py): + props += "n" + if "d" in px and "d" in py: + props += "d" + props += "u" + return props + + +class AndN(Miniscript): + # [X] NOTIF 0 ELSE [Y] ENDIF + # andor(X,Y,0) + NAME = "and_n" + NARGS = 2 + ARGCLS = Miniscript + + def inner_compile(self): + return ( + self.args[0].compile() + + b"\x64\x00\x67" + + self.args[1].compile() + + b"\x68" + ) + + def __len__(self): + return self.len_args() + 4 + + @property + def type(self): + # type same as Y/Z + return self.args[1].type + + def verify(self): + # requires: X is Bdu; Y and Z are both B, K, or V + super().verify() + if self.args[0].type != "B": + raise ValueError("and_n: X should be 'B'") + px = self.args[0].properties + if "d" not in px and "u" not in px: + raise ValueError("and_n: X should be 'du'") + if self.args[1].type != "B": + raise ValueError("and_n: Y should be B") + + @property + def properties(self): + # props: z=zXzYzZ; o=zXoYoZ or oXzYzZ; u=uYuZ; d=dZ + props = "" + px, py = [arg.properties for arg in self.args] + pz = "zud" + if "z" in px and "z" in py and "z" in pz: + props += "z" + if ("z" in px and "o" in py and "o" in pz) or ( + "o" in px and "z" in py and "z" in pz + ): + props += "o" + if "u" in py and "u" in pz: + props += "u" + if "d" in pz: + props += "d" + return props + + +class OrB(Miniscript): + # [X] [Z] BOOLOR + NAME = "or_b" + NARGS = 2 + ARGCLS = Miniscript + TYPE = "B" + + def inner_compile(self): + return self.args[0].compile() + self.args[1].compile() + b"\x9b" + + def __len__(self): + return self.len_args() + 1 + + def verify(self): + # X is Bd; Z is Wd + super().verify() + if self.args[0].type != "B": + raise ValueError("or_b: X should be B") + if "d" not in self.args[0].properties: + raise ValueError("or_b: X should be d") + if self.args[1].type != "W": + raise ValueError("or_b: Z should be W") + if "d" not in self.args[1].properties: + raise ValueError("or_b: Z should be d") + + @property + def properties(self): + # z=zXzZ; o=zXoZ or zZoX; d; u + props = "" + px, pz = [arg.properties for arg in self.args] + if "z" in px and "z" in pz: + props += "z" + if ("z" in px and "o" in pz) or ("z" in pz and "o" in px): + props += "o" + props += "du" + return props + + +class OrC(Miniscript): + # [X] NOTIF [Z] ENDIF + NAME = "or_c" + NARGS = 2 + ARGCLS = Miniscript + TYPE = "V" + + def inner_compile(self): + return self.args[0].compile() + b"\x64" + self.args[1].compile() + b"\x68" + + def __len__(self): + return self.len_args() + 2 + + def verify(self): + # X is Bdu; Z is V + super().verify() + if self.args[0].type != "B": + raise ValueError("or_c: X should be B") + if self.args[1].type != "V": + raise ValueError("or_c: Z should be V") + px = self.args[0].properties + if "d" not in px or "u" not in px: + raise ValueError("or_c: X should be du") + + @property + def properties(self): + # z=zXzZ; o=oXzZ + props = "" + px, pz = [arg.properties for arg in self.args] + if "z" in px and "z" in pz: + props += "z" + if "o" in px and "z" in pz: + props += "o" + return props + + +class OrD(Miniscript): + # [X] IFDUP NOTIF [Z] ENDIF + NAME = "or_d" + NARGS = 2 + ARGCLS = Miniscript + TYPE = "B" + + def inner_compile(self): + return self.args[0].compile() + b"\x73\x64" + self.args[1].compile() + b"\x68" + + def __len__(self): + return self.len_args() + 3 + + def verify(self): + # X is Bdu; Z is B + super().verify() + if self.args[0].type != "B": + raise ValueError("or_d: X should be B") + if self.args[1].type != "B": + raise ValueError("or_d: Z should be B") + px = self.args[0].properties + if "d" not in px or "u" not in px: + raise ValueError("or_d: X should be du") + + @property + def properties(self): + # z=zXzZ; o=oXzZ; d=dZ; u=uZ + props = "" + px, pz = [arg.properties for arg in self.args] + if "z" in px and "z" in pz: + props += "z" + if "o" in px and "z" in pz: + props += "o" + if "d" in pz: + props += "d" + if "u" in pz: + props += "u" + return props + + +class OrI(Miniscript): + # IF [X] ELSE [Z] ENDIF + NAME = "or_i" + NARGS = 2 + ARGCLS = Miniscript + + def inner_compile(self): + return ( + b"\x63" + + self.args[0].compile() + + b"\x67" + + self.args[1].compile() + + b"\x68" + ) + + def __len__(self): + return self.len_args() + 3 + + def verify(self): + # both are B, K, or V + super().verify() + if self.args[0].type != self.args[1].type: + raise ValueError("or_i: X and Z should be the same type") + if self.args[0].type not in "BKV": + raise ValueError("or_i: X and Z should be B K or V") + + @property + def type(self): + return self.args[0].type + + @property + def properties(self): + # o=zXzZ; u=uXuZ; d=dX or dZ + props = "" + px, pz = [arg.properties for arg in self.args] + if "z" in px and "z" in pz: + props += "o" + if "u" in px and "u" in pz: + props += "u" + if "d" in px or "d" in pz: + props += "d" + return props + + +class Thresh(Miniscript): + # [X1] [X2] ADD ... [Xn] ADD ... EQUAL + NAME = "thresh" + NARGS = None + ARGCLS = (Number, Miniscript) + TYPE = "B" + + def inner_compile(self): + return ( + self.args[1].compile() + + b"".join([arg.compile()+b"\x93" for arg in self.args[2:]]) + + self.args[0].compile() + + b"\x87" + ) + + def __len__(self): + return self.len_args() + len(self.args) - 1 + + def verify(self): + # 1 <= k <= n; X1 is Bdu; others are Wdu + super().verify() + if self.args[0].num < 1 or self.args[0].num >= len(self.args): + raise ValueError( + "thresh: Invalid k! Should be 1 <= k <= %d, got %d" + % (len(self.args) - 1, self.args[0].num) + ) + if self.args[1].type != "B": + raise ValueError("thresh: X1 should be B") + px = self.args[1].properties + if "d" not in px or "u" not in px: + raise ValueError("thresh: X1 should be du") + for i, arg in enumerate(self.args[2:]): + if arg.type != "W": + raise ValueError("thresh: X%d should be W" % (i + 1)) + p = arg.properties + if "d" not in p or "u" not in p: + raise ValueError("thresh: X%d should be du" % (i + 1)) + + @property + def properties(self): + # z=all are z; o=all are z except one is o; d; u + props = "" + parr = [arg.properties for arg in self.args[1:]] + zarr = ["z" for p in parr if "z" in p] + if len(zarr) == len(parr): + props += "z" + noz = [p for p in parr if "z" not in p] + if len(noz) == 1 and "o" in noz[0]: + props += "o" + props += "du" + return props + + +class Multi(Miniscript): + # ... CHECKMULTISIG + NAME = "multi" + NARGS = None + ARGCLS = (Number, Key) + TYPE = "B" + PROPS = "ndu" + N_MAX = 20 + + def inner_compile(self): + # scr = [arg.compile() for arg in self.args[1:]] + # optimization - it is all keys with known length (xonly keys not allowed here) + scr = [b'\x21' + arg.key_bytes() for arg in self.args[1:]] + if self.NAME == "sortedmulti": + scr.sort() + return ( + self.args[0].compile() + + b"".join(scr) + + Number(len(self.args) - 1).compile() + + b"\xae" + ) + + def __len__(self): + return self.len_args() + 2 + + def m_n(self): + return self.args[0].num, len(self.args[1:]) + + def verify(self): + super().verify() + N = (len(self.args) - 1) + assert N <= self.N_MAX, 'M/N range' + M = self.args[0].num + if M < 1 or M > N: + raise ValueError( + "M must be <= N: 1 <= M <= %d, got %d" % ((len(self.args) - 1), self.args[0].num) + ) + + +class Sortedmulti(Multi): + # ... CHECKMULTISIG + NAME = "sortedmulti" + + +class Multi_a(Multi): + # CHECKSIG CHECKSIGADD ... CHECKSIGADD EQUALVERIFY + NAME = "multi_a" + PROPS = "du" + N_MAX = MAX_TR_SIGNERS + + def inner_compile(self): + from opcodes import OP_CHECKSIGADD, OP_NUMEQUAL, OP_CHECKSIG + script = b"" + # scr = [arg.compile() for arg in self.args[1:]] + # optimization - it is all keys with known length (only xonly keys allowed here) + scr = [b"\x20" + arg.key_bytes() for arg in self.args[1:]] + if self.NAME == "sortedmulti_a": + scr.sort() + + for i, key in enumerate(scr): + script += key + if i == 0: + script += bytes([OP_CHECKSIG]) + else: + script += bytes([OP_CHECKSIGADD]) + + script += self.args[0].compile() # M (threshold) + script += bytes([OP_NUMEQUAL]) + return script + + def __len__(self): + # len(M) + len(k0) ... + len(kN) + len(keys) + 1 + return self.len_args() + len(self.args) + + +class Sortedmulti_a(Multi_a): + # CHECKSIG CHECKSIGADD ... CHECKSIGADD EQUALVERIFY + NAME = "sortedmulti_a" + + +class Pk(OneArg): + # CHECKSIG + NAME = "pk" + ARGCLS = Key + TYPE = "B" + PROPS = "ondu" + + def inner_compile(self): + return self.carg + b"\xac" + + def __len__(self): + return self.len_args() + 1 + + +class Pkh(OneArg): + # DUP HASH160 EQUALVERIFY CHECKSIG + NAME = "pkh" + ARGCLS = KeyHash + TYPE = "B" + PROPS = "ndu" + + def inner_compile(self): + return b"\x76\xa9" + self.carg + b"\x88\xac" + + def __len__(self): + return self.len_args() + 4 + + +OPERATORS = [ + PkK, + PkH, + Older, + After, + Sha256, + Hash256, + Ripemd160, + Hash160, + AndOr, + AndV, + AndB, + AndN, + OrB, + OrC, + OrD, + OrI, + Thresh, + Multi, + Sortedmulti, + Multi_a, + Sortedmulti_a, + Pk, + Pkh, +] +OPERATOR_NAMES = [cls.NAME for cls in OPERATORS] + + +class Wrapper(OneArg): + ARGCLS = Miniscript + + @property + def op(self): + return type(self).__name__.lower() + + def to_string(self, *args, **kwargs): + # more wrappers follow + if isinstance(self.arg, Wrapper): + return self.op + self.arg.to_string(*args, **kwargs) + # we are the last wrapper + return self.op + ":" + self.arg.to_string(*args, **kwargs) + + +class A(Wrapper): + # TOALTSTACK [X] FROMALTSTACK + TYPE = "W" + + def inner_compile(self): + return b"\x6b" + self.carg + b"\x6c" + + def __len__(self): + return len(self.arg) + 2 + + def verify(self): + super().verify() + if self.arg.type != "B": + raise ValueError("a: X should be B") + + @property + def properties(self): + props = "" + px = self.arg.properties + if "d" in px: + props += "d" + if "u" in px: + props += "u" + return props + + +class S(Wrapper): + # SWAP [X] + TYPE = "W" + + def inner_compile(self): + return b"\x7c" + self.carg + + def __len__(self): + return len(self.arg) + 1 + + def verify(self): + super().verify() + if self.arg.type != "B": + raise ValueError("s: X should be B") + if "o" not in self.arg.properties: + raise ValueError("s: X should be o") + + @property + def properties(self): + props = "" + px = self.arg.properties + if "d" in px: + props += "d" + if "u" in px: + props += "u" + return props + + +class C(Wrapper): + # [X] CHECKSIG + TYPE = "B" + + def inner_compile(self): + return self.carg + b"\xac" + + def __len__(self): + return len(self.arg) + 1 + + def verify(self): + super().verify() + if self.arg.type != "K": + raise ValueError("c: X should be K") + + @property + def properties(self): + props = "" + px = self.arg.properties + for p in ["o", "n", "d"]: + if p in px: + props += p + props += "u" + return props + + +class T(Wrapper): + # [X] 1 + TYPE = "B" + + def inner_compile(self): + return self.carg + b"\x51" # Number(1).compile() --> b"\x51" + + def __len__(self): + return len(self.arg) + 1 + + def verify(self): + super().verify() + if self.arg.type != "V": + raise ValueError("t: X must be of type V") + + @property + def properties(self): + # z=zXzY; o=zXoY or zYoX; n=nX or zXnY; u=uY + px = self.arg.properties + py = "zu" + props = "" + if "z" in px and "z" in py: + props += "z" + if ("z" in px and "o" in py) or ("z" in py and "o" in px): + props += "o" + if "n" in px or ("z" in px and "n" in py): + props += "n" + if "u" in py: + props += "u" + return props + + +class D(Wrapper): + # DUP IF [X] ENDIF + TYPE = "B" + + def inner_compile(self): + return b"\x76\x63" + self.carg + b"\x68" + + def __len__(self): + return len(self.arg) + 3 + + def verify(self): + super().verify() + if self.arg.type != "V": + raise ValueError("d: X should be V") + if "z" not in self.arg.properties: + raise ValueError("d: X should be z") + + @property + def properties(self): + # https://github.com/bitcoin/bitcoin/pull/24906 + if self.taproot: + props = "ndu" + else: + props = "nd" + px = self.arg.properties + if "z" in px: + props += "o" + return props + + +class V(Wrapper): + # [X] VERIFY (or VERIFY version of last opcode in [X]) + TYPE = "V" + + def inner_compile(self): + """Checks last check code and makes it verify""" + if self.carg[-1] in [0xAC, 0xAE, 0x9C, 0x87]: + return self.carg[:-1] + bytes([self.carg[-1] + 1]) + return self.carg + b"\x69" + + def verify(self): + super().verify() + if self.arg.type != "B": + raise ValueError("v: X should be B") + + @property + def properties(self): + props = "" + px = self.arg.properties + for p in ["z", "o", "n"]: + if p in px: + props += p + return props + + +class J(Wrapper): + # SIZE 0NOTEQUAL IF [X] ENDIF + TYPE = "B" + + def inner_compile(self): + return b"\x82\x92\x63" + self.carg + b"\x68" + + def verify(self): + super().verify() + if self.arg.type != "B": + raise ValueError("j: X should be B") + if "n" not in self.arg.properties: + raise ValueError("j: X should be n") + + @property + def properties(self): + props = "nd" + px = self.arg.properties + for p in ["o", "u"]: + if p in px: + props += p + return props + + +class N(Wrapper): + # [X] 0NOTEQUAL + TYPE = "B" + + def inner_compile(self): + return self.carg + b"\x92" + + def __len__(self): + return len(self.arg) + 1 + + def verify(self): + super().verify() + if self.arg.type != "B": + raise ValueError("n: X should be B") + + @property + def properties(self): + props = "u" + px = self.arg.properties + for p in ["z", "o", "n", "d"]: + if p in px: + props += p + return props + + +class L(Wrapper): + # IF 0 ELSE [X] ENDIF + TYPE = "B" + + def inner_compile(self): + return b"\x63\x00\x67" + self.carg + b"\x68" + + def __len__(self): + return len(self.arg) + 4 + + def verify(self): + # both are B, K, or V + super().verify() + if self.arg.type != "B": + raise ValueError("or_i: X and Z should be the same type") + + @property + def properties(self): + # o=zXzZ; u=uXuZ; d=dX or dZ + props = "d" + pz = self.arg.properties + if "z" in pz: + props += "o" + if "u" in pz: + props += "u" + return props + + +class U(L): + # IF [X] ELSE 0 ENDIF + def inner_compile(self): + return b"\x63" + self.carg + b"\x67\x00\x68" + + def __len__(self): + return len(self.arg) + 4 + + +WRAPPERS = [A, S, C, T, D, V, J, N, L, U] +WRAPPER_NAMES = [w.__name__.lower() for w in WRAPPERS] \ No newline at end of file diff --git a/shared/mk4.py b/shared/mk4.py index 6bffbd18e..69b024ced 100644 --- a/shared/mk4.py +++ b/shared/mk4.py @@ -11,8 +11,8 @@ def make_flash_fs(): os.VfsLfs2.mkfs(fl) os.mount(fl, '/flash') - - os.mkdir('/flash/settings') + os.chdir('/flash') + os.mkdir('settings') def make_psram_fs(): # Filesystem is wiped and rebuilt on each boot before this point, but @@ -58,8 +58,7 @@ def init0(): try: make_psram_fs() - except BaseException as exc: - sys.print_exception(exc) + except: pass if version.is_devmode: try: @@ -71,10 +70,13 @@ def init0(): rng_seeding() async def dev_enable_repl(*a): - # Mk4: Enable serial port connection. You'll have to break case open. + # Enable serial port connection. You'll have to break case open. + from ux import ux_show_story + from utils import wipe_if_deltamode wipe_if_deltamode() + if not version.is_devmode: return # allow REPL access ckcc.vcp_enabled(True) @@ -83,15 +85,4 @@ async def dev_enable_repl(*a): await ux_show_story("""\ The serial port has now been enabled.\n\n3.3v TTL on Tx/Rx/Gnd pads @ 115,200 bps.""") -def wipe_if_deltamode(): - # If in deltamode, give up and wipe self rather do - # a thing that might reveal true master secret... - - from pincodes import pa - - if not pa.is_deltamode(): - return - - callgate.fast_wipe() - # EOF diff --git a/shared/msgsign.py b/shared/msgsign.py new file mode 100644 index 000000000..144dc6516 --- /dev/null +++ b/shared/msgsign.py @@ -0,0 +1,512 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Signatures over text ... not transactions. +# +import stash, chains, sys, gc, ngu, ujson, version +from ubinascii import b2a_base64, a2b_base64 +from ubinascii import hexlify as b2a_hex +from ubinascii import unhexlify as a2b_hex +from uhashlib import sha256 +from public_constants import MSG_SIGNING_MAX_LENGTH +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH +from charcodes import KEY_QR, KEY_NFC, KEY_CANCEL +from ux import (ux_show_story, OK, ux_enter_bip32_index, ux_input_text, the_ux, + import_export_prompt, ux_aborted) +from utils import problem_file_line, to_ascii_printable, show_single_address +from files import CardSlot, CardMissingError, needs_microsd + +def rfc_signature_template(msg, addr, sig): + # RFC2440 style signatures, popular + # since the genesis block, but not really part of any BIP as far as I know. + # + return [ + "-----BEGIN BITCOIN SIGNED MESSAGE-----\n", + "%s\n" % msg, + "-----BEGIN BITCOIN SIGNATURE-----\n", + "%s\n" % addr, + "%s\n" % sig, + "-----END BITCOIN SIGNATURE-----\n" + ] + +def parse_armored_signature_file(contents): + # XXX limited parser: will fail w/ messages containing dashes + sep = "-----" + assert contents.count(sep) == 6, "Armor text MUST be surrounded by exactly five (5) dashes." + + temp = contents.split(sep) + msg = temp[2].strip() + addr_sig = temp[4].strip() + addr, sig_str = addr_sig.split() + + return msg, addr, sig_str + +def verify_signature(msg, addr, sig_str): + # Look at a base64 signature, and given address. Do full verification. + # - raise on errors + # - return warnings as string: can only be mismatch between addr format encoded in recid + warnings = "" + script = None + hash160 = None + invalid_addr_fmt_msg = "Invalid address format - must be one of p2pkh, p2sh-p2wpkh, or p2wpkh." + invalid_addr = "Invalid signature for message." + + if addr[0] in "1mn": + addr_fmt = AF_CLASSIC + decoded_addr = ngu.codecs.b58_decode(addr) + hash160 = decoded_addr[1:] # remove prefix + elif addr.startswith("bc1q") or addr.startswith("tb1q") or addr.startswith("bcrt1q"): + if len(addr) > 44: # testnet/mainnet max singlesig len 42, regtest 44 + # p2wsh + raise ValueError(invalid_addr_fmt_msg) + addr_fmt = AF_P2WPKH + _, _, hash160 = ngu.codecs.segwit_decode(addr) + elif addr[0] in "32": + addr_fmt = AF_P2WPKH_P2SH + decoded_addr = ngu.codecs.b58_decode(addr) + script = decoded_addr[1:] # remove prefix + else: + raise ValueError(invalid_addr_fmt_msg) + + try: + sig_bytes = a2b_base64(sig_str) + if not sig_bytes or len(sig_bytes) != 65: + # can return b'' in case of wrong, can also raise + raise ValueError("invalid encoding") + + header_byte = sig_bytes[0] + header_base = chains.current_chain().sig_hdr_base(addr_fmt) + if (header_byte - header_base) not in (0, 1, 2, 3): + # wrong header value only - this can still verify OK + warnings += "Specified address format does not match signature header byte format." + + # least two significant bits + rec_id = (header_byte - 27) & 0x03 + # need to normalize it to 31 base for ngu + new_header_byte = 31 + rec_id + sig = ngu.secp256k1.signature(bytes([new_header_byte]) + sig_bytes[1:]) + except ValueError as e: + raise ValueError("Parsing signature failed - %s." % str(e)) + + digest = chains.current_chain().hash_message(msg.encode('ascii')) + try: + rec_pubkey = sig.verify_recover(digest) + except ValueError as e: + raise ValueError("Invalid signature for msg - %s." % str(e)) + + rec_pubkey_bytes = rec_pubkey.to_bytes() + rec_hash160 = ngu.hash.hash160(rec_pubkey_bytes) + + if script: + target = bytes([0, 20]) + rec_hash160 + target = ngu.hash.hash160(target) + if target != script: + raise ValueError(invalid_addr) + else: + if rec_hash160 != hash160: + raise ValueError(invalid_addr) + + return warnings + +async def verify_armored_signed_msg(contents, digest_check=True): + # Verify on-disk checksums of files listed inside a signed file. + # - digest_check=False for NFC cases, where we do not have filesystem + from glob import dis + + dis.fullscreen("Verifying...") + + try: + msg, addr, sig_str = parse_armored_signature_file(contents) + except Exception as e: + e_line = problem_file_line(e) + await ux_show_story("Malformed signature file. %s %s" % (str(e), e_line), title="FAILURE") + return + + try: + sig_warn = verify_signature(msg, addr, sig_str) + except Exception as e: + await ux_show_story(str(e), title="ERROR") + return + + title = "CORRECT" + warn_msg = "" + err_msg = "" + story = "Good signature by address:\n%s" % show_single_address(addr) + + if digest_check: + digest_prob = verify_signed_file_digest(msg) + if digest_prob: + err, digest_warn = digest_prob + if digest_warn: + title = "WARNING" + wmsg_base = "not present. Contents verification not possible." + if len(digest_warn) == 1: + fname = digest_warn[0][0] + warn_msg += "'%s' is %s" % (fname, wmsg_base) + else: + warn_msg += "Files:\n" + "\n".join("> %s" % fname for fname, _ in digest_warn) + warn_msg += "\nare %s" % wmsg_base + + if err: + title = "ERROR" + for fname, calc, got in err: + err_msg += ("Referenced file '%s' has wrong contents.\n" + "Got:\n%s\n\nExpected:\n%s" % (fname, got, calc)) + + if sig_warn: + # we know not ours only because wrong recid header used & not BIP-137 compliant + story = "Correctly signed, but not by this Coldcard. %s" % sig_warn + + await ux_show_story('\n\n'.join(m for m in [err_msg, story, warn_msg] if m), title=title) + +async def verify_txt_sig_file(filename): + # copy message into memory + try: + with CardSlot() as card: + with card.open(filename, 'rt') as fd: + text = fd.read() + except CardMissingError: + await needs_microsd() + return + except Exception as e: + await ux_show_story('Error: ' + str(e)) + return + + await verify_armored_signed_msg(text) + +async def msg_sign_ux_get_subpath(addr_fmt): + # Ask for account number, and maybe change component of path for signature. + # - return full derivation path to be used. + purpose = chains.af_to_bip44_purpose(addr_fmt) + chain_n = chains.current_chain().b44_cointype + + acct = await ux_enter_bip32_index('Account Number:') or 0 + + ch = await ux_show_story(title="Change?", + msg="Press (0) to use internal/change address," + " %s to use external/receive address." % OK, escape="0") + change = 1 if ch == '0' else 0 + + idx = await ux_enter_bip32_index('Index Number:') or 0 + + return "m/%dh/%dh/%dh/%d/%d" % (purpose, chain_n, acct, change, idx) + + +def sign_export_contents(content_list, deriv, addr_fmt, pk=None): + # Return signed message over hashes of files. + msg2sign = make_signature_file_msg(content_list) + bitcoin_digest = chains.current_chain().hash_message(msg2sign) + sig_bytes, addr = sign_message_digest(bitcoin_digest, deriv, "Signing...", addr_fmt, pk=pk) + sig = b2a_base64(sig_bytes).decode().strip() + + return rfc_signature_template(addr=addr, msg=msg2sign.decode(), sig=sig) + +def verify_signed_file_digest(msg): + # Look inside a list of hashs and file names, and + # verify at their actual hashes and return list of issues if any. + parsed_msg = parse_signature_file_msg(msg) + if not parsed_msg: + # not our format + return + + try: + err, warn = [], [] + with CardSlot() as card: + for digest, fname in parsed_msg: + path = card.abs_path(fname) + if not card.exists(path): + warn.append((fname, None)) + continue + path = card.abs_path(fname) + + md = sha256() + with open(path, "rb") as f: + while True: + chunk = f.read(1024) + if not chunk: + break + md.update(chunk) + + h = b2a_hex(md.digest()).decode().strip() + if h != digest: + err.append((fname, h, digest)) + except: + # fail silently if issues with reading files or SD issues + # no digest checking + return + + return err, warn + +def write_sig_file(content_list, derive=None, addr_fmt=AF_CLASSIC, pk=None, sig_name=None): + if derive is None: + ct = chains.current_chain().b44_cointype + derive = "m/44'/%d'/0'/0/0" % ct + + fpath = content_list[0][1] + if len(content_list) > 1: + # we're signing contents of more files - need generic name for sig file + assert sig_name + sig_nice = sig_name + ".sig" + sig_fpath = fpath.rsplit("/", 1)[0] + "/" + sig_nice + else: + sig_fpath = fpath.rsplit(".", 1)[0] + ".sig" + sig_nice = sig_fpath.split("/")[-1] + + sig_gen = sign_export_contents([(h, f.split("/")[-1]) for h, f in content_list], + derive, addr_fmt, pk=pk) + + with open(sig_fpath, 'wt') as fd: + for i, part in enumerate(sig_gen): + fd.write(part) + + return sig_nice + +def validate_text_for_signing(text, only_printable=True): + # Check for some UX/UI traps in the message itself. + # - messages must be short and ascii only. Our charset is limited + # - too many spaces, leading/trailing can be an issue + # MSG_MAX_SPACES = 4 # impt. compared to -=- positioning + + result = to_ascii_printable(text, only_printable=only_printable) + + length = len(result) + assert length >= 2, "msg too short (min. 2)" + assert length <= MSG_SIGNING_MAX_LENGTH, "msg too long (max. %d)" % MSG_SIGNING_MAX_LENGTH + assert " " not in result, 'too many spaces together in msg(max. 3)' + # other confusion w/ whitepace + assert result[0] != ' ', 'leading space(s) in msg' + assert result[-1] != ' ', 'trailing space(s) in msg' + + # looks ok + return result + +def addr_fmt_from_subpath(subpath): + if not subpath: + af = "p2pkh" + elif subpath[:4] == "m/84": + af = "p2wpkh" + elif subpath[:4] == "m/49": + af = "p2sh-p2wpkh" + else: + af = "p2pkh" + return af + +def parse_msg_sign_request(data): + subpath = "" + addr_fmt = None + is_json = False + + # sparrow compat + if "signmessage" in data: + try: + mark, subpath, *msg_line = data.split(" ", 2) + assert mark == "signmessage" + # subpath will be verified & cleaned later + assert msg_line[0][:6] == "ascii:" + text = msg_line[0][6:] + return text, subpath, addr_fmt_from_subpath(subpath), is_json + except:pass + # === + + try: + data_dict = ujson.loads(data.strip()) + text = data_dict.get("msg", None) + if text is None: + raise AssertionError("MSG required") + subpath = data_dict.get("subpath", subpath) + addr_fmt = data_dict.get("addr_fmt", addr_fmt) + is_json = True + except ValueError: + lines = data.split("\n") + assert lines, "min 1 line" + assert len(lines) <= 3, "max 3 lines" + + if len(lines) == 1: + text = lines[0] + elif len(lines) == 2: + text, subpath = lines + else: + text, subpath, addr_fmt = lines + + if not addr_fmt: + addr_fmt = addr_fmt_from_subpath(subpath) + + if not subpath: + subpath = chains.STD_DERIVATIONS[addr_fmt] + subpath = subpath.format( + coin_type=chains.current_chain().b44_cointype, + account=0, change=0, idx=0 + ) + + return text, subpath, addr_fmt, is_json + + +def make_signature_file_msg(content_list): + # list of tuples consisting of (hash, file_name) + return b"\n".join([ + b2a_hex(h) + b" " + fname.encode() + for h, fname in content_list + ]) + +def parse_signature_file_msg(msg): + # only succeed for our format digest + 2 spaces + fname + try: + res = [] + lines = msg.split('\n') + for ln in lines: + d, fn = ln.split(' ') + # should not need to strip if our file format, so dont + # is hex? is 32 bytes long? + assert len(a2b_hex(d)) == 32 + res.append((d, fn)) + + return res + except: + return + +def sign_message_digest(digest, subpath, prompt, addr_fmt=AF_CLASSIC, pk=None): + # do the signature itself! + from glob import dis + + ch = chains.current_chain() + + if prompt: + dis.fullscreen(prompt, percent=.25) + + if pk is None: + with stash.SensitiveValues() as sv: + node = sv.derive_path(subpath) + dis.progress_sofar(50, 100) + pk = node.privkey() + addr = ch.address(node, addr_fmt) + else: + # if private key is provided, derivation subpath is ignored + # and given private key is used for signing. + node = ngu.hdnode.HDNode().from_chaincode_privkey(bytes(32), pk) + dis.progress_sofar(50, 100) + addr = ch.address(node, addr_fmt) + + dis.progress_sofar(75, 100) + + rv = ngu.secp256k1.sign(pk, digest, 0).to_bytes() + + # AF_CLASSIC header byte base 31 is returned by default from ngu - NOOP + if addr_fmt != AF_CLASSIC: + # ngu only produces header base for compressed p2pkh, anyways get only rec_id + rv = bytearray(rv) + rec_id = (rv[0] - 27) & 0x03 + rv[0] = rec_id + ch.sig_hdr_base(addr_fmt=addr_fmt) + + dis.progress_bar_show(1) + + return rv, addr + +async def ux_sign_msg(txt, approved_cb=None, kill_menu=True): + from menu import MenuSystem, MenuItem + + async def done(_1, _2, item): + from auth import approve_msg_sign + + text, af = item.arg + subpath = await msg_sign_ux_get_subpath(af) + + await approve_msg_sign(text, subpath, af, approved_cb=approved_cb, + kill_menu=kill_menu, only_printable=False) + + # pick address format + rv = [ + MenuItem(chains.addr_fmt_label(af), f=done, arg=(txt, af)) + for af in chains.SINGLESIG_AF + ] + the_ux.push(MenuSystem(rv)) + +async def msg_signing_done(signature, address, text): + ch = await import_export_prompt("Signed Msg") + if ch == KEY_CANCEL: + return + + if isinstance(ch, dict): + await sd_sign_msg_done(signature, address, text, "msg_sign", **ch) + elif version.has_qr and ch == KEY_QR: + from ux_q1 import qr_msg_sign_done + await qr_msg_sign_done(signature, address, text) + elif ch in KEY_NFC+"3": + from glob import NFC + if NFC: + await NFC.msg_sign_done(signature, address, text) + + +async def sign_with_own_address(subpath, addr_fmt): + # used for cases where we already have the key picked, but need the message: + # * address_explorer custom path + # * positive ownership test + + to_sign = await ux_input_text("", scan_ok=True, prompt="Enter MSG") # max len is 100 only here + if not to_sign: return + + from auth import approve_msg_sign + await approve_msg_sign(to_sign, subpath, addr_fmt, approved_cb=msg_signing_done, kill_menu=True) + +async def sd_sign_msg_done(signature, address, text, base=None, orig_path=None, + slot_b=None, force_vdisk=False): + from glob import dis + dis.fullscreen('Generating...') + + out_fn = None + sig = b2a_base64(signature).decode('ascii').strip() + + while 1: + # try to put back into same spot + # add -signed to end. + target_fname = base + '-signed.txt' + lst = [orig_path] + if orig_path: + lst.append(None) + + for path in lst: + try: + with CardSlot(readonly=True, slot_b=slot_b, force_vdisk=force_vdisk) as card: + out_full, out_fn = card.pick_filename(target_fname, path) + out_path = path + if out_full: break + except CardMissingError: + prob = 'Missing card.\n\n' + out_fn = None + + if not out_fn: + # need them to insert a card + prob = '' + else: + # attempt write-out + try: + dis.fullscreen("Saving...") + + with CardSlot(slot_b=slot_b, force_vdisk=force_vdisk) as card: + with card.open(out_full, 'wt') as fd: + # save in full RFC style + # gen length is 6 + gen = rfc_signature_template(addr=address, msg=text, sig=sig) + for i, part in enumerate(gen): + fd.write(part) + + # success and done! + break + + except OSError as exc: + prob = 'Failed to write!\n\n%s\n\n' % exc + # sys.print_exception(exc) + # fall through to try again + + # prompt them to input another card? + ch = await ux_show_story(prob + "Please insert an SDCard to receive signed message, " + "and press %s." % OK, title="Need Card") + if ch == 'x': + await ux_aborted() + return + + # done. + msg = "Created new file:\n\n%s" % out_fn + await ux_show_story(msg, title='File Signed') + + + +# EOF diff --git a/shared/multisig.py b/shared/multisig.py index f0bb1620d..2aa63f2ec 100644 --- a/shared/multisig.py +++ b/shared/multisig.py @@ -1,1628 +1,62 @@ # (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -# multisig.py - support code for multisig signing and p2sh in general. +# multisig.py - ms coordinator code mostly + some utils # import stash, chains, ustruct, ure, uio, sys, ngu, uos, ujson, version -from utils import xfp2str, str2xfp, swab32, cleanup_deriv_path, keypath_to_str, to_ascii_printable -from utils import str_to_keypath, problem_file_line, parse_extended_key, get_filesize -from ux import ux_show_story, ux_confirm, ux_dramatic_pause, ux_clear_keys -from ux import import_export_prompt, ux_enter_bip32_index, show_qr_code, ux_enter_number, OK, X +from public_constants import AF_P2WSH, AF_P2WSH_P2SH +from ubinascii import hexlify as b2a_hex +from utils import xfp2str, problem_file_line, get_filesize from files import CardSlot, CardMissingError, needs_microsd -from descriptor import MultisigDescriptor, multisig_descriptor_template -from public_constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT, MAX_SIGNERS -from menu import MenuSystem, MenuItem, NonDefaultMenuItem -from opcodes import OP_CHECKMULTISIG -from exceptions import FatalPSBTIssue +from ux import ux_show_story, ux_dramatic_pause, ux_enter_number, ux_enter_bip32_index +from public_constants import MAX_SIGNERS from glob import settings -from charcodes import KEY_NFC, KEY_CANCEL, KEY_QR -from wallet import WalletABC, MAX_BIP32_IDX +from charcodes import KEY_QR +from desc_utils import Key, KeyOriginInfo -# PSBT Xpub trust policies -TRUST_VERIFY = const(0) -TRUST_OFFER = const(1) -TRUST_PSBT = const(2) - -class MultisigOutOfSpace(RuntimeError): - pass - -def disassemble_multisig_mn(redeem_script): - # pull out just M and N from script. Simple, faster, no memory. - - assert MAX_SIGNERS == 15 - assert redeem_script[-1] == OP_CHECKMULTISIG, 'need CHECKMULTISIG' - - M = redeem_script[0] - 80 - N = redeem_script[-2] - 80 - - return M, N - -def disassemble_multisig(redeem_script): - # Take apart a standard multisig's redeem/witness script, and return M/N and public keys - # - only for multisig scripts, not general purpose - # - expect OP_1 (pk1) (pk2) (pk3) OP_3 OP_CHECKMULTISIG for 1 of 3 case - # - returns M, N, (list of pubkeys) - # - for very unlikely/impossible asserts, dont document reason; otherwise do. - from serializations import disassemble - - M, N = disassemble_multisig_mn(redeem_script) - assert 1 <= M <= N <= MAX_SIGNERS, 'M/N range' - assert len(redeem_script) == 1 + (N * 34) + 1 + 1, 'bad len' - - # generator function - dis = disassemble(redeem_script) - - # expect M value first - ex_M, opcode = next(dis) - assert ex_M == M and opcode == None, 'bad M' - - # need N pubkeys - pubkeys = [] - for idx in range(N): - data, opcode = next(dis) - assert opcode == None and len(data) == 33, 'data' - assert data[0] == 0x02 or data[0] == 0x03, 'Y val' - pubkeys.append(data) - - assert len(pubkeys) == N - - # next is N value - ex_N, opcode = next(dis) - assert ex_N == N and opcode == None - - # finally, the opcode: CHECKMULTISIG - data, opcode = next(dis) - assert opcode == OP_CHECKMULTISIG - - # must have reached end of script at this point - try: - next(dis) - raise AssertionError("too long") - except StopIteration: - # expected, since we're reading past end - pass - - return M, N, pubkeys - -def make_redeem_script(M, nodes, subkey_idx, bip67=True): - # take a list of BIP-32 nodes, and derive Nth subkey (subkey_idx) and make - # a standard M-of-N redeem script for that. Applies BIP-67 sorting by default. - N = len(nodes) - assert 1 <= M <= N <= MAX_SIGNERS - - pubkeys = [] - for n in nodes: - copy = n.copy() - copy.derive(subkey_idx, False) - # 0x21 = 33 = len(pubkey) = OP_PUSHDATA(33) - pubkeys.append(b'\x21' + copy.pubkey()) - del copy - - if bip67: - pubkeys.sort() - - # serialize redeem script - pubkeys.insert(0, bytes([80 + M])) - pubkeys.append(bytes([80 + N, OP_CHECKMULTISIG])) - - return b''.join(pubkeys) - -class MultisigWallet(WalletABC): - # Capture the info we need to store long-term in order to participate in a - # multisig wallet as a co-signer. - # - can be saved to nvram - # - can be imported from a simple text file - # - can be displayed to user in a menu (and deleted) - # - required during signing to verify change outputs - # - can reconstruct any redeem script from this - # Challenges: - # - can be big, taking big % of 4k storage in nvram - # - complex object, want to have flexibility going forward - FORMAT_NAMES = [ - (AF_P2SH, 'p2sh'), - (AF_P2WSH, 'p2wsh'), - (AF_P2WSH_P2SH, 'p2sh-p2wsh'), # preferred - (AF_P2WSH_P2SH, 'p2wsh-p2sh'), # obsolete (now an alias) - ] - - # optional: user can short-circuit many checks (system wide, one power-cycle only) - disable_checks = False - - def __init__(self, name, m_of_n, xpubs, addr_fmt=AF_P2SH, chain_type='BTC', bip67=True): - self.storage_idx = -1 - - self.name = name - assert len(m_of_n) == 2 - self.M, self.N = m_of_n - self.chain_type = chain_type or 'BTC' - assert len(xpubs[0]) == 3 - self.xpubs = xpubs # list of (xfp(int), deriv, xpub(str)) - self.addr_fmt = addr_fmt # address format for wallet - self.bip67 = bip67 - - # calc useful cache value: numeric xfp+subpath, with lookup - self.xfp_paths = {} - for xfp, deriv, xpub in self.xpubs: - self.xfp_paths[xfp] = str_to_keypath(xfp, deriv) - - assert len(self.xfp_paths) == self.N, 'dup XFP' # not supported - - @classmethod - def render_addr_fmt(cls, addr_fmt): - for k, v in cls.FORMAT_NAMES: - if k == addr_fmt: - return v.upper() - return '?' - - def render_path(self, change_idx, idx): - # assuming shared derivations for all cosigners. Wrongish. - derivs, _ = self.get_deriv_paths() - if len(derivs) > 1: - deriv = '(various)' - else: - deriv = derivs[0] - return deriv + '/%d/%d' % (change_idx, idx) - - @property - def chain(self): - return chains.get_chain(self.chain_type) - - @classmethod - def get_trust_policy(cls): - - which = settings.get('pms', None) - - if which is None: - which = TRUST_VERIFY if cls.exists() else TRUST_OFFER - - return which - - def serialize(self): - # return a JSON-able object - - opts = dict() - if self.addr_fmt != AF_P2SH: - opts['ft'] = self.addr_fmt - if self.chain_type != 'BTC': - opts['ch'] = self.chain_type - - # Data compression: most legs will all use same derivation. - # put a int(0) in place and set option 'pp' to be derivation - # (used to be common_prefix assumption) - pp = list(sorted(set(d for _,d,_ in self.xpubs))) - if len(pp) == 1: - # generate old-format data, to preserve firmware downgrade path - xp = [(a, c) for a,deriv,c in self.xpubs] - opts['pp'] = pp[0] - else: - # allow for distinct deriv paths on each leg - opts['d'] = pp - xp = [(a, pp.index(deriv),c) for a,deriv,c in self.xpubs] - - # make list already, will become one after json ser/deser - res = [self.name, (self.M, self.N), xp, opts] - if not self.bip67: - # wallets that do not follow BIP-67 are backwards incompatible - res.append(0) - - return res - - @classmethod - def deserialize(cls, vals, idx=-1): - # take json object, make instance. - bip67 = 1 # default enabled, requires 5-element serialization to disable - if len(vals) == 5: - bip67 = vals[-1] - vals = vals[:-1] - - name, m_of_n, xpubs, opts = vals - - if len(xpubs[0]) == 2: - # promote from old format to new: assume common prefix is the derivation - # for all of them - # PROBLEM: we don't have enough info if no common prefix can be assumed - common_prefix = opts.get('pp', None) - if not common_prefix: - # TODO: this should raise a warning, not supported anymore - common_prefix = 'm' - common_prefix = common_prefix.replace("'", "h") - xpubs = [(a, common_prefix, b) for a,b in xpubs] - else: - # new format decompression - if 'd' in opts: - derivs = [p.replace("'", "h") for p in opts.get('d')] - xpubs = [(a, derivs[b], c) for a,b,c in xpubs] - - rv = cls(name, m_of_n, xpubs, addr_fmt=opts.get('ft', AF_P2SH), - chain_type=opts.get('ch', 'BTC'), bip67=bool(bip67)) - rv.storage_idx = idx - return rv - - @classmethod - def iter_wallets(cls, M=None, N=None, not_idx=None, addr_fmt=None): - # yield MS wallets we know about, that match at least right M,N if known. - # - this is only place we should be searching this list, please!! - lst = settings.get('multisig', []) - - for idx, rec in enumerate(lst): - if idx == not_idx: - # ignore one by index - continue - - if M or N: - # peek at M/N - has_m, has_n = tuple(rec[1]) - if M is not None and has_m != M: continue - if N is not None and has_n != N: continue - - if addr_fmt is not None: - opts = rec[3] - af = opts.get('ft', AF_P2SH) - if af != addr_fmt: continue - - yield cls.deserialize(rec, idx) - - def get_xfp_paths(self): - # return list of lists [xfp, *deriv] - return list(self.xfp_paths.values()) - - @classmethod - def find_match(cls, M, N, xfp_paths, addr_fmt=None): - # Find index of matching wallet - # - xfp_paths is list of lists: [xfp, *path] like in psbt files - # - M and N must be known - # - returns instance, or None if not found - for rv in cls.iter_wallets(M, N, addr_fmt=addr_fmt): - if rv.matching_subpaths(xfp_paths): - return rv - - return None - - @classmethod - def find_candidates(cls, xfp_paths, addr_fmt=None, M=None): - # Return a list of matching wallets for various M values. - # - xpfs_paths should already be sorted - # - returns set of matches, of any M value - - # we know N, but not M at this point. - N = len(xfp_paths) - - matches = [] - for rv in cls.iter_wallets(M=M, addr_fmt=addr_fmt): - if rv.matching_subpaths(xfp_paths): - matches.append(rv) - - return matches - - def matching_subpaths(self, xfp_paths): - # Does this wallet use same set of xfp values, and - # the same prefix path per-each xfp, as indicated - # xfp_paths (unordered)? - # - could also check non-prefix part is all non-hardened - if len(xfp_paths) != len(self.xfp_paths): - # cannot be the same if len(w0.N) != len(w1.N) - # maybe check duplicates first? - return False - for x in xfp_paths: - if x[0] not in self.xfp_paths: - return False - prefix = self.xfp_paths[x[0]] - - if len(x) < len(prefix): - # PSBT specs a path shorter than wallet's xpub - #print('path len: %d vs %d' % (len(prefix), len(x))) - return False - - comm = len(prefix) - if tuple(prefix[:comm]) != tuple(x[:comm]): - # xfp => maps to wrong path - #print('path mismatch:\n%r\n%r\ncomm=%d' % (prefix[:comm], x[:comm], comm)) - return False - - return True - - def assert_matching(self, M, N, xfp_paths): - # compare in-memory wallet with details recovered from PSBT - # - xfp_paths must be sorted already - assert (self.M, self.N) == (M, N), "M/N mismatch" - assert len(xfp_paths) == N, "XFP count" - if self.disable_checks: return - assert self.matching_subpaths(xfp_paths), "wrong XFP/derivs" - - @classmethod - def quick_check(cls, M, N, xfp_xor): - # quicker? USB method. - rv = [] - for ms in cls.iter_wallets(M, N): - x = 0 - for xfp in ms.xfp_paths.keys(): - x ^= xfp - if x != xfp_xor: continue - - return True - - return False - - @classmethod - def get_all(cls): - # return them all, as a generator - return cls.iter_wallets() - - @classmethod - def exists(cls): - # are there any wallets defined? - return bool(settings.get('multisig', False)) - - @classmethod - def get_by_idx(cls, nth): - # instance from index number (used in menu) - lst = settings.get('multisig', []) - try: - obj = lst[nth] - except IndexError: - return None - - return cls.deserialize(obj, nth) - - def commit(self): - # data to save - # - important that this fails immediately when nvram overflows - obj = self.serialize() - - v = settings.get('multisig', []) - orig = v.copy() - if not v or self.storage_idx == -1: - # create - self.storage_idx = len(v) - v.append(obj) - else: - # update in place - v[self.storage_idx] = obj - - settings.set('multisig', v) - - # save now, rather than in background, so we can recover - # from out-of-space situation - try: - settings.save() - except: - # back out change; no longer sure of NVRAM state - try: - settings.set('multisig', orig) - settings.save() - except: pass # give up on recovery - - raise MultisigOutOfSpace - - def has_similar(self): - # check if we already have a saved duplicate to this proposed wallet - # - return (name_change, diff_items, count_similar) where: - # - name_change is existing wallet that has exact match, different name - # - diff_items: text list of similarity/differences - # - count_similar: same N, same xfp+paths - - lst = self.get_xfp_paths() - c = self.find_match(self.M, self.N, lst, addr_fmt=self.addr_fmt) - if c: - # All details are same: M/N, paths, addr fmt - if sorted(self.xpubs) != sorted(c.xpubs): - # this also applies to non-BIP-67 type multisig wallets - # multi(2,A,B) is treated as duplicate of multi(2,B,A) - # consensus-wise they are different script/wallet but CC - # don't allow to import one if other already imported - return None, ['xpubs'], 0 - elif self.bip67 != c.bip67: - # treat same keys inside different desc multi/sortedmulti as duplicates - # sortedmulti(2,A,B) is considered same as multi(2,A,B) or multi(2,B,A) - # do not allow to import multi if sortedmulti with the same set of keys - # already imported and vice-versa - return None, ["BIP-67 clash"], 1 - elif self.name == c.name: - return None, [], 1 - else: - return c, ['name'], 0 - - similar = MultisigWallet.find_candidates(lst) - if not similar: - # no matches, good. - return None, [], 0 - - # See if the xpubs are changing, which is risky... other differences like - # name are okay. - diffs = set() - for c in similar: - if c.M != self.M: - diffs.add('M differs') - if c.addr_fmt != self.addr_fmt: - diffs.add('address type') - if c.name != self.name: - diffs.add('name') - if c.xpubs != self.xpubs: - diffs.add('xpubs') - - return None, diffs, len(similar) - - def delete(self): - # remove saved entry - # - important: not expecting more than one instance of this class in memory - assert self.storage_idx >= 0 - - # safety check - for existing in self.iter_wallets(M=self.M, N=self.N, addr_fmt=self.addr_fmt): - if existing.storage_idx != self.storage_idx: continue - break - else: - raise IndexError # consistency bug - - lst = settings.get('multisig', []) - del lst[self.storage_idx] - if lst: - settings.set('multisig', lst) - else: - settings.remove_key('multisig') - settings.save() - - self.storage_idx = -1 - - def xpubs_with_xfp(self, xfp): - # return set of indexes of xpubs with indicated xfp - return set(xp_idx for xp_idx, (wxfp, _, _) in enumerate(self.xpubs) - if wxfp == xfp) - - def yield_addresses(self, start_idx, count, change_idx=0): - # Assuming a suffix of /0/0 on the defined prefix's, yield - # possible deposit addresses for this wallet. - ch = self.chain - - assert self.addr_fmt, 'no addr fmt known' - - # setup - nodes = [] - paths = [] - for xfp, deriv, xpub in self.xpubs: - # load bip32 node for each cosigner - node = ch.deserialize_node(xpub, AF_P2SH) - node.derive(change_idx, False) - # indicate path used (for UX) - path = "[%s/%s/%d/{idx}]" % (xfp2str(xfp), deriv[2:], change_idx) - nodes.append(node) - paths.append(path) - - idx = start_idx - while count: - if idx > MAX_BIP32_IDX: - break - # make the redeem script, convert into address - script = make_redeem_script(self.M, nodes, idx, self.bip67) - addr = ch.p2sh_address(self.addr_fmt, script) - - yield idx, addr, [p.format(idx=idx) for p in paths], script - - idx += 1 - count -= 1 - - def validate_script(self, redeem_script, subpaths=None, xfp_paths=None): - # Check we can generate all pubkeys in the redeem script, raise on errors. - # - working from pubkeys in the script, because duplicate XFP can happen - # - if disable_checks is set better to handle in caller, but we're also neutered - # - # redeem_script: what we expect and we were given - # subpaths: pubkey => (xfp, *path) - # xfp_paths: (xfp, *path) in same order as pubkeys in redeem script - - subpath_help = [] - used = set() - ch = self.chain - - M, N, pubkeys = disassemble_multisig(redeem_script) - assert M==self.M and N == self.N, 'wrong M/N in script' - - if self.disable_checks: return ['UNVERIFIED'] - - for pk_order, pubkey in enumerate(pubkeys): - check_these = [] - - # TODO: this could be simpler now that XFP is unique per co-signer - if subpaths: - # in PSBT, we are given a map from pubkey to xfp/path, use it - # while remembering it's potentially one-2-many - assert pubkey in subpaths, "unexpected pubkey" - xfp, *path = subpaths[pubkey] - - for xp_idx, (wxfp, _, xpub) in enumerate(self.xpubs): - if wxfp != xfp: continue - if xp_idx in used: continue # only allow once - check_these.append((xp_idx, path)) - else: - # Without PSBT, USB caller must provide xfp+path - # in same order as they occur inside redeem script. - # Working solely from the redeem script's pubkeys, we - # wouldn't know which xpub to use, nor correct path for it. - xfp, *path = xfp_paths[pk_order] - - for xp_idx in self.xpubs_with_xfp(xfp): - if xp_idx in used: continue # only allow once - check_these.append((xp_idx, path)) - - here = None - too_shallow = False - for xp_idx, path in check_these: - if not self.bip67: - assert xp_idx == pk_order, "script key order" - - # matched fingerprint, try to make pubkey that needs to match - xpub = self.xpubs[xp_idx][-1] - - node = ch.deserialize_node(xpub, AF_P2SH); assert node - dp = node.depth() - - #print("%s => deriv=%s dp=%d len(path)=%d path=%s" % - # (xfp2str(xfp), self.xpubs[xp_idx][1], dp, len(path), path)) - - if not (0 <= dp <= len(path)): - # obscure case: xpub isn't deep enough to represent - # indicated path... not wrong really. - too_shallow = True - continue - - for sp in path[dp:]: - assert not (sp & 0x80000000), 'hard deriv' - node.derive(sp, False) # works in-place - - found_pk = node.pubkey() - - # Document path(s) used. Not sure this is useful info to user tho. - # - Do not show what we can't verify: we don't really know the hardeneded - # part of the path from fingerprint to here. - here = '[%s]' % xfp2str(xfp) - if dp != len(path): - here = here[:-1] + ('/_'*dp) + keypath_to_str(path[dp:], '/', 0) + "]" - - if found_pk != pubkey: - # Not a match but not an error by itself, since might be - # another dup xfp to look at still. - - #print('pk mismatch: %s => %s != %s' % ( - # here, b2a_hex(found_pk), b2a_hex(pubkey))) - continue - - subpath_help.append(here) - - used.add(xp_idx) - break - else: - msg = 'pk#%d wrong' % (pk_order+1) - if not check_these: - msg += ', unknown XFP' - elif here: - msg += ', tried: ' + here - if too_shallow: - msg += ', too shallow' - raise AssertionError(msg) - - if self.bip67 and pk_order: - # verify sorted order - assert bytes(pubkey) > bytes(pubkeys[pk_order-1]), 'BIP-67 violation' - - assert len(used) == self.N, 'not all keys used: %d of %d' % (len(used), self.N) - - return subpath_help - - @classmethod - def from_simple_text(cls, lines): - # standard multisig file format - more than one line - has_mine = 0 - M, N = -1, -1 - deriv = None - name = None - xpubs = [] - addr_fmt = AF_P2SH - my_xfp = settings.get('xfp') - for ln in lines: - # remove comments - comm = ln.find('#') - if comm == 0: - continue - if comm != -1: - if not ln[comm + 1:comm + 2].isdigit(): - ln = ln[0:comm] - - ln = ln.strip() - - if ':' not in ln: - if 'pub' in ln: - # pointless optimization: allow bare xpub if we can calc xfp - label = '00000000' - value = ln - else: - # complain? - # if ln: print("no colon: " + ln) - continue - else: - label, value = ln.split(':', 1) - label = label.lower() - - value = value.strip() - - if label == 'name': - name = value - elif label == 'policy': - try: - # accepts: 2 of 3 2/3 2,3 2 3 etc - mat = ure.search(r'(\d+)\D*(\d+)', value) - assert mat - M = int(mat.group(1)) - N = int(mat.group(2)) - assert 1 <= M <= N <= MAX_SIGNERS - except: - raise AssertionError('bad policy line') - - elif label == 'derivation': - # reveal the path derivation for following key(s) - try: - assert value, 'blank' - deriv = cleanup_deriv_path(value) - except BaseException as exc: - raise AssertionError('bad derivation line: ' + str(exc)) - - elif label == 'format': - # pick segwit vs. classic vs. wrapped version - value = value.lower() - for fmt_code, fmt_label in cls.FORMAT_NAMES: - if value == fmt_label: - addr_fmt = fmt_code - break - else: - raise AssertionError('bad format line') - elif len(label) == 8: - try: - xfp = str2xfp(label) - except: - # complain? - # print("Bad xfp: " + ln) - continue - - # deserialize, update list and lots of checks - is_mine = cls.check_xpub(xfp, value, deriv, chains.current_chain().ctype, my_xfp, xpubs) - if is_mine: - has_mine += 1 - - return name, addr_fmt, xpubs, has_mine, M, N - - @classmethod - def from_descriptor(cls, descriptor: str): - # excpect descriptor here if only one line, normal multisig file requires more lines - has_mine = 0 - my_xfp = settings.get('xfp') - xpubs = [] - - desc = MultisigDescriptor.parse(descriptor) - for xfp, deriv, xpub in desc.keys: - deriv = cleanup_deriv_path(deriv) - is_mine = cls.check_xpub(xfp, xpub, deriv, chains.current_chain().ctype, my_xfp, xpubs) - if is_mine: - has_mine += 1 - return None, desc.addr_fmt, xpubs, has_mine, desc.M, desc.N, desc.is_sorted - - def to_descriptor(self): - return MultisigDescriptor( - M=self.M, N=self.N, - keys=self.xpubs, - addr_fmt=self.addr_fmt, - is_sorted=self.bip67, - ) - - @classmethod - def from_file(cls, config, name=None): - # Given a simple text file, parse contents and create instance (unsaved). - # format is: label: value - # where label is: - # name: nameforwallet - # policy: M of N - # format: p2sh (+etc) - # derivation: m/45h/0 (common prefix) - # (8digithex): xpub of cosigner - # - # Descriptor support - # * text file containing multisig descriptor - # - # quick checks: - # - name: 1-20 ascii chars - # - M of N line (assume N of N if not spec'd) - # - xpub: any bip32 serialization we understand, but be consistent - # - expect_chain = chains.current_chain().ctype - if MultisigDescriptor.is_descriptor(config): - _, addr_fmt, xpubs, has_mine, M, N, bip67 = cls.from_descriptor(config) - if not bip67 and not settings.get("unsort_ms", 0): - # BIP-67 disabled, but unsort_ms not allowed - raise - raise AssertionError('Unsorted multisig "multi(...)" not allowed') - else: - # oldschool - bip67 = True - lines = [line for line in config.split('\n') if line] # remove empty lines - parsed_name, addr_fmt, xpubs, has_mine, M, N = cls.from_simple_text(lines) - if parsed_name: - # if name provided in file, use that instead of name inferred from filename - name = parsed_name - - assert len(xpubs), 'need xpubs' - - if M == N == -1: - # default policy: all keys - N = M = len(xpubs) - - if not name: - # provide a default name - name = '%d-of-%d' % (M, N) - - try: - name = to_ascii_printable(name) - assert 1 <= len(name) <= 20 - except: - raise AssertionError('name must be ascii, 1..20 long') - - assert 1 <= M <= N <= MAX_SIGNERS, 'M/N range' - assert N == len(xpubs), 'wrong # of xpubs, expect %d' % N - assert addr_fmt & AFC_SCRIPT, 'script style addr fmt' - - # check we're included... do not insert ourselves, even tho we - # have enough info, simply because other signers need to know my xpubkey anyway - assert has_mine != 0, 'my key not included' - assert has_mine == 1, 'my key included more than once' - - # done. have all the parts - return cls(name, (M, N), xpubs, addr_fmt=addr_fmt, - chain_type=expect_chain, bip67=bip67) - - @classmethod - def check_xpub(cls, xfp, xpub, deriv, expect_chain, my_xfp, xpubs): - # Shared code: consider an xpub for inclusion into a wallet, if ok, append - # to list: xpubs with a tuple: (xfp, deriv, xpub) - # return T if it's our own key - # - deriv can be None, and in very limited cases can recover derivation path - # - could enforce all same depth, and/or all depth >= 1, but - # seems like more restrictive than needed, so "m" is allowed - - try: - # Note: addr fmt detected here via SLIP-132 isn't useful - node, chain, _ = parse_extended_key(xpub) - except: - raise AssertionError('unable to parse xpub') - - try: - assert node.privkey() == None # 'no privkeys plz' - except ValueError: - pass - - if expect_chain == "XRT": - # HACK but there is no difference extended_keys - just bech32 hrp - assert chain.ctype == "XTN" - else: - assert chain.ctype == expect_chain, 'wrong chain' - - depth = node.depth() - - if depth == 1: - if not xfp: - # allow a shortcut: zero/omit xfp => use observed parent value - xfp = swab32(node.parent_fp()) - else: - # generally cannot check fingerprint values, but if we can, do so. - if not cls.disable_checks: - assert swab32(node.parent_fp()) == xfp, 'xfp depth=1 wrong' - - assert xfp, 'need fingerprint' # happens if bare xpub given - - # In most cases, we cannot verify the derivation path because it's hardened - # and we know none of the private keys involved. - if depth == 1: - # but derivation is implied at depth==1 - kn, is_hard = node.child_number() - if is_hard: kn |= 0x80000000 - guess = keypath_to_str([kn], skip=0) - - if deriv: - if not cls.disable_checks: - assert guess == deriv, '%s != %s' % (guess, deriv) - else: - deriv = guess # reachable? doubt it - - assert deriv, 'empty deriv' # or force to be 'm'? - assert deriv[0] == 'm' - - # path length of derivation given needs to match xpub's depth - if not cls.disable_checks: - p_len = deriv.count('/') - assert p_len == depth, 'deriv %d != %d xpub depth (xfp=%s)' % ( - p_len, depth, xfp2str(xfp)) - - if xfp == my_xfp: - # its supposed to be my key, so I should be able to generate pubkey - # - might indicate collision on xfp value between co-signers, - # and that's not supported - with stash.SensitiveValues() as sv: - chk_node = sv.derive_path(deriv) - assert node.pubkey() == chk_node.pubkey(), \ - "[%s/%s] wrong pubkey" % (xfp2str(xfp), deriv[2:]) - - # serialize xpub w/ BIP-32 standard now. - # - this has effect of stripping SLIP-132 confusion away - xpubs.append((xfp, deriv, chain.serialize_public(node, AF_P2SH))) - - return (xfp == my_xfp) - - def make_fname(self, prefix, suffix='txt'): - rv = '%s-%s.%s' % (prefix, self.name, suffix) - return rv.replace(' ', '_') - - async def export_electrum(self): - # Generate and save an Electrum JSON file. - from export import make_json_wallet - - def doit(): - rv = dict(seed_version=17, use_encryption=False, - wallet_type='%dof%d' % (self.M, self.N)) - - ch = self.chain - - # the important stuff. - for idx, (xfp, deriv, xpub) in enumerate(self.xpubs): - - node = None - if self.addr_fmt != AF_P2SH: - # CHALLENGE: we must do slip-132 format [yz]pubs here when not p2sh mode. - node = ch.deserialize_node(xpub, AF_P2SH); assert node - xp = ch.serialize_public(node, self.addr_fmt) - else: - xp = xpub - - rv['x%d/' % (idx+1)] = dict( - hw_type='coldcard', type='hardware', - ckcc_xfp=xfp, - label='Coldcard %s' % xfp2str(xfp), - derivation=deriv, xpub=xp) - - # sign export with first p2pkh key - return ujson.dumps(rv), False, False - - await make_json_wallet('Electrum multisig wallet', doit, - fname_pattern=self.make_fname('el', 'json')) - - async def export_wallet_file(self, mode="exported from", extra_msg=None, descriptor=False, - core=False, desc_pretty=True): - # create a text file with the details; ready for import to next Coldcard - from glob import NFC, dis - - my_xfp = xfp2str(settings.get('xfp')) - if core: - name = "Bitcoin Core" - fname_pattern = self.make_fname('bitcoin-core') - elif descriptor: - name = "Descriptor" - fname_pattern = self.make_fname('desc') - else: - name = "Coldcard" - fname_pattern = self.make_fname('export') - - hdr = '%s %s' % (mode, my_xfp) - label = "%s multisig setup" % name - - choice = await import_export_prompt("%s file" % label, is_import=False, - no_qr=not version.has_qwerty) - if choice == KEY_CANCEL: - return - - dis.fullscreen("Wait...") - if choice in (KEY_NFC, KEY_QR): - with uio.StringIO() as fp: - self.render_export(fp, hdr_comment=hdr, descriptor=descriptor, - core=core, desc_pretty=desc_pretty) - if choice == KEY_NFC: - await NFC.share_text(fp.getvalue()) - else: - try: - await show_qr_code(fp.getvalue()) - except (ValueError, RuntimeError): - if version.has_qwerty: - # do BBQr on Q - from ux_q1 import show_bbqr_codes - await show_bbqr_codes('U', fp.getvalue(), label) - return - - try: - with CardSlot(**choice) as card: - fname, nice = card.pick_filename(fname_pattern) - - # do actual write - with open(fname, 'w+') as fp: - self.render_export(fp, hdr_comment=hdr, descriptor=descriptor, - core=core, desc_pretty=desc_pretty) - # fp.seek(0) - # contents = fp.read() - # TODO re-enable once we know how to proceed with regards to with which key to sign - # from auth import write_sig_file - # h = ngu.hash.sha256s(contents.encode()) - # sig_nice = write_sig_file([(h, fname)]) - - msg = '%s file written:\n\n%s' % (label, nice) - # msg += '\n\nColdcard multisig signature file written:\n\n%s' % sig_nice - if extra_msg: - msg += extra_msg - - await ux_show_story(msg) - - except CardMissingError: - await needs_microsd() - return - except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) - return - - def render_export(self, fp, hdr_comment=None, descriptor=False, core=False, desc_pretty=True): - if descriptor: - # serialize descriptor - desc_obj = self.to_descriptor() - if core: - core_obj = desc_obj.bitcoin_core_serialize() - core_str = ujson.dumps(core_obj) - print("importdescriptors '%s'\n" % core_str, file=fp) - else: - if desc_pretty: - desc = desc_obj.pretty_serialize() - else: - desc = desc_obj.serialize() - print("%s\n" % desc, file=fp) - else: - if hdr_comment: - print("# Coldcard Multisig setup file (%s)\n#" % hdr_comment, file=fp) - - print("Name: %s\nPolicy: %d of %d" % (self.name, self.M, self.N), file=fp) - - if self.addr_fmt != AF_P2SH: - print("Format: " + self.render_addr_fmt(self.addr_fmt), file=fp) - - last_deriv = None - for xfp, deriv, val in self.xpubs: - if last_deriv != deriv: - print("\nDerivation: %s\n" % deriv, file=fp) - last_deriv = deriv - - print('%s: %s' % (xfp2str(xfp), val), file=fp) - - @classmethod - def guess_addr_fmt(cls, npath): - # Assuming the bips are being respected, what address format will be used, - # based on indicated numeric subkey path observed. - # - return None if unsure, no errors - # - #( "m/45h", 'p2sh', AF_P2SH), - #( "m/48h/{coin}h/0h/1h", 'p2sh_p2wsh', AF_P2WSH_P2SH), - #( "m/48h/{coin}h/0h/2h", 'p2wsh', AF_P2WSH) - - top = npath[0] & 0x7fffffff - if top == npath[0]: - # non-hardened top? rare/bad - return - - if top == 45: - return AF_P2SH - - if top == 48: - if len(npath) < 4: return - - last = npath[3] & 0x7fffffff - if last == 1: - return AF_P2WSH_P2SH - if last == 2: - return AF_P2WSH - - @classmethod - def import_from_psbt(cls, M, N, xpubs_list): - # given the raw data from PSBT global header, offer the user - # the details, and/or bypass that all and just trust the data. - # - xpubs_list is a list of (xfp+path, binary BIP-32 xpub) - # - already know not in our records. - trust_mode = cls.get_trust_policy() - - if trust_mode == TRUST_VERIFY: - # already checked for existing import and wasn't found, so fail - raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet") - - # build up an in-memory version of the wallet. - # - capture address format based on path used for my leg (if standards compliant) - - assert N == len(xpubs_list) - assert 1 <= M <= N <= MAX_SIGNERS, 'M/N range' - my_xfp = settings.get('xfp') - - expect_chain = chains.current_chain().ctype - xpubs = [] - has_mine = 0 - - for k, v in xpubs_list: - xfp, *path = ustruct.unpack_from('<%dI' % (len(k)//4), k, 0) - xpub = ngu.codecs.b58_encode(v) - is_mine = cls.check_xpub(xfp, xpub, keypath_to_str(path, skip=0), - expect_chain, my_xfp, xpubs) - if is_mine: - has_mine += 1 - addr_fmt = cls.guess_addr_fmt(path) - - assert has_mine == 1 # 'my key not included' - - name = 'PSBT-%d-of-%d' % (M, N) - # this will always create sortedmulti multisig (BIP-67) - # because BIP-174 came years after wide spread acceptance of BIP-67 policy - ms = cls(name, (M, N), xpubs, chain_type=expect_chain, addr_fmt=addr_fmt or AF_P2SH) - - # may just keep in-memory version, no approval required, if we are - # trusting PSBT's today, otherwise caller will need to handle UX w.r.t new wallet - return ms, (trust_mode != TRUST_PSBT) - - def validate_psbt_xpubs(self, xpubs_list): - # The xpubs provided in PSBT must be exactly right, compared to our record. - # But we're going to use our own values from setup time anyway. - # Check: - # - chain codes match what we have stored already - # - pubkey vs. path will be checked later - # - xfp+path already checked when selecting this wallet - # - some cases we cannot check, so count those for a warning - # Any issue here is a fraud attempt in some way, not innocent. - # But it would not have tricked us and so the attack targets some other signer. - assert len(xpubs_list) == self.N - - for k, v in xpubs_list: - xfp, *path = ustruct.unpack_from('<%dI' % (len(k)//4), k, 0) - xpub = ngu.codecs.b58_encode(v) - - # cleanup and normalize xpub - tmp = [] - self.check_xpub(xfp, xpub, keypath_to_str(path, skip=0), self.chain_type, 0, tmp) - (_, deriv, xpub_reserialized) = tmp[0] - assert deriv # because given as arg - - if self.disable_checks: - # allow wrong derivation paths in PSBT; but also allows usage when - # old pre-3.2.1 MS wallet lacks derivation details for all legs - continue - - # find in our records. - for (x_xfp, x_deriv, x_xpub) in self.xpubs: - if x_xfp != xfp: continue - # found matching XFP - assert deriv == x_deriv - - assert xpub_reserialized == x_xpub, 'xpub wrong (xfp=%s)' % xfp2str(xfp) - break - else: - assert False # not reachable, since we picked wallet based on xfps - - def get_deriv_paths(self): - # List of unique derivation paths being used. Often length one. - # - also a rendered single-value summary - derivs = sorted(set(d for _,d,_ in self.xpubs)) - - if len(derivs) == 1: - dsum = derivs[0] - else: - dsum = 'Varies (%d)' % len(derivs) - - return derivs, dsum - - async def confirm_import(self): - # prompt them about a new wallet, let them see details and then commit change. - M, N = self.M, self.N - - if M == N == 1: - exp = 'The one signer must approve spends.' - elif M == N: - exp = 'All %d co-signers must approve spends.' % N - elif M == 1: - exp = 'Any signature from %d co-signers will approve spends.' % N - else: - exp = '{M} signatures, from {N} possible co-signers, will be required to approve spends.'.format(M=M, N=N) - - # Look for duplicate stuff - name_change, diff_items, num_dups = self.has_similar() - - is_dup = False - if name_change: - story = 'Update NAME only of existing multisig wallet?' - elif num_dups and isinstance(diff_items, list): - # failures only - story = "Duplicate wallet." - if diff_items: - story += diff_items[0] - else: - story += ' All details are the same as existing!' - is_dup = True - elif diff_items: - # Concern here is overwrite when similar, but we don't overwrite anymore, so - # more of a warning about funny business. - story = '''\ -WARNING: This new wallet is similar to an existing wallet, but will NOT replace it. Consider deleting previous wallet first. Differences: \ -''' + ', '.join(diff_items) - else: - story = 'Create new multisig wallet?' - - derivs, dsum = self.get_deriv_paths() - - if not self.bip67 and not is_dup: - # do not need to warn if duplicate, won;t be allowed to import anyways - story += "\nWARNING: BIP-67 disabled! Unsorted multisig - order of keys in descriptor/backup is crucial" - - story += '''\n -Wallet Name: - {name} - -Policy: {M} of {N} - -{exp} - -Addresses: - {at} - -Derivation: - {dsum} - -Press (1) to see extended public keys, '''.format(M=M, N=N, name=self.name, exp=exp, dsum=dsum, - at=self.render_addr_fmt(self.addr_fmt)) - if not is_dup: - story += ('%s to approve, %s to cancel.' % (OK, X)) - else: - story += '%s to cancel' % X - - ux_clear_keys(True) - while 1: - ch = await ux_show_story(story, escape='1') - - if ch == '1': - await self.show_detail(verbose=False) - continue - - if ch == 'y' and not is_dup: - # save to nvram, may raise MultisigOutOfSpace - if name_change: - name_change.delete() - - assert self.storage_idx == -1 - self.commit() - await ux_dramatic_pause("Saved.", 2) - break - - return ch - - async def show_detail(self, verbose=True): - # Show the xpubs; might be 2k or more rendered. - msg = uio.StringIO() - - if verbose: - if not self.bip67: - msg.write("WARNING: BIP-67 disabled! Unsorted multisig - order of keys in descriptor/backup is crucial.\n\n") - - vmsg = ('Policy: {M} of {N}\n' - 'Blockchain: {ctype}\n' - 'Addresses: {at}\n\n') - vmsg = vmsg.format(M=self.M, N=self.N, ctype=self.chain_type, - at=self.render_addr_fmt(self.addr_fmt)) - msg.write(vmsg) - - # order of keys in self.xpubs is same as order of keys in CC import format or descriptor - for idx, (xfp, deriv, xpub) in enumerate(self.xpubs): - if idx: - msg.write('\n---===---\n\n') - - msg.write('%s:\n %s\n\n%s\n' % (xfp2str(xfp), deriv, xpub)) - - if self.addr_fmt != AF_P2SH: - # SLIP-132 format [yz]pubs here when not p2sh mode. - # - has same info as proper bitcoin serialization, but looks much different - node = self.chain.deserialize_node(xpub, AF_P2SH) - xp = self.chain.serialize_public(node, self.addr_fmt) - - msg.write('\nSLIP-132 equiv:\n%s\n' % xp) - - return await ux_show_story(msg, title=self.name) - -async def no_ms_yet(*a): - # action for 'no wallets yet' menu item - await ux_show_story("You don't have any multisig wallets yet.") - -def disable_checks_chooser(): - ch = ['Normal', 'Skip Checks'] - - def xset(idx, text): - MultisigWallet.disable_checks = bool(idx) - - return int(MultisigWallet.disable_checks), ch, xset - -async def disable_checks_menu(*a): - from menu import start_chooser - - if not MultisigWallet.disable_checks: - ch = await ux_show_story('''\ -With many different wallet vendors and implementors involved, it can \ -be hard to create a PSBT consistent with the many keys involved. \ -With this setting, you can \ -disable the more stringent verification checks your Coldcard normally provides. - -USE AT YOUR OWN RISK. These checks exist for good reason! Signed txn may \ -not be accepted by network. - -This settings lasts only until power down. - -Press (4) to confirm entering this DANGEROUS mode. -''', escape='4') - - if ch != '4': return - - start_chooser(disable_checks_chooser) - - -def psbt_xpubs_policy_chooser(): - # Chooser for trust policy - ch = ['Verify Only', 'Offer Import', 'Trust PSBT'] - - def xset(idx, text): - settings.set('pms', idx) - - return MultisigWallet.get_trust_policy(), ch, xset - -async def trust_psbt_menu(*a): - # show a story then go into chooser - from menu import start_chooser - - ch = await ux_show_story('''\ -This setting controls what the Coldcard does \ -with the co-signer public keys (XPUB) that may \ -be provided inside a PSBT file. Three choices: - -- Verify Only. Do not import the xpubs found, but do \ -verify the correct wallet already exists on the Coldcard. - -- Offer Import. If it's a new multisig wallet, offer to import \ -the details and store them as a new wallet in the Coldcard. - -- Trust PSBT. Use the wallet data in the PSBT as a temporary, -multisig wallet, and do not import it. This permits some \ -deniability and additional privacy. - -When the XPUB data is not provided in the PSBT, regardless of the above, \ -we require the appropriate multisig wallet to already exist \ -on the Coldcard. Default is to 'Offer' unless a multisig wallet already \ -exists, otherwise 'Verify'.''') - - if ch == 'x': return - start_chooser(psbt_xpubs_policy_chooser) - -def unsorted_ms_chooser(): - ch = ['Do Not Allow', 'Allow'] - - def xset(idx, text): - settings.set('unsort_ms', idx) - from actions import goto_top_menu - goto_top_menu() - - return settings.get('unsort_ms', 0), ch, xset - -async def unsorted_ms_menu(*a): - from menu import start_chooser - - if not settings.get("unsort_ms", None): - ch = await ux_show_story( - 'With this setting ON, it is allowed to import and operate' - ' "multi(...)" unsorted multisig wallets that do not follow BIP-67.' - ' It is of CRUCIAL importance for unsorted wallets, to backup multisig descriptor' - ' and preserve order of the keys in it.' - ' Many popular wallets like Sparrow and Electrum do NOT support "multi(...)".' - '\n\nUSE AT YOUR OWN RISK. Disabling BIP-67 is discouraged!' - '\n\nPress (4) to confirm allowing "multi(...)"', escape='4') - - if ch != '4': return - - else: - # unsort_ms enabled - assume he is going to disable - # check any multi(...) imported - ms = settings.get("multisig", []) - multi_names = [m[0] for m in ms if len(m) == 5] - if multi_names: - # do not allow to disable if any multi(...) imported - # list by name what needs to be removed - await ux_show_story( - "Remove already saved multi(...) wallets first.\n\n%s" - % multi_names - ) - return - - start_chooser(unsorted_ms_chooser) - -class MultisigMenu(MenuSystem): - - @classmethod - def construct(cls): - # Dynamic menu with user-defined names of wallets shown - - if not MultisigWallet.exists(): - rv = [MenuItem('(none setup yet)', f=no_ms_yet)] - else: - rv = [] - for ms in MultisigWallet.get_all(): - rv.append(MenuItem('%d/%d: %s' % (ms.M, ms.N, ms.name), - menu=make_ms_wallet_menu, arg=ms.storage_idx)) - from glob import NFC - rv.append(MenuItem('Import from File', f=import_multisig)) - rv.append(MenuItem('Import from QR', f=import_multisig_qr, - predicate=version.has_qwerty, shortcut=KEY_QR)) - rv.append(MenuItem('Import via NFC', f=import_multisig_nfc, - predicate=bool(NFC), shortcut=KEY_NFC)) - rv.append(MenuItem('Export XPUB', f=export_multisig_xpubs)) - rv.append(MenuItem('Create Airgapped', f=create_ms_step1)) - rv.append(MenuItem('Trust PSBT?', f=trust_psbt_menu)) - rv.append(MenuItem('Skip Checks?', f=disable_checks_menu)) - rv.append(NonDefaultMenuItem('Unsorted Multisig' if version.has_qwerty else "Unsorted Multi", - 'unsort_ms', - f=unsorted_ms_menu)) - - return rv - - def update_contents(self): - # Reconstruct the list of wallets on this dynamic menu, because - # we added or changed them and are showing that same menu again. - tmp = self.construct() - self.replace_items(tmp) - - -async def make_multisig_menu(*a): - # list of all multisig wallets, and high-level settings/actions - from pincodes import pa - - if not pa.has_secrets(): - await ux_show_story("You must have wallet seed before creating multisig wallets.") - return - - rv = MultisigMenu.construct() - return MultisigMenu(rv) - -async def make_ms_wallet_menu(menu, label, item): - # details, actions on single multisig wallet - ms = MultisigWallet.get_by_idx(item.arg) - if not ms: return - - rv = [ - MenuItem('"%s"' % ms.name, f=ms_wallet_detail, arg=ms), - MenuItem('View Details', f=ms_wallet_detail, arg=ms), - MenuItem('Delete', f=ms_wallet_delete, arg=ms), - ] - if ms.bip67: - rv += [ - MenuItem('Coldcard Export', f=ms_wallet_ckcc_export, arg=(ms, {})), - MenuItem('Electrum Wallet', f=ms_wallet_electrum_export, arg=ms), - ] - # only way to export non-BIP-67 ms wallet is descriptors (+core export) - rv.append(MenuItem('Descriptors', menu=make_ms_wallet_descriptor_menu, arg=ms)) - return rv - -async def make_ms_wallet_descriptor_menu(menu, label, item): - # descriptor menu - ms = item.arg - if not ms: - return - - rv = [ - MenuItem('View Descriptor', f=ms_wallet_show_descriptor, arg=ms), - MenuItem('Export', f=ms_wallet_ckcc_export, - arg=(ms, {"descriptor": True, "desc_pretty": False})), - MenuItem('Bitcoin Core', f=ms_wallet_ckcc_export, - arg=(ms, {"descriptor": True, "core": True})), - ] - return rv - -async def ms_wallet_delete(menu, label, item): - ms = item.arg - - # delete - if not await ux_confirm("Delete this multisig wallet (%s)?\n\nFunds may be impacted." - % ms.name): - await ux_dramatic_pause('Aborted.', 3) - return - - ms.delete() - await ux_dramatic_pause('Deleted.', 3) - - # update/hide from menu - #menu.update_contents() - - from ux import the_ux - # pop stack - the_ux.pop() - - m = the_ux.top_of_stack() - m.update_contents() - -async def ms_wallet_ckcc_export(menu, label, item): - # create a text file with the details; ready for import to next Coldcard - ms = item.arg[0] - kwargs = item.arg[1] - await ms.export_wallet_file(**kwargs) - -async def ms_wallet_show_descriptor(menu, label, item): - from glob import dis - dis.fullscreen("Wait...") - ms = item.arg - desc = ms.to_descriptor() - desc_str = desc.serialize() - ch = await ux_show_story("Press (1) to export in pretty human readable format.\n\n" + desc_str, escape="1") - if ch == "1": - await ms.export_wallet_file(descriptor=True, desc_pretty=True) - -async def ms_wallet_electrum_export(menu, label, item): - # create a JSON file that Electrum can use. Challenges: - # - file contains derivation paths for each co-signer to use - # - electrum is using BIP-43 with purpose=48 (purpose48_derivation) to make paths like: - # m/48h/1h/0h/2h - # - above is now called BIP-48 - # - other signers might not be coldcards (we don't know) - # solution: - # - when building air-gap, pick address type at that point, and matching path to suit - # - could check path prefix and addr_fmt make sense together, but meh. - ms = item.arg - from actions import electrum_export_story - - derivs, dsum = ms.get_deriv_paths() - - msg = 'The new wallet will have derivation path:\n %s\n and use %s addresses.\n' % ( - dsum, MultisigWallet.render_addr_fmt(ms.addr_fmt) ) - - if await ux_show_story(electrum_export_story(msg)) != 'y': - return - - await ms.export_electrum() - - -async def ms_wallet_detail(menu, label, item): - # show details of single multisig wallet - from glob import dis - ms = item.arg - dis.fullscreen("Wait...") - return await ms.show_detail() - - -async def export_multisig_xpubs(*a): - # WAS: Create a single text file with lots of docs, and all possible useful xpub values. - # THEN: Just create the one-liner xpub export value they need/want to support BIP-45 - # NOW: Export JSON with one xpub per useful address type and semi-standard derivation path - # - # Consumer for this file is supposed to be ourselves, when we build on-device multisig. - # - however some 3rd parties are making use of it as well. - # - from glob import NFC, dis - from ux import import_export_prompt - - xfp = xfp2str(settings.get('xfp', 0)) - chain = chains.current_chain() - - fname_pattern = 'ccxp-%s.json' % xfp - label = "Multisig XPUB" - - msg = '''\ -This feature creates a small file containing \ -the extended public keys (XPUB) you would need to join \ -a multisig wallet. - -Public keys for BIP-48 conformant paths are used: - -P2SH-P2WSH: - m/48h/{coin}h/{{acct}}h/1h -P2WSH: - m/48h/{coin}h/{{acct}}h/2h - -{ok} to continue. {x} to abort.'''.format(coin=chain.b44_cointype, ok=OK, x=X) - - ch = await ux_show_story(msg) - if ch != "y": - return - - acct_num = await ux_enter_bip32_index('Account Number:') or 0 - - choice = await import_export_prompt("%s file" % label, is_import=False, - no_qr=not version.has_qwerty) - - if choice == KEY_CANCEL: - return - - dis.fullscreen('Generating...') - - todo = [ - ( "m/45h", 'p2sh', AF_P2SH), # iff acct_num == 0 - ( "m/48h/{coin}h/{acct_num}h/1h", 'p2sh_p2wsh', AF_P2WSH_P2SH ), - ( "m/48h/{coin}h/{acct_num}h/2h", 'p2wsh', AF_P2WSH ), - ] - - def render(fp): - fp.write('{\n') - with stash.SensitiveValues() as sv: - for deriv, name, fmt in todo: - if fmt == AF_P2SH and acct_num: - continue - dd = deriv.format(coin=chain.b44_cointype, acct_num=acct_num) - node = sv.derive_path(dd) - xp = chain.serialize_public(node, fmt) - fp.write(' "%s_deriv": "%s",\n' % (name, dd)) - fp.write(' "%s": "%s",\n' % (name, xp)) - xpub = chain.serialize_public(node) - descriptor_template = multisig_descriptor_template(xpub, dd, xfp, fmt) - if descriptor_template is None: - continue - fp.write(' "%s_desc": "%s",\n' % (name, descriptor_template)) - - fp.write(' "account": "%d",\n' % acct_num) - fp.write(' "xfp": "%s"\n}\n' % xfp) - - if choice in (KEY_NFC, KEY_QR): - with uio.StringIO() as fp: - render(fp) - if choice == KEY_NFC: - await NFC.share_json(fp.getvalue()) - elif version.has_qwerty: - from ux_q1 import show_bbqr_codes - await show_bbqr_codes('J', fp.getvalue(), label) - return - - try: - with CardSlot(**choice) as card: - fname, nice = card.pick_filename(fname_pattern) - # do actual write: manual JSON here so more human-readable. - with open(fname, 'w+') as fp: - render(fp) - # fp.seek(0) - # contents = fp.read() - # TODO re-enable once we know how to proceed with regards to with which key to sign - # from auth import write_sig_file - # h = ngu.hash.sha256s(contents.encode()) - # sig_nice = write_sig_file([(h, fname)]) - - except CardMissingError: - await needs_microsd() - return - except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) - return - - msg = '%s file written:\n\n%s' % (label, nice) - # msg += '\n\nMultisig XPUB signature file written:\n\n%s' % sig_nice - await ux_show_story(msg) - -async def validate_xpub_for_ms(obj, af_str, chain, my_xfp, xpubs): - # Read xpub and validate from JSON received via SD card or BBQr - # - obj => JSON object (mapping) - # - af_str => address format we expect/need - - # value in file is BE32, but we want LE32 internally - # - KeyError here handled by caller - xfp = str2xfp(obj['xfp']) - deriv = cleanup_deriv_path(obj[af_str + '_deriv']) - ln = obj.get(af_str) - - return MultisigWallet.check_xpub(xfp, ln, deriv, chain.ctype, my_xfp, xpubs) - -async def ms_coordinator_qr(af_str, my_xfp, chain): +async def ms_coordinator_qr(af_str, my_xfp): # Scan a number of JSON files from BBQr w/ derive, xfp and xpub details. # - from ux_q1 import QRScannerInteraction + from ux_q1 import QRScannerInteraction, decode_qr_result, QRDecodeExplained + + def convertor(got): + file_type, _, data = decode_qr_result(got, expect_bbqr=True) + if isinstance(data, bytes): + # we expect BBQr, but simple QR also possible here + data = data.decode() + + if file_type == 'U': + data = data.strip() + if data[0] == '{' and data[-1] == '}': + file_type = 'J' + if file_type == 'J': + try: + return ujson.loads(data) + except: + raise QRDecodeExplained('Unable to decode JSON data') + else: + for line in data.split("\n"): + if len(line) > 112 and ("pub" in line): + return line.strip() num_mine = 0 num_files = 0 - xpubs = [] + keys = [] msg = 'Scan Exported XPUB from Coldcard' while True: - vals = await QRScannerInteraction().scan_json(msg) - if vals is None: + key = await QRScannerInteraction().scan_general(msg, convertor, enter_quits=True) + if key is None: break - try: - is_mine = await validate_xpub_for_ms(vals, af_str, chain, my_xfp, xpubs) + if isinstance(key, dict): + k = Key.from_cc_json(key, af_str) + else: + k = Key.from_string(key) + + num_mine += k.validate(my_xfp) + keys.append(k) + except KeyError as e: # random JSON will end up here msg = "Missing value: %s" % str(e) @@ -1632,19 +66,17 @@ async def ms_coordinator_qr(af_str, my_xfp, chain): msg = "Failure: %s" % str(e) continue - if is_mine: - num_mine += 1 num_files += 1 msg = "Number of keys scanned: %d" % num_files - return xpubs, num_mine, num_files + return keys, num_mine, num_files -async def ms_coordinator_file(af_str, my_xfp, chain, slot_b=None): +async def ms_coordinator_file(af_str, my_xfp, slot_b=None): num_mine = 0 num_files = 0 - xpubs = [] + keys = [] try: with CardSlot(slot_b=slot_b) as card: for path in card.get_paths(): @@ -1653,7 +85,9 @@ async def ms_coordinator_file(af_str, my_xfp, chain, slot_b=None): # ignore subdirs continue - if not fn.startswith('ccxp-') or not fn.endswith('.json'): + if fn.endswith('.bsms'): + pass # allows files with [xfp/p/a/t/h]xpub + elif not fn.startswith('ccxp-') or not fn.endswith('.json'): # wrong prefix/suffix: ignore continue @@ -1663,19 +97,34 @@ async def ms_coordinator_file(af_str, my_xfp, chain, slot_b=None): # sigh, OS/filesystem variations file_size = var[1] if len(var) == 2 else get_filesize(full_fname) - if not (0 <= file_size <= 1100): + if not (0 <= file_size <= 1500): # out of range size continue try: with open(full_fname, 'rt') as fp: - vals = ujson.load(fp) - - is_mine = await validate_xpub_for_ms(vals, af_str, chain, - my_xfp, xpubs) - if is_mine: - num_mine += 1 - + try: + # CC multisig XPUBs JSON expected + vals = ujson.load(fp) + except: + # try looking for BIP-380 key expression + fp.seek(0) + for line in fp.readlines(): + if len(line) > 112 and ("pub" in line): + vals = line.strip() + break + + try: + if isinstance(vals, dict): + k = Key.from_cc_json(vals, af_str) + else: + k = Key.from_string(vals) + except Exception as e: + # sys.print_exception(e) + raise + + num_mine += k.validate(my_xfp) + keys.append(k) num_files += 1 except CardMissingError: @@ -1683,16 +132,31 @@ async def ms_coordinator_file(af_str, my_xfp, chain, slot_b=None): except Exception as exc: # show something for coders, but no user feedback - sys.print_exception(exc) + # sys.print_exception(exc) continue except CardMissingError: await needs_microsd() return - return xpubs, num_mine, num_files + return keys, num_mine, num_files + -async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH, is_qr=False): +def add_own_xpub(chain, acct_num, addr_fmt, secret=None): + # Build out what's required for using master secret (or another + # encoded secret) as a co-signer + deriv = "48h/%dh/%dh/%dh" % (chain.b44_cointype, acct_num, + 2 if addr_fmt == AF_P2WSH else 1) + + with stash.SensitiveValues(secret=secret) as sv: + the_xfp = xfp2str(sv.get_xfp()) + koi = KeyOriginInfo.from_string(the_xfp + "/" + deriv) + node = sv.derive_path(deriv, register=False) + key = Key(node, koi, chain_type=chain.ctype) + return key + + +async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH, is_qr=False, for_ccc=None): # collect all xpub- exports (must be >= 1) to make "air gapped" wallet # - function f specifies a way how to collect co-signer info - currently SD and QR (Q only) # - ask for M value @@ -1705,21 +169,21 @@ async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH, is_qr=False) my_xfp = settings.get('xfp') if is_qr: - xpubs, num_mine, num_files = await ms_coordinator_qr(mode, my_xfp, chain) + keys, num_mine, num_files = await ms_coordinator_qr(mode, my_xfp) else: - xpubs, num_mine, num_files = await ms_coordinator_file(mode, my_xfp, chain) + keys, num_mine, num_files = await ms_coordinator_file(mode, my_xfp) if CardSlot.both_inserted(): # handle dual slot usage: assumes slot A used by first call above - bxpubs, bnum_mine, bnum_files = await ms_coordinator_file(mode, my_xfp, - chain, True) - xpubs.extend(bxpubs) + bkeys, bnum_mine, bnum_files = await ms_coordinator_file(mode, my_xfp, + slot_b=True) + keys.extend(bkeys) num_mine += bnum_mine num_files += bnum_files # remove dups; easy to happen if you double-tap the export - xpubs = list(set(xpubs)) + keys = list(set(keys)) - if not xpubs or (len(xpubs) == 1 and num_mine): + if not keys or (len(keys) == 1 and num_mine): if is_qr: msg = "No XPUBs scanned. Exit." else: @@ -1727,45 +191,82 @@ async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH, is_qr=False) " Must have filename: ccxp-....json") await ux_show_story(msg) return - - # add myself if not included already ? - if not num_mine: + + if for_ccc: + secret, ccc_ms_count = for_ccc + # Always include 2 keys from CCC: own master (key A) and key C + # - force them to same derivation. + acct = await ux_enter_bip32_index('CCC Account Number:') or 0 + + dis.fullscreen("Wait...") + a = add_own_xpub(chain, acct, addr_fmt) # master: key A + c = add_own_xpub(chain, acct, addr_fmt, secret=secret) + + # problem: above file searching may find xpub export from key C + # (or our master seed, exported) .. we can't add them again, + # since xfp are not unique and that's probably not what they wanted + got_xfps = [a.origin.fingerprint, c.origin.fingerprint] + keys = [k for k in keys if k.origin.fingerprint not in got_xfps] + + if not keys: + await ux_show_story("Need at least one other co-signer (key B).") + return + + # master seed is always key0, key C is key1, k2..kn backup keys + keys = [a, c] + keys + num_mine += 2 + + elif not num_mine: + # add myself if not included already? As an option. ch = await ux_show_story("Add current Coldcard with above XFP ?", title="[%s]" % xfp2str(my_xfp)) if ch == "y": acct = await ux_enter_bip32_index('Account Number:') or 0 dis.fullscreen("Wait...") - deriv = "m/48h/%dh/%dh/%dh" % (chain.b44_cointype, acct, - 2 if addr_fmt == AF_P2WSH else 1) - with stash.SensitiveValues() as sv: - node = sv.derive_path(deriv) - xpubs.append((my_xfp, deriv, chain.serialize_public(node, AF_P2SH))) + keys.append(add_own_xpub(chain, acct, addr_fmt)) num_mine += 1 - N = len(xpubs) + N = len(keys) if (N > MAX_SIGNERS) or (N < 2): await ux_show_story("Invalid number of signers,min is 2 max is %d." % MAX_SIGNERS) return - # pick useful M value to start - M = await ux_enter_number("How many need to sign?(M)", N, can_cancel=True) - if not M: - await ux_dramatic_pause('Aborted.', 2) - return # user cancel + if for_ccc: + M = 2 + else: + # pick useful M value to start + M = await ux_enter_number("How many need to sign?(M)", N, can_cancel=True) + if not M: + await ux_dramatic_pause('Aborted.', 2) + return # user cancel dis.fullscreen("Wait...") # create appropriate object assert 1 <= M <= N <= MAX_SIGNERS - name = 'CC-%d-of-%d' % (M, N) - ms = MultisigWallet(name, (M, N), xpubs, chain_type=chain.ctype, addr_fmt=addr_fmt) + if for_ccc: + name = "Coldcard Co-sign" if version.has_qwerty else "CCC" + if ccc_ms_count: + # make name unique for each CCC wallet, but they can edit + name += " #%d" % (ccc_ms_count + 1) + else: + name = 'CC-%d-of-%d' % (M, N) + + from miniscript import Sortedmulti, Number + from wallet import MiniScriptWallet + from descriptor import Descriptor + + desc_obj = Descriptor(miniscript=Sortedmulti(Number(M), *keys), + addr_fmt=addr_fmt) + # no need to validate here - as all the keys are already validated + msc = MiniScriptWallet.from_descriptor_obj(name, desc_obj) if num_mine: - from auth import NewEnrollRequest, UserAuthorizedAction + from auth import NewMiniscriptEnrollRequest, UserAuthorizedAction - UserAuthorizedAction.active_request = NewEnrollRequest(ms) + UserAuthorizedAction.active_request = NewMiniscriptEnrollRequest(msc) # menu item case: add to stack from ux import the_ux @@ -1773,24 +274,26 @@ async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH, is_qr=False) else: # we cannot enroll multisig in which we do not participate # thou we can put descriptor on screen or on SD - await ms.export_wallet_file(descriptor=True, desc_pretty=False) + # cannot sign export if my key not included + await msc.export_wallet_file(sign=False) -async def create_ms_step1(*a): +async def create_ms_step1(*a, for_ccc=None): # Show story, have them pick address format. ch = None is_qr = False if version.has_qr: # They have a scanner, could do QR codes... - ch = await ux_show_story("Press "+ KEY_QR + " to scan multisg XPUBs from "\ - "QR codes (BBQr) or ENTER to use SD card(s).", title="QR or SD Card?") + ch = await ux_show_story("Press " + KEY_QR + " to scan multisig XPUBs from " + "QR codes (BBQr) or ENTER to use SD card(s).", + title="QR or SD Card?") if ch == KEY_QR: is_qr = True - ch = await ux_show_story("Press ENTER for default address format (P2WSH, segwit), "\ + ch = await ux_show_story("Press ENTER for default address format (P2WSH, segwit), " "otherwise, press (1) for P2SH-P2WSH.", title="Address Format", - escape="1") + escape="1") else: ch = await ux_show_story('''\ @@ -1808,81 +311,10 @@ async def create_ms_step1(*a): return try: - return await ondevice_multisig_create(n, f, is_qr) + return await ondevice_multisig_create(n, f, is_qr, for_ccc=for_ccc) except Exception as e: + # sys.print_exception(e) await ux_show_story('Failed to create multisig.\n\n%s\n%s' % (e, problem_file_line(e)), title="ERROR") - -async def import_multisig_nfc(*a): - from glob import NFC - # this menu option should not be available if NFC is disabled - try: - return await NFC.import_multisig_nfc() - except Exception as e: - await ux_show_story(title="ERROR", msg="Failed to import multisig. %s" % str(e)) - -async def import_multisig_qr(*a): - from auth import maybe_enroll_xpub - from ux_q1 import QRScannerInteraction - data = await QRScannerInteraction().scan_text('Scan Multisig from a QR code') - if not data: - # pressed CANCEL - return - - try: - maybe_enroll_xpub(config=data) - except Exception as e: - await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) - -async def import_multisig(*a): - # pick text file from SD card, import as multisig setup file - from actions import file_picker - from glob import VD - - force_vdisk = False - if VD: - prompt = "Press (1) to import multisig wallet file from SD Card" - escape = "1" - if VD is not None: - prompt += ", press (2) to import from Virtual Disk" - escape += "2" - prompt += "." - ch = await ux_show_story(prompt, escape=escape) - if ch == "1": - force_vdisk=False - elif ch == "2": - force_vdisk = True - else: - return - - def possible(filename): - with open(filename, 'rt') as fd: - for ln in fd: - if "sh(" in ln or "wsh(" in ln: - # descriptor import - return True - if 'pub' in ln: - return True - - fn = await file_picker(suffix=['.txt', '.json'], min_size=100, max_size=20*200, - taster=possible, force_vdisk=force_vdisk) - if not fn: return - - try: - with CardSlot(force_vdisk=force_vdisk) as card: - with open(fn, 'rt') as fp: - data = fp.read() - except CardMissingError: - await needs_microsd() - return - - from auth import maybe_enroll_xpub - try: - possible_name = (fn.split('/')[-1].split('.'))[0] - maybe_enroll_xpub(config=data, name=possible_name) - except Exception as e: - #import sys; sys.print_exception(e) - await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) - # EOF diff --git a/shared/nfc.py b/shared/nfc.py index 2a7c57e15..be66c5001 100644 --- a/shared/nfc.py +++ b/shared/nfc.py @@ -7,7 +7,7 @@ # - has GPIO signal "??" which is multipurpose on its own pin # - this chip chosen because it can disable RF interaction # -import utime, ngu, ndef, stash +import utime, ngu, ndef, stash, chains from uasyncio import sleep_ms import uasyncio as asyncio from ustruct import pack, unpack @@ -15,7 +15,7 @@ from ubinascii import b2a_base64, a2b_base64 from ux import ux_show_story, ux_wait_keydown, OK, X -from utils import B2A, problem_file_line, parse_addr_fmt_str, txid_from_fname +from utils import B2A, problem_file_line, txid_from_fname from public_constants import AF_CLASSIC from charcodes import KEY_ENTER, KEY_CANCEL @@ -107,13 +107,14 @@ async def wipe(self, full_wipe): from glob import dis here = bytes(256) end = 8196 - for pos in range(0, end, 256) : + for pos in range(0, end, 256): self.i2c.writeto_mem(I2C_ADDR_USER, pos, here, addrsize=16) - if pos == 256 and not full_wipe: break + if (pos == 256) and not full_wipe: break # 6ms per 16 byte row, worst case, so ~100ms here per iter! 3.2seconds total if full_wipe: dis.progress_bar_show(pos / end) + await self.wait_ready() # system config area (flash cells, but affect operation): table 12 @@ -224,6 +225,14 @@ def setup(self): self.set_rf_disable(1) + async def share_loop(self, n, **kws): + while 1: + done = await self.share_start(n, **kws) + if done: + # do not wipe if we are not done + await self.wipe(kws.get("is_secret", False)) + break + async def share_signed_txn(self, txid, file_offset, txn_len, txn_sha): # we just signed something, share it over NFC if txn_len >= MAX_NFC_SIZE: @@ -231,13 +240,20 @@ async def share_signed_txn(self, txid, file_offset, txn_len, txn_sha): return n = ndef.ndefMaker() + line2 = None if txid is not None: n.add_text('Signed Transaction: ' + txid) n.add_custom('bitcoin.org:txid', a2b_hex(txid)) # want binary + line2 = self.txid_line2(txid) + n.add_custom('bitcoin.org:sha256', txn_sha) n.add_large_object('bitcoin.org:txn', file_offset, txn_len) - return await self.share_start(n) + return await self.share_loop(n, line2=line2) + + @staticmethod + def txid_line2(txid): + return "Signed TXID: %s⋯%s" % (txid[0:8], txid[-8:]) async def share_push_tx(self, url, txid, txn, txn_sha, line2=None): # Given a signed TXN, we convert to URL which a web backend can broadcast directly @@ -267,13 +283,9 @@ async def share_push_tx(self, url, txid, txn, txn_sha, line2=None): n.add_url(url, https=is_https) if line2 is None: - line2 = "Signed TXID: %s⋯%s" % (txid[0:8], txid[-8:]) - - while 1: - done = await self.share_start(n, prompt="Tap to broadcast, CANCEL when done", - line2=line2) + line2 = self.txid_line2(txid) - if done: break + await self.share_loop(n, prompt="Tap to broadcast, CANCEL when done", line2=line2) async def push_tx_from_file(self): # Pick (signed txn) file from SD card and broadcast via PushTx @@ -343,24 +355,19 @@ async def share_psbt(self, file_offset, psbt_len, psbt_sha, label=None): return n = ndef.ndefMaker() - n.add_text(label or 'Partly signed PSBT') + label = label or 'Partly signed PSBT' + n.add_text(label) n.add_custom('bitcoin.org:sha256', psbt_sha) n.add_large_object('bitcoin.org:psbt', file_offset, psbt_len) - return await self.share_start(n) - - async def share_deposit_address(self, addr, **kws): - n = ndef.ndefMaker() - n.add_text('Deposit Address') - n.add_custom('bitcoin.org:addr', addr.encode()) - return await self.share_start(n, **kws) + return await self.share_loop(n, line2=label) async def share_json(self, json_data, **kws): # a text file of JSON for programs to read n = ndef.ndefMaker() n.add_mime_data('application/json', json_data) - return await self.share_start(n, **kws) + return await self.share_loop(n, **kws) async def share_text(self, data, **kws): # share text from a list of values @@ -368,7 +375,7 @@ async def share_text(self, data, **kws): n = ndef.ndefMaker() n.add_text(data) - return await self.share_start(n, **kws) + return await self.share_loop(n, **kws) async def wait_ready(self): # block until chip ready to continue (ACK happens) @@ -394,11 +401,12 @@ async def setup_gpio(self): self.write_dyn(GPO_CTRL_Dyn, 0x01) # GPO_EN self.read_dyn(IT_STS_Dyn) # clear interrupt - async def ux_animation(self, write_mode, allow_enter=True, prompt=None, line2=None): + async def ux_animation(self, write_mode, allow_enter=True, prompt=None, line2=None, + is_secret=False): # Run the pretty animation, and detect both when we are written, and/or key to exit/abort. # - similar when "read" and then removed from field # - return T if aborted by user - from glob import dis, numpad + from glob import dis await self.wait_ready() self.set_rf_disable(0) @@ -467,8 +475,6 @@ async def ux_animation(self, write_mode, allow_enter=True, prompt=None, line2=No break self.set_rf_disable(1) - if not write_mode: - await self.wipe(False) return aborted @@ -476,9 +482,7 @@ async def share_start(self, ndef_obj, **kws): # do the UX while we are sharing a value over NFC # - assumpting is people know what they are scanning # - x key to abort early, but also self-clears - await self.big_write(ndef_obj.bytes()) - return await self.ux_animation(False, **kws) async def start_nfc_rx(self, **kws): @@ -514,8 +518,7 @@ async def start_nfc_rx(self, **kws): await self.wipe(False) return rv - - async def start_psbt_rx(self): + async def start_psbt_rx(self, miniscript_wallet=None): from auth import psbt_encoding_taster, TXN_INPUT_OFFSET from auth import UserAuthorizedAction, ApproveTransaction from ux import the_ux @@ -540,10 +543,7 @@ async def start_psbt_rx(self): if urn == 'urn:nfc:ext:bitcoin.org:sha256' and len(msg) == 32: # probably produced by another Coldcard: SHA256 over expected contents psbt_sha = bytes(msg) - except Exception as e: - # dont crash when given garbage - import sys; sys.print_exception(e) - pass + except Exception: pass # dont crash when given garbage if psbt_in is None: await ux_show_story("Could not find PSBT in what was written.", title="Sorry!") @@ -564,44 +564,13 @@ async def start_psbt_rx(self): # start signing UX UserAuthorizedAction.cleanup() - UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, 0x0, psbt_sha=psbt_sha, - approved_cb=self.signing_done) + UserAuthorizedAction.active_request = ApproveTransaction( + psbt_len, psbt_sha=psbt_sha, input_method="nfc", + output_encoder=output_encoder, miniscript_wallet=miniscript_wallet, + ) # kill any menu stack, and put our thing at the top the_ux.push(UserAuthorizedAction.active_request) - async def signing_done(self, psbt): - # User approved the PSBT, and signing worked... share result over NFC (only) - from auth import TXN_OUTPUT_OFFSET, try_push_tx - from version import MAX_TXN_LEN - from sffile import SFFile - - txid = None - - # asssume they want final transaction when possible, else PSBT output - is_comp = psbt.is_complete() - - # re-serialize the PSBT back out (into PSRAM) - with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as fd: - if is_comp: - txid = psbt.finalize(fd) - else: - psbt.serialize(fd) - - self.result = (fd.tell(), fd.checksum.digest()) - - out_len, out_sha = self.result - - if is_comp: - if txid and await try_push_tx(out_len, txid, out_sha): - return # success, exit - - await self.share_signed_txn(txid, TXN_OUTPUT_OFFSET, out_len, out_sha) - else: - await self.share_psbt(TXN_OUTPUT_OFFSET, out_len, out_sha) - - # ? show txid on screen ? - # thank them? - @classmethod async def selftest(cls): # Check for chip present, field present .. and that it works @@ -610,10 +579,12 @@ async def selftest(cls): n.setup() assert n.uid - aborted = await n.share_text("NFC is working: %s" % n.get_uid(), allow_enter=False) + nn = ndef.ndefMaker() + nn.add_text("NFC is working: %s" % n.get_uid()) + + aborted = await n.share_start(nn, allow_enter=False) assert not aborted, "Aborted" - async def share_file(self): # Pick file from SD card and share over NFC... from actions import file_picker @@ -659,82 +630,33 @@ def is_suitable(fname): else: raise ValueError(ext) - async def import_multisig_nfc(self, *a): - # user is pushing a file downloaded from another CC over NFC - # - would need an NFC app in between for the sneakernet step - # get some data - data = await self.start_nfc_rx() - if not data: return - - winner = None - for urn, msg, meta in ndef.record_parser(data): - if len(msg) < 70: continue - msg = bytes(msg).decode() # from memory view - # multi( catches both multi( and sortedmulti( - if 'pub' in msg or "multi(" in msg: - winner = msg - break - - if not winner: - await ux_show_story('Unable to find multisig descriptor.') - return - - from auth import maybe_enroll_xpub - try: - maybe_enroll_xpub(config=winner) - except Exception as e: - #import sys; sys.print_exception(e) - await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) - async def import_ephemeral_seed_words_nfc(self, *a): - data = await self.start_nfc_rx() - if not data: return + def f(m): + sm = m.decode().strip().split(" ") + if len(sm) in stash.SEED_LEN_OPTS: + return sm - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode().strip() # from memory view - split_msg = msg.split(" ") - if len(split_msg) in stash.SEED_LEN_OPTS: - winner = split_msg - break - - if not winner: - await ux_show_story('Unable to find seed words') - return - - try: - from seed import set_ephemeral_seed_words - await set_ephemeral_seed_words(winner, meta='NFC Import') - except Exception as e: - #import sys; sys.print_exception(e) - await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) + winner = await self._nfc_reader(f, 'Unable to find seed words') - async def confirm_share_loop(self, string): - while True: - # added loop here as NFC send can fail, or not send the data - # and in that case one would have to start from beginning (send us cmd, approve, etc.) - # => get chance to check if you received the data and if something went wrong - retry just send - await self.share_text(string) - ch = await ux_show_story(title="Shared", msg="Press %s to share again, otherwise %s to stop." % (OK, X)) - if ch != "y": - break + if winner: + try: + from seed import set_ephemeral_seed_words + await set_ephemeral_seed_words(winner, origin='NFC Import') + except Exception as e: + #import sys; sys.print_exception(e) + await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) async def address_show_and_share(self): - from auth import show_address, ApproveMessageSign + from auth import show_address - data = await self.start_nfc_rx() - if not data: return + def f(m): + sm = m.decode().split("\n") + if 1 <= len(sm) <= 2: + return sm - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view - split_msg = msg.split("\n") - if 1 <= len(split_msg) <= 2: - winner = split_msg - break + winner = await self._nfc_reader(f, 'Expected address and derivation path.') if not winner: - await ux_show_story('Expected address and derivation path.') return if len(winner) == 1: @@ -743,7 +665,7 @@ async def address_show_and_share(self): else: subpath, addr_fmt_str = winner try: - addr_fmt = parse_addr_fmt_str(addr_fmt_str) + addr_fmt = chains.parse_addr_fmt_str(addr_fmt_str) except AssertionError as e: await ux_show_story(str(e)) return @@ -754,133 +676,124 @@ async def address_show_and_share(self): await the_ux.interact() # need this otherwise NFC animation takes over async def start_msg_sign(self): - from auth import UserAuthorizedAction, ApproveMessageSign - from ux import the_ux - - UserAuthorizedAction.cleanup() + from auth import approve_msg_sign - data = await self.start_nfc_rx() - if not data: return - - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view - split_msg = msg.split("\n") + def f(m): + m = m.decode() + split_msg = m.split("\n") if 1 <= len(split_msg) <= 3: - winner = split_msg - break + return m + winner = await self._nfc_reader(f, 'Unable to find correctly formated message to sign.') if not winner: - await ux_show_story('Unable to find correctly formated message to sign.') return - if len(winner) == 1: - text = winner[0] - subpath = "m" - addr_fmt = AF_CLASSIC - elif len(winner) == 2: - text, subpath = winner - addr_fmt = AF_CLASSIC # maybe default to native segwit? - else: - # len(winner) == 3 - text, subpath, addr_fmt = winner - - UserAuthorizedAction.check_busy(ApproveMessageSign) - try: - UserAuthorizedAction.active_request = ApproveMessageSign( - text, subpath, addr_fmt, approved_cb=self.msg_sign_done - ) - the_ux.push(UserAuthorizedAction.active_request) - except AssertionError as exc: - await ux_show_story("Problem: %s\n\nMessage to be signed must be a single line of ASCII text." % exc) - return + await approve_msg_sign(None, None, None, approved_cb=self.msg_sign_done, + msg_sign_request=winner) async def msg_sign_done(self, signature, address, text): - from auth import rfc_signature_template_gen + from msgsign import rfc_signature_template sig = b2a_base64(signature).decode('ascii').strip() - armored_str = "".join(rfc_signature_template_gen(addr=address, msg=text, sig=sig)) - await self.confirm_share_loop(armored_str) + armored_str = "".join(rfc_signature_template(addr=address, msg=text, sig=sig)) + await self.share_text(armored_str) async def verify_sig_nfc(self): - from auth import verify_armored_signed_msg + from msgsign import verify_armored_signed_msg - data = await self.start_nfc_rx() - if not data: return + f = lambda x: x.decode().strip() if b"SIGNED MESSAGE" in x else None + winner = await self._nfc_reader(f, 'Unable to find signed message.') - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view - if "SIGNED MESSAGE" in msg: - winner = msg.strip() - break + if winner: + await verify_armored_signed_msg(winner, digest_check=False) - if not winner: - await ux_show_story('Unable to find signed message.') - return + async def read_address(self): + # Read an address or BIP-21 url and parse out addr (just one) + from utils import decode_bip21_text - await verify_armored_signed_msg(winner, digest_check=False) + def f(m): + m = m.decode() + what, vals = decode_bip21_text(m) + if what == 'addr': + return vals + + winner = await self._nfc_reader(f, 'Unable to find address from NFC data.') + + return winner async def verify_address_nfc(self): # Get an address or complete bip-21 url even and search it... slow. - from utils import decode_bip21_text + _, addr, args = await self.read_address() + if addr: + from ownership import OWNERSHIP + await OWNERSHIP.search_ux(addr, args) + + async def read_extended_private_key(self): + f = lambda x: x.decode().strip() if b"prv" in x else None + return await self._nfc_reader(f, 'Unable to find extended private key.') + + async def read_tapsigner_b64_backup(self): + f = lambda x: a2b_base64(x.decode()) if 150 <= len(x) <= 280 else None + return await self._nfc_reader(f, 'Unable to find base64 encoded TAPSIGNER backup.') + async def _nfc_reader(self, func, fail_msg): data = await self.start_nfc_rx() if not data: return winner = None for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view + msg = bytes(msg) try: - what, vals = decode_bip21_text(msg) - if what == 'addr': - winner = vals[1] + r = func(msg) + if r is not None: + winner = r break - except ValueError: + except: pass if not winner: - await ux_show_story('Unable to find address from NFC data.') - return - - from ownership import OWNERSHIP - await OWNERSHIP.search_ux(winner) - - async def read_extended_private_key(self): - data = await self.start_nfc_rx() - if not data: return - - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view - if "prv" in msg: - winner = msg.strip() - break - - if not winner: - await ux_show_story('Unable to find extended private key.') + await ux_show_story(fail_msg) return return winner - async def read_tapsigner_b64_backup(self): - data = await self.start_nfc_rx() - if not data: return - - winner = None - for urn, msg, meta in ndef.record_parser(data): - msg = bytes(msg).decode() # from memory view + async def read_bsms_token(self): + def f(m): + m = m.decode().strip() try: - if 150 <= len(msg) <= 280: - winner = a2b_base64(msg) - break - except: - pass + int(m, 16) + return m + except: pass + + return await self._nfc_reader(f, 'Unable to find BSMS token in NDEF data') + async def read_bsms_data(self): + def f(m): + m = m.decode().strip() # from memory view + try: + if "BSMS" in m or int(m[:6], 16): + # unencrypted/encrypted case + return m + except: pass + + return await self._nfc_reader(f, 'Unable to find BSMS data in NDEF data') + + async def import_miniscript_nfc(self): + def f(m): + if len(m) < 70: return + m = m.decode() + # TODO this should be Descriptor.is_descriptor() ? + if 'pub' in m: + return m + + winner = await self._nfc_reader(f, 'Unable to find miniscript descriptor expected in NDEF') if not winner: - await ux_show_story('Unable to find base64 encoded TAPSIGNER backup.') return - return winner + from auth import maybe_enroll_xpub + try: + maybe_enroll_xpub(config=winner) + except Exception as e: + await ux_show_story('Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) # EOF diff --git a/shared/notes.py b/shared/notes.py index 108bfd975..d1fd1f76e 100644 --- a/shared/notes.py +++ b/shared/notes.py @@ -13,7 +13,7 @@ from charcodes import KEY_QR, KEY_NFC, KEY_CANCEL from charcodes import KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6 from lcd_display import CHARS_W -from utils import problem_file_line, url_decode +from utils import problem_file_line, url_unquote, wipe_if_deltamode # title, username and such are limited that they fit on the one line both in # text entry (W-2) and also in menu display (W-3) @@ -21,7 +21,18 @@ ONE_LINE = CHARS_W-2 async def make_notes_menu(*a): - if settings.get('notes', False) == False: + from pincodes import pa + + if pa.hobbled_mode: + # Read only version of menu system + # - used when spending policy in effect + # - must have some notes already, or unreachable + assert NoteContent.count() + rv = NotesMenu(NotesMenu.construct_readonly()) + rv.readonly = True + return rv + + if not settings.get('secnap', False): # Explain feature, and then enable if interested. Drop them into menu. ch = await ux_show_story('''\ Enable this feature to store short text notes and passwords inside the Coldcard. @@ -34,15 +45,17 @@ async def make_notes_menu(*a): if ch != 'y': return - # mark as enabled (altho empty) - settings.set('notes', []) + # mark as enabled + settings.set('secnap', True) + if settings.get('notes', None) is None: + settings.set('notes', []) # need to correct top menu now, so this choice is there. goto_top_menu() return NotesMenu(NotesMenu.construct()) -async def get_a_password(old_value): +async def get_a_password(old_value, min_len=0, max_len=128): # Get a (new) password as a string. # - does some fun generation as well. @@ -96,12 +109,14 @@ async def _toggle_case(was): handlers = {KEY_F1: _pick_12, KEY_F2: _pick_24, KEY_F3: _pick_dense, KEY_F4: _do_dumb, KEY_F6: _toggle_case, KEY_F5: _bip85} - return await ux_input_text(old_value, confirm_exit=False, max_len=128, scan_ok=True, - b39_complete=True, prompt='Password', placeholder='(optional)', - funct_keys=(fmsg, handlers)) + return await ux_input_text(old_value, confirm_exit=False, max_len=max_len, min_len=min_len, + scan_ok=True, b39_complete=True, prompt='Password', + placeholder='(optional)', funct_keys=(fmsg, handlers)) class NotesMenu(MenuSystem): + readonly = False + @classmethod def construct(cls): # Dynamic menu with user-defined names of notes shown @@ -110,9 +125,12 @@ def construct(cls): MenuItem('New Password', f=cls.new_note, arg='p'), ShortcutItem(KEY_QR, f=cls.quick_create)] - if not NoteContent.count(): + cnt = NoteContent.count() + if not cnt: rv = news + [ MenuItem('Disable Feature', f=cls.disable_notes) ] else: + wipe_if_deltamode() + rv = [] for note in NoteContent.get_all(): rv.append(MenuItem('%d: %s' % (note.idx+1, note.title), menu=note.make_menu)) @@ -121,14 +139,37 @@ def construct(cls): rv.append(MenuItem('Export All', f=cls.export_all)) + if cnt >= 2: + rv.append(MenuItem('Sort By Title', f=cls.sort_titles)) + rv.append(MenuItem('Import', f=import_from_other)) return rv + @classmethod + def construct_readonly(cls): + # When only allowed to view, no export/add new/delete. + wipe_if_deltamode() + + rv = [] + for note in NoteContent.get_all(): + rv.append(MenuItem('%d: %s' % (note.idx+1, note.title), + menu=note.make_menu, arg=True)) # readonly=True + + return rv + @classmethod async def export_all(cls, *a): await start_export(NoteContent.get_all()) + @classmethod + async def sort_titles(cls, menu, _, item): + # sort by title, one time and then reconstruct menu + NoteContent.sort_all() + + # force redraw + menu.update_contents() + @classmethod async def quick_create(cls, menu, _, item): # using QR, created a Note (never a password) with auto-generated title. @@ -145,7 +186,7 @@ async def quick_create(cls, menu, _, item): if got.startswith('otpauth://totp/'): # see - tmp.title = url_decode(got[15:]).split('?', 1)[0] + tmp.title = url_unquote(got[15:]).split('?', 1)[0] elif got.startswith('otpauth-migration://offline'): # see tmp.title = 'Google Auth' @@ -159,7 +200,6 @@ async def quick_create(cls, menu, _, item): await tmp._save_ux(menu) await cls.drill_to(menu, tmp) - def update_contents(self): # Reconstruct the list of notes on this dynamic menu, because # we added or changed them and are showing that same menu again. @@ -170,6 +210,7 @@ def update_contents(self): async def disable_notes(cls, *a): # they don't want feature anymore; already checked no notes in effect # - no need for confirm, they aren't loosing anything + settings.remove_key('secnap') settings.remove_key('notes') settings.save() @@ -188,8 +229,8 @@ async def new_note(cls, menu, _, item): async def drill_to(cls, menu, item): # make it so looks like we drilled down into the new note menu.goto_idx(item.idx) - m = MenuSystem(await item.make_menu()) - the_ux.push(m) + m = await item._make_menu() + the_ux.push(MenuSystem(m)) class NoteContentBase: @@ -223,6 +264,17 @@ def count(cls): # how many do we have? return len(settings.get('notes', [])) + @classmethod + def sort_all(cls): + # sort and resave all notes based on title + # - careful: self.idx values will be wrong for any existing instances + # - 'title' is only common field to subclasses + notes = cls.get_all() + notes.sort(key=lambda j: j.title.lower()) + + settings.put('notes', [n.serialize() for n in notes]) + settings.save() + async def delete(self, *a): # Remove note ok = await ux_confirm("Everything about this note/password will be lost.") @@ -246,7 +298,7 @@ async def delete(self, *a): await ux_dramatic_pause('Deleted.', 3) - async def share_nfc(self, menu, _, item): + async def share_nfc(self, a, b, item): # share something via NFC -- if small enough and enabled from glob import NFC @@ -256,12 +308,26 @@ async def share_nfc(self, menu, _, item): if len(v) < 8000: # see MAX_NFC_SIZE await NFC.share_text(v) + async def view_qr(self, k): + # full screen QR + try: + await show_qr_code(getattr(self, k), msg=self.title, is_secret=True) + except Exception as exc: + # - not all data can be a QR (non-text, binary, zeros) + # - might be too big for single QR + # - may be a RuntimeError(n) where n is line number inside uqr + await ux_show_story("Unable to display as QR.\n\nError: " + str(exc)) + + async def view_qr_menu(self, a, b, item): + await self.view_qr(item.arg) + async def _save_ux(self, menu): is_new = self.save() if not is_new: # change our own menu contents - menu.replace_items(await self.make_menu()) + mi = await self._make_menu() + menu.replace_items(mi) # update parent parent = the_ux.parent_of(menu) @@ -289,29 +355,50 @@ async def export(self, *a): # single export await start_export([self]) + async def sign_txt_msg(self, a, b, item): + from msgsign import ux_sign_msg, msg_signing_done + txt = item.arg + await ux_sign_msg(txt, approved_cb=msg_signing_done, kill_menu=False) + + def sign_misc_menu_item(self): + return MenuItem("Sign Note Text", f=self.sign_txt_msg, arg=self.misc) + + class PasswordContent(NoteContentBase): # "Passwords" have a few more fields and are more structured flds = ['title', 'user', 'password', 'site', 'misc' ] type_label = 'password' - async def make_menu(self, *a): + async def _make_menu(self, readonly=False): rv = [MenuItem('"%s"' % self.title, f=self.view)] if self.user: rv.append(MenuItem('↳ %s' % self.user, f=self.view)) if self.site: rv.append(MenuItem('↳ %s' % self.site, f=self.view)) - #if self.misc: rv.append(MenuItem('↳ (notes)', f=self.view)) - return rv + [ + # if self.misc: rv.append(MenuItem('↳ (notes)', f=self.view)) + rv += [ MenuItem('View Password', f=self.view_pw), MenuItem('Send Password', f=self.send_pw, predicate=lambda: settings.get('du', True)), - MenuItem('Export', f=self.export), - MenuItem('Edit Metadata', f=self.edit), - MenuItem('Delete', f=self.delete), - MenuItem('Change Password', f=self.change_pw), - ShortcutItem(KEY_QR, f=self.view_qr), - ShortcutItem(KEY_NFC, f=self.share_nfc, arg='password'), + ] + if not readonly: + rv += [ + MenuItem('Export', f=self.export), + MenuItem('Edit Metadata', f=self.edit), + MenuItem('Delete', f=self.delete), + MenuItem('Change Password', f=self.change_pw), + ] + rv += [ + self.sign_misc_menu_item(), + ShortcutItem(KEY_QR, f=self.view_qr_menu, arg=self.type_label), + ShortcutItem(KEY_NFC, f=self.share_nfc, arg=self.type_label), ] + return rv + + async def make_menu(self, a, b, item): + items = await self._make_menu(readonly=item.arg) + return MenuSystem(items) + async def view(self, *a): pl = len(self.password) m = '' @@ -350,7 +437,7 @@ async def view_pw(self, *a): ch = await ux_show_story(msg, title=self.title, escape=KEY_QR, hint_icons=KEY_QR) if ch == KEY_QR: - await self.view_qr() + await self.view_qr(self.type_label) async def send_pw(self, *a): # use USB to send it -- weak at present @@ -362,10 +449,6 @@ async def send_pw(self, *a): "we cannot type at this time.") await single_send_keystrokes(self.password) - async def view_qr(self, *a): - # full screen QR - await show_qr_code(self.password, msg=self.title) - async def edit(self, menu, _, item): # Edit, also used for add new @@ -429,33 +512,34 @@ class NoteContent(NoteContentBase): flds = ['title', 'misc'] type_label = 'note' - async def make_menu(self, *a): + async def _make_menu(self, readonly=False): # Details and actions for this Note - return [ + rv = [ MenuItem('"%s"' % self.title, f=self.view), MenuItem('View Note', f=self.view), - MenuItem('Edit', f=self.edit), - MenuItem('Delete', f=self.delete), - MenuItem('Export', f=self.export), - ShortcutItem(KEY_QR, f=self.view_qr), + ] + if not readonly: + rv += [ + MenuItem('Edit', f=self.edit), + MenuItem('Delete', f=self.delete), + MenuItem('Export', f=self.export), + ] + rv += [ + self.sign_misc_menu_item(), + ShortcutItem(KEY_QR, f=self.view_qr_menu, arg="misc"), ShortcutItem(KEY_NFC, f=self.share_nfc, arg='misc'), ] + return rv + + async def make_menu(self, a, b, item): + items = await self._make_menu(readonly=item.arg) + return MenuSystem(items) async def view(self, *a): ch = await ux_show_story(self.misc, title=self.title, escape=KEY_QR, hint_icons=KEY_QR) if ch == KEY_QR: - await self.view_qr() - - async def view_qr(self, *a): - # full screen QR - try: - await show_qr_code(self.misc, msg=self.title) - except Exception as exc: - # - not all data can be a QR (non-text, binary, zeros) - # - might be too big for single QR - # - may be a RuntimeError(n) where n is line number inside uqr - await ux_show_story("Unable to display as QR.\n\nError: "+str(exc)) + await self.view_qr("misc") async def edit(self, menu, _, item): # Edit, also used for add new @@ -498,16 +582,16 @@ async def edit(self, menu, _, item): async def start_export(notes): # Save out notes/passwords from glob import NFC - from auth import write_sig_file + from msgsign import write_sig_file import ujson as json from ux_q1 import show_bbqr_codes singular = (len(notes) == 1) item = notes[0].type_label if singular else 'all notes & passwords' - choice = await import_export_prompt(item, is_import=False, title="Data Export", no_nfc=True, - footnotes="\n\nWARNING: No encryption happens here. " - "Your secrets will be cleartext.") + choice = await import_export_prompt(item, title="Data Export", no_nfc=True, + footnotes="WARNING: No encryption happens here." + " Your secrets will be cleartext.") if choice == KEY_CANCEL: return @@ -536,7 +620,7 @@ async def start_export(notes): await needs_microsd() return except Exception as e: - await ux_show_story('Failed to write!\n\n\n'+str(e)) + await ux_show_story('Failed to write!\n\n'+str(e)) return msg = 'Export file written:\n\n%s\n\nSignature file written:\n\n%s' % ( @@ -565,14 +649,11 @@ async def import_from_other(menu, *a): else: def contains_json(fname): if not fname.endswith('.json'): return False - print(fname) try: obj = json.load(open(fname, 'rt')) assert 'coldcard_notes' in obj return True - except Exception as exc: - import sys; sys.print_exception(exc) - pass + except: pass fn = await file_picker(min_size=8, max_size=100000, taster=contains_json, **choice) if not fn: return @@ -581,7 +662,13 @@ def contains_json(fname): records = json.load(open(fn, 'rt')) # We have some JSON, parsed now. - # - should dedup, but we aren't + await import_from_json(records) + + await ux_dramatic_pause('Saved.', 3) + menu.update_contents() + +async def import_from_json(records): + # should dedup, but we aren't try: assert 'coldcard_notes' in records, 'Incorrect format' @@ -591,14 +678,11 @@ def contains_json(fname): was = list(settings.get('notes', [])) was.extend(new) - settings.put('notes', was) + settings.set('notes', was) + settings.set('secnap', True) settings.save() except Exception as e: await ux_show_story(title="Failure", msg=str(e) + '\n\n' + problem_file_line(e)) - - await ux_dramatic_pause('Saved.', 3) - menu.update_contents() - # EOF diff --git a/shared/nvstore.py b/shared/nvstore.py index 6151c2c04..f162dc6de 100644 --- a/shared/nvstore.py +++ b/shared/nvstore.py @@ -8,13 +8,13 @@ # - recover from empty/blank/failed chips w/o user action # # Result: -# - up to 4k of values supported (after json encoding) -# - encrypted and stored in SPI flash, in last 128k area +# - up to a few k of values supported (after json encoding) +# - encrypted and stored in main flash, in a dedicated 512k area # - AES encryption key is derived from actual wallet secret # - if logged out, then use fixed key instead (ie. it's public) # - you cannot move data between slots because AES-CTR with CTR seed based on slot # # - SHA-256 check on decrypted data -# - (Mk4) each slot is a file on /flash/settings +# - each "slot" is a file in /flash/settings; in Mk1-3 was SPI flash block # - os.sync() not helpful because block device under filesystem doesnt implement it # import os, ujson, ustruct, ckcc, gc, ngu, aes256ctr, version @@ -32,7 +32,8 @@ # batt_to = (when on battery only) idle timeout period # _age = internal verison number for data (see below) # tested = selftest has been completed successfully -# multisig = list of defined multisig wallets (complex) +# multisig = list of defined multisig wallets (complex) [before removal of MultisigWallet] +# miniscript = list of defined miniscript wallets, including multisig (complex) # pms = trust/import/distrust xpubs found in PSBT files # fee_limit = (int) percentage of tx value allowed as max fee # axi = index of last selected address in explorer @@ -56,13 +57,18 @@ # seedvault = (bool) opt-in enable seed vault feature # seeds = list of stored secrets for seedvault feature # bright = (int:0-255) LCD brightness when on battery +# secnap = (bool) opt-in enable Secure Notes & Passwords feature # notes = (complex) Secure notes held for user, see notes.py # accts = (list of tuples: (addr_fmt, account#)) Single-sig wallets we've seen them use # aei = (bool) allow changing start index in Address Explorer # b85max = (bool) allow max BIP-32 int value in BIP-85 derivations # ptxurl = (str) URL for PushTx feature, clear to disable feature # hmx = (bool) Force display of current XFP in home menu, even w/o tmp seed active -# unsort_ms = (bool) Allow unsorted multisig with BIP-67 disabled +# ccc = (complex) If present, CCC feature is enabled and key details stored here. +# ktrx = (privkey) Key teleport Rx has been started, this will be our keypair +# aemscsv = (bool) opt-in enable more verbose CSV output for miniscript wallets with Derivations and Scripts +# sssp = (complex) If present, a (single signer) spending-policy is defined (maybe disabled) +# lfr = (string) If present, the reason why Spending Policy blocked last transaction # Stored w/ key=00 for access before login # _skip_pin = hard code a PIN value (dangerous, only for debug) @@ -76,16 +82,19 @@ # terms_ok = customer has signed-off on the terms of sale # settings linked to seed -# LINKED_SETTINGS = ["multisig", "tp", "ovc", "xfp", "xpub", "words"] +# LINKED_SETTINGS = ["miniscript", "tp", "ovc", "xfp", "xpub", "words"] # settings that does not make sense to copy to temporary secret # LINKED_SETTINGS += ["sd2fa", "usr", "axi", "hsmcmd"] # prelogin settings - do not need to be part of other saved settings # PRELOGIN_SETTINGS = ["_skip_pin", "nick", "rngk", "lgto", "kbtn", "terms_ok"] # keep these settings only if unspecified on the other end -KEEP_IF_BLANK_SETTINGS = ["bkpw", "wa", "sighshchk", "emu", "rz", "b39skip", - "axskip", "del", "pms", "idle_to", "batt_to", "bright"] +KEEP_IF_BLANK_SETTINGS = ["wa", "sighshchk", "emu", "rz", "b39skip", + "axskip", "del", "pms", "idle_to", "batt_to", + "bright"] -SEEDVAULT_FIELDS = ['seeds', 'seedvault', 'xfp', 'words'] +# key value pairs saved directly to master seed settings +# held in RAM for tmp seed sessions +MASTER_FIELDS = ['seeds', 'seedvault', 'xfp', 'words', "bkpw", "sssp"] NUM_SLOTS = const(100) SLOTS = range(NUM_SLOTS) @@ -175,6 +184,13 @@ def get_capacity(self): return (blocks-bfree) / blocks def _open_file(self, pos, mode='rb'): + if 'w' in mode: + # make directory, when needed (recovery/robustness) + try: + os.stat(MK4_WORKDIR) + except OSError: # ENOENT + os.mkdir(MK4_WORKDIR[:-1]) + return open(MK4_FILENAME(pos), mode) def _slot_is_blank(self, pos, buf): @@ -191,13 +207,13 @@ def _wipe_slot(self, pos): fn = MK4_FILENAME(pos) try: os.remove(fn) - except Exception: - # Error (ENOENT) expected here when saving first time, because the + except: + # OSError (ENOENT) expected here when saving first time, because the # "old" slot was not in use pass def _read_slot(self, pos, decryptor): - # Mk4 is just reading a binary file and decrypt as we go. + # read a binary file and decrypt as we go. with self._open_file(pos) as fd: # missing ftell(), so emulate ln = fd.seek(0, 2) @@ -242,9 +258,12 @@ def _write_slot(self, pos, aes): fd.write(aes(chk.digest())) def _used_slots(self): - # mk4: faster list of slots in use; doesn't open them - files = os.listdir(MK4_WORKDIR) - return [int(fn[0:-4], 16) for fn in files if fn.endswith('.aes')] + # list of slots in use; doesn't open them + try: + files = os.listdir(MK4_WORKDIR) + return [int(fn[0:-4], 16) for fn in files if fn.endswith('.aes')] + except: + return [] def _nonempty_slots(self, dis=None): # generate slots that are non-empty @@ -265,10 +284,11 @@ def _nonempty_slots(self, dis=None): def leaving_master_seed(self): # going from master seed to a tmp seed, so capture a few values we need. + self.save_if_dirty() SettingsObject.master_nvram_key = self.nvram_key - for fn in SEEDVAULT_FIELDS: + for fn in MASTER_FIELDS: curr = self.current.get(fn, None) if curr is not None: SettingsObject.master_sv_data[fn] = curr @@ -284,7 +304,7 @@ def return_to_master_seed(self): SettingsObject.master_sv_data.clear() SettingsObject.master_nvram_key = None - def master_set(self, key, value): + def master_set(self, key, value, master_only=False): # Set a value, and it must be saved under the master seed's # Concern is we may be changing a setting from a tmp seed mode # - always does a save @@ -295,6 +315,7 @@ def master_set(self, key, value): self.set(key, value) self.save() else: + assert not master_only # harder, slower: have to load, change and write master = SettingsObject(nvram_key=SettingsObject.master_nvram_key) master.load() @@ -303,7 +324,7 @@ def master_set(self, key, value): del master # track our copies - if key in SEEDVAULT_FIELDS: + if key in MASTER_FIELDS: SettingsObject.master_sv_data[key] = value def master_get(self, kn, default=None): @@ -315,7 +336,7 @@ def master_get(self, kn, default=None): return self.get(kn, default) # LIMITATION: only supporting a few values we know we will need - assert kn in SEEDVAULT_FIELDS + assert kn in MASTER_FIELDS res = SettingsObject.master_sv_data.get(kn, default) if res is None: return default @@ -391,8 +412,9 @@ def put(self, kn, v): set = put def remove_key(self, kn): - self.current.pop(kn, None) - self.changed() + if kn in self.current: + self.current.pop(kn, None) + self.changed() def merge_previous_active(self, previous): import pyb @@ -400,7 +422,7 @@ def merge_previous_active(self, previous): if previous: for k in KEEP_IF_BLANK_SETTINGS: - if k in previous and k not in self.current: + if (k in previous) and (k not in self.current): self.current[k] = previous[k] # nfc, usb, vidsk handling @@ -450,11 +472,8 @@ async def write_out(self): call_later_ms(250, self.write_out) def find_spot(self, not_here=0): - # search for a blank sector to use - # - check randomly and pick first blank one (wear leveling, deniability) - # - we will write and then erase old slot + # search for a blank slot to use # - if "full", blow away a random one - # on mk4, use the filesystem to see what's already taken avail = set(SLOTS) - set(self._used_slots()) avail.discard(not_here) diff --git a/shared/opcodes.py b/shared/opcodes.py index d015d1737..7224795f0 100644 --- a/shared/opcodes.py +++ b/shared/opcodes.py @@ -82,7 +82,7 @@ #OP_RSHIFT = const(153) #OP_BOOLAND = const(154) #OP_BOOLOR = const(155) -#OP_NUMEQUAL = const(156) +OP_NUMEQUAL = const(156) #OP_NUMEQUALVERIFY = const(157) #OP_NUMNOTEQUAL = const(158) #OP_LESSTHAN = const(159) @@ -114,6 +114,7 @@ #OP_NOP8 = const(183) #OP_NOP9 = const(184) #OP_NOP10 = const(185) +OP_CHECKSIGADD = const(186) #OP_NULLDATA = const(252) #OP_PUBKEYHASH = const(253) #OP_PUBKEY = const(254) diff --git a/shared/ownership.py b/shared/ownership.py index 2eb6c9773..968eb22bb 100644 --- a/shared/ownership.py +++ b/shared/ownership.py @@ -2,16 +2,17 @@ # # ownership.py - store a cache of hashes related to addresses we might control. # -import os, sys, chains, ngu, struct, version +import os, chains, ngu, struct, version from glob import settings from ucollections import namedtuple from ubinascii import hexlify as b2a_hex from exceptions import UnknownAddressExplained +from utils import problem_file_line, show_single_address +from public_constants import AFC_SCRIPT, AF_P2WPKH_P2SH, AF_P2SH, AF_P2WSH_P2SH, AF_P2TR # Track many addresses, but in compressed form # - map from random Bech32/Base58 payment address to (wallet) + keypath -# - only normal (external, not change) addresses, and won't consider -# any keypath that does not end in 0/* +# - won't consider any keypath that does not end in <0;1>/* # - store only hints, since we can re-construct any address and want to fully verify # - try to keep private between different duress wallets, and seed vaults # - storing bulk data into LFS, not settings @@ -38,7 +39,7 @@ # target 3 flash blocks, max file size => 764 addresses MAX_ADDRS_STORED = const(764) # =((3*512) - OWNERSHIP_FILE_HDR_LEN) // HASH_ENC_LEN -BONUS_GAP_LIMIT = const(20) +BONUS_AFTER_MATCH = const(20) # number of addresses to still generate after match found def encode_addr(addr, salt): # Convert text address to something we can store while preserving privacy. @@ -49,12 +50,13 @@ class AddressCacheFile: def __init__(self, wallet, change_idx): self.wallet = wallet self.change_idx = change_idx - desc = wallet.to_descriptor().serialize() + desc = wallet.to_descriptor().to_string(internal=False) h = b2a_hex(ngu.hash.sha256d(wallet.chain.ctype + desc)) self.fname = h[0:32] + '-%d.own' % change_idx self.salt = h[32:] self.count = 0 self.hdr = None + self.fd = None self.peek() @@ -64,9 +66,6 @@ def nice_name(self): rv += ' (change)' return rv - def exists(self): - return bool(self.count) - def peek(self): # see what we have on-disk; just reads header. try: @@ -80,7 +79,7 @@ def peek(self): except OSError: return except Exception as exc: - sys.print_exception(exc) + # sys.print_exception(exc) self.count = 0 self.hdr = None return @@ -104,15 +103,14 @@ def setup(self, change_idx, start_idx): self.fd.write(hdr) def append(self, addr): - if addr is None: - # close file, done - self.fd.close() - del self.fd - return - - assert '_' not in addr self.fd.write(encode_addr(addr, self.salt)) + def close(self): + # close file, done + if self.fd is not None: + self.fd.close() + self.fd = None + def fast_search(self, addr): # Do the easy part of the searching, using the existing file's contents. # - generates candidate path subcomponents; might be false positive @@ -120,6 +118,7 @@ def fast_search(self, addr): from glob import dis if not self.hdr or not self.count: + # cache empty return with open(self.fname, 'rb') as fd: @@ -131,7 +130,7 @@ def fast_search(self, addr): chk = encode_addr(addr, self.salt) for idx in range(self.count): if buf[idx*HASH_ENC_LEN : (idx*HASH_ENC_LEN)+HASH_ENC_LEN] == chk: - yield (self.change_idx, idx) + yield self.change_idx, idx dis.progress_sofar(idx, self.count) @@ -147,93 +146,114 @@ def build_and_search(self, addr): # - return subpath for a hit or None from glob import dis - bonus = 0 match = None start_idx = self.count count = MAX_ADDRS_STORED - start_idx if count <= 0: - return None + return match self.setup(self.change_idx, start_idx) - for idx,here,*_ in self.wallet.yield_addresses(start_idx, count, - change_idx=self.change_idx): - - if here == addr: - # Found it! But keep going a little for next time. - match = (self.change_idx, idx) - + bonus = None + for idx,here,*_ in self.wallet.yield_addresses(start_idx, count, self.change_idx): self.append(here) self.count += 1 - if match: + + if bonus: + if bonus >= BONUS_AFTER_MATCH: + # do (at most) 20 more - limited by 'start_idx' & 'count' + break bonus += 1 - if match and bonus >= BONUS_GAP_LIMIT: - self.append(None) - return match - dis.progress_sofar(idx-start_idx, count) + if here == addr: + # match but keep going + match = (self.change_idx, idx) + bonus = 1 - self.append(None) + dis.progress_sofar(idx - start_idx, count) - return None + self.close() + return match class OwnershipCache: @classmethod - def saver(cls, wallet, change_idx, start_idx): - # when we are generating many addresses for export, capture them + def saver(cls, wallet, change_idx, start_idx, count): + # when we are generating many addresses for export, capture them (if suitable) # as we go with this function - # - not change -- only main addrs + if not count: + return + if change_idx not in (0, 1): + return + if start_idx >= MAX_ADDRS_STORED: + return + file = AddressCacheFile(wallet, change_idx) + current_pos = file.count + + if start_idx > current_pos: + # nothing to do here, we are missing some addresses in the middle + return + if (start_idx + count) <= current_pos: + # we already have all these addresses + return - if file.exists(): - # don't save to existing file, has some already - return None + file.setup(change_idx, current_pos) - try: - file.setup(change_idx, start_idx) - except: - # in some cases we don't want to save anything, not an error - return None + def doit(addr, idx): + if addr is None: + file.close() + elif (idx < MAX_ADDRS_STORED) and idx >= current_pos: + file.append(addr) - return file.append + return doit @classmethod - def search(cls, addr): - # Find it! - # - returns wallet object, and tuple2 of final 2 subpath components + def filter(cls, addr, args): + # Filter possible candidates! # - if you start w/ testnet, we'll follow that - from multisig import MultisigWallet - from public_constants import AFC_SCRIPT, AF_P2WPKH_P2SH, AF_P2SH, AF_P2WSH_P2SH + from wallet import MiniScriptWallet from glob import dis ch = chains.current_chain() + args = args or {} addr_fmt = ch.possible_address_fmt(addr) if not addr_fmt: # might be valid address over on testnet vs mainnet - nm = ch.name if ch.ctype != 'BTC' else 'Bitcoin Mainnet' - raise UnknownAddressExplained('That address is not valid on ' + nm) + raise UnknownAddressExplained('That address is not valid on ' + ch.name) - possibles = [] + # user has specified specific (named) wallet + named_wal = args.get("wallet", None) + if named_wal: + # quick search without deserialization + res = list(MiniScriptWallet.iter_wallets(name=named_wal)) + if not res: + raise UnknownAddressExplained("Wallet '%s' not defined." % named_wal) - if addr_fmt & AFC_SCRIPT: - # multisig or script at least.. must exist already - possibles.extend(MultisigWallet.iter_wallets(addr_fmt=addr_fmt)) + # only return desired named wallet, no other wallets are searched + return res + possibles = [] + if addr_fmt == AF_P2TR: + possibles.extend([w for w in MiniScriptWallet.iter_wallets() if w.addr_fmt == AF_P2TR]) + if addr_fmt & AFC_SCRIPT: + # multisig or script at least... must exist already + afs = [addr_fmt] if addr_fmt == AF_P2SH: # might look like P2SH but actually be AF_P2WSH_P2SH - possibles.extend(MultisigWallet.iter_wallets(addr_fmt=AF_P2WSH_P2SH)) + # wrapped segwit is more used than legacy + afs = [AF_P2WSH_P2SH, AF_P2SH] # Might be single-sig p2wpkh wrapped in p2sh ... but that was a transition # thing that hopefully is going away, so if they have any multisig wallets, # defined, assume that that's the only p2sh address source. addr_fmt = AF_P2WPKH_P2SH - # TODO: add tapscript and such fancy stuff here + possibles.extend(MiniScriptWallet.iter_wallets(addr_fmts=afs)) try: # Construct possible single-signer wallets, always at least account=0 case @@ -247,89 +267,138 @@ def search(cls, addr): if af == addr_fmt and acct_num: w = MasterSingleSigWallet(addr_fmt, account_idx=acct_num) possibles.append(w) - except ValueError: pass # if not single sig address format + except (KeyError, ValueError): + pass # if not single sig address format if not possibles: # can only happen w/ scripts; for single-signer we have things to check raise UnknownAddressExplained( - "No suitable multisig wallets are currently defined.") - - # "quick" check first, before doing any generations + "No suitable multisig/miniscript wallets are currently defined.") - count = 0 - phase2 = [] - for change_idx in (0, 1): - files = [AddressCacheFile(w, change_idx) for w in possibles] - for f in files: - if dis.has_lcd: - dis.fullscreen('Searching wallet(s)...', line2=f.nice_name()) - else: - dis.fullscreen('Searching...') + # ordering here + return possibles - for maybe in f.fast_search(addr): - ok = f.check_match(addr, maybe) - if not ok: continue # false positive - will happen - - # found winner. - return f.wallet, maybe - - if f.count < MAX_ADDRS_STORED: - phase2.append(f) + @classmethod + def search_wallet_cache(cls, addr, cf): + # - returns wallet object, and tuple2 of final 2 subpath components + # "quick" check first, before doing any generations + # external chain first, then internal (change) + for maybe in cf.fast_search(addr): + ok = cf.check_match(addr, maybe) + if ok: + return cf.wallet, maybe + return None, None - count += f.count + @classmethod + def search_build_wallet(cls, addr, cf): # maybe we haven't calculated all the addresses yet, so do that # - very slow, but only needed once; any negative (failed) search causes this # - could stop when match found, but we go a bit beyond that for next time # - we could search all in parallel, rather than serially because # more likely to find a match with low index... but seen as too much memory + result = cf.build_and_search(addr) + if result: + # found it, so report it and stop + return cf.wallet, result - for f in phase2: - b4 = f.count - if dis.has_lcd: - dis.fullscreen("Generating addresses...", line2=f.nice_name()) - else: - dis.fullscreen("Generating...") - - result = f.build_and_search(addr) - if result: - # found it, so report it and stop - return f.wallet, result + # possible phase 3: other seedvault... slow, rare and not implemented + return None, None - count += f.count - b4 + @classmethod + def search(cls, addr, args=None): + from glob import dis - # possible phase 3: other seedvault... slow, rare and not implemented + dis.fullscreen("Wait...") + + matches = OWNERSHIP.filter(addr, args) + + # build cache files for both external & internal chain + cachefs = [] + for w in matches: + cachefs.append(AddressCacheFile(w, 0)) + cachefs.append(AddressCacheFile(w, 1)) + + for cf in cachefs: + msg = "Searching wallet(s)..." if dis.has_lcd else "Searching..." + dis.fullscreen(msg, line2=cf.nice_name()) + wallet, subpath = OWNERSHIP.search_wallet_cache(addr, cf) + if wallet: + # first arg from_cache=True + return True, wallet, subpath + + # nothing found in existing cache files + c = 0 + for cf in cachefs: + msg = "Generating addresses..." if dis.has_lcd else "Generating..." + dis.fullscreen(msg, line2=cf.nice_name()) + wallet, subpath = OWNERSHIP.search_build_wallet(addr, cf) + c += cf.count + if wallet: + # first arg from_cache=False + return False, wallet, subpath - raise UnknownAddressExplained('Searched %d candidates without finding a match.' % count) + else: + raise UnknownAddressExplained('Searched %d candidate addresses in %d wallet(s)' + ' without finding a match.' % (c, len(matches))) @classmethod - async def search_ux(cls, addr): + async def search_ux(cls, addr, args): # Provide a simple UX. Called functions do fullscreen, progress bar stuff. from ux import ux_show_story, show_qr_code from charcodes import KEY_QR + from wallet import MiniScriptWallet from public_constants import AFC_BECH32, AFC_BECH32M try: - wallet, subpath = OWNERSHIP.search(addr) + _, wallet, subpath = cls.search(addr, args) + is_complex = isinstance(wallet, MiniScriptWallet) + + msg = show_single_address(addr) + msg += '\n\nFound in wallet:\n' + wallet.name + + msg += '\n\nDerivation path:\n' + if hasattr(wallet, "render_path"): + sp = wallet.render_path(*subpath) + msg += sp + else: + sp = None + msg += ".../%d/%d" % subpath + + if is_complex: + esc = "" + else: + esc = "0" + msg += "\n\nPress (0) to sign message with this key." - msg = addr - msg += '\n\nFound in wallet:\n ' + wallet.name - msg += '\nDerivation path:\n ' + wallet.render_path(*subpath) + title = "Verified" if version.has_qwerty: - esc = KEY_QR + esc += KEY_QR + title += " Address" else: - msg += '\n\nPress (1) for QR' - esc = '1' + msg += ' (1) for address QR' + esc += '1' + title += "!" while 1: - ch = await ux_show_story(msg, title="Verified Address", - escape=esc, hint_icons=KEY_QR) - if ch != esc: break - await show_qr_code(addr, is_alnum=(wallet.addr_fmt & (AFC_BECH32 | AFC_BECH32M)), - msg=addr) + ch = await ux_show_story(msg, title=title, escape=esc, hint_icons=KEY_QR) + if ch in ("1"+KEY_QR): + await show_qr_code( + addr, + is_alnum=(wallet.addr_fmt & (AFC_BECH32 | AFC_BECH32M)), + msg=addr, is_addrs=True + ) + elif not is_complex and (ch == "0"): # only singlesig + from msgsign import sign_with_own_address + await sign_with_own_address(sp, wallet.addr_fmt) + else: + break except UnknownAddressExplained as exc: - await ux_show_story(addr + '\n\n' + str(exc), title="Unknown Address") + await ux_show_story(show_single_address(addr) + '\n\n' + str(exc), title="Unknown Address") + except Exception as e: + await ux_show_story('Ownership search failed.\n\n%s\n%s' % (e, problem_file_line(e))) + @classmethod def note_subpath_used(cls, subpath): @@ -363,8 +432,6 @@ def note_wallet_used(cls, addr_fmt, subaccount): # - if they explore it (non-zero subaccount) # - if they sign those paths # - but ignore testnet vs. not - from glob import settings - if subaccount == 0: # only interested in non-zero subaccounts return diff --git a/shared/paper.py b/shared/paper.py index 952b667f5..745f36d73 100644 --- a/shared/paper.py +++ b/shared/paper.py @@ -3,10 +3,10 @@ # # paper.py - generate paper wallets, based on random values (not linked to wallet) # -import ujson +import ujson, ngu, chains from ubinascii import hexlify as b2a_hex -from utils import imported -from public_constants import AF_CLASSIC, AF_P2WPKH +from utils import imported, problem_file_line +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR from ux import ux_show_story, ux_dramatic_pause from files import CardSlot, CardMissingError, needs_microsd from actions import file_picker @@ -29,10 +29,6 @@ SECP256K1_ORDER = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xba\xae\xdc\xe6\xaf\x48\xa0\x3b\xbf\xd2\x5e\x8c\xd0\x36\x41\x41" -# Aprox. time of this feature release (Nov 20/2019) so no need to scan -# blockchain earlier than this during "importmulti" -FEATURE_RELEASE_TIME = const(1574277000) - # These very-specific text values are matched on the Coldcard; cannot be changed. class placeholders: addr = b'ADDRESS_XXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # 37 long @@ -51,6 +47,12 @@ def __init__(self, my_menu): self.my_menu = my_menu self.template_fn = None self.is_segwit = False + self.is_taproot = False + + def atype(self): + if self.is_taproot: return 2, 'Taproot P2TR' + if self.is_segwit: return 1, 'Segwit P2WPKH' + return 0, 'Classic P2PKH' async def pick_template(self, *a): fn = await file_picker(suffix='.pdf', min_size=20000, taster=template_taster, @@ -62,17 +64,17 @@ async def pick_template(self, *a): def addr_format_chooser(self, *a): # simple bool choice def set(idx, text): - self.is_segwit = bool(idx) + self.is_segwit = idx == 1 + self.is_taproot = idx == 2 self.update_menu() - return int(self.is_segwit), ['Classic P2PKH', 'Segwit P2WPKH'], set + return self.atype()[0], ['Classic P2PKH', 'Segwit P2WPKH', 'Taproot P2TR'], set def update_menu(self): # Reconstruct the menu contents based on our state. self.my_menu.replace_items([ MenuItem("Don't make PDF" if not self.template_fn else 'Making PDF', f=self.pick_template), - MenuItem('Classic P2PKH' if not self.is_segwit else 'Segwit P2WPKH', - chooser=self.addr_format_chooser), + MenuItem(self.atype()[1], chooser=self.addr_format_chooser), MenuItem('Use Dice', f=self.use_dice), MenuItem('GENERATE WALLET', f=self.doit), ], keep_position=True) @@ -83,7 +85,7 @@ async def doit(self, *a, have_key=None): try: import ngu - from auth import write_sig_file + from msgsign import write_sig_file from chains import current_chain from serializations import hash160 from stash import blank_object @@ -104,12 +106,16 @@ async def doit(self, *a, have_key=None): dis.fullscreen("Rendering...") # make payment address - digest = hash160(pubkey) - ch = current_chain() + ch = chains.current_chain() if self.is_segwit: - addr = ngu.codecs.segwit_encode(ch.bech32_hrp, 0, digest) + af = AF_P2WPKH + elif self.is_taproot: + af = AF_P2TR + pubkey = pubkey[1:] else: - addr = ngu.codecs.b58_encode(ch.b58_addr + digest) + af = AF_CLASSIC + + addr = ch.pubkey_to_address(pubkey, af) wif = ngu.codecs.b58_encode(ch.b58_privkey + privkey + b'\x01') @@ -164,8 +170,10 @@ async def doit(self, *a, have_key=None): else: nice_pdf = '' - nice_sig = write_sig_file(sig_cont, pk=privkey, sig_name=basename, - addr_fmt=AF_P2WPKH if self.is_segwit else AF_CLASSIC) + nice_sig = None + if af != AF_P2TR: + nice_sig = write_sig_file(sig_cont, pk=privkey, sig_name=basename, + addr_fmt=AF_P2WPKH if self.is_segwit else AF_CLASSIC) # Half-hearted attempt to cleanup secrets-contaminated memory # - better would be force user to reboot @@ -178,14 +186,14 @@ async def doit(self, *a, have_key=None): await needs_microsd() return except Exception as e: - from utils import problem_file_line - await ux_show_story('Failed to write!\n\n\n'+problem_file_line(e)) + await ux_show_story('Failed to write!\n\n'+problem_file_line(e)) return story = "Done! Created file(s):\n\n%s" % nice_txt if nice_pdf: story += "\n\n%s" % nice_pdf - story += "\n\n%s" % nice_sig + if nice_sig: + story += "\n\n%s" % nice_sig await ux_show_story(story) async def use_dice(self, *a): @@ -214,10 +222,17 @@ def make_txt(self, fp, addr, wif, privkey, qr_addr=None, qr_wif=None): fp.write('Bitcoin Core command:\n\n') # new hotness: output descriptors - desc = ('wpkh(%s)' if self.is_segwit else 'pkh(%s)') % wif - multi = ujson.dumps(dict(timestamp=FEATURE_RELEASE_TIME, desc=append_checksum(desc))) - fp.write(" bitcoin-cli importmulti '[%s]'\n\n" % multi) - fp.write('# OR (more compatible, but slower)\n\n bitcoin-cli importprivkey "%s"\n\n' % wif) + if self.is_taproot: + desc = 'tr(%s)' + elif self.is_segwit: + desc = 'wpkh(%s)' + else: + desc = 'pkh(%s)' + desc = desc % wif + descriptor = ujson.dumps(dict(timestamp="now", desc=append_checksum(desc))) + fp.write(" bitcoin-cli importdescriptors '[%s]'\n\n" % descriptor) + if not self.is_taproot: + fp.write('# OR (only supported with legacy wallets)\n\n bitcoin-cli importprivkey "%s"\n\n' % wif) if qr_addr and qr_wif: fp.write('\n\n--- QR Codes --- (requires UTF-8, unicode, white background)\n\n\n\n') diff --git a/shared/pincodes.py b/shared/pincodes.py index e4160187f..7b6ca8118 100644 --- a/shared/pincodes.py +++ b/shared/pincodes.py @@ -3,8 +3,7 @@ # pincodes.py - manage PIN code (which map to wallet seeds) # import ustruct, ckcc, version, chains, stash -# from ubinascii import hexlify as b2a_hex -from callgate import enter_dfu +from callgate import enter_dfu, get_is_bricked from bip39 import wordlist_en # See ../stm32/bootloader/pins.h for source of these constants. @@ -127,17 +126,14 @@ def __init__(self): self.private_state = 0 # opaque data, but preserve self.cached_main_pin = bytearray(32) + # If set, a spending policy is in effect, and so even tho we know the master + # seed, we are not going to let them see it, nor sign things we dont like, etc. + self.hobbled_mode = False - assert MAX_PIN_LEN == 32 # update FMT otherwise - assert ustruct.calcsize(PIN_ATTEMPT_FMT_V1) == PIN_ATTEMPT_SIZE_V1 - assert ustruct.calcsize(PIN_ATTEMPT_FMT_V2_ADDITIONS) == PIN_ATTEMPT_SIZE - PIN_ATTEMPT_SIZE_V1 - - # check for bricked system early - import callgate - if callgate.get_is_bricked(): - # die right away if it's not going to work - print("SE bricked") - callgate.enter_dfu(3) + #assert MAX_PIN_LEN == 32 # update FMT otherwise + #assert ustruct.calcsize(PIN_ATTEMPT_FMT_V1) == PIN_ATTEMPT_SIZE_V1 + #assert ustruct.calcsize(PIN_ATTEMPT_FMT_V2_ADDITIONS) \ + # == PIN_ATTEMPT_SIZE - PIN_ATTEMPT_SIZE_V1 def __repr__(self): return '' % ( @@ -177,7 +173,7 @@ def marshal(self, msg, is_duress=False, is_brickme=False, new_secret=None, old_pin = self.pin assert len(new_pin) <= MAX_PIN_LEN - assert old_pin != None + assert old_pin is not None assert len(old_pin) <= MAX_PIN_LEN else: new_pin = b'' @@ -339,10 +335,6 @@ def setup(self, pin, secondary=False): return self.state_flags - def delay(self): - # obsolete since Mk3, but called from login.py - self.roundtrip(1) - def login(self): # test we have the PIN code right, and unlock access if so. chk = self.roundtrip(2) @@ -418,9 +410,13 @@ def new_main_secret(self, raw_secret=None, chain=None, bip39pw='', blank=False, # Main secret has changed: reset the settings+their key, # and capture xfp/xpub # if None is provided as raw_secret -> restore to main seed + import glob from glob import settings, dis stash.SensitiveValues.clear_cache() + # invalidate descriptor cache - upon new secret load + glob.DESC_CACHE.clear() + bypass_tmp = False stash.bip39_passphrase = bool(bip39pw) @@ -473,6 +469,7 @@ def new_main_secret(self, raw_secret=None, chain=None, bip39pw='', blank=False, def tmp_secret(self, encoded, chain=None, bip39pw=''): # Use indicated secret and stop using the SE; operate like this until reboot from glob import settings + from utils import xfp2str from nvstore import SettingsObject val = bytes(encoded + bytes(AE_SECRET_LEN - len(encoded))) @@ -483,7 +480,9 @@ def tmp_secret(self, encoded, chain=None, bip39pw=''): target_nvram_key = None if encoded is not None: # disallow using master seed as temporary - master_err = "Cannot use master seed as temporary." + xfp = xfp2str(settings.master_get("xfp", 0)) + master_err = ("Cannot use master seed as temporary. BUT you have just successfully " + "tested recovery of your master seed [%s].") % xfp target_nvram_key = settings.hash_key(val) if SettingsObject.master_nvram_key: assert self.tmp_value @@ -530,10 +529,24 @@ def is_deltamode(self): from trick_pins import TC_DELTA_MODE return bool(self.delay_required & TC_DELTA_MODE) + def get_tc_values(self): # Mk4 only # return (tc_flags, tc_arg) return self.delay_required, self.delay_achieved + + @staticmethod + async def enforce_brick(): + # check for bricked system early + if get_is_bricked(): + try: + # regardless of settings, become a forever calculator after brickage. + while version.has_qwerty: + from calc import login_repl + await login_repl() + finally: + # die right away if it's not going to work + enter_dfu(3) # singleton diff --git a/shared/precomp_tag_hash.py b/shared/precomp_tag_hash.py new file mode 100644 index 000000000..7ba0a7c95 --- /dev/null +++ b/shared/precomp_tag_hash.py @@ -0,0 +1,12 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# taproot precomputed tag hashes +# +# SHA256(TapLeaf) +TAP_LEAF_H = b'\xae\xea\x8f\xdcB\x08\x981\x05sKX\x08\x1d\x1e&8\xd3_\x1c\xb5@\x08\xd4\xd3W\xca\x03\xbex\xe9\xee' +# SHA256(TapBranch) +TAP_BRANCH_H = b'\x19A\xa1\xf2\xe5n\xb9_\xa2\xa9\xf1\x94\xbe\\\x01\xf7!o3\xed\x82\xb0\x91F4\x90\xd0[\xf5\x16\xa0\x15' +# SHA256(TapTweak) +TAP_TWEAK_H = b'\xe8\x0f\xe1c\x9c\x9c\xa0P\xe3\xaf\x1b9\xc1C\xc6>B\x9c\xbc\xeb\x15\xd9@\xfb\xb5\xc5\xa1\xf4\xafW\xc5\xe9' +# SHA256(TapSighash) +TAP_SIGHASH_H = b'\xf4\nH\xdfK*p\xc8\xb4\x92K\xf2eFa\xed=\x95\xfdf\xa3\x13\xeb\x87#u\x97\xc6(\xe4\xa01' \ No newline at end of file diff --git a/shared/psbt.py b/shared/psbt.py index 6cc30b88d..422790e14 100644 --- a/shared/psbt.py +++ b/shared/psbt.py @@ -2,35 +2,48 @@ # # psbt.py - understand PSBT file format: verify and generate them # +import stash, gc, history, sys, ngu, ckcc, version, chains +from ucollections import OrderedDict from ustruct import unpack_from, unpack, pack from ubinascii import hexlify as b2a_hex -from utils import xfp2str, B2A, keypath_to_str, problem_file_line +from utils import xfp2str, B2A, keypath_to_str, validate_derivation_path_length, problem_file_line from utils import seconds2human_readable, datetime_from_timestamp, datetime_to_str -import stash, gc, history, sys, ngu, ckcc, chains from uhashlib import sha256 from uio import BytesIO +from charcodes import KEY_ENTER from sffile import SizerFile -from multisig import MultisigWallet, disassemble_multisig, disassemble_multisig_mn +from chains import taptweak, tapleaf_hash, NLOCK_IS_TIME, AF_TO_STR_AF +from wallet import MiniScriptWallet, TRUST_PSBT, TRUST_VERIFY from exceptions import FatalPSBTIssue, FraudulentChangeOutput -from serializations import ser_compact_size, deser_compact_size, hash160, hash256 -from serializations import CTxIn, CTxInWitness, CTxOut, ser_string, ser_uint256, COutPoint +from serializations import ser_compact_size, deser_compact_size, hash160 +from serializations import CTxIn, CTxInWitness, CTxOut, ser_string, COutPoint from serializations import ser_sig_der, uint256_from_str, ser_push_data from serializations import SIGHASH_ALL, SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ANYONECANPAY -from serializations import ALL_SIGHASH_FLAGS +from serializations import ALL_SIGHASH_FLAGS, SIGHASH_DEFAULT +from opcodes import OP_CHECKMULTISIG, OP_RETURN from glob import settings +from precomp_tag_hash import TAP_TWEAK_H, TAP_SIGHASH_H from public_constants import ( PSBT_GLOBAL_UNSIGNED_TX, PSBT_GLOBAL_XPUB, PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_WITNESS_UTXO, PSBT_IN_PARTIAL_SIG, PSBT_IN_SIGHASH_TYPE, PSBT_IN_REDEEM_SCRIPT, PSBT_IN_WITNESS_SCRIPT, PSBT_IN_BIP32_DERIVATION, PSBT_IN_FINAL_SCRIPTSIG, PSBT_IN_FINAL_SCRIPTWITNESS, PSBT_OUT_REDEEM_SCRIPT, PSBT_OUT_WITNESS_SCRIPT, - PSBT_OUT_BIP32_DERIVATION, PSBT_OUT_SCRIPT, PSBT_OUT_AMOUNT, PSBT_GLOBAL_VERSION, + PSBT_OUT_BIP32_DERIVATION, PSBT_OUT_TAP_BIP32_DERIVATION, PSBT_OUT_TAP_INTERNAL_KEY, + PSBT_IN_TAP_BIP32_DERIVATION, PSBT_IN_TAP_INTERNAL_KEY, PSBT_IN_TAP_KEY_SIG, PSBT_OUT_TAP_TREE, + PSBT_IN_TAP_MERKLE_ROOT, PSBT_IN_TAP_LEAF_SCRIPT, PSBT_IN_TAP_SCRIPT_SIG, + TAPROOT_LEAF_TAPSCRIPT, TAPROOT_LEAF_MASK, + PSBT_OUT_SCRIPT, PSBT_OUT_AMOUNT, PSBT_GLOBAL_VERSION, PSBT_GLOBAL_TX_MODIFIABLE, PSBT_GLOBAL_OUTPUT_COUNT, PSBT_GLOBAL_INPUT_COUNT, PSBT_GLOBAL_FALLBACK_LOCKTIME, PSBT_GLOBAL_TX_VERSION, PSBT_IN_PREVIOUS_TXID, PSBT_IN_OUTPUT_INDEX, PSBT_IN_SEQUENCE, PSBT_IN_REQUIRED_TIME_LOCKTIME, - PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, MAX_PATH_DEPTH, MAX_SIGNERS + PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, MAX_SIGNERS, + AF_P2WSH, AF_P2WSH_P2SH, AF_P2SH, AF_P2TR, AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH, + AFC_SEGWIT, AF_BARE_PK ) +psbt_tmp256 = bytearray(256) + # PSBT proprietary keytype PSBT_PROPRIETARY = const(0xFC) @@ -91,6 +104,17 @@ def _skip_n_objs(fd, n, cls): return rv +def disassemble_multisig_mn(redeem_script): + # pull out just M and N from script. Simple, faster, no memory. + + if not redeem_script or (redeem_script[-1] != OP_CHECKMULTISIG): + return None, None + + M = redeem_script[0] - 80 + N = redeem_script[-2] - 80 + + return M, N + def calc_txid(fd, poslen, body_poslen=None): # Given the (pos,len) of a transaction in a file, return the txid for that txn. # - doesn't validate data @@ -201,48 +225,65 @@ def parse(self, fd): if ks is None: break if ks == 0: break + key_pos = fd.tell() + 1 # first element is ktype + key = fd.read(ks) vs = deser_compact_size(fd) - assert vs != None, 'eof' + assert vs is not None, 'eof' kt = key[0] if kt in self.no_keys: - assert len(key) == 1 # not expecting key + assert len(key) == 1 # not expecting key # storing offset and length only! Mostly. if kt in self.short_values: actual = fd.read(vs) - self.store(kt, bytes(key), actual) else: # skip actual data for now # TODO: could this be stored more compactly? proxy = (fd.tell(), vs) fd.seek(vs, 1) + # store just coords for both key & val + if kt == PSBT_PROPRIETARY: + ident, subtype, _ = decode_prop_key(key[1:]) + # examine only Coinkite proprietary keys + if (ident == PSBT_PROP_CK_ID) and (subtype == PSBT_ATTESTATION_SUBTYPE): + # prop key for attestation does not have keydata because the + # value is a recoverable signature (already contains pubkey) + # just save what we can handle + self.attestation = proxy + + self.store(kt, (key_pos, ks-1), proxy) + + def coord_write(self, out_fd, val, ktype=None): + pos, ll = val + if ktype is None: + out_fd.write(ser_compact_size(ll)) + else: + out_fd.write(ser_compact_size(ll+1)) + out_fd.write(bytes([ktype])) - self.store(kt, bytes(key), proxy) + self.fd.seek(pos) + while ll: + t = self.fd.read(min(64, ll)) + out_fd.write(t) + ll -= len(t) def write(self, out_fd, ktype, val, key=b''): # serialize helper: write w/ size and key byte - out_fd.write(ser_compact_size(1 + len(key))) - out_fd.write(bytes([ktype]) + key) + if isinstance(key, tuple): + self.coord_write(out_fd, key, ktype) + else: + out_fd.write(ser_compact_size(1 + len(key))) + out_fd.write(bytes([ktype]) + key) if isinstance(val, tuple): - (pos, ll) = val - out_fd.write(ser_compact_size(ll)) - self.fd.seek(pos) - while ll: - t = self.fd.read(min(64, ll)) - out_fd.write(t) - ll -= len(t) - - elif isinstance(val, list): - # for subpaths lists (LE32 ints) - assert ktype in (PSBT_IN_BIP32_DERIVATION, PSBT_OUT_BIP32_DERIVATION) - out_fd.write(ser_compact_size(len(val) * 4)) - for i in val: - out_fd.write(pack(' [xfp, *path] - # - will be single entry for non-p2sh ins and outs - - if not self.subpaths: - return 0 + def parse_xfp_path(self, coords): + # coords are expected to be value from subpaths or taproot subpaths + return list(unpack_from('<%dI' % (coords[1] // 4), self.get(coords))) - if self.num_our_keys != None: - # already been here once - return self.num_our_keys + def handle_zero_xfp(self, xfp_path, my_xfp, warnings=None): + # Tricky & Useful: if xfp of zero is observed in file, assume that's a + # placeholder for my XFP value. Replace on the fly. Great when master + # XFP is unknown because PSBT built from derived XPUB only. Also privacy. + if xfp_path[0] == 0: + xfp_path[0] = my_xfp - num_ours = 0 - for pk in self.subpaths: - assert len(pk) in {33, 65}, "hdpath pubkey len" + if warnings is not None: + if not any(True for k, _ in warnings if 'XFP' in k): + warnings.append(('Zero XFP', + 'Assuming XFP of zero should be replaced by correct XFP')) + return xfp_path + + def parse_taproot_subpaths(self, my_xfp, warnings, cosign_xfp=None): + my_sp_idxs = [] + parsed_subpaths = OrderedDict() + for i in range(len(self.taproot_subpaths)): + key, val = self.taproot_subpaths[i] + assert key[1] == 32 # "PSBT_IN_TAP_BIP32_DERIVATION xonly-pubkey length != 32" + xonly_pk = self.get(key) + pos, length = val + end_pos = pos + length + self.fd.seek(pos) + leaf_hash_len = deser_compact_size(self.fd) + if leaf_hash_len: + self.fd.seek(32*leaf_hash_len, 1) + else: + self.ik_idx = i + + curr_pos = self.fd.tell() + # this position is where actual xfp+path starts + # save it for faster access + to_read = end_pos - curr_pos + self.taproot_subpaths[i] = (key, (val[0], val[1], (curr_pos, to_read))) + # internal key is allowed to go from master + # unspendable path can be just a bare xonly pubkey + allow_master = True if not leaf_hash_len else False + validate_derivation_path_length(to_read, allow_master=allow_master) + v = self.fd.read(to_read) + here = list(unpack_from('<%dI' % (to_read // 4), v)) + here = self.handle_zero_xfp(here, my_xfp, warnings) + parsed_subpaths[xonly_pk] = [leaf_hash_len] + here + if (here[0] == my_xfp) or (cosign_xfp and (here[0] == cosign_xfp)): + my_sp_idxs.append(i) + + if my_sp_idxs: + self.sp_idxs = my_sp_idxs + + return parsed_subpaths + + def parse_non_taproot_subpaths(self, my_xfp, warnings, cosign_xfp=None): + parsed_subpaths = OrderedDict() + my_sp_idxs = [] + for i, (key, val) in enumerate(self.subpaths): + # len pubkey 33 + 1 byte PSBT keys specifier + assert key[1] in {33, 65}, "hdpath pubkey len" + pk = self.get(key) if len(pk) == 33: assert pk[0] in {0x02, 0x03}, "uncompressed pubkey" - vl = self.subpaths[pk][1] - - # force them to use a derived key, never the master - assert vl >= 8, 'too short key path' - assert (vl % 4) == 0, 'corrupt key path' - assert (vl//4) <= MAX_PATH_DEPTH, 'too deep' - + validate_derivation_path_length(val[1]) # promote to a list of ints - v = self.get(self.subpaths[pk]) - here = list(unpack_from('<%dI' % (vl//4), v)) - - # Tricky & Useful: if xfp of zero is observed in file, assume that's a - # placeholder for my XFP value. Replace on the fly. Great when master - # XFP is unknown because PSBT built from derived XPUB only. Also privacy. - if here[0] == 0: - here[0] = my_xfp - if not any(True for k,_ in warnings if 'XFP' in k): - warnings.append(('Zero XFP', - 'Assuming XFP of zero should be replaced by correct XFP')) + here = self.parse_xfp_path(val) + here = self.handle_zero_xfp(here, my_xfp, warnings) - # update in place - self.subpaths[pk] = here + parsed_subpaths[pk] = here + if (here[0] == my_xfp) or (cosign_xfp and (here[0] == cosign_xfp)): + my_sp_idxs.append(i) - if here[0] == my_xfp: - num_ours += 1 - else: - # Address that isn't based on my seed; might be another leg in a p2sh, - # or an input we're not supposed to be able to sign... and that's okay. - pass + # else: + # Address that isn't based on my seed; might be another leg in a p2sh, + # or an input we're not supposed to be able to sign... and that's okay. - self.num_our_keys = num_ours - return num_ours + if my_sp_idxs: + self.sp_idxs = my_sp_idxs + return parsed_subpaths + def parse_subpaths(self, my_xfp, warnings, cosign_xfp=None): + # - creates dictionary: pubkey => [xfp, *path] (self.subpaths) + # - creates dictionary: pubkey => [leaf_hash_list, xfp, *path] (self.taproot_subpaths) + if self.taproot_subpaths: + return self.parse_taproot_subpaths(my_xfp, warnings, cosign_xfp) + elif self.subpaths: + return self.parse_non_taproot_subpaths(my_xfp, warnings, cosign_xfp) + #return None in/output does not have any key-path info # Track details of each output of PSBT # class psbtOutputProxy(psbtProxy): - no_keys = { PSBT_OUT_REDEEM_SCRIPT, PSBT_OUT_WITNESS_SCRIPT } + no_keys = { PSBT_OUT_REDEEM_SCRIPT, PSBT_OUT_WITNESS_SCRIPT, PSBT_OUT_TAP_INTERNAL_KEY, PSBT_OUT_TAP_TREE } - blank_flds = ('unknown', 'subpaths', 'redeem_script', 'witness_script', - 'is_change', 'num_our_keys', 'amount', 'script', 'attestation') + blank_flds = ('unknown', 'subpaths', 'redeem_script', 'witness_script', 'sp_idxs', + 'is_change', 'amount', 'script', 'attestation', 'proprietary', + 'taproot_internal_key', 'taproot_subpaths', 'taproot_tree', 'ik_idx') def __init__(self, fd, idx): super().__init__() # things we track - #self.subpaths = None # a dictionary if non-empty + #self.subpaths = None # a dictionary if non-empty + #self.taproot_subpaths = None # a dictionary if non-empty + #self.taproot_internal_key = None + #self.taproot_tree = None + #self.ik_idx = None # index of taproot internal key in taproot_subpaths #self.redeem_script = None #self.witness_script = None #self.script = None @@ -331,13 +413,29 @@ def __init__(self, fd, idx): self.parse(fd) + # not needed + # def parse_taproot_tree(self): + # length = self.taproot_tree[1] + # + # res = [] + # while length: + # tree = BytesIO(self.get(self.taproot_tree)) + # depth = tree.read(1) + # leaf_version = tree.read(1)[0] + # assert (leaf_version & ~TAPROOT_LEAF_MASK) == 0 + # script_len, nb = deser_compact_size(tree, ret_num_bytes=True) + # script = tree.read(script_len) + # res.append((depth, leaf_version, script)) + # length -= (2 + nb + script_len) + # + # return res def store(self, kt, key, val): # do not forget that key[0] includes kt (type) if kt == PSBT_OUT_BIP32_DERIVATION: if not self.subpaths: - self.subpaths = {} - self.subpaths[key[1:]] = val + self.subpaths = [] + self.subpaths.append((key,val)) elif kt == PSBT_OUT_REDEEM_SCRIPT: self.redeem_script = val elif kt == PSBT_OUT_WITNESS_SCRIPT: @@ -347,26 +445,27 @@ def store(self, kt, key, val): elif kt == PSBT_OUT_AMOUNT: self.amount = val elif kt == PSBT_PROPRIETARY: - prefix, subtype, keydata = decode_prop_key(key[1:]) - # examine only Coinkite proprietary keys - if prefix == PSBT_PROP_CK_ID: - if subtype == PSBT_ATTESTATION_SUBTYPE: - # prop key for attestation does not have keydata because the - # value is a recoverable signature (already contains pubkey) - self.attestation = self.get(val) + self.proprietary = self.proprietary or [] + self.proprietary.append((key, val)) + elif kt == PSBT_OUT_TAP_INTERNAL_KEY: + self.taproot_internal_key = val + elif kt == PSBT_OUT_TAP_BIP32_DERIVATION: + self.taproot_subpaths = self.taproot_subpaths or [] + self.taproot_subpaths.append((key, val)) + elif kt == PSBT_OUT_TAP_TREE: + self.taproot_tree = val else: - self.unknown = self.unknown or {} - if key in self.unknown: - raise FatalPSBTIssue("Duplicate key. Key for unknown value already provided in output.") - self.unknown[key] = val + self.unknown = self.unknown or [] + pos, length = key + self.unknown.append(((pos-1, length+1), val)) def serialize(self, out_fd, is_v2): wr = lambda *a: self.write(out_fd, *a) if self.subpaths: - for k in self.subpaths: - wr(PSBT_OUT_BIP32_DERIVATION, self.subpaths[k], k) + for k, v in self.subpaths: + wr(PSBT_OUT_BIP32_DERIVATION, v, k) if self.redeem_script: wr(PSBT_OUT_REDEEM_SCRIPT, self.redeem_script) @@ -374,18 +473,29 @@ def serialize(self, out_fd, is_v2): if self.witness_script: wr(PSBT_OUT_WITNESS_SCRIPT, self.witness_script) + if self.taproot_internal_key: + wr(PSBT_OUT_TAP_INTERNAL_KEY, self.taproot_internal_key) + + if self.taproot_subpaths: + for k, v in self.taproot_subpaths: + wr(PSBT_OUT_TAP_BIP32_DERIVATION, v, k) + + if self.taproot_tree: + wr(PSBT_OUT_TAP_TREE, self.taproot_tree) + if is_v2: wr(PSBT_OUT_SCRIPT, self.script) wr(PSBT_OUT_AMOUNT, self.amount) - if self.attestation: - wr(PSBT_PROPRIETARY, self.attestation, encode_prop_key(PSBT_PROP_CK_ID, PSBT_ATTESTATION_SUBTYPE)) + if self.proprietary: + for k, v in self.proprietary: + wr(PSBT_PROPRIETARY, v, k) if self.unknown: - for k, v in self.unknown.items(): - wr(k[0], v, k[1:]) + for k, v in self.unknown: + wr(None, v, k) - def validate(self, out_idx, txo, my_xfp, active_multisig, parent): + def determine_my_change(self, out_idx, txo, parsed_subpaths, parent): # Do things make sense for this output? # NOTE: We might think it's a change output just because the PSBT @@ -395,141 +505,114 @@ def validate(self, out_idx, txo, my_xfp, active_multisig, parent): # any output info provided better be right, or fail as "fraud" # - full key derivation and validation is done during signing, and critical. # - we raise fraud alarms, since these are not innocent errors - # - num_ours = self.parse_subpaths(my_xfp, parent.warnings) - - if num_ours == 0: - # - not considered fraud because other signers looking at PSBT may have them - # - user will see them as normal outputs, which they are from our PoV. - return # - must match expected address for this output, coming from unsigned txn - addr_type, addr_or_pubkey, is_segwit = txo.get_address() + af, addr_or_pubkey = txo.get_address() - if len(self.subpaths) == 1: - # p2pk, p2pkh, p2wpkh cases - expect_pubkey, = self.subpaths.keys() - else: - # p2wsh/p2sh cases need full set of pubkeys, and therefore redeem script - expect_pubkey = None - - if addr_type == 'p2pk': - # output is public key (not a hash, much less common) + if (not self.sp_idxs) or (af in [OP_RETURN, None]): + # num_ours == 0 + # - not considered fraud because other signers looking at PSBT may have them + # - user will see them as normal outputs, which they are from our PoV. + # OP_RETURN + # - nothing we can do with anchor outputs + # UNKNOWN + # - scripts that we do not understand + return af + + msc = parent.active_miniscript + if msc and MiniScriptWallet.disable_checks: + # Without validation, we have to assume all outputs + # will be taken from us, and are not really change. + return af + + # certain short-cuts + if msc: + if af in [AF_CLASSIC, AF_P2WPKH, AF_BARE_PK]: + # signing with miniscript wallet - single sig outputs definitely not change + return af + + elif parent.active_singlesig and (af == AF_P2WSH): + # we are signing single sig inputs - p2wsh is def not a change + return af + + def fraud(idx, af, err=""): + raise FraudulentChangeOutput(idx, "%s change output is fraudulent\n\n%s" % ( + AF_TO_STR_AF[af], err + )) + + if af == AF_BARE_PK: + # output is compressed public key (not a hash, much less common) + # uncompressed public keys not supported! assert len(addr_or_pubkey) == 33 + assert len(parsed_subpaths) == 1 + target, = parsed_subpaths.keys() - if addr_or_pubkey != expect_pubkey: - raise FraudulentChangeOutput(out_idx, "P2PK change output is fraudulent") - - self.is_change = True - return - - # Figure out what the hashed addr should be - pkh = addr_or_pubkey - - if addr_type == 'p2sh': - # P2SH or Multisig output - - # Can be both, or either one depending on address type - redeem_script = self.get(self.redeem_script) if self.redeem_script else None - witness_script = self.get(self.witness_script) if self.witness_script else None - - if expect_pubkey: - # num_ours == 1 and len(subpaths) == 1, single sig, we only allow p2sh-p2wpkh - if not redeem_script: - # Perhaps an omission, so let's not call fraud on it - # But definately required, else we don't know what script we're sending to. - raise FatalPSBTIssue("Missing redeem script for output #%d" % out_idx) - - target_spk = bytes([0xa9, 0x14]) + hash160(redeem_script) + bytes([0x87]) - if not is_segwit and len(redeem_script) == 22 and \ - redeem_script[0] == 0 and redeem_script[1] == 20 and \ - txo.scriptPubKey == target_spk: - # it's actually segwit p2wpkh inside p2sh - pkh = redeem_script[2:22] - expect_pkh = hash160(expect_pubkey) - else: - # unknown or wrong script - # p2sh-p2pkh also fall into this category - expect_pkh = None - + elif af in (AF_CLASSIC, AF_P2WPKH): + # P2PKH & P2WPKH (public key has, whether witness v0 or legacy) + # input is hash160 of a single public key + assert len(addr_or_pubkey) == 20 + assert len(parsed_subpaths) == 1 + target, = parsed_subpaths.keys() + target = hash160(target) + + elif af in (AF_P2SH, AF_P2WSH): # both p2sh & p2wsh covered here + if msc: + # scriptPubkey can be compared against script that we build + # if exact match change if not - not change + # no need for redeem/witness script + # for instance liana & core do not provide witness/redeem + try: + xfp_paths = list(parsed_subpaths.values()) + # if subpaths do not match, it is not desired wallet - so no change + # but also not a fraud + if msc.matching_subpaths(xfp_paths): + msc.validate_script_pubkey(txo.scriptPubKey, xfp_paths) + self.is_change = True + except AssertionError as e: + # sys.print_exception(e) + fraud(out_idx, af, e) + return af + + # we do not have active miniscript - must be single sig otherwise, not a change + if len(parsed_subpaths) == 1 and (af == AF_P2SH): + expect_pubkey, = parsed_subpaths.keys() + target_spk, _ = chains.current_chain().script_pubkey(AF_P2WPKH_P2SH, + pubkey=expect_pubkey) + af = AF_P2WPKH_P2SH + if txo.scriptPubKey != target_spk: + fraud(out_idx, af, "spk mismatch") + # it's actually segwit p2wpkh inside p2sh + target = target_spk[2:-1] else: - # Multisig change output, for wallet we're supposed to be a part of. - # - our key must be part of it - # - must look like input side redeem script (same fingerprints) - # - assert M/N structure of output to match any inputs we have signed in PSBT! - # - assert all provided pubkeys are in redeem script, not just ours - # - we get all of that by re-constructing the script from our wallet details - if not redeem_script and not witness_script: - # Perhaps an omission, so let's not call fraud on it - # But definately required, else we don't know what script we're sending to. - raise FatalPSBTIssue( - "Missing redeem/witness script for multisig output #%d" % out_idx - ) - - # it cannot be change if it doesn't precisely match our multisig setup - if not active_multisig: - # - might be a p2sh output for another wallet that isn't us - # - not fraud, just an output with more details than we need. - self.is_change = False - return - - if MultisigWallet.disable_checks: - # Without validation, we have to assume all outputs - # will be taken from us, and are not really change. - self.is_change = False - return + # done, not a change, subpaths > 1 or p2wsh (and not active miniscript) + return af - # redeem script must be exactly what we expect - # - pubkeys will be reconstructed from derived paths here - # - BIP-45, BIP-67 rules applied (BIP-67 optional from now - depending on imported descriptor) - # - p2sh-p2wsh needs witness script here, not redeem script value - # - if details provided in output section, must our match multisig wallet + elif af == AF_P2TR: + if msc: try: - active_multisig.validate_script(witness_script or redeem_script, - subpaths=self.subpaths) - except BaseException as exc: - raise FraudulentChangeOutput(out_idx, - "P2WSH or P2SH change output script: %s" % exc) - - if is_segwit: - # p2wsh case - # - need witness script and check it's hash against proposed p2wsh value - assert len(addr_or_pubkey) == 32 - expect_wsh = ngu.hash.sha256s(witness_script) - if expect_wsh != addr_or_pubkey: - raise FraudulentChangeOutput(out_idx, "P2WSH witness script has wrong hash") - - self.is_change = True - return - - if witness_script: - # p2sh-p2wsh case (because it had witness script) - expect_rs = b'\x00\x20' + ngu.hash.sha256s(witness_script) - - if redeem_script and expect_rs != redeem_script: - # iff they provide a redeeem script, then it needs to match - # what we expect it to be - raise FraudulentChangeOutput(out_idx, - "P2SH-P2WSH redeem script provided, and doesn't match") - - expect_pkh = hash160(expect_rs) - else: - # old BIP-16 style; looks like payment addr - expect_pkh = hash160(redeem_script) - - elif addr_type == 'p2pkh': - # input is hash160 of a single public key - assert len(addr_or_pubkey) == 20 - expect_pkh = hash160(expect_pubkey) - else: - # we don't know how to "solve" this type of input - return + xfp_paths = [v[1:] for v in parsed_subpaths.values() if len(v[1:]) > 1] + if msc.matching_subpaths(xfp_paths): + msc.validate_script_pubkey(txo.scriptPubKey, xfp_paths) + self.is_change = True + except AssertionError as e: + fraud(out_idx, af, e) + return af + + if len(parsed_subpaths) == 1: + expect_pubkey, = parsed_subpaths.keys() + target = taptweak(expect_pubkey) + else: + # done, not a change, subpaths > 1 (and not active miniscript) + return af - if pkh != expect_pkh: - raise FraudulentChangeOutput(out_idx, "Change output is fraudulent") + # only basic single signature, non-miniscript scripts get here + assert parent.active_singlesig + if addr_or_pubkey != target: + fraud(out_idx, af) # We will check pubkey value at the last second, during signing. self.is_change = True + return af # Track details of each input of PSBT @@ -540,15 +623,19 @@ class psbtInputProxy(psbtProxy): short_values = { PSBT_IN_SIGHASH_TYPE } # only part-sigs have a key to be stored. - no_keys = { PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_WITNESS_UTXO, PSBT_IN_SIGHASH_TYPE, - PSBT_IN_REDEEM_SCRIPT, PSBT_IN_WITNESS_SCRIPT, PSBT_IN_FINAL_SCRIPTSIG, - PSBT_IN_FINAL_SCRIPTWITNESS } + no_keys = {PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_WITNESS_UTXO, PSBT_IN_SIGHASH_TYPE, + PSBT_IN_REDEEM_SCRIPT, PSBT_IN_WITNESS_SCRIPT, PSBT_IN_FINAL_SCRIPTSIG, + PSBT_IN_FINAL_SCRIPTWITNESS,PSBT_IN_TAP_KEY_SIG, + PSBT_IN_TAP_INTERNAL_KEY, PSBT_IN_TAP_MERKLE_ROOT} blank_flds = ( - 'unknown', 'utxo', 'witness_utxo', 'sighash', 'redeem_script', 'witness_script', - 'fully_signed', 'is_segwit', 'is_multisig', 'is_p2sh', 'num_our_keys', - 'required_key', 'scriptSig', 'amount', 'scriptCode', 'added_sig', 'previous_txid', - 'prevout_idx', 'sequence', 'req_time_locktime', 'req_height_locktime' + 'unknown', 'witness_utxo', 'sighash', 'redeem_script', 'witness_script', 'sp_idxs', + 'fully_signed', 'af', 'is_miniscript', "subpaths", 'utxo', 'utxo_spk', + 'amount', 'previous_txid', 'part_sigs', 'added_sigs', 'prevout_idx', 'sequence', + 'req_time_locktime', 'req_height_locktime', + 'taproot_merkle_root', 'taproot_script_sigs', 'taproot_scripts', 'use_keypath', + 'taproot_subpaths', 'taproot_internal_key', 'taproot_key_sig', 'tr_added_sigs', + 'ik_idx', ) def __init__(self, fd, idx): @@ -556,30 +643,35 @@ def __init__(self, fd, idx): #self.utxo = None #self.witness_utxo = None - self.part_sig = {} + #self.part_sigs = [] + #self.added_sigs = [] # signatures that we added (current siging session) #self.sighash = None - self.subpaths = {} # will typically be non-empty for all inputs + #self.subpaths = [] # will be empty if taproot #self.redeem_script = None #self.witness_script = None # Non-zero if one or more of our signing keys involved in input - #self.num_our_keys = None + #self.sp_idxs = list of indexes leading to our key in self.subpaths # things we've learned #self.fully_signed = False # we can't really learn this until we take apart the UTXO's scriptPubKey - #self.is_segwit = None - #self.is_multisig = None - #self.is_p2sh = False + #self.af = None # string representation of address format aka. script type - #self.required_key = None # which of our keys will be used to sign input - #self.scriptSig = None #self.amount = None - #self.scriptCode = None # only expected for segwit inputs - - # after signing, we'll have a signature to add to output PSBT - #self.added_sig = None + #self.utxo_spk = None # scriptPubKey for input utxo + + # === will be empty if non-taproot === + # self.taproot_subpaths = {} + # self.taproot_internal_key = None + # self.taproot_key_sig = None + # self.taproot_merkle_root = None + # self.taproot_script_sigs = None + # self.taproot_scripts = None + # self.use_keypath = None # signing taproot inputs that have script path with internal key + # self.ik_idx = None # index of taproot internal key in taproot_subpaths + # === #self.previous_txid = None #self.prevout_idx = None @@ -589,6 +681,31 @@ def __init__(self, fd, idx): self.parse(fd) + @property + def is_segwit(self): + return self.af & AFC_SEGWIT + + def get_taproot_script_sigs(self): + # returns set of (xonly, script) provided via PSBT_IN_TAP_SCRIPT_SIG + # we do not parse control blocks (k) not needed + parsed_taproot_script_sigs = set() + for k, v in self.taproot_script_sigs or []: + key = self.get(k) + xonly, script_hash = key[:32], key[32:] + parsed_taproot_script_sigs.add((xonly, script_hash)) + + return parsed_taproot_script_sigs + + def get_taproot_scripts(self): + # returns set of scripts provided via PSBT_IN_TAP_LEAF_SCRIPT + # we do not parse control blocks (k) not needed + t_scr = {} + for k, v in self.taproot_scripts or []: + script = self.get(v) + t_scr[script[:-1]] = script[-1] # only script, and script version + + return t_scr + def has_relative_timelock(self, txin): # https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31) @@ -615,47 +732,9 @@ def has_relative_timelock(self, txin): return is_timebased, res - def validate(self, idx, txin, my_xfp, parent): - # Validate this txn input: given deserialized CTxIn and maybe witness - - # TODO: tighten these - if self.witness_script: - assert self.witness_script[1] >= 30 - if self.redeem_script: - assert self.redeem_script[1] >= 22 - - # require path for each addr, check some are ours - - # rework the pubkey => subpath mapping - self.parse_subpaths(my_xfp, parent.warnings) - - if self.part_sig: - # How complete is the set of signatures so far? - # - assuming PSBT creator doesn't give us extra data not required - # - seems harmless if they fool us into thinking already signed; we do nothing - # - could also look at pubkey needed vs. sig provided - # - could consider structure of MofN in p2sh cases - self.fully_signed = (len(self.part_sig) >= len(self.subpaths)) - else: - # No signatures at all yet for this input (typical non multisig) - self.fully_signed = False - - if self.utxo: - # Important: they might be trying to trick us with an un-related - # funding transaction (UTXO) that does not match the input signature we're making - # (but if it's segwit, the ploy wouldn't work, Segwit FtW) - # - challenge: it's a straight dsha256() for old serializations, but not for newer - # segwit txn's... plus I don't want to deserialize it here. - try: - observed = uint256_from_str(calc_txid(self.fd, self.utxo)) - except: - raise AssertionError("Trouble parsing UTXO given for input #%d" % idx) - - assert txin.prevout.hash == observed, "utxo hash mismatch for input #%d" % idx - def handle_none_sighash(self): if self.sighash is None: - self.sighash = SIGHASH_ALL + self.sighash = SIGHASH_DEFAULT if self.taproot_subpaths else SIGHASH_ALL def has_utxo(self): # do we have a copy of the corresponding UTXO? @@ -713,183 +792,262 @@ def get_utxo(self, idx): return utxo - - def determine_my_signing_key(self, my_idx, utxo, my_xfp, psbt): + def determine_my_signing_key(self, my_idx, addr_or_pubkey, my_xfp, psbt, parsed_subpaths, utxo): # See what it takes to sign this particular input # - type of script # - which pubkey needed - # - scriptSig value # - also validates redeem_script when present + merkle_root = redeem_script = None - self.amount = utxo.nValue - - if not self.subpaths or self.fully_signed: - # without xfp+path we will not be able to sign this input - # - okay if fully signed - # - okay if payjoin or other multi-signer (not multisig) txn - self.required_key = None + if self.af == OP_RETURN: return - self.is_multisig = False - self.is_p2sh = False - which_key = None + if self.af is None: + # If this is reached, we do not understand the output well + # enough to allow the user to authorize the spend, so fail hard. + raise FatalPSBTIssue('Unhandled scriptPubKey: ' + b2a_hex(addr_or_pubkey).decode()) + + if psbt.active_miniscript or psbt.active_singlesig: + # we have already set one of these - sow we can use some short-cuts + if psbt.active_miniscript and (self.af in (AF_CLASSIC, AF_P2WPKH, AF_BARE_PK)): + # signing with miniscript wallet - ignore single sig utxos + self.sp_idxs = None + return + elif psbt.active_singlesig and (self.af == AF_P2WSH): + # we are signing single sig inputs - ignore p2wsh utxos + self.sp_idxs = None + return - addr_type, addr_or_pubkey, addr_is_segwit = utxo.get_address() - if addr_is_segwit and not self.is_segwit: - self.is_segwit = True + if self.af == AF_BARE_PK: + # input is single compressed public key (less common) + # uncompressed public keys not supported! + assert len(addr_or_pubkey) == 33 - if addr_type == 'p2sh': - # multisig input - self.is_p2sh = True + for i, pubkey in enumerate(parsed_subpaths): + if pubkey == addr_or_pubkey: + assert i == self.sp_idxs[0] + break + else: + # pubkey provided is just wrong vs. UTXO + raise FatalPSBTIssue('Input #%d: pubkey wrong' % my_idx) + elif self.af in (AF_CLASSIC, AF_P2WPKH): + # P2PKH & P2WPKH + # input is hash160 of a single public key + + for i, pubkey in enumerate(parsed_subpaths): + if hash160(pubkey) == addr_or_pubkey: + assert i == self.sp_idxs[0] + break + else: + # none of the pubkeys provided hashes to that address + raise FatalPSBTIssue('Input #%d: pubkey vs. address wrong' % my_idx) + + elif self.af in (AF_P2WSH, AF_P2SH): # we must have the redeem script already (else fail) ks = self.witness_script or self.redeem_script if not ks: raise FatalPSBTIssue("Missing redeem/witness script for input #%d" % my_idx) redeem_script = self.get(ks) - self.scriptSig = redeem_script + native_v0 = (self.af == AF_P2WSH) + + if not native_v0 and (len(redeem_script) == 22) and \ + redeem_script[0] == 0 and redeem_script[1] == 20 and \ + len(parsed_subpaths) == 1: + + for i, pubkey in enumerate(parsed_subpaths): + target_spk, _ = chains.current_chain().script_pubkey(AF_P2WPKH_P2SH, + pubkey=pubkey) + if target_spk == utxo.scriptPubKey: + # it's actually segwit p2wpkh inside p2sh + self.af = AF_P2WPKH_P2SH + assert i == self.sp_idxs[0] - # new cheat: psbt creator probably telling us exactly what key - # to use, by providing exactly one. This is ideal for p2sh wrapped p2pkh - if len(self.subpaths) == 1: - which_key, = self.subpaths.keys() else: # Assume we'll be signing with any key we know - # - limitation: we cannot be two legs of a multisig # - but if partial sig already in place, ignore that one - for pubkey, path in self.subpaths.items(): - if self.part_sig and (pubkey in self.part_sig): - # pubkey has already signed, so ignore - continue - - if path[0] == my_xfp: + self.is_miniscript = True + # values will always be coords for both pubkey and signature at this point + done_keys = set() + if self.part_sigs: + done_keys = {self.get(k) for k,_ in self.part_sigs} + + for i, (pubkey, path) in enumerate(parsed_subpaths.items()): + if pubkey in done_keys: + # pubkey has already signed, so - do not sign again + if i in self.sp_idxs: + # remove from sp_idxs so we do not attempt to sign again + self.sp_idxs.remove(i) + + elif path[0] == my_xfp: # slight chance of dup xfps, so handle - if not which_key: - which_key = set() - - which_key.add(pubkey) - - if not addr_is_segwit and \ - len(redeem_script) == 22 and \ - redeem_script[0] == 0 and redeem_script[1] == 20: - # it's actually segwit p2pkh inside p2sh - addr_type = 'p2sh-p2wpkh' - addr = redeem_script[2:22] - self.is_segwit = True + assert i in self.sp_idxs + + if self.witness_script and (not native_v0) and (self.redeem_script[1] == 34): + # bugfix + self.af = AF_P2WSH_P2SH + assert self.redeem_script[1] == 34 + + if self.af in (AF_P2WSH, AF_P2WSH_P2SH): + # for both P2WSH & P2SH-P2WSH + if not self.witness_script: + raise FatalPSBTIssue('Need witness script for input #%d' % my_idx) + + elif self.af == AF_P2TR: + if len(parsed_subpaths) == 1: + # keyspend without a script path + assert self.taproot_merkle_root is None, "merkle_root should not be defined for simple keyspend" + assert self.ik_idx is not None + xonly_pubkey, lhs_path = list(parsed_subpaths.items())[0] + lhs, path = lhs_path[0], lhs_path[1:] + assert not lhs, "LeafHashes have to be empty for internal key" + assert self.sp_idxs[0] == 0 + assert taptweak(xonly_pubkey) == addr_or_pubkey else: - # multiple keys involved, we probably can't do the finalize step - self.is_multisig = True + # tapscript (is always miniscript wallet) + self.is_miniscript = True - if self.witness_script and not self.is_segwit and self.is_multisig: - # bugfix - addr_type = 'p2sh-p2wsh' - self.is_segwit = True + if self.taproot_merkle_root is not None: + merkle_root = self.get(self.taproot_merkle_root) - elif addr_type == 'p2pkh': - # input is hash160 of a single public key - self.scriptSig = utxo.scriptPubKey - addr = addr_or_pubkey + for i, (xonly_pubkey, lhs_path) in enumerate(parsed_subpaths.items()): + if i not in self.sp_idxs: + # # ignore keys that does not have correct xfp specified in PSBT + continue - for pubkey in self.subpaths: - if hash160(pubkey) == addr: - which_key = pubkey - break + lhs, path = lhs_path[0], lhs_path[1:] + assert path[0] == my_xfp + assert merkle_root is not None, "Merkle root not defined" + if self.ik_idx == i: + assert not lhs + output_key = taptweak(xonly_pubkey, merkle_root) + if output_key == addr_or_pubkey: + # if we find a possibility to spend keypath (internal_key) - we do keypath + # even though script path is available + self.sp_idxs = [i] + self.use_keypath = True + break # done ignoring all other possibilities + else: + internal_key = self.get(self.taproot_internal_key) + output_pubkey = taptweak(internal_key, merkle_root) + if addr_or_pubkey == output_pubkey: + assert i in self.sp_idxs + + if self.is_miniscript: + if not self.sp_idxs: return + if psbt.active_singlesig: + # if we already considered single signature inputs for signing + # do not even consider to sign with miniscript wallet(s) + # maybe we removed + self.sp_idxs = None + return # required key is None + + if self.af == AF_P2TR: + xfp_paths = [item[1:] + for item in parsed_subpaths.values() + if len(item[1:]) > 1] else: - # none of the pubkeys provided hashes to that address - raise FatalPSBTIssue('Input #%d: pubkey vs. address wrong' % my_idx) - - elif addr_type == 'p2pk': - # input is single public key (less common) - self.scriptSig = utxo.scriptPubKey - assert len(addr_or_pubkey) == 33 - - if addr_or_pubkey in self.subpaths: - which_key = addr_or_pubkey + xfp_paths = list(parsed_subpaths.values()) + + if psbt.active_miniscript: + if not MiniScriptWallet.disable_checks: + if not psbt.active_miniscript.matching_subpaths(xfp_paths): + # not input from currently selected wallet + self.sp_idxs = None + return else: - # pubkey provided is just wrong vs. UTXO - raise FatalPSBTIssue('Input #%d: pubkey wrong' % my_idx) - - else: - # we don't know how to "solve" this type of input - pass - - if self.is_multisig and which_key: - # We will be signing this input, so - # - find which wallet it is or - # - check it's the right M/N to match redeem script - - #print("redeem: %s" % b2a_hex(redeem_script)) - M, N = disassemble_multisig_mn(redeem_script) - xfp_paths = list(self.subpaths.values()) - xfp_paths.sort() - - if not psbt.active_multisig: - # search for multisig wallet - wal = MultisigWallet.find_match(M, N, xfp_paths) + # if we do have actual script at hand, guess M/N for better matching + # basic multisig matching + M, N = disassemble_multisig_mn(redeem_script) + wal = MiniScriptWallet.find_match(xfp_paths, self.af, M, N) if not wal: - raise FatalPSBTIssue('Unknown multisig wallet') + # not an input from wallet that we have enrolled + self.sp_idxs = None + return - psbt.active_multisig = wal - else: - # check consistent w/ already selected wallet - psbt.active_multisig.assert_matching(M, N, xfp_paths) + psbt.active_miniscript = wal - # validate redeem script, by disassembling it and checking all pubkeys try: - psbt.active_multisig.validate_script(redeem_script, subpaths=self.subpaths) - except BaseException as exc: - sys.print_exception(exc) - raise FatalPSBTIssue('Input #%d: %s' % (my_idx, exc)) - - if not which_key and DEBUG: - print("no key: input #%d: type=%s segwit=%d a_or_pk=%s scriptPubKey=%s" % ( - my_idx, addr_type, self.is_segwit or 0, - b2a_hex(addr_or_pubkey), b2a_hex(utxo.scriptPubKey))) - - self.required_key = which_key - - if self.is_segwit: - if ('pkh' in addr_type): - # This comment from : - # - # Please note that for a P2SH-P2WPKH, the scriptCode is always 26 - # bytes including the leading size byte, as 0x1976a914{20-byte keyhash}88ac, - # NOT the redeemScript nor scriptPubKey - # - # Also need this scriptCode for native segwit p2pkh - # - assert not self.is_multisig - self.scriptCode = b'\x19\x76\xa9\x14' + addr + b'\x88\xac' - elif not self.scriptCode: - # Segwit P2SH. We need the witness script to be provided. - if not self.witness_script: - raise FatalPSBTIssue('Need witness script for input #%d' % my_idx) - - # "scriptCode is witnessScript preceeded by a - # compactSize integer for the size of witnessScript" - self.scriptCode = ser_string(self.get(self.witness_script)) - - # Could probably free self.subpaths and self.redeem_script now, but only if we didn't - # need to re-serialize as a PSBT. + # contains PSBT merkle root verification (if taproot) + if not MiniScriptWallet.disable_checks: + psbt.active_miniscript.validate_script_pubkey(self.utxo_spk, + xfp_paths, merkle_root) + except BaseException as e: + # sys.print_exception(e) + raise FatalPSBTIssue('Input #%d: %s\n\n' % (my_idx, e) + problem_file_line(e)) + + else: + # single signature utxo + if psbt.active_miniscript: + # complex wallet is active - so this is not for us to sign + self.sp_idxs = None + return + + psbt.active_singlesig = True + + def segwit_v0_scriptCode(self): + # only v0 segwit + # only needed for sighash + assert self.is_segwit and (self.af != AF_P2TR) + if self.af == AF_P2WPKH: + return b'\x19\x76\xa9\x14' + self.utxo_spk[2:2+20] + b'\x88\xac' + elif self.af == AF_P2WPKH_P2SH: + return b'\x19\x76\xa9\x14' + self.get(self.redeem_script)[2:22] + b'\x88\xac' + elif self.af in (AF_P2WSH, AF_P2WSH_P2SH): + # "scriptCode is witnessScript preceeded by a + # compactSize integer for the size of witnessScript" + return ser_string(self.get(self.witness_script)) + + def get_scriptSig(self): + if self.af in [AF_BARE_PK, AF_CLASSIC]: + return self.utxo_spk + elif self.af in (AF_P2SH, AF_P2WSH_P2SH, AF_P2WPKH_P2SH): + return self.get(self.redeem_script) + else: + return b"" def store(self, kt, key, val): # Capture what we are interested in. - if kt == PSBT_IN_NON_WITNESS_UTXO: self.utxo = val elif kt == PSBT_IN_WITNESS_UTXO: self.witness_utxo = val elif kt == PSBT_IN_PARTIAL_SIG: - self.part_sig[key[1:]] = val + # taproot inputs do not have part sigs + # only populate the attribute if present + if not self.part_sigs: + self.part_sigs = [] + # do not load anything (both key and val are coordinates) + # actual signatures (71 bytes) we do not need them until finalization + # public keys are enough for validation we will get them as needed + self.part_sigs.append((key, val)) elif kt == PSBT_IN_BIP32_DERIVATION: - self.subpaths[key[1:]] = val + if self.subpaths is None: + self.subpaths = [] + self.subpaths.append((key, val)) elif kt == PSBT_IN_REDEEM_SCRIPT: self.redeem_script = val elif kt == PSBT_IN_WITNESS_SCRIPT: self.witness_script = val elif kt == PSBT_IN_SIGHASH_TYPE: self.sighash = unpack('= 1 - xfp_paths.append(h) + parsed_xpubs.append((xp, h)) if h[0] == self.my_xfp: has_mine += 1 @@ -1227,63 +1434,58 @@ async def handle_xpubs(self): if not has_mine: raise FatalPSBTIssue('My XFP not involved') - candidates = MultisigWallet.find_candidates(xfp_paths) + # don't want to guess M if not needed, but we need it + af, M, N = self.guess_M_of_N() + if not N: + # not multisig, but we can still verify: + # - miniscript cannot be imported from PSBT (we lack descriptor in PSBT) + # - XFP should be one of ours (checked above). + # - too slow to re-derive it here, so nothing more to validate at this point + return - if len(candidates) == 1: - # exact match (by xfp+deriv set) .. normal case - self.active_multisig = candidates[0] - else: - # don't want to guess M if not needed, but we need it - M, N = self.guess_M_of_N() + assert N == len(self.xpubs) - if not N: - # not multisig, but we can still verify: - # - XFP should be one of ours (checked above). - # - too slow to re-derive it here, so nothing more to validate at this point - return + # Validate good match here. The xpubs must be exactly right, but + # we're going to use our own values from setup time anyway and not trusting + # new values without user interaction. + # Check: + # - chain codes match what we have stored already + # - pubkey vs. path will be checked later + # - xfp+path already checked above when selecting wallet + # Any issue here is a fraud attempt in some way, not innocent. + wal = MiniScriptWallet.find_match([i[1] for i in parsed_xpubs], af, M, N) - assert N == len(xfp_paths) - - for c in candidates: - if c.M == M and c.N == N: - self.active_multisig = c - break - # if not active_multisig set in this loop - # appropriate candidate was not found - # --> continue to import from psbt prompt + if wal: + # exact match (by xfp+deriv set) .. normal case + self.active_miniscript = wal + # now proper check should follow - matching actual master pubkeys + # but is it needed?, we just matched the wallet + # and are going to use our own data for verification anyway + if not self.active_miniscript.disable_checks: + self.active_miniscript.validate_psbt_xpubs(parsed_xpubs) - del candidates + else: + trust_mode = MiniScriptWallet.get_trust_policy() + # already checked for existing import and wasn't found, so fail + if trust_mode == TRUST_VERIFY: + raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet") - if not self.active_multisig: # Maybe create wallet, for today, forever, or fail, etc. - proposed, need_approval = MultisigWallet.import_from_psbt(M, N, self.xpubs) - if need_approval: + proposed = MiniScriptWallet.import_from_psbt(af, M, N, parsed_xpubs) + if trust_mode != TRUST_PSBT: # do a complex UX sequence, which lets them save new wallet from glob import hsm_active if hsm_active: raise FatalPSBTIssue("MS enroll not allowed in HSM mode") - ch = await proposed.confirm_import() - if ch != 'y': + approved = await proposed.confirm_import() + if not approved: raise FatalPSBTIssue("Refused to import new wallet") - self.active_multisig = proposed - else: - # Validate good match here. The xpubs must be exactly right, but - # we're going to use our own values from setup time anyway and not trusting - # new values without user interaction. - # Check: - # - chain codes match what we have stored already - # - pubkey vs. path will be checked later - # - xfp+path already checked above when selecting wallet - # Any issue here is a fraud attempt in some way, not innocent. - self.active_multisig.validate_psbt_xpubs(self.xpubs) - - if not self.active_multisig: - # not clear if an error... might be part-way to importing, and - # the data is optional anyway, etc. If they refuse to import, - # we should not reach this point (ie. raise something to abort signing) - return + self.active_miniscript = proposed + + # must have wallet at this point + assert self.active_miniscript def ux_relative_timelocks(self, tb, bb): # visualize 10 largest timelock to user @@ -1312,11 +1514,11 @@ def ux_relative_timelocks(self, tb, bb): # Block height relative lock-time if num_bb == 1: idx, val = bb[0] - msg = "Input %d. has relative block height timelock of %d blocks" % ( + msg = "Input %d. has relative block height timelock of %d blocks\n" % ( idx, val ) elif all(bb[0][1] == i[1] for i in bb): - msg = "%d inputs have relative block height timelock of %d blocks" % ( + msg = "%d inputs have relative block height timelock of %d blocks\n" % ( num_bb, bb[0][1] ) else: @@ -1334,11 +1536,11 @@ def ux_relative_timelocks(self, tb, bb): if num_tb == 1: idx, val = tb[0] val = seconds2human_readable(val) - msg = "Input %d. has relative time-based timelock of:\n %s" % ( + msg = "Input %d. has relative time-based timelock of:\n %s\n" % ( idx, val ) elif all(tb[0][1] == i[1] for i in tb): - msg = "%d inputs have relative time-based timelock of:\n %s" % ( + msg = "%d inputs have relative time-based timelock of:\n %s\n" % ( num_tb, seconds2human_readable(tb[0][1]) ) else: @@ -1352,6 +1554,15 @@ def ux_relative_timelocks(self, tb, bb): self.ux_notes.append(("Time-based RTL", msg)) + def validate_unkonwn(self, obj, label): + # find duplicate unknown values in different PSBT parts + if not obj.unknown: + return + + if len({self.get(k) for k,_ in obj.unknown}) < len(obj.unknown): + raise FatalPSBTIssue("Duplicate key. Key for unknown value" + " already provided in %s." % label) + async def validate(self): # Do a first pass over the txn. Raise assertions, be terse tho because # these messages are rarely seen. These are syntax/fatal errors. @@ -1376,113 +1587,202 @@ async def validate(self): assert not self.has_goc, "v0 requires exclusion of global output count" assert not self.has_gtv, "v0 requires exclusion of global txn version" assert self.txn, "v0 requires inclusion of global unsigned tx" - assert self.txn[1] > 63, 'txn too short' + assert self.txn[1] > 61, 'txn too short' assert self.fallback_locktime is None, "v0 requires exclusion of global fallback locktime" assert self.txn_modifiable is None, "v0 requires exclusion of global txn modifiable" - for idx, txo in self.output_iter(): - out = self.outputs[idx] + assert len(self.inputs) == self.num_inputs, 'ni mismatch' + + assert self.num_outputs >= 1, 'need outputs' + + self.validate_unkonwn(self, "global namespace") + + inp_have_subpath = False + for i in self.inputs: + if i.subpaths or i.taproot_subpaths: + inp_have_subpath = True + if self.is_v2: # v2 requires inclusion - assert out.amount - assert out.script + assert i.prevout_idx is not None + assert i.previous_txid + if i.req_time_locktime is not None: + assert i.req_time_locktime >= NLOCK_IS_TIME + if i.req_height_locktime is not None: + assert 0 < i.req_height_locktime < NLOCK_IS_TIME else: # v0 requires exclusion - assert out.amount is None - assert out.script is None - - # time based relative locks - tb_rel_locks = [] - # block height based relative locks - bb_rel_locks = [] - smallest_nsequence = 0xffffffff - # this parses the input TXN in-place - for idx, txin in self.input_iter(): - inp = self.inputs[idx] + assert i.prevout_idx is None + assert i.previous_txid is None + assert i.sequence is None + assert i.req_time_locktime is None + assert i.req_height_locktime is None + + if i.witness_script: + assert i.witness_script[1] >= 30 + if i.redeem_script: + assert i.redeem_script[1] >= 22 + + if i.taproot_internal_key: + assert i.taproot_internal_key[1] == 32 # "PSBT_IN_TAP_INTERNAL_KEY length != 32" + + if i.taproot_key_sig: + # "PSBT_IN_TAP_KEY_SIG length != 64 or 65" + assert i.taproot_key_sig[1] in (64, 65) + + if i.part_sigs: + for k, v in i.part_sigs: + assert k[1] == 33 + # valid signature can also be 60 bytes or less (needs grinding) + # 69 bytes - where both r & s are 31 bytes + # 73 -> high-s & high-r + assert v[1] <= 73, "DER sig len" + + if i.taproot_script_sigs: + for k, v in i.taproot_script_sigs: + # PSBT_IN_TAP_SCRIPT_SIG + 32 bytes xonly pubkey + leafhash 32 bytes + assert k[1] == 64 + # The 64 or 65 byte Schnorr signature for this pubkey and leaf combination + assert v[1] in (64, 65) + + if i.taproot_scripts: + for k, v in i.taproot_scripts: + assert k[1] > 32 # "PSBT_IN_TAP_LEAF_SCRIPT control block is too short" + assert (k[1] - 1) % 32 == 0 # "PSBT_IN_TAP_LEAF_SCRIPT control block is not valid" + assert v[1] != 0 # "PSBT_IN_TAP_LEAF_SCRIPT cannot be empty" + + if i.sighash and (i.sighash not in ALL_SIGHASH_FLAGS): + raise FatalPSBTIssue("Unsupported sighash flag 0x%x" % i.sighash) + + self.validate_unkonwn(i, "input") + + for o in self.outputs: if self.is_v2: # v2 requires inclusion - assert inp.prevout_idx is not None - assert inp.previous_txid - if inp.req_time_locktime is not None: - assert inp.req_time_locktime >= 500000000 - if inp.req_height_locktime is not None: - assert 0 < inp.req_height_locktime < 500000000 + assert o.amount + assert o.script else: # v0 requires exclusion - assert inp.prevout_idx is None - assert inp.previous_txid is None - assert inp.sequence is None - assert inp.req_time_locktime is None - assert inp.req_height_locktime is None - - self.inputs[idx].validate(idx, txin, self.my_xfp, self) - if self.txn_version >= 2: - has_rtl = self.inputs[idx].has_relative_timelock(txin) - if has_rtl: - if has_rtl[0]: - tb_rel_locks.append((idx, has_rtl[1])) - else: - bb_rel_locks.append((idx, has_rtl[1])) - - if txin.nSequence < smallest_nsequence: - smallest_nsequence = txin.nSequence - - if isinstance(self.lock_time, int) and self.lock_time > 0: - if smallest_nsequence == 0xffffffff: - self.warnings.append(( - "Bad Locktime", - "Locktime has no effect! None of the nSequences decremented." - )) - else: - msg = "This tx can only be spent after " - if self.lock_time < 500000000: - msg += "block height of %d" % self.lock_time - else: - try: - dt = datetime_from_timestamp(self.lock_time) - msg += datetime_to_str(dt) - except: - msg += "%d (unix timestamp)" % self.lock_time + assert o.amount is None + assert o.script is None - msg += " (MTP)" # median time past - msg += "\n" - self.ux_notes.append(("Abs Locktime", msg)) + if o.taproot_internal_key: + assert o.taproot_internal_key[1] == 32 # "PSBT_OUT_TAP_INTERNAL_KEY length != 32" - # create UX for users about tx level relative timelocks (nSequence) - self.ux_relative_timelocks(tb_rel_locks, bb_rel_locks) + self.validate_unkonwn(o, "output") - assert len(self.inputs) == self.num_inputs, 'ni mismatch' + if not inp_have_subpath: + # Can happen w/ Electrum in watch-mode on XPUB. It doesn't know XFP and + # so doesn't insert that into PSBT. + # or PSBT provider forgot to include subpaths + raise FatalPSBTIssue('PSBT inputs do not contain any key path information.') # if multisig xpub details provided, they better be right and/or offer import if self.xpubs: await self.handle_xpubs() - assert self.num_outputs >= 1, 'need outputs' - if DEBUG: - our_keys = sum(1 for i in self.inputs if i.num_our_keys) - - print("PSBT: %d inputs, %d output, %d fully-signed, %d ours" % ( - self.num_inputs, self.num_outputs, - sum(1 for i in self.inputs if i and i.fully_signed), our_keys)) + print("PSBT: %d inputs, %d output" % (self.num_inputs, self.num_outputs)) - def consider_outputs(self): + def consider_outputs(self, len_pths, hard_p, prefix_pths, idx_max, cosign_xfp=None): + from glob import dis # scan ouputs: # - is it a change address, defined by redeem script (p2sh) or key we know is ours # - mark change outputs, so perhaps we don't show them to users total_out = 0 total_change = 0 + num_op_return = 0 + num_op_return_size = 0 + num_unknown_scripts = 0 + zero_val_outs = 0 # only those that are not OP_RETURN are considered self.num_change_outputs = 0 + validate_inp_pths = False + path_len = None + max_gap = idx_max + 200 + + # We aren't seeing shared input path lengths. + # They are probably doing weird stuff, so leave them alone + # and do not validate against inputs paths + if len(len_pths) == 1: + path_len = 0 + for pl in len_pths: + path_len = pl + break + if path_len > 2: + validate_inp_pths = True + + dis.fullscreen("Validating...", line2="Outputs") + for idx, txo in self.output_iter(): + dis.progress_sofar(idx, self.num_outputs) output = self.outputs[idx] + + parsed_subpaths = output.parse_subpaths(self.my_xfp, self.warnings, cosign_xfp) + # perform output validation - output.validate(idx, txo, self.my_xfp, self.active_multisig, self) + af = output.determine_my_change(idx, txo, parsed_subpaths, self) + assert txo.nValue >= 0, "negative output value: o%d" % idx total_out += txo.nValue + + if (txo.nValue == 0) and (af != OP_RETURN): + # OP_RETURN outputs have nValue=0 standard + zero_val_outs += 1 + if output.is_change: self.num_change_outputs += 1 total_change += txo.nValue + if validate_inp_pths: + # Enforce some policy on change outputs: + # - need to "look like" they are going to same wallet as inputs came from + # - range limit last two path components (numerically) + # - same pattern of hard/not hardened components + # - MAX_PATH_DEPTH already enforced before this point + # - (single-sig only) check ther is only 0,1 at change index + is_cmplx = (len(parsed_subpaths) > 1) + for i, xpath in enumerate(parsed_subpaths.values()): + if i not in output.sp_idxs: continue + p = xpath[2:] if output.taproot_subpaths else xpath[1:] + + iss = None + if len(p) != path_len: + iss = "has wrong path length (%d not %d)" % (len(p), path_len) + elif tuple(bool(i & 0x80000000) for i in p) not in hard_p: + iss = "has different hardening pattern" + elif tuple(p[:-2]) not in prefix_pths: + iss = "goes to diff path prefix" + elif not is_cmplx and ((p[-2] & 0x7fffffff) not in {0,1}): + iss = "2nd last component not 0 or 1" + elif (p[-1] & 0x7fffffff) > max_gap: + iss = "last component beyond reasonable gap" + + if iss: + msg = "Output#%d: %s: %s" % (idx, iss, keypath_to_str(p, skip=0)) + if len(hard_p) == 1 and len(prefix_pths) == 1: + # message can be more verbose + # fastest way to get first element from the set + # without modifying the set is for-loop + for hp in hard_p: + break + for pp in prefix_pths: + break + msg += " not %s/{0~1}%s/{0~%d}%s expected" % ( + keypath_to_str(pp, skip=0), + "'" if hp[-2] else "", + max_gap, + "'" if hp[-1] else "" + ) + self.warnings.append(('Troublesome Change Outs', msg)) + + if af == OP_RETURN: + num_op_return += 1 + if len(txo.scriptPubKey) > 83: + num_op_return_size += 1 + + elif af is None: + num_unknown_scripts += 1 + if self.total_value_out is None: self.total_value_out = total_out else: @@ -1496,16 +1796,16 @@ def consider_outputs(self): '%s != %s' % (self.total_change_value, total_change) # check fee is reasonable - if self.total_value_out == 0: - per_fee = 100 - else: - the_fee = self.calculate_fee() - if the_fee is None: - return - if the_fee < 0: - raise FatalPSBTIssue("Outputs worth more than inputs!") + the_fee = self.calculate_fee() + if the_fee is None: + return + if the_fee < 0: + raise FatalPSBTIssue("Outputs worth more than inputs!") + if self.total_value_out: per_fee = the_fee * 100 / self.total_value_out + else: + per_fee = 100 fee_limit = settings.get('fee_limit', DEFAULT_MAX_FEE_PERCENTAGE) @@ -1516,168 +1816,175 @@ def consider_outputs(self): self.warnings.append(('Big Fee', 'Network fee is more than ' '5%% of total value (%.1f%%).' % per_fee)) - self.consolidation_tx = (self.num_change_outputs == self.num_outputs) - - # Enforce policy related to change outputs - self.consider_dangerous_change(self.my_xfp) - - def consider_dangerous_sighash(self): - # Check sighash flags are legal, useful, and safe. Warn about - # some risks if user has enabled special sighash values. - - sh_unusual = False - none_sh = False - - for input in self.inputs: - # only if it is our input - one that will be eventually sign - if input.num_our_keys: - if input.sighash is not None: - # All inputs MUST have SIGHASH that we are able to sign. - if input.sighash not in ALL_SIGHASH_FLAGS: - raise FatalPSBTIssue("Unsupported sighash flag 0x%x" % input.sighash) - - if input.sighash != SIGHASH_ALL: - sh_unusual = True - - if input.sighash in (SIGHASH_NONE, SIGHASH_NONE|SIGHASH_ANYONECANPAY): - none_sh = True - - if sh_unusual and not settings.get("sighshchk"): - if self.consolidation_tx: - # policy: all inputs must be sighash ALL in purely consolidation txn - raise FatalPSBTIssue("Only sighash ALL is allowed for pure consolidation transactions.") - - if none_sh: - # sighash NONE or NONE|ANYONECANPAY is proposed: block - raise FatalPSBTIssue("Sighash NONE is not allowed as funds could be going anywhere.") + if (num_op_return > 1) or num_op_return_size: + mm = "" + if num_op_return > 1: + mm += "\nMultiple OP_RETURN outputs: %d" % num_op_return + if num_op_return_size: + mm += "\nOP_RETURN > 80 bytes" + self.warnings.append( + ("OP_RETURN", + "TX may not be relayed by some nodes.%s" % mm)) - if none_sh: + if num_unknown_scripts: self.warnings.append( - ("Danger", "Destination address can be changed after signing (sighash NONE).") + ('Output?', + 'Sending to %d not well understood script(s).' % num_unknown_scripts) ) - elif sh_unusual: + + if zero_val_outs: self.warnings.append( - ("Caution", "Some inputs have unusual SIGHASH values not used in typical cases.") + ('Zero Value', + 'Non-standard zero value output(s).') ) - def consider_dangerous_change(self, my_xfp): - # Enforce some policy on change outputs: - # - need to "look like" they are going to same wallet as inputs came from - # - range limit last two path components (numerically) - # - same pattern of hard/not hardened components - # - MAX_PATH_DEPTH already enforced before this point - # - in_paths = [] - for inp in self.inputs: - if inp.fully_signed: continue - if not inp.required_key: continue - if not inp.subpaths: continue # not expected if we're signing it - for path in inp.subpaths.values(): - if path[0] == my_xfp: - in_paths.append(path[1:]) - - if not in_paths: - # We aren't adding any signatures? Can happen but we're going to be - # showing a warning about that elsewhere. - return - - shortest = min(len(i) for i in in_paths) - longest = max(len(i) for i in in_paths) - if shortest != longest or shortest <= 2: - # We aren't seeing shared input path lengths. - # They are probbably doing weird stuff, so leave them alone. - return - - # Assumption: hard/not hardened depths will match for all address in wallet - def hard_bits(p): - return [bool(i & 0x80000000) for i in p] - - # Assumption: common wallets modulate the last two components only - # of the path. Typically m/.../change/index where change is {0, 1} - # and index changes slowly over lifetime of wallet (increasing) - path_len = shortest - path_prefix = in_paths[0][0:-2] - idx_max = max(i[-1]&0x7fffffff for i in in_paths) + 200 - hard_pattern = hard_bits(in_paths[0]) - - probs = [] - for nout, out in enumerate(self.outputs): - if not out.is_change: continue - # it's a change output, okay if a p2sh change; we're looking at paths - for path in out.subpaths.values(): - if path[0] != my_xfp: continue # possible in p2sh case - - path = path[1:] - if len(path) != path_len: - iss = "has wrong path length (%d not %d)" % (len(path), path_len) - elif hard_bits(path) != hard_pattern: - iss = "has different hardening pattern" - elif path[0:len(path_prefix)] != path_prefix: - iss = "goes to diff path prefix" - elif (path[-2]&0x7fffffff) not in {0, 1}: - iss = "2nd last component not 0 or 1" - elif (path[-1]&0x7fffffff) > idx_max: - iss = "last component beyond reasonable gap" - else: - # looks ok - continue - - probs.append("Output#%d: %s: %s not %s/{0~1}%s/{0~%d}%s expected" - % (nout, iss, keypath_to_str(path, skip=0), - keypath_to_str(path_prefix, skip=0), - "'" if hard_pattern[-2] else "", - idx_max, "'" if hard_pattern[-1] else "", - )) - break + self.consolidation_tx = (self.num_change_outputs == self.num_outputs) + dis.progress_bar_show(1) - for p in probs: - self.warnings.append(('Troublesome Change Outs', p)) + if DEBUG: + print("PSBT change outputs: %d out of %d" % ( + self.num_change_outputs, len(self.outputs) + )) - def consider_inputs(self): + def consider_inputs(self, cosign_xfp=None): # Look at the UTXO's that we are spending. Do we have them? Do the # hashes match, and what values are we getting? # Important: parse incoming UTXO to build total input value + # check nSequences & nLockTime and warn about TX level locktimes + from glob import dis + foreign = [] total_in = 0 + presigned_inputs = set() + # time based relative locks + tb_rel_locks = [] + # block height based relative locks + bb_rel_locks = [] + smallest_nsequence = 0xffffffff + + # collect some input path data from subapths + # later used for change outputs path validation + length_p = set() + hard_pattern = set() + prefix_p = set() + idx_max = 0 + my_cnt = 0 + + dis.fullscreen("Validating...", line2="Inputs") for i, txi in self.input_iter(): + dis.progress_sofar(i, self.num_inputs) inp = self.inputs[i] - if inp.fully_signed: - self.presigned_inputs.add(i) + + if inp.part_sigs: + # How complete is the set of signatures so far? + # - assuming PSBT creator doesn't give us extra data not required + # - seems harmless if they fool us into thinking already signed; we do nothing + # - could also look at pubkey needed vs. sig provided + # - could consider structure of MofN in p2sh cases + if len(inp.part_sigs) >= len(inp.subpaths): + inp.fully_signed = True + + if inp.taproot_key_sig: + inp.fully_signed = True + + if inp.utxo: + # Important: they might be trying to trick us with an un-related + # funding transaction (UTXO) that does not match the input signature we're making + # (but if it's segwit, the ploy wouldn't work, Segwit FtW) + # - challenge: it's a straight dsha256() for old serializations, but not for newer + # segwit txn's... plus I don't want to deserialize it here. + try: + observed = uint256_from_str(calc_txid(self.fd, inp.utxo)) + except: + raise AssertionError("Trouble parsing UTXO given for input #%d" % i) + + assert txi.prevout.hash == observed, "utxo hash mismatch for input #%d" % i + + if self.txn_version >= 2: + has_rtl = inp.has_relative_timelock(txi) + if has_rtl: + if has_rtl[0]: + tb_rel_locks.append((i, has_rtl[1])) + else: + bb_rel_locks.append((i, has_rtl[1])) + + if txi.nSequence < smallest_nsequence: + smallest_nsequence = txi.nSequence + + parsed_subpaths = inp.parse_subpaths(self.my_xfp, self.warnings, cosign_xfp) if not inp.has_utxo(): - if inp.num_our_keys and not inp.fully_signed: + if inp.sp_idxs and not inp.fully_signed: # we cannot proceed if the input is ours and there is no UTXO raise FatalPSBTIssue('Missing own UTXO(s). Cannot determine value being signed') - else: - # input clearly not ours - foreign.append(i) - continue - # pull out just the CTXOut object (expensive) + # input clearly not ours + foreign.append(i) + continue + + # pull out just the CTXOut object + # very expensive for non-witness utxo (whole tx) + # less expensive for witness UTXO (just necessary TxOut) + # utxo = inp.get_utxo(txi.prevout.n) + inp.amount = utxo.nValue + assert inp.amount >= 0, "negative input value: i%d" % i + total_in += inp.amount + + inp.af, addr_or_pubkey = utxo.get_address() + # save scriptPubKey of utxo for later use + # needed for P2WPKH scriptCode calculation + # needed for P2PK & P2PKH scriptSig (when finalizing) + # needed for each input if we sign at least one P2TR input + inp.utxo_spk = utxo.scriptPubKey + + if inp.sp_idxs: + my_cnt += 1 + if inp.fully_signed: + presigned_inputs.add(i) + if inp.sp_idxs and (not inp.fully_signed): + # Look at what kind of input this will be, and therefore what + # type of signing will be required, and which key we need. + # - also validates redeem_script when present + # - also finds appropriate miniscript wallet to be used + inp.determine_my_signing_key(i, addr_or_pubkey, self.my_xfp, self, + parsed_subpaths, utxo) + + # determine_my_signing_key may have removed sp_idxs + # meaning we're not going to sign this input - other wallet in use + if not inp.sp_idxs: + continue - assert utxo.nValue > 0 - total_in += utxo.nValue + # parsed subpaths are OrderedDict - matches sp_idxs + for ii, xpath in enumerate(parsed_subpaths.values()): + if ii not in inp.sp_idxs: continue + p = xpath[2:] if inp.taproot_subpaths else xpath[1:] + length_p.add(len(p)) # ignore xfp + hard_pattern.add(tuple(bool(i & 0x80000000) for i in p)) + prefix_p.add(tuple(p[:-2])) - # Look at what kind of input this will be, and therefore what - # type of signing will be required, and which key we need. - # - also validates redeem_script when present - # - also finds appropriate multisig wallet to be used - inp.determine_my_signing_key(i, utxo, self.my_xfp, self) + index = p[-1] & 0x7fffffff + if index > idx_max: + idx_max = index - # iff to UTXO is segwit, then check it's value, and also - # capture that value, since it's supposed to be immutable - if inp.is_segwit: - history.verify_amount(txi.prevout, inp.amount, i) + # iff to UTXO is segwit, then check it's value, and also + # capture that value, since it's supposed to be immutable + if inp.af and inp.is_segwit: + history.verify_amount(txi.prevout, inp.amount, i) - del utxo + if inp.af == AF_P2TR: + # based on this we know whether we can drop inp.utxo_xpk + # attribute after creating sighash + self.my_tr_in = True - # XXX scan witness data provided, and consider those ins signed if not multisig? + if not my_cnt: + raise FatalPSBTIssue('None of the keys involved in this transaction ' + 'belong to this Coldcard (need %s).' % xfp2str(self.my_xfp)) if not foreign: # no foreign inputs, we can calculate the total input value - assert total_in > 0 + assert total_in > 0, "zero value txn" self.total_value_in = total_in else: # 1+ inputs don't belong to us, we can't calculate the total input value @@ -1687,61 +1994,113 @@ def consider_inputs(self): ("Unable to calculate fee", "Some input(s) haven't provided UTXO(s): " + seq_to_str(foreign)) ) - if len(self.presigned_inputs) == self.num_inputs: - # Maybe wrong for multisig cases? Maybe they want to add their + if len(presigned_inputs) == self.num_inputs: + # Maybe wrong f cases? Maybe they want to add their # own signature, even tho N of M is satisfied?! raise FatalPSBTIssue('Transaction looks completely signed already?') # We should know pubkey required for each input now. # - but we may not be the signer for those inputs, which is fine. # - TODO: but what if not SIGHASH_ALL - no_keys = set(n for n,inp in enumerate(self.inputs) - if inp.required_key == None and not inp.fully_signed) + no_keys = set( + n + for n,inp in enumerate(self.inputs) + if (not inp.sp_idxs) and (not inp.fully_signed) + ) + # HWI blocker + # if len(no_keys) == self.num_inputs: + # # nothing to sign for us + # raise FatalPSBTIssue("Nothing to sign here") + if no_keys: # This is seen when you re-sign same signed file by accident (multisig) - # - case of len(no_keys)==num_inputs is handled by consider_keys + # - case of len(no_keys)==num_inputs is handled by consider_inputs self.warnings.append(('Limited Signing', - 'We are not signing these inputs, because we do not know the key: ' + - seq_to_str(no_keys))) + "We are not signing these inputs, because we either don't know the key," + " inputs belong to different wallet, or we have already signed: " + seq_to_str(no_keys))) - if self.presigned_inputs: + if presigned_inputs: # this isn't really even an issue for some complex usage cases self.warnings.append(('Partly Signed Already', 'Some input(s) provided were already completely signed by other parties: ' + - seq_to_str(self.presigned_inputs))) + seq_to_str(presigned_inputs))) - if MultisigWallet.disable_checks: - self.warnings.append(('Danger', 'Some multisig checks are disabled.')) + if isinstance(self.lock_time, int) and self.lock_time > 0: + if smallest_nsequence == 0xffffffff: + self.warnings.append(( + "Bad Locktime", + "Locktime has no effect! None of the nSequences decremented." + )) + else: + msg = "This tx can only be spent after " + if self.lock_time < NLOCK_IS_TIME: + msg += "block height of %d" % self.lock_time + else: + try: + dt = datetime_from_timestamp(self.lock_time) + msg += datetime_to_str(dt) + except: + msg += "%d (unix timestamp)" % self.lock_time - def calculate_fee(self): - # what miner's reward is included in txn? - if self.total_value_in is None: - return None - return self.total_value_in - self.total_value_out + msg += " (MTP)" # median time past + msg += "\n" + self.ux_notes.append(("Abs Locktime", msg)) - def consider_keys(self): - # check we posess the right keys for the inputs - cnt = sum(1 for i in self.inputs if i.num_our_keys) - if cnt: return + # create UX for users about tx level relative timelocks (nSequence) + self.ux_relative_timelocks(tb_rel_locks, bb_rel_locks) + + if MiniScriptWallet.disable_checks: + self.warnings.append(('Danger', 'Some miniscript checks are disabled.')) + + if DEBUG: + print("PSBT inputs: %d inputs contain our key, %d fully-signed" % ( + my_cnt, len(presigned_inputs))) - # collect a list of XFP's given in file that aren't ours - others = set() + dis.progress_bar_show(1) + + # useful info from all our parsed paths - will be validated against change outputs + return length_p, hard_pattern, prefix_p, idx_max + + def consider_dangerous_sighash(self): + # Check sighash flags are legal, useful, and safe. Warn about + # some risks if user has enabled special sighash values. + # can only be run after consider_outputs is done + sh_unusual = False + none_sh = False for inp in self.inputs: - if not inp.subpaths: continue - for path in inp.subpaths.values(): - others.add(path[0]) + if inp.sp_idxs and not inp.fully_signed: + if inp.sighash: + if inp.sighash is not None: + if inp.sighash not in (SIGHASH_ALL, SIGHASH_DEFAULT): + sh_unusual = True - if not others: - # Can happen w/ Electrum in watch-mode on XPUB. It doesn't know XFP and - # so doesn't insert that into PSBT. - raise FatalPSBTIssue('PSBT does not contain any key path information.') + if inp.sighash in (SIGHASH_NONE, SIGHASH_NONE | SIGHASH_ANYONECANPAY): + none_sh = True - others.discard(self.my_xfp) - msg = ', '.join(xfp2str(i) for i in others) + if sh_unusual and not settings.get("sighshchk"): + if self.consolidation_tx: + # policy: all inputs must be sighash ALL in purely consolidation txn + raise FatalPSBTIssue("Only sighash ALL/DEFAULT is allowed" + " for pure consolidation transactions.") - raise FatalPSBTIssue('None of the keys involved in this transaction ' - 'belong to this Coldcard (need %s, found %s).' - % (xfp2str(self.my_xfp), msg)) + if none_sh: + # sighash NONE or NONE|ANYONECANPAY is proposed: block + raise FatalPSBTIssue("Sighash NONE is not allowed as funds could be going anywhere.") + + if none_sh: + self.warnings.append( + ("Danger", "Destination address can be changed after signing (sighash NONE).") + ) + elif sh_unusual: + self.warnings.append( + ("Caution", "Some inputs have unusual SIGHASH values not used in typical cases.") + ) + + def calculate_fee(self): + # what miner's reward is included in txn? + if self.total_value_in is None: + return None + return self.total_value_in - self.total_value_out @classmethod def read_psbt(cls, fd): @@ -1802,12 +2161,12 @@ def serialize(self, out_fd, upgrade_txn=False): wr(PSBT_GLOBAL_VERSION, pack(' single key - which_key = inp.required_key + assert len(inp.sp_idxs) == 1 + sp_idx = inp.sp_idxs[0] - - assert not inp.added_sig, "already done??" - assert which_key in inp.subpaths, 'unk key' + assert not inp.added_sigs, "already done??" + assert not inp.taproot_key_sig, "already done taproot??" - if inp.subpaths[which_key][0] != self.my_xfp: - # we don't have the key for this subkey - # (redundant, required_key wouldn't be set) - continue + if inp.taproot_subpaths: + schnorrsig = True + pubk = inp.taproot_subpaths[sp_idx][0] + sp = inp.taproot_subpaths[sp_idx][1][2] + else: + pubk = inp.subpaths[sp_idx][0] + sp = inp.subpaths[sp_idx][1] + int_pth = self.handle_zero_xfp(self.parse_xfp_path(sp), self.my_xfp, None) + skp = keypath_to_str(int_pth) # get node required - skp = keypath_to_str(inp.subpaths[which_key]) node = sv.derive_path(skp, register=False) - # expensive test, but works... and important pu = node.pubkey() - assert pu == which_key, \ + if schnorrsig: + pu = pu[1:] + + assert pu == self.get(pubk), \ "Path (%s) led to wrong pubkey for input#%d"%(skp, in_idx) + to_sign.append((node, pubk)) + # track wallet usage - OWNERSHIP.note_subpath_used(inp.subpaths[which_key]) + OWNERSHIP.note_subpath_used(int_pth) + + # normal operation with valid sighash + if not inp.is_segwit: + # Hash by serializing/blanking various subparts of the transaction + txi.scriptSig = inp.get_scriptSig() + digest = self.make_txn_sighash(in_idx, txi, inp.sighash) + else: + # Hash the inputs and such in totally new ways, based on BIP-143 + if not inp.taproot_subpaths: + digest = self.make_txn_segwit_sighash(in_idx, txi, inp.amount, + inp.segwit_v0_scriptCode(), + inp.sighash) + elif not tr_sh: + # taproot keyspend + digest = self.make_txn_taproot_sighash(in_idx, hash_type=inp.sighash) + # else: + # sighashes for tapscript spend are calculated later if sv.deltamode: # Current user is actually a thug with a slightly wrong PIN, so we # do have access to the private keys and could sign txn, but we # are going to silently corrupt our signatures. - digest = bytes(range(32)) - else: - if not inp.is_segwit: - # Hash by serializing/blanking various subparts of the transaction - digest = self.make_txn_sighash(in_idx, txi, inp.sighash) - else: - # Hash the inputs and such in totally new ways, based on BIP-143 - digest = self.make_txn_segwit_sighash(in_idx, txi, - inp.amount, inp.scriptCode, inp.sighash) - - # The precious private key we need - pk = node.privkey() - - #print("privkey %s" % b2a_hex(pk).decode('ascii')) - #print(" pubkey %s" % b2a_hex(which_key).decode('ascii')) - #print(" digest %s" % b2a_hex(digest).decode('ascii')) - - # Do the ACTUAL signature ... finally!!! - - # We need to grind sometimes to get a positive R - # value that will encode (after DER) into a shorter string. - # - saves on miner's fee (which might be expected/required) - # - blends in with Bitcoin Core signatures which do this from 0.17.0 - - n = 0 # retry num - while True: - # time to produce signature on stm32: ~25.1ms - result = ngu.secp256k1.sign(pk, digest, n).to_bytes() - - if result[1] < 0x80: - # - no need to check for low S value as those are generated by default - # by secp256k1 lib - # - to produce 71 bytes long signature (both low S low R values), - # we need on average 2 retries - # - worst case ~25 grinding iterations need to be performed total - break - - n += 1 + digest = ngu.hash.sha256d(digest) - # DER serialization after we have low S and low R values in our signature - r = result[1:33] - s = result[33:65] - der_sig = ser_sig_der(r, s, inp.sighash) - - # private key no longer required - stash.blank_object(pk) - stash.blank_object(node) - del pk, node, pu, skp, n - - inp.added_sig = (which_key, der_sig) + # we no longer need utxo_spk if: + # - none of the inputs that we're signing is P2TR + # - this input is not P2PK or P2PKH, otherwise we need utxo_spk for scriptSig + if not self.my_tr_in and (inp.af not in (AF_BARE_PK, AF_CLASSIC)): + try: + del inp.utxo_spk + except AttributeError: pass # may not have UTXO - # Could remove sighash from input object - it is not required, takes space, - # and is already in signature or is implicit by not being part of the - # signature (taproot SIGHASH_DEFAULT) - ## inp.sighash = None + # The precious private key we need + for i, (node, pk_coord) in enumerate(to_sign): + sk = node.privkey() + # Do the ACTUAL signature ... finally!!! + if schnorrsig: + kp = ngu.secp256k1.keypair(sk) + xonly_pk = kp.xonly_pubkey().to_bytes() + if tr_sh: + # in tapscript keys are not tweaked, just sign with the key in the script + taproot_script_sigs = inp.get_taproot_script_sigs() + inp.tr_added_sigs = inp.tr_added_sigs or {} + + for taproot_script, leaf_ver in tr_sh[i]: + _key = (xonly_pk, tapleaf_hash(taproot_script, leaf_ver)) + if _key in taproot_script_sigs: + continue # already done ? + + digest = self.make_txn_taproot_sighash(in_idx, hash_type=inp.sighash, + scriptpath=True, + script=taproot_script, leaf_ver=leaf_ver) + + if sv.deltamode: + digest = ngu.hash.sha256d(digest) + + sig = ngu.secp256k1.sign_schnorr(sk, digest, ngu.random.bytes(32)) + # in the common case of SIGHASH_DEFAULT, encoded as '0x00', a space optimization MUST be made by + # 'omitting' the sighash byte, resulting in a 64-byte signature with SIGHASH_DEFAULT assumed + if inp.sighash != SIGHASH_DEFAULT: + sig += bytes([inp.sighash]) + + # separate container for PSBT_IN_TAP_SCRIPT_SIG that we added + inp.tr_added_sigs[_key] = sig + else: + # BIP 341 states: "If the spending conditions do not require a script path, + # the output key should commit to an unspendable script path instead of having no script path. + # This can be achieved by computing the output key point as Q = P + int(hashTapTweak(bytes(P)))G." + tweak = xonly_pk + if inp.taproot_merkle_root and inp.use_keypath: + # we have a script path but internal key is spendable by us + # merkle root needs to be added to tweak with internal key + # merkle root was already verified against registered script in determine_my_signing_key + tweak += self.get(inp.taproot_merkle_root) + + tweak = ngu.hash.sha256t(TAP_TWEAK_H, tweak, True) + kpt = kp.xonly_tweak_add(tweak) + sig = ngu.secp256k1.sign_schnorr(kpt, digest, ngu.random.bytes(32)) + if inp.sighash != SIGHASH_DEFAULT: + sig += bytes([inp.sighash]) + + # in the common case of SIGHASH_DEFAULT, encoded as '0x00', a space optimization MUST be made by + # 'omitting' the sighash byte, resulting in a 64-byte signature with SIGHASH_DEFAULT assumed + inp.taproot_key_sig = sig + + del kpt + + del kp + else: + der_sig = self.ecdsa_grind_sign(sk, digest, inp.sighash) + inp.added_sigs = inp.added_sigs or [] + inp.added_sigs.append((pk_coord, der_sig)) - success.add(in_idx) + # private key no longer required + stash.blank_object(sk) + stash.blank_object(node) + del sk, node - if self.is_v2: - self.set_modifiable_flag(inp) + if self.is_v2: + self.set_modifiable_flag(inp) - # memory cleanup - del result, r, s + if drop_sighash: + # only drop after modifiable is set, in case of PSBTv2 + # SIGHASH_DEFAULT if taproot + # SIGHASH_ALL if non-taproot + inp.sighash = None + del to_sign gc.collect() # done. @@ -2092,6 +2600,108 @@ def make_txn_sighash(self, replace_idx, replacement, sighash_type): # double SHA256 return ngu.hash.sha256s(rv.digest()) + def make_txn_taproot_sighash(self, input_index, hash_type=SIGHASH_DEFAULT, scriptpath=False, script=None, + codeseparator_pos=-1, annex=None, leaf_ver=TAPROOT_LEAF_TAPSCRIPT): + # BIP-341 + fd = self.fd + old_pos = fd.tell() + + out_type = SIGHASH_ALL if (hash_type == SIGHASH_DEFAULT) else (hash_type & 3) + in_type = hash_type & SIGHASH_ANYONECANPAY + + if not self.hashValues and in_type != SIGHASH_ANYONECANPAY: + hashPrevouts = sha256() + hashSequence = sha256() + hashValues = sha256() + hashScriptPubKeys = sha256() + # input side + for in_idx, txi in self.input_iter(): + hashPrevouts.update(txi.prevout.serialize()) + hashSequence.update(pack(" @@ -2176,26 +2786,153 @@ def make_txn_segwit_sighash(self, replace_idx, replacement, amount, scriptCode, # double SHA256 return ngu.hash.sha256s(rv.digest()) + def miniscript_input_complete(self, inp): + desc = self.active_miniscript.to_descriptor() + if desc.is_basic_multisig: + # we can only finalize multisig inputs from all miniscript set + M, N = desc.miniscript.m_n() + ll = 0 + if inp.part_sigs: + ll += len(inp.part_sigs) + if inp.added_sigs: + ll += len(inp.added_sigs) + if ll >= M: + return True + return False + def is_complete(self): # Are all the inputs (now) signed? - # some might have been given as signed - signed = len(self.presigned_inputs) - # plus we added some signatures - for inp in self.inputs: - if inp.is_multisig: - # but we can't combine/finalize multisig stuff, so will never't be 'final' + for i, inp in enumerate(self.inputs): + if inp.fully_signed: + # was fully signed before (fully signed works with part_sigs only) + continue + elif inp.taproot_key_sig: + continue + elif inp.is_miniscript and self.active_miniscript: + if self.miniscript_input_complete(inp): + continue return False - if inp.added_sig: - signed += 1 + ll = len(inp.added_sigs) if inp.added_sigs else 0 + ll += len(inp.part_sigs) if inp.part_sigs else 0 + if inp.subpaths and (len(inp.subpaths) == ll): + continue + + # input is not signed - and therefore tx is not complete + return False + + return True + + def multisig_signatures(self, inp): + assert self.active_miniscript + desc = self.active_miniscript.to_descriptor() + assert desc.is_basic_multisig + M, N = desc.miniscript.m_n() + + # collect all signatures and parse them if some just coords + full_sigs = {} + if inp.added_sigs: + # what we add is always in memory (not coordinates to PSRAM) + for pk_coord, sig in inp.added_sigs: + full_sigs[self.get(pk_coord)] = sig + + if inp.part_sigs: + # what others added is always just coordinates + for k, v in inp.part_sigs: + full_sigs[self.get(k)] = self.get(v) + # === + + if desc.is_sortedmulti: + # BIP-67 easy just sort by public keys + sigs = [sig for pk, sig in sorted(full_sigs.items())] + else: + # need to respect the order of keys in actual descriptor + sigs = [] + for key in desc.keys: + for k, v in inp.subpaths: + pk = self.get(k) + xfp = self.handle_zero_xfp(self.parse_xfp_path(v), self.my_xfp, None)[0] + # if xfp matches but pk not in all_sigs -> signer haven't signed + # it is ok in threshold multisig - just skip + if (key.origin.cc_fp == xfp) and (pk in full_sigs): + sigs.append(full_sigs[pk]) + break + + # save space and only provide necessary amount of signatures (smaller tx, less fees) + return sigs[:M] - return signed == self.num_inputs + def singlesig_signature(self, inp): + # return signature that we added + # or one signature from partial sigs if input is fully sign + if inp.added_sigs: + assert len(inp.added_sigs) == 1 + return self.get(inp.added_sigs[0][0]), inp.added_sigs[0][1] + + if inp.part_sigs: + assert len(inp.part_sigs) == 1 + pk, sig = inp.part_sigs[0] + return self.get(pk), self.get(sig) + + def miniscript_xfps_needed(self): + # provide the set of xfp's that still need to sign PSBT + # - used to find which multisig-signer needs to go next + rv = set() + done_keys = set() + + for inp in self.inputs: + if inp.fully_signed: + continue + + if inp.taproot_subpaths: + if inp.taproot_key_sig: + # already signed + continue + + # only get this once for each input + if inp.taproot_script_sigs: + for xo, _ in inp.get_taproot_script_sigs(): + done_keys.add(xo) + + if inp.tr_added_sigs: + for (xo, _) in inp.tr_added_sigs: + done_keys.add(xo) + + for i, (k, v) in enumerate(inp.taproot_subpaths): + xpk = self.get(k) + if inp.ik_idx == i: + # internal key + if self.active_miniscript.ik_u: + # no way to sign with unspend + continue + else: + if xpk in done_keys: + continue + + # add xfp + xfp = self.handle_zero_xfp(self.parse_xfp_path(v[2]), self.my_xfp, None)[0] + rv.add(xfp) + + else: + if inp.part_sigs: + for k, _ in inp.part_sigs: + done_keys.add(self.get(k)) + + if inp.added_sigs: + for k, _ in inp.added_sigs: + done_keys.add(self.get(k)) + + for k, v in inp.subpaths: + if self.get(k) not in done_keys: + xfp = self.handle_zero_xfp(self.parse_xfp_path(v), self.my_xfp, None)[0] + rv.add(xfp) + + return rv def finalize(self, fd): # Stream out the finalized transaction, with signatures applied - # - assumption is it's complete already. + # - raise if not complete already # - returns the TXID of resulting transaction # - but in segwit case, needs to re-read to calculate it # - fd must be read/write and seekable to support txid calc @@ -2218,29 +2955,39 @@ def finalize(self, fd): for in_idx, txi in self.input_iter(): inp = self.inputs[in_idx] - if inp.is_segwit: - - if inp.is_p2sh: - # multisig (p2sh) segwit still requires the script here. - txi.scriptSig = ser_string(inp.scriptSig) + # first check - if no signature(s) - fail soon + if inp.is_miniscript and not inp.use_keypath: + assert self.miniscript_input_complete(inp), 'Incomplete signature set on input #%d' % in_idx + else: + # single signature + if inp.af == AF_P2TR: + assert inp.taproot_key_sig, 'No signature on input #%d' % in_idx else: - # major win for segwit (p2pkh): no redeem script bloat anymore - txi.scriptSig = b'' + ssig = self.singlesig_signature(inp) + assert ssig, 'No signature on input #%d' % in_idx - # Actual signature will be in witness data area + if inp.is_segwit: + # p2sh-p2wsh & p2sh-p2wpkh still need redeem here (redeem is witness scriptPubKey) + txi.scriptSig = inp.get_scriptSig() + # for p2wpkh & p2wsh inp.scriptSig is b'' (no redeem script bloat anymore) - do not ser_string + if txi.scriptSig: + txi.scriptSig = ser_string(inp.get_scriptSig()) + # Actual signature will be in witness data area else: # insert the new signature(s), assuming fully signed txn. - assert inp.added_sig, 'No signature on input #%d'%in_idx - assert not inp.is_multisig, 'Multisig PSBT combine not supported' - - pubkey, der_sig = inp.added_sig - - s = b'' - s += ser_push_data(der_sig) - s += ser_push_data(pubkey) - - txi.scriptSig = s + if inp.is_miniscript: + # p2sh multisig (non-segwit) + sigs = self.multisig_signatures(inp) + ss = b"\x00" + for sig in sigs: + ss += ser_push_data(sig) + + ss += ser_push_data(self.get(inp.redeem_script)) + txi.scriptSig = ss + else: + pubkey, der_sig = ssig + txi.scriptSig = ser_push_data(der_sig) + ser_push_data(pubkey) fd.write(txi.serialize()) @@ -2261,14 +3008,25 @@ def finalize(self, fd): for in_idx, wit in self.input_witness_iter(): inp = self.inputs[in_idx] - if inp.is_segwit and inp.added_sig: + if inp.is_segwit: # put in new sig: wit is a CTxInWitness assert not wit.scriptWitness.stack, 'replacing non-empty?' - assert not inp.is_multisig, 'Multisig PSBT combine not supported' - - pubkey, der_sig = inp.added_sig - assert pubkey[0] in {0x02, 0x03} and len(pubkey) == 33, "bad v0 pubkey" - wit.scriptWitness.stack = [der_sig, pubkey] + if inp.taproot_key_sig: + # segwit v1 (taproot) + w = inp.taproot_key_sig + if isinstance(w, tuple): + w = self.get(w) + # can be 65 bytes if sighash != SIGHASH_DEFAULT (0x00) + assert len(w) in (64, 65) + wit.scriptWitness.stack = [w] + elif inp.is_miniscript: + sigs = self.multisig_signatures(inp) + wit.scriptWitness.stack = [b""] + sigs + [self.get(inp.witness_script)] + else: + # segwit v0 + pubkey, der_sig = self.singlesig_signature(inp) + assert pubkey[0] in {0x02, 0x03} and len(pubkey) == 33, "bad v0 pubkey" + wit.scriptWitness.stack = [der_sig, pubkey] fd.write(wit.serialize()) diff --git a/shared/pwsave.py b/shared/pwsave.py index ebc297975..81355fb49 100644 --- a/shared/pwsave.py +++ b/shared/pwsave.py @@ -7,6 +7,7 @@ from ux import ux_dramatic_pause, ux_confirm, ux_show_story, OK, X from utils import xfp2str, problem_file_line, B2A from menu import MenuItem, MenuSystem +from glob import settings class PassphraseSaver: @@ -110,7 +111,6 @@ async def apply(menu, idx, item): from ux import ux_show_story from seed import set_bip39_passphrase from pincodes import pa - from glob import settings bypass_tmp = True pw, expect_xfp = item.arg @@ -253,7 +253,6 @@ def filename(self, card): @classmethod def get_nonces(cls): # this is the only setting: list of nonce values we have saved to various cards - from glob import settings return settings.get('sd2fa') or [] def read_card(self): @@ -288,7 +287,6 @@ def enforce_policy(cls): except: # die. wrong import callgate - from glob import settings settings.remove_key("sd2fa") settings.save() callgate.fast_wipe(silent=False) @@ -353,8 +351,6 @@ async def enroll(self): async def remove(self, nonce): # remove indicated nonce from records # - doesn't delete file, since might not have card anymore and useless w/o nonce - from glob import settings - v = self.get_nonces() assert nonce in v, 'missing card nonce' v2 = [i for i in v if i != nonce] diff --git a/shared/qrs.py b/shared/qrs.py index 9f55d720b..6afd5e2b2 100644 --- a/shared/qrs.py +++ b/shared/qrs.py @@ -3,11 +3,11 @@ # qrs.py - QR Display related UX # import framebuf, uqr -from ux import UserInteraction, ux_wait_keyup, the_ux -from charcodes import (KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_NFC, - KEY_END, KEY_PAGE_UP, KEY_PAGE_DOWN, KEY_ENTER, KEY_CANCEL) +from ux import UserInteraction, ux_wait_keyup, the_ux from version import has_qwerty - +from exceptions import QRTooBigError +from charcodes import (KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_NFC, + KEY_END, KEY_ENTER, KEY_CANCEL) # TODO: This class has a terrible API! @@ -17,15 +17,24 @@ class QRDisplaySingle(UserInteraction): # Show a single QR code for (typically) a list of addresses, or a single value. - def __init__(self, addrs, is_alnum, start_n=0, sidebar=None, msg=None): + def __init__(self, addrs, is_alnum, start_n=0, sidebar=None, msg=None, + is_addrs=False, force_msg=False, allow_nfc=True, is_secret=False, + change_idxs=None, can_raise=True): self.is_alnum = is_alnum self.idx = 0 # start with first address self.invert = False # looks better, but neither mode is ideal self.addrs = addrs self.sidebar = sidebar self.start_n = start_n + self.is_addrs = is_addrs self.msg = msg self.qr_data = None + self.force_msg = force_msg + self.allow_nfc = allow_nfc + # only used for NFC sharing secret material - full chip wipe if is_secret=True + self.is_secret = is_secret + self.change_idxs = change_idxs or [] + self.can_raise = can_raise def calc_qr(self, msg): # Version 2 would be nice, but can't hold what we need, even at min error correction, @@ -56,6 +65,11 @@ def idx_hint(self): # numbers, letters, etc. return str(self.start_n + self.idx) if len(self.addrs) > 1 else None + def is_change(self): + if self.idx in self.change_idxs: + return True + return False + def redraw(self): # Redraw screen. from glob import dis @@ -63,17 +77,36 @@ def redraw(self): # what we are showing inside the QR body = self.addrs[self.idx] + idx_hint = self.idx_hint() + + msg = None + if self.msg: + msg = self.msg + else: + if isinstance(body, str): + # sanity check + msg = body # make the QR, if needed. if not self.qr_data: dis.busy_bar(True) + try: + self.calc_qr(body) + except Exception: + dis.busy_bar(False) + if not self.can_raise: + dis.draw_qr_error(idx_hint, msg) + return - self.calc_qr(body) + # other code paths require raise to switch to BBQr + raise QRTooBigError # draw display dis.busy_bar(False) - dis.draw_qr_display(self.qr_data, self.msg or body, self.is_alnum, - self.sidebar, self.idx_hint(), self.invert) + dis.draw_qr_display(self.qr_data, msg, self.is_alnum, + self.sidebar, idx_hint, self.invert, + is_addr=self.is_addrs, force_msg=self.force_msg, + is_change=self.is_change()) async def interact_bare(self): from glob import NFC, dis @@ -88,13 +121,15 @@ async def interact_bare(self): self.redraw() continue elif NFC and (ch == '3' or ch == KEY_NFC): - # Share any QR over NFC! - await NFC.share_text(self.addrs[self.idx]) - self.redraw() + if not self.allow_nfc: + # not a valid as text over NFC sometimes; treat as cancel + break + else: + # Share any QR over NFC! + await NFC.share_text(self.addrs[self.idx], is_secret=self.is_secret) + self.redraw() continue elif ch in 'xy'+KEY_ENTER+KEY_CANCEL: - if dis.has_lcd: - dis.real_clear() # bugfix break elif len(self.addrs) == 1: continue @@ -116,6 +151,10 @@ async def interact_bare(self): self.qr_data = None self.redraw() + # bugfix + if dis.has_lcd: + dis.real_clear() + async def interact(self): await self.interact_bare() the_ux.pop() diff --git a/shared/queues.py b/shared/queues.py index bea6301a7..f27223fbc 100644 --- a/shared/queues.py +++ b/shared/queues.py @@ -72,7 +72,7 @@ def qsize(self): # Number of items in the queue. return len(self._queue) def empty(self): # Return True if the queue is empty, False otherwise. - return len(self._queue) == 0 + return not self._queue def full(self): # Return True if there are maxsize items in the queue. # Note: if the Queue was initialized with maxsize=0 (the default) or diff --git a/shared/scanner.py b/shared/scanner.py index 02dd939ce..4dd91c79c 100644 --- a/shared/scanner.py +++ b/shared/scanner.py @@ -201,7 +201,7 @@ async def scan_once(self): if not rv: continue if rv[0:2] == 'B$' and bbqr.collect(rv): - # BBQr protocol detected; collect more data + # BBQr protocol detected, accepted need to collect more data continue break diff --git a/shared/seed.py b/shared/seed.py index 38627a4cf..b2a47a014 100644 --- a/shared/seed.py +++ b/shared/seed.py @@ -10,30 +10,46 @@ # - 'abandon' * 17 + 'agent' # - 'abandon' * 11 + 'about' # -import ngu, uctypes, bip39, random, stash, version +import ngu, uctypes, bip39, random, version, ure, chains from ucollections import OrderedDict from menu import MenuItem, MenuSystem -from utils import xfp2str, parse_extended_key, swab32, pad_raw_secret, problem_file_line +from utils import xfp2str, swab32 +from utils import deserialize_secret, problem_file_line, wipe_if_deltamode from uhashlib import sha256 from ux import ux_show_story, the_ux, ux_dramatic_pause, ux_confirm, OK, X -from ux import PressRelease, ux_input_numbers, ux_input_text, show_qr_code +from ux import PressRelease, ux_input_text, show_qr_code from actions import goto_top_menu -from stash import SecretStash, ZeroSecretException +from stash import SecretStash, SensitiveValues from ubinascii import hexlify as b2a_hex from pwsave import PassphraseSaver, PassphraseSaverMenu from glob import settings, dis from pincodes import pa from nvstore import SettingsObject -from files import CardMissingError, needs_microsd, CardSlot -from charcodes import KEY_QR, KEY_ENTER, KEY_CANCEL, KEY_CLEAR - +from files import CardMissingError, needs_microsd +from charcodes import KEY_QR, KEY_ENTER, KEY_CANCEL, KEY_NFC +from uasyncio import sleep_ms +from ucollections import namedtuple # seed words lengths we support: 24=>256 bits, and recommended VALID_LENGTHS = (24, 18, 12) # bit flag that means "also include bare prefix as a valid word" _PREFIX_MARKER = const(1<<26) - + +# what we store (in JSON as a tuple) for each seed vault key. +# - 'encoded' is hex, and has is trimmed of right side zeros +VaultEntry = namedtuple('VaultEntry', 'xfp encoded label origin') + +def not_hobbled_mode(): + # used as menu predicate and similar + return not pa.hobbled_mode + +def seed_vault_iter(): + # iterate over all seeds in the vault; returns VaultEntry instances. + # raw vault entries are list type when json.loaded from flash + for lst in settings.master_get("seeds", []): + yield VaultEntry(*lst) + def letter_choices(sofar='', depth=0, thres=5): # make a list of word completions based on indicated prefix if not sofar: @@ -138,23 +154,62 @@ class WordNestMenu(MenuSystem): done_cb = None def __init__(self, num_words=None, has_checksum=True, done_cb=commit_new_words, - items=None, is_commit=False): + items=None, is_commit=False, menu_cbf=None, prefix="", words=None): if num_words is not None: WordNestMenu.target_words = num_words WordNestMenu.has_checksum = has_checksum WordNestMenu.words = [] - assert done_cb WordNestMenu.done_cb = done_cb is_commit = True + if words: + WordNestMenu.words = words + if not items: - items = [MenuItem(i, menu=self.next_menu) for i in letter_choices()] + ch = letter_choices(prefix) + if menu_cbf: + items = [MenuItem(i, f=menu_cbf) for i in ch] + else: + items = [MenuItem(i, menu=self.next_menu) for i in ch] self.is_commit = is_commit super(WordNestMenu, self).__init__(items) + @classmethod + async def get_n_words(cls, num_words): + rv = [] + for _ in range(num_words): + rv = await cls.get_word(rv, num_words) + + return rv + + @classmethod + async def get_word(cls, words=None, target_words=None): + # Just block until N words are provided. May only work before menus start? + from glob import numpad + + async def menu_done_cbf(menu, b, c): + # duplicates some of the logic of next_menu + if c.label[-1] == '-': + lc = c.label[0:-1] + else: + cls.words.append(c.label) + numpad.abort_ux() + return + + m = cls(prefix=lc, menu_cbf=menu_done_cbf) + the_ux.push(m) + await the_ux.interact() + + m = cls(num_words=target_words, menu_cbf=menu_done_cbf, has_checksum=False, words=words) + + the_ux.push(m) + await the_ux.interact() + + return cls.words + @staticmethod async def next_menu(self, idx, choice): @@ -215,7 +270,7 @@ def pop_all(cls): while isinstance(the_ux.top_of_stack(), cls): the_ux.pop() - def on_cancel(self): + async def on_cancel(self): # user pressed cancel on a menu (so he's going upwards) # - if it's a step where we added to the word list, undo that. # - but keep them in our system until: @@ -273,9 +328,16 @@ def late_draw(self, dis): async def show_words(words, prompt=None, escape=None, extra='', ephemeral=False): - msg = (prompt or 'Record these %d secret words!\n') % len(words) - from ux import ux_render_words + from glob import NFC + + if prompt: + title = None + msg = prompt + else: + m = 'Record these %d secret words!' % len(words) + title, msg = (m, "") if version.has_qwerty else (None, m+"\n") + msg += ux_render_words(words) msg += '\n\nPlease check and double check your notes.' @@ -283,22 +345,30 @@ async def show_words(words, prompt=None, escape=None, extra='', ephemeral=False) # user can skip quiz for ephemeral secrets msg += " There will be a test!" + escape = (escape or '') + '1' if not version.has_qwerty: - escape = (escape or '') + '1' - extra += 'Press (1) to view as QR Code. ' - else: - escape = (escape or '') + KEY_QR - extra += 'Press '+ KEY_QR + ' to view as QR Code. ' + title = None + extra += 'Press (1) to view as QR Code' + if NFC: + extra += ", (3) to share via NFC" + escape += "3" + extra += "." if extra: msg += '\n\n' msg += extra while 1: - ch = await ux_show_story(msg, escape=escape, sensitive=True) - if ch == '1' or ch == KEY_QR: - await show_qr_code(' '.join(w[0:4] for w in words), True) + rv = ' '.join(w[0:4] for w in words) + ch = await ux_show_story(msg, title=title, escape=escape, sensitive=True, + hint_icons=KEY_QR+(KEY_NFC if NFC else '')) + if ch in ('1'+KEY_QR): + await show_qr_code(rv, True, is_secret=True) + continue + if NFC and (ch in "3"+KEY_NFC): + await NFC.share_text(rv, is_secret=True) continue + break return ch @@ -411,27 +481,35 @@ async def new_from_dice(nwords): await commit_new_words(words) def in_seed_vault(encoded): - # Test if indicated xfp (or currently active XFP) is in the seed vault already. - seeds = settings.master_get("seeds", []) - if seeds: - ss = stash.SecretStash.storage_serialize(encoded) - if ss in [s[1] for s in seeds]: + # Test if indicated secret is in the seed vault already. + hss = None + for rec in seed_vault_iter(): + if not hss: + hss = SecretStash.storage_serialize(encoded) + if hss == rec.encoded: return True + return False -async def add_seed_to_vault(encoded, meta=None): +async def add_seed_to_vault(encoded, origin=None, label=None): if not settings.master_get("seedvault", False): # seed vault disabled + # this can be re-enabled by attacker in deltamode return - if pa.is_secret_blank(): + if pa.is_secret_blank() or pa.is_deltamode(): # do not save anything if no SE secret yet + # do not offer any access to SV in deltamode return # do not offer to store secrets that are already in vault if in_seed_vault(encoded): return + # stay "read only" in hobbled mode + if pa.hobbled_mode: + return + main_xfp = settings.master_get("xfp", 0) # parse encoded @@ -457,10 +535,9 @@ async def add_seed_to_vault(encoded, meta=None): return # Save it into master settings - seeds.append((new_xfp_str, - stash.SecretStash.storage_serialize(encoded), - xfp_ui, - meta)) + rec = VaultEntry(xfp=new_xfp_str, encoded=SecretStash.storage_serialize(encoded), + label=(label or xfp_ui), origin=origin) + seeds.append(list(rec)) settings.master_set("seeds", seeds) @@ -469,9 +546,10 @@ async def add_seed_to_vault(encoded, meta=None): return True async def set_ephemeral_seed(encoded, chain=None, summarize_ux=True, bip39pw='', - is_restore=False, meta=None): - if not is_restore: - await add_seed_to_vault(encoded, meta=meta) + is_restore=False, origin=None, label=None): + # Capture tmp seed into vault, if so enabled, and regardless apply it as new tmp. + if not is_restore and not_hobbled_mode(): + await add_seed_to_vault(encoded, origin=origin, label=label) dis.fullscreen("Wait...") applied, err_msg = pa.tmp_secret(encoded, chain=chain, bip39pw=bip39pw) @@ -488,11 +566,11 @@ async def set_ephemeral_seed(encoded, chain=None, summarize_ux=True, bip39pw='', return applied -async def set_ephemeral_seed_words(words, meta): +async def set_ephemeral_seed_words(words, origin): dis.progress_bar_show(0.1) encoded = seed_words_to_encoded_secret(words) dis.progress_bar_show(0.5) - await set_ephemeral_seed(encoded, meta=meta) + await set_ephemeral_seed(encoded, origin=origin) goto_top_menu() async def ephemeral_seed_generate_from_dice(nwords): @@ -509,7 +587,7 @@ async def ephemeral_seed_generate_from_dice(nwords): words = await approve_word_list(seed, nwords, ephemeral=True) if words: dis.fullscreen("Applying...") - await set_ephemeral_seed_words(words, meta='Dice') + await set_ephemeral_seed_words(words, origin='Dice') def generate_seed(): # Generate 32 bytes of best-quality high entropy TRNG bytes. @@ -532,7 +610,7 @@ async def make_new_wallet(nwords): async def ephemeral_seed_import(nwords): async def import_done_cb(words): dis.fullscreen("Applying...") - await set_ephemeral_seed_words(words, meta='Imported') + await set_ephemeral_seed_words(words, origin='Imported') if version.has_qwerty: from ux_q1 import seed_word_entry @@ -546,17 +624,17 @@ async def ephemeral_seed_generate(nwords): words = await approve_word_list(seed, nwords, ephemeral=True) if words: dis.fullscreen("Applying...") - await set_ephemeral_seed_words(words, meta="TRNG Words") + await set_ephemeral_seed_words(words, origin="TRNG Words") async def set_seed_extended_key(extended_key): encoded, chain = xprv_to_encoded_secret(extended_key) set_seed_value(encoded=encoded, chain=chain) goto_top_menu(first_time=True) -async def set_ephemeral_seed_extended_key(extended_key, meta=None): +async def set_ephemeral_seed_extended_key(extended_key, origin=None): encoded, chain = xprv_to_encoded_secret(extended_key) dis.fullscreen("Applying...") - await set_ephemeral_seed(encoded=encoded, chain=chain, meta=meta) + await set_ephemeral_seed(encoded=encoded, chain=chain, origin=origin) goto_top_menu() async def approve_word_list(seed, nwords, ephemeral=False): @@ -625,17 +703,25 @@ def seed_words_to_encoded_secret(words): return nv def xprv_to_encoded_secret(xprv): - node, chain, _ = parse_extended_key(xprv, private=True) - if node is None: - raise ValueError("Failed to parse extended private key.") + # read an xprv/tprv/etc and return BIP-32 node and what chain it's on. + # - can handle any garbage line + # - returns (node, chain) + # - people are using SLIP132 so we need this + ln = xprv.strip() + pat = ure.compile('.prv[A-Za-z0-9]+') + found = pat.search(ln) + assert found, "not extended privkey" + # serialize, and note version code + node, chain, addr_fmt, is_private = chains.slip132_deserialize(found.group(0)) + assert node, "wrong extended privkey" nv = SecretStash.encode(xprv=node) node.blank() return nv, chain # need to know chain def set_seed_value(words=None, encoded=None, chain=None): - # Save the seed words (or other encoded private key) into secure element, - # and reboot. BIP-39 passphrase is not set at this point (empty string). + # Save the seed words (or other encoded private key) into secure element. + # BIP-39 passphrase is not set at this point (empty string). if words: nv = seed_words_to_encoded_secret(words) else: @@ -660,13 +746,12 @@ def set_seed_value(words=None, encoded=None, chain=None): async def calc_bip39_passphrase(pw, bypass_tmp=False): from glob import dis, settings - from pincodes import pa dis.fullscreen("Working...") current_xfp = settings.get("xfp", 0) - with stash.SensitiveValues(bip39pw=pw, bypass_tmp=bypass_tmp) as sv: + with SensitiveValues(bip39pw=pw, bypass_tmp=bypass_tmp) as sv: # can't do it without original seed words (late, but caller has checked) assert sv.mode == 'words', sv.mode nv = SecretStash.encode(xprv=sv.node) @@ -677,7 +762,7 @@ async def calc_bip39_passphrase(pw, bypass_tmp=False): async def set_bip39_passphrase(pw, bypass_tmp=False, summarize_ux=True): nv, xfp, parent_xfp = await calc_bip39_passphrase(pw, bypass_tmp=bypass_tmp) ret = await set_ephemeral_seed(nv, summarize_ux=summarize_ux, bip39pw=pw, - meta="BIP-39 Passphrase on [%s]" % xfp2str(parent_xfp)) + origin="BIP-39 Passphrase on [%s]" % xfp2str(parent_xfp)) dis.draw_status(bip39=int(bool(pw)), xfp=xfp, tmp=1) return ret @@ -707,7 +792,7 @@ async def remember_ephemeral_seed(): # address cache, settings from tmp seeds / seedvault seeds # rebuild fs as we want to save current tmp settings immediately from files import wipe_flash_filesystem - wipe_flash_filesystem(True) + wipe_flash_filesystem() dis.draw_status(bip39=0, tmp=0) dis.fullscreen('Saving...') @@ -818,7 +903,7 @@ async def _set(menu, label, item): from glob import dis dis.fullscreen("Applying...") - xfp, encoded = item.arg + encoded = item.arg # 72 bytes binary await set_ephemeral_seed(encoded, is_restore=True) @@ -828,42 +913,40 @@ async def _set(menu, label, item): async def _remove(menu, label, item): from glob import dis, settings - idx, xfp_str, encoded = item.arg + esc = "" + tmp_val = False + idx, rec, encoded = item.arg + current_active = (pa.tmp_value == bytes(encoded)) + + msg = "Remove seed from seed vault" + if pa.tmp_value and current_active: + tmp_val = True + msg += "?\n\n" + else: + msg += (" and delete its settings?\n\n" + "Press %s to continue, press (1) to " + "only remove from seed vault and keep " + "encrypted settings for later use.\n\n") % OK + esc += "1" - msg = ("Remove seed from seed vault and delete its " - "settings?\n\nPress %s to continue, press (1) to " - "only remove from seed vault and keep " - "encrypted settings for later use.\n\n" - "WARNING: Funds will be lost if wallet is" - " not backed-up elsewhere.") % OK + msg += "WARNING: Funds will be lost if wallet is not backed-up elsewhere." - ch = await ux_show_story(title="[" + xfp_str + "]", msg=msg, escape="1") + ch = await ux_show_story(title="[" + rec.xfp + "]", msg=msg, escape=esc) if ch == "x": return - dis.fullscreen("Saving...") + assert not_hobbled_mode() - wipe_slot = (ch != "1") - tmp_val = False + dis.fullscreen("Saving...") - if pa.tmp_value: - tmp_val = True + wipe_slot = not current_active and (ch != "1") if wipe_slot: - # are we deleting current active ephemeral wallet - # and its settings ? - # slot wiping - if tmp_val: - # wipe current settings - settings.blank() - pa.tmp_value = False - settings.return_to_master_seed() - else: - # in main settings - xs = SettingsObject() - xs.set_key(encoded) - xs.load() - xs.blank() - del xs + xs = SettingsObject() + xs.set_key(encoded) + xs.load() + xs.blank() + del xs + # CAUTION: will get shadow copy if in tmp seed mode already seeds = settings.master_get("seeds", []) @@ -885,13 +968,13 @@ async def _remove(menu, label, item): @staticmethod async def _detail(menu, label, item): - xfp_str, encoded, name, meta = item.arg + rec, encoded = item.arg - # - first byte represents type of secret (internal encoding flag) + # - first byte represents type of secret (internal encoding flags) txt = SecretStash.summary(encoded[0]) - detail = "Name:\n%s\n\nMaster XFP:\n%s\n\nOrigin:\n%s\n\nSecret Type:\n%s" \ - % (name, xfp_str, meta, txt) + detail = "Name:\n%s\n\nMaster XFP: %s\nSecret Type: %s\n\nOrigin:\n%s\n\n" \ + % (rec.label, rec.xfp, txt, rec.origin) await ux_show_story(detail) @@ -901,30 +984,30 @@ async def _rename(menu, label, item): from glob import dis from ux import ux_input_text - idx, xfp_str = item.arg - - seeds = settings.master_get("seeds", []) - chk_xfp, encoded, old_name, meta = seeds[idx] - assert chk_xfp == xfp_str + assert not_hobbled_mode() - new_name = await ux_input_text(old_name, confirm_exit=False, max_len=40) + idx, old = item.arg + new_label = await ux_input_text(old.label, confirm_exit=False, max_len=40) - if not new_name: + if not new_label: return dis.fullscreen("Saving...") + seeds = settings.master_get("seeds", []) # save it - seeds[idx] = (chk_xfp, encoded, new_name, meta) - + seeds[idx] = (old.xfp, old.encoded, new_label, old.origin) # need to load and work on master secrets, will be slow if on tmp seed settings.master_set("seeds", seeds) # update label in sub-menu - menu.items[0].label = new_name - menu.items[0].arg = menu.items[0].arg[0:2] + (new_name,) + menu.items[0].arg[3:] + menu.items[0].label = new_label + # take old arg, in rename we cannot change encoded value, so it can be used without + # the need to deserialize it again + _, encoded = menu.items[0].arg + menu.items[0].arg = VaultEntry(*seeds[idx]), encoded - # .. and name in parent menu too + # and name in parent menu too parent = the_ux.parent_of(menu) if parent: parent.update_contents() @@ -933,6 +1016,8 @@ async def _rename(menu, label, item): async def _add_current_tmp(*a): from pincodes import pa + assert not_hobbled_mode() + assert pa.tmp_value main_xfp = settings.master_get("xfp", 0) @@ -952,10 +1037,9 @@ async def _add_current_tmp(*a): seeds = settings.master_get("seeds", []) # Save it into master settings - seeds.append((new_xfp_str, - stash.SecretStash.storage_serialize(pa.tmp_value), - xfp_ui, - "unknown origin")) + seeds.append(list(VaultEntry(new_xfp_str, + SecretStash.storage_serialize(pa.tmp_value), + xfp_ui, "unknown origin"))) settings.master_set("seeds", seeds) @@ -967,31 +1051,38 @@ async def _add_current_tmp(*a): @classmethod def construct(cls): # Dynamic menu with user-defined names of seeds shown - from glob import settings from pincodes import pa rv = [] add_current_tmp = MenuItem("Add current tmp", f=cls._add_current_tmp) - seeds = settings.master_get("seeds", []) + seeds = list(seed_vault_iter()) if not seeds: rv.append(MenuItem('(none saved yet)')) - if pa.tmp_value: - rv.append(add_current_tmp) - rv.append(MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu)) + if not_hobbled_mode(): + if pa.tmp_value: + rv.append(add_current_tmp) + rv.append(MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu)) else: + wipe_if_deltamode() + tmp_in_sv = False - for i, (xfp_str, encoded, name, meta) in enumerate(seeds): + for i, rec in enumerate(seeds): is_active = False - encoded = pad_raw_secret(encoded) + + # de-serialize encoded secret + encoded = deserialize_secret(rec.encoded) if encoded == pa.tmp_value: is_active = tmp_in_sv = True + submenu = [ - MenuItem(name, f=cls._detail, arg=(xfp_str, encoded, name, meta)), - MenuItem('Use This Seed', f=cls._set, arg=(xfp_str, encoded)), - MenuItem('Rename', f=cls._rename, arg=(i, xfp_str)), - MenuItem('Delete', f=cls._remove, arg=(i, xfp_str, encoded)), + MenuItem(rec.label, f=cls._detail, arg=(rec, encoded)), + MenuItem('Use This Seed', f=cls._set, arg=encoded), + MenuItem('Rename', f=cls._rename, arg=(i, rec), + predicate=not_hobbled_mode), + MenuItem('Delete', f=cls._remove, arg=(i, rec, encoded), + predicate=not_hobbled_mode), ] if is_active: submenu[1] = MenuItem("Seed In Use") @@ -1002,14 +1093,14 @@ def construct(cls): # DO NOT offer any modification api (rename/delete) submenu = submenu[:2] - item = MenuItem('%2d: %s' % (i+1, name), menu=MenuSystem(submenu)) + item = MenuItem('%2d: %s' % (i+1, rec.label), menu=MenuSystem(submenu)) if is_active: item.is_chosen = lambda: True rv.append(item) if pa.tmp_value: - if seeds and (not tmp_in_sv): + if seeds and (not tmp_in_sv) and not_hobbled_mode(): # give em chance to store current active rv.append(add_current_tmp) @@ -1024,6 +1115,44 @@ def update_contents(self): tmp = self.construct() self.replace_items(tmp) +class SeedVaultChooserMenu(MenuSystem): + def __init__(self, words_only=False): + self.result = None + + items = [] + for i, rec in enumerate(seed_vault_iter()): + if words_only and not SecretStash.is_words(deserialize_secret(rec.encoded)): + continue + + item = MenuItem('%2d: %s' % (i+1, rec.label), arg=rec, f=self.picked) + items.append(item) + + if not items: + items.append(MenuItem("(none suitable)")) + + super().__init__(items) + + async def picked(self, menu, idx, mi): + assert menu == self + + # show as "checked", for a touch + menu.chosen = idx + menu.show() + await sleep_ms(100) + + self.result = mi.arg + the_ux.pop() # causes interact to stop + + @classmethod + async def pick(cls, **kws): + # nice simple blocking menu present and pick + m = cls(**kws) + + the_ux.push(m) + await m.interact() + + return m.result + class EphemeralSeedMenu(MenuSystem): @staticmethod @@ -1042,8 +1171,9 @@ async def ephemeral_seed_generate_from_dice(menu, label, item): def construct(cls): from glob import NFC from actions import nfc_recv_ephemeral, import_xprv - from actions import restore_temporary, scan_any_qr + from actions import restore_backup, scan_any_qr from tapsigner import import_tapsigner_backup_file + from xor_seed import xor_restore_start from charcodes import KEY_QR import_ephemeral_menu = [ @@ -1060,32 +1190,31 @@ def construct(cls): ] rv = [ - MenuItem("Generate Words", menu=gen_ephemeral_menu), + MenuItem("Generate Words", menu=gen_ephemeral_menu, predicate=not_hobbled_mode), MenuItem('Import from QR Scan', predicate=version.has_qr, shortcut=KEY_QR, f=scan_any_qr, arg=(True, True)), MenuItem("Import Words", menu=import_ephemeral_menu), MenuItem("Import XPRV", f=import_xprv, arg=True), # ephemeral=True MenuItem("Tapsigner Backup", f=import_tapsigner_backup_file, arg=True), # ephemeral=True - MenuItem("Coldcard Backup", f=restore_temporary), + MenuItem("Coldcard Backup", f=restore_backup, arg=True), # tmp=True + MenuItem("Restore Seed XOR", f=xor_restore_start), ] return rv async def make_ephemeral_seed_menu(*a): + if (not pa.tmp_value) and (not settings.master_get("seedvault", False)): # force a warning on them, unless they are already doing it. - ch = await ux_show_story( + if not await ux_confirm( "Temporary seed is a secret completely separate " "from the master seed, typically held in device RAM and " "not persisted between reboots in the Secure Element. " - "Enable the Seed Vault feature to store these secrets longer-term." - "\n\nPress (4) to prove you read to the end" - " of this message and accept all consequences.", + "Enable the Seed Vault feature to store these secrets longer-term.", title="WARNING", - escape="4" - ) - if ch != "4": + confirm_key="4" + ): return rv = EphemeralSeedMenu.construct() @@ -1191,7 +1320,7 @@ async def restore_saved(*a): return PassphraseSaverMenu(items) - def on_cancel(self): + async def on_cancel(self): if not version.has_qwerty: # zip to cancel item when they fail to exit via X button self.goto_idx(self.count - 1) @@ -1206,7 +1335,9 @@ async def word_menu(self, *a): @classmethod async def add_numbers(cls, *a): # Mk4 only: add some digits (quick, easy) - pw = await ux_input_numbers(cls.pp_sofar, cls.check_length) + from ux_mk4 import ux_input_digits + + pw = await ux_input_digits(cls.pp_sofar) if pw is not None: cls.pp_sofar = pw cls.check_length() @@ -1285,7 +1416,7 @@ async def apply_pass_value(new_pp): return await set_ephemeral_seed(nv, summarize_ux=False, bip39pw=new_pp, - meta="BIP-39 Passphrase on [%s]" % parent_xfp_str) + origin="BIP-39 Passphrase on [%s]" % parent_xfp_str) if ch == '1': try: diff --git a/shared/selftest.py b/shared/selftest.py index 8600328ee..4550b1716 100644 --- a/shared/selftest.py +++ b/shared/selftest.py @@ -194,6 +194,7 @@ async def test_secure_element(): dis.fullscreen("Wait...") set_genuine() ux_clear_keys() + dis.busy_bar(False) ng = get_genuine() assert ng != gg # "Could not invert LED" @@ -321,14 +322,25 @@ async def test_microsd(): from files import CardSlot import os + def _is_inserted(slot_num): + if num_sd_slots > 1: + if slot_num == 0: + return CardSlot.sd_detect() == 0 + elif slot_num == 1: + return CardSlot.sd_detect2() == 0 + else: + assert False + else: + return CardSlot.is_inserted() + async def wait_til_state(num, want): title = 'MicroSD Card' if num_sd_slots > 1: title += ' ' + chr(65+num) - label_test(title +':', 'Remove' if CardSlot.is_inserted() else 'Insert') + label_test(title +':', 'Remove' if _is_inserted(num) else 'Insert') while 1: - if want == CardSlot.is_inserted(): return + if want == _is_inserted(num): return await sleep_ms(100) if ux_poll_key(): raise RuntimeError("MicroSD test aborted") @@ -336,23 +348,23 @@ async def wait_til_state(num, want): for slot_num in range(num_sd_slots): # test presence switch for ph in range(7): - await wait_til_state(slot_num, not CardSlot.is_inserted()) + await wait_til_state(slot_num, not _is_inserted(slot_num)) - if ph >= 2 and CardSlot.is_inserted(): + if ph >= 2 and _is_inserted(slot_num): # debounce await sleep_ms(100) - if CardSlot.is_inserted(): break + if _is_inserted(slot_num): break if ux_poll_key(): raise RuntimeError("MicroSD test aborted") label_test('MicroSD Card:', 'Testing') # card inserted - assert CardSlot.is_inserted() #, "SD not present?" + assert _is_inserted(slot_num) #, "SD not present?" with CardSlot(slot_b=slot_num) as card: - _, fn = card.pick_filename('test-delme.txt') + fn, _ = card.pick_filename('test-delme.txt') with open(fn, 'wt') as fd: fd.write("Hello") @@ -365,9 +377,7 @@ async def wait_til_state(num, want): await wait_til_state(slot_num, False) - async def start_selftest(): - try: if version.has_battery: await test_battery() @@ -403,6 +413,5 @@ async def start_selftest(): except (RuntimeError, AssertionError) as e: e = str(e) or problem_file_line(e) await ux_show_story("Test failed:\n" + str(e), 'FAIL') - # EOF diff --git a/shared/serializations.py b/shared/serializations.py index 33de34543..df28bae60 100755 --- a/shared/serializations.py +++ b/shared/serializations.py @@ -16,10 +16,10 @@ """ from ubinascii import hexlify as b2a_hex -from ubinascii import unhexlify as a2b_hex import ustruct as struct import ngu from opcodes import * +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2SH, AF_P2WSH, AF_BARE_PK, AF_P2TR # single-shot hash functions sha256 = ngu.hash.sha256s @@ -30,6 +30,7 @@ def bytes_to_hex_str(s): return str(b2a_hex(s), 'ascii') +SIGHASH_DEFAULT = const(0) # in taproot meaning same as SIGHASH_ALL (over whole TX) SIGHASH_ALL = const(1) SIGHASH_NONE = const(2) SIGHASH_SINGLE = const(3) @@ -37,6 +38,7 @@ def bytes_to_hex_str(s): # list containing all flags that we support signing for ALL_SIGHASH_FLAGS = [ + SIGHASH_DEFAULT, SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE, @@ -56,14 +58,23 @@ def ser_compact_size(l): else: return struct.pack("= 253 + num_bytes += 2 elif nit == 254: nit = struct.unpack("= 0x1_0000 + num_bytes += 4 elif nit == 255: nit = struct.unpack("= 0x1_0000_0000 + num_bytes += 8 + if ret_num_bytes: + return nit, num_bytes return nit def deser_string(f): @@ -80,7 +91,6 @@ def deser_uint256(f): r += t << (i * 32) return r - def ser_uint256(u): rs = b"" for i in range(8): @@ -88,7 +98,6 @@ def ser_uint256(u): u >>= 32 return rs - def uint256_from_str(s): r = 0 t = struct.unpack("> 24) & 0xFF v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) return v - def deser_vector(f, c): nit = deser_compact_size(f) r = [] @@ -112,7 +119,6 @@ def deser_vector(f, c): r.append(t) return r - # ser_function_name: Allow for an alternate serialization function on the # entries in the vector (we use this for serializing the vector of transactions # for a witness block). @@ -125,7 +131,6 @@ def ser_vector(l, ser_function_name=None): r += i.serialize() return r - def deser_uint256_vector(f): nit = deser_compact_size(f) r = [] @@ -134,29 +139,22 @@ def deser_uint256_vector(f): r.append(t) return r - def ser_uint256_vector(l): r = ser_compact_size(len(l)) for i in l: r += ser_uint256(i) return r - def deser_string_vector(f): nit = deser_compact_size(f) - r = [] - for i in range(nit): - t = deser_string(f) - r.append(t) - return r - + return [deser_string(f) for _ in range(nit)] def ser_string_vector(l): r = ser_compact_size(len(l)) for sv in l: r += ser_string(sv) - return r + return r def deser_int_vector(f): nit = deser_compact_size(f) @@ -166,7 +164,6 @@ def deser_int_vector(f): r.append(t) return r - def ser_int_vector(l): r = ser_compact_size(len(l)) for i in l: @@ -177,16 +174,18 @@ def ser_push_data(dd): # "compile" data to be pushed on the script stack # - will be minimal sized, but only supports size ranges we're likely to see ll = len(dd) - assert 2 <= ll <= 255 - - if ll <= 75: + if ll < 0x4c: return bytes([ll]) + dd # OP_PUSHDATAn + data + elif ll <= 0xff: + return bytes([0x4c, ll]) + dd # 0x4c = 76 => OP_PUSHDATA1 + size + data + elif ll <= 0xffff: + return bytes([0x4d]) + struct.pack(b' OP_PUSHDATA2 else: - return bytes([76, ll]) + dd # 0x4c = 76 => OP_PUSHDATA1 + size + data + assert False def ser_push_int(n): # push a small integer onto the stack - from opcodes import OP_0, OP_1, OP_16, OP_PUSHDATA1 + from opcodes import OP_0, OP_1 if n == 0: return bytes([OP_0]) @@ -220,11 +219,13 @@ def disassemble(script): #print('dis %d: number=%d' % (offset, (c - OP_1 + 1))) yield (c - OP_1 + 1, None) elif c == OP_PUSHDATA1: - cnt = script[offset]; offset += 1 + cnt = script[offset] + offset += 1 yield (script[offset:offset+cnt], None) offset += cnt elif c == OP_PUSHDATA2: - cnt = struct.unpack_from("H", script, offset) + # up to 65535 bytes + cnt, = struct.unpack_from("H", script, offset) offset += 2 yield (script[offset:offset+cnt], None) offset += cnt @@ -237,11 +238,12 @@ def disassemble(script): # OP_0 included here #print('dis %d: opcode=%d' % (offset, c)) yield (None, c) - except: + except Exception as e: + # import sys;sys.print_exception(e) raise ValueError("bad script") -def ser_sig_der(r, s, sighash_type=1): +def ser_sig_der(r, s, sighash_type=SIGHASH_ALL): # Take R and S values from a signature and encode into DER format. sig = b"\x30" @@ -358,33 +360,48 @@ def serialize(self): return r def get_address(self): - # Detect type of output from scriptPubKey, and return 3-tuple: - # (addr_type_code, addr, is_segwit) + # Detect type of output from scriptPubKey, and return 2-tuple: + # (addr_type_code, pubkey/pubkeyhash/scripthash) # 'addr' is byte string, either 20 or 32 long + if self.is_p2tr(): + return AF_P2TR, self.scriptPubKey[2:2+32] - if len(self.scriptPubKey) == 22 and \ - self.scriptPubKey[0] == 0 and self.scriptPubKey[1] == 20: - # aka. P2WPKH - return 'p2pkh', self.scriptPubKey[2:2+20], True + if self.is_p2wpkh(): + return AF_P2WPKH, self.scriptPubKey[2:2+20] - if len(self.scriptPubKey) == 34 and \ - self.scriptPubKey[0] == 0 and self.scriptPubKey[1] == 32: - # aka. P2WSH - return 'p2sh', self.scriptPubKey[2:2+32], True + if self.is_p2wsh(): + return AF_P2WSH, self.scriptPubKey[2:2+32] if self.is_p2pkh(): - return 'p2pkh', self.scriptPubKey[3:3+20], False + return AF_CLASSIC, self.scriptPubKey[3:3+20] if self.is_p2sh(): - return 'p2sh', self.scriptPubKey[2:2+20], False + # can be: + # * bare P2SH + # * P2SH-P2WPKH + # * P2SH-P2WSH + return AF_P2SH, self.scriptPubKey[2:2+20] if self.is_p2pk(): # rare, pay to full pubkey - return 'p2pk', self.scriptPubKey[2:2+33], False + return AF_BARE_PK, self.scriptPubKey[2:2+33] + + if self.scriptPubKey[0] == OP_RETURN: + return OP_RETURN, self.scriptPubKey + + return None, self.scriptPubKey + + def is_p2tr(self): + return len(self.scriptPubKey) == 34 and \ + (OP_1 <= self.scriptPubKey[0] <= OP_16) and self.scriptPubKey[1] == 0x20 + + def is_p2wpkh(self): + return len(self.scriptPubKey) == 22 and \ + self.scriptPubKey[0] == 0 and self.scriptPubKey[1] == 0x14 - # If this is reached, we do not understand the output well - # enough to allow the user to authorize the spend, so fail hard. - raise ValueError('scriptPubKey template fail: ' + b2a_hex(self.scriptPubKey).decode()) + def is_p2wsh(self): + return len(self.scriptPubKey) == 34 and \ + self.scriptPubKey[0] == 0 and self.scriptPubKey[1] == 0x20 def is_p2sh(self): return len(self.scriptPubKey) == 23 and self.scriptPubKey[0] == 0xa9 \ @@ -489,7 +506,7 @@ def deserialize(self, f): self.nVersion = struct.unpack(" number of words @@ -138,9 +140,34 @@ def decode(secret, _bip39pw=''): return 'master', ms, hd + @staticmethod + def is_words(secret): + # return False or number of words: 12, 18, 24 + marker = secret[0] + if marker & 0x80: + return len_to_numwords(_len_from_marker(marker)) + return False + + @staticmethod + def decode_words(secret, bin_mode=False): + # Give a list of BIP-39 words from an encoded secret. Must be "words" type. + # - if bin_mode, return binary string representing the words, based on BIP-39 + ll = _len_from_marker(secret[0]) + + # note: + # - byte length > number of words + # - not storing checksum + assert ll in [16, 24, 32] + + # make master secret, using the memonic words, and passphrase (or empty string) + seed_bits = secret[1:1+ll] + + return bip39.b2a_words(seed_bits).split() if not bin_mode else seed_bits + @staticmethod def storage_serialize(secret): # make it a JSON-compatible field + # - converse: utils.deserialize_secret() return B2A(bytes(secret).rstrip(b"\x00")) @staticmethod @@ -153,7 +180,7 @@ def summary(marker): if marker & 0x80: # seed phrase - ll = len_from_marker(marker) + ll = _len_from_marker(marker) return '%d words' % len_to_numwords(ll) if marker == 0x00: @@ -177,7 +204,7 @@ class SensitiveValues: _cache_secret = None _cache_used = None - def __init__(self, secret=None, bip39pw='', bypass_tmp=False): + def __init__(self, secret=None, bip39pw='', bypass_tmp=False, enforce_delta=False): self.spots = [] self._bip39pw = bip39pw @@ -195,7 +222,12 @@ def __init__(self, secret=None, bip39pw='', bypass_tmp=False): if not pa.has_secrets(): raise ZeroSecretException + self.deltamode = pa.is_deltamode() + if self.deltamode and enforce_delta: + # wipe self before fetching secret + import callgate + callgate.fast_wipe() if self._cache_secret and not bypass_tmp: # they are using new BIP39 passphrase but we already have raw secret @@ -326,6 +358,9 @@ def capture_xpub(self): return xfp + def get_xfp(self): + return swab32(self.node.my_fp()) + def register(self, item): # Caller can add his own sensitive (derived?) data to our wiper # typically would be byte arrays or byte strings, but also @@ -388,13 +423,4 @@ def encryption_key(self, salt): self.register(pk) return pk - def encoded_secret(self): - # we do not support master as secret - only extended keys and mnemonics - if self.mode == "xprv": - nv = SecretStash.encode(xprv=self.node) - else: - assert self.mode == "words" - nv = SecretStash.encode(seed_phrase=self.raw) - return nv - # EOF diff --git a/shared/tapsigner.py b/shared/tapsigner.py index 76f9d11b5..9bce68d4d 100644 --- a/shared/tapsigner.py +++ b/shared/tapsigner.py @@ -33,7 +33,7 @@ async def import_tapsigner_backup_file(_1, _2, item): from pincodes import pa assert pa.is_secret_blank() # "must not have secret" - meta = "from " + origin = "from " label = "TAPSIGNER encrypted backup file" choice = await import_export_prompt(label, is_import=True) @@ -67,9 +67,9 @@ async def import_tapsigner_backup_file(_1, _2, item): continue break else: - fn = await file_picker(suffix="aes", min_size=100, max_size=160, **choice) + fn = await file_picker(suffix=".aes", min_size=100, max_size=160, **choice) if not fn: return - meta += (" (%s)" % fn) + origin += (" (%s)" % fn) try: with CardSlot(**choice) as card: with open(fn, 'rb') as fp: @@ -103,6 +103,6 @@ async def import_tapsigner_backup_file(_1, _2, item): await ux_show_story(title="FAILURE", msg=str(e)) continue - await import_extended_key_as_secret(extended_key, ephemeral, meta=meta) + await import_extended_key_as_secret(extended_key, ephemeral, origin=origin) # EOF diff --git a/shared/teleport.py b/shared/teleport.py new file mode 100644 index 000000000..44d5487d4 --- /dev/null +++ b/shared/teleport.py @@ -0,0 +1,789 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# teleport.py - Magically transport extremely sensitive data between the +# secure environment of two Q's. +# +import ngu, aes256ctr, bip39, json, ndef, chains +from utils import xfp2str, deserialize_secret +from ubinascii import unhexlify as a2b_hex +from ubinascii import hexlify as b2a_hex +from glob import settings, dis +from ux import ux_show_story, ux_confirm, the_ux, ux_dramatic_pause +from ux_q1 import show_bbqr_codes, QRScannerInteraction, ux_input_text +from charcodes import KEY_QR, KEY_NFC, KEY_CANCEL +from bbqr import b32encode, b32decode +from menu import MenuItem, MenuSystem +from notes import NoteContentBase +from sffile import SFFile +from wallet import MiniScriptWallet +from stash import SensitiveValues, SecretStash, blank_object, bip39_passphrase + +# One page github-hosted static website that shows QR based on URL contents pushed by NFC +KT_DOMAIN = 'keyteleport.com' + +# No length/size worries with simple secrets, but massive notes and big PSBT, +# with lots of UTXO, cannot be passed via NFC URL, because we are limited by +# NFC chip (8k) and URL length (4k or less) inside. BBQr is not limited however. +# - but the website is ready to make animated BBQr nicely +NFC_SIZE_LIMIT = const(4096) + +def short_bbqr(type_code, data): + # Short-circuit basic BBQr encoding here: always Base32, single part: 1 of 1 + # - used only for NFC link, where website may split again into parts + hdr = 'B$2%s0100' % type_code + + return hdr + b32encode(data) + +def txt_grouper(txt): + # split into 2-char groups and add spaces -- to make it easier to read/remember + return ' '.join(txt[n:n+2] for n in range(0, len(txt), 2)) + +async def nfc_push_kt(qrdata): + # NFC push to send them to our QR-rendering website + + url = KT_DOMAIN + '/#' + qrdata + + n = ndef.ndefMaker() + n.add_url(url, https=True) + + from glob import NFC + await NFC.share_loop(n, prompt="View QR on web", line2=KT_DOMAIN) + +async def kt_start_rx(*a): + # menu item to "start a receive" operation + + rx_key = settings.get("ktrx") + + if rx_key: + # Maybe re-use same one? Vaguely risky? Concern is they are confused and + # we don't want to lose the pubkey if they should be scanning not here. + ch = await ux_show_story('''Looks like last attempt wasn't completed. \ +You need to do QR scan of data from the sender to move to the next step. \ +We will re-use same values as last try, unless you press (R) for new values to be picked.''', + title='Reuse Pubkey?', escape='r'+KEY_QR, hint_icons=KEY_QR) + + if ch == KEY_QR: + # help them scan now! + x = QRScannerInteraction() + await x.scan_anything(expect_secret=False, tmp=False) + return + elif ch == 'r': + # wipe and restart; sender's work might be lost + rx_key = None + else: + # keep old keypair -- they might be confused + kp = ngu.secp256k1.keypair(a2b_hex(rx_key)) + + if not rx_key: + # pick a random key pair, just for this session + kp = ngu.secp256k1.keypair() + + settings.set("ktrx", b2a_hex(kp.privkey())) + settings.save() + + short_code, payload = generate_rx_code(kp) + + msg = '''To receive sensitive data from another COLDCARD, \ +share this Receiver Password with sender: + + %s = %s + +and show the QR on next screen to the sender. ENTER or %s to show here''' % ( + short_code, txt_grouper(short_code), KEY_QR) + + await tk_show_payload('R', payload, 'Key Teleport: Receive', msg, cta='Show to Sender') + +def generate_rx_code(kp): + # Receiver-side password: given a pubkey (33 bytes, compressed format) + # - construct an 8-digit decimal "password" + # - it's a AES key, but only 26 bits worth + pubkey = bytearray(kp.pubkey().to_bytes()) # default: compressed format + #assert len(pubkey) == 33 + + # - want the code to be deterministic, but I also don't want to save it + nk = ngu.hash.sha256d(kp.privkey() + b'COLCARD4EVER') + + # first byte will be 0x02 or 0x03 (Y coord) -- remove those known 7 bits + pubkey[0] ^= nk[20] & 0xfe + + num = '%08d' % (int.from_bytes(nk[4:8], 'big') % 1_0000_0000) + + # encryption after baby key stretch + kk = ngu.hash.sha256s(num.encode()) + enc = aes256ctr.new(kk).cipher(pubkey) + + return num, enc + +def decrypt_rx_pubkey(code, payload): + # given a 8-digit numeric code, make the key and then decrypt/checksum check + # - every value works, there is no fail. + kk = ngu.hash.sha256s(code.encode()) + rx_pubkey = bytearray(aes256ctr.new(kk).cipher(payload)) + + # first byte will be 0x02 or 0x03 but other 7 bits are noise + rx_pubkey[0] &= 0x01 + rx_pubkey[0] |= 0x02 + + # validate that it's on the curve... otherwise the code is wrong + try: + ngu.secp256k1.pubkey(rx_pubkey) + + return rx_pubkey + except: + return None + +async def tk_show_payload(type_code, payload, title, msg, cta=None): + # show the QR and/or NFC + # - MAYBE: make easier/faster to pick NFC from QR screen and vice-versa + from glob import NFC + + hints = KEY_QR + if NFC and len(payload) < NFC_SIZE_LIMIT: + hints += KEY_NFC + msg += ' or %s to view on your phone' % KEY_NFC + + msg += '. CANCEL to stop.' + + # simply show the QR + while 1: + ch = await ux_show_story(msg, title=title, hint_icons=hints) + + if ch == KEY_NFC and NFC: + await nfc_push_kt(short_bbqr(type_code, payload)) + elif ch == KEY_QR or ch == 'y': + # NOTE: CTA rarely seen, but maybe sometimes? + await show_bbqr_codes(type_code, payload, msg=cta) + elif ch == 'x': + return + +async def kt_start_send(rx_data): + # a QR was scanned and it held (most of) a pubkey + # - they want to send to this guy + # - ask them what to send, etc + + while 1: + # - ask for the sender's password -- nearly any value will be accepted + code = await ux_input_text('', confirm_exit=False, hex_only=True, max_len=8, + prompt='Teleport Password (number)', min_len=8, b39_complete=False, scan_ok=False, + placeholder='########', funct_keys=None, force_xy=None) + if not code: return + + rx_pubkey = decrypt_rx_pubkey(code, rx_data) + + if rx_pubkey: + break + + # I think only about 50% odds of catching an incorrect code. Not sure. + ch = await ux_show_story( + "Incorrect Teleport Password. You can try again or CANCEL to stop.") + if ch == 'x': return + + msg = '''You can now Key Teleport secrets! Choose what to share on next screen.\ +\n +WARNING: Receiver will have full access to all Bitcoin controlled by these keys!''' + + ch = await ux_show_story(msg, title="Key Teleport: Send") + if ch != 'y': return + + # pick what to send from a series of submenus + menu = SecretPickerMenu(rx_pubkey) + the_ux.push(menu) + +async def kt_do_send(rx_pubkey, dtype, raw=None, obj=None, prefix=b'', rx_label='the receiver', kp=None): + # We are rendering a QR and showing it to them for sending to another Q + dis.fullscreen("Wait...") + cleartext = dtype.encode() + (raw or json.dumps(obj).encode()) + dis.progress_bar_show(0.1) + + # Pick and show noid key to sender + noid_key, txt = pick_noid_key() + + dis.progress_bar_show(0.25) + + # all new EC key + my_keypair = kp or ngu.secp256k1.keypair() + + dis.progress_bar_show(0.75) + + payload = prefix + encode_payload(my_keypair, rx_pubkey, noid_key, cleartext, + for_psbt=bool(prefix)) + + dis.progress_bar_show(1) + + msg = "Share this password with %s, via some different channel:"\ + "\n\n %s = %s\n\n" % (rx_label, txt, txt_grouper(txt)) + msg += "ENTER to view QR" + + await tk_show_payload('S' if not prefix else 'E', payload, + 'Teleport Password', msg, cta='Show to Receiver') + + if not prefix: + # not PSBT case ... reset menus, we are deep! + from actions import goto_top_menu + goto_top_menu() + +def pick_noid_key(): + # pick an 40 bit password, shown as base32 + # - on rx, libngu base32 decoder will convert '018' into 'OLB' + # - but a little tempted to removed vowels here? + k = ngu.random.bytes(5) + txt = b32encode(k).upper() + + return k, txt + +async def kt_decode_rx(is_psbt, payload): + # we are getting data back from a sender, decode it. + dis.fullscreen("Wait...") + + prompt = 'Teleport Password (text)' + + if not is_psbt: + rx_key = settings.get("ktrx") + if not rx_key: + await ux_show_story("Not expecting any teleports. You need to start over.") + + await kt_start_rx() # help them to start over? idk maybe not. + return + + his_pubkey = payload[0:33] + body = payload[33:] + pair = ngu.secp256k1.keypair(a2b_hex(rx_key)) + + ses_key, body = decode_step1(pair, his_pubkey, body) + else: + # Multisig PSBT: will need to iterate over a few wallets and each N-1 possible senders + if not MiniScriptWallet.exists(): + await ux_show_story("Incoming PSBT requires miniscript wallet(s) to be already setup, but you have none.") + return + + ses_key, body, sender_xfp = MiniScriptWallet.kt_search_rxkey(payload) + + if sender_xfp is not None: + prompt = 'Teleport Password from [%s]' % xfp2str(sender_xfp) + + if not ses_key: + # when ECDH fails, it's truncation or wrong RX key (due to sender using old rx key, + # or the numeric code the sender entered was wrong, etc) + await ux_show_story("QR code was damaged, "+ + ("numeric password was wrong, " if not is_psbt else "")+ + "or it was sent to a different user. " + "Sender must start again.", title="Teleport Fail") + return + + while 1: + # ask for noid key + pw = await ux_input_text('', confirm_exit=False, hex_only=False, max_len=8, + prompt=prompt, min_len=8, b39_complete=False, scan_ok=False, + placeholder='********', funct_keys=None, force_xy=None) + if not pw: return + + dis.fullscreen("Wait...") + try: + assert len(pw) == 8 + noid_key = b32decode(pw) # case insenstive, and smart about confused chars + final = decode_step2(ses_key, noid_key, body) + if final is not None: + break + except: pass + + ch = await ux_show_story( + "Incorrect Teleport Password. You can try again or CANCEL to stop.") + if ch == 'x': return + # will ask again + + # success w/ decoding. but maybe something goes wrong or they reject a confirm step + # so keep the rx key alive still + + await kt_accept_values(chr(final[0]), final[1:]) + +async def kt_accept_values(dtype, raw): + # We got some secret, decode it more, and save it. + ''' + - `s` - secret, encoded per stash.py + - `r` - raw XPRV mode - 64 bytes follow which are the chain code then master privkey + - `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 includes name, source of key) + - `p` - binary PSBT to be signed + - `b` - complete system backup file (text, internal format) + ''' + from flow import has_se_secrets, goto_top_menu + from pincodes import pa + + enc = None + origin = 'Teleported' + label = None + + if pa.hobbled_mode and dtype != 'p': + await ux_show_story('Only PSBT for multisig accepted in this mode.', title='FAILED') + return + + + if dtype == 's': + # words / bip 32 master / xprv, etc + enc = bytearray(72) + enc[0:len(raw)] = raw + + elif dtype == 'x': + # it's an XPRV, but in binary.. some extra data we throw away here; sigh + # XXX no way to send this .. but was thinking of address explorer + txt = ngu.codecs.b58_encode(raw) + node, ch, _, _ = chains.slip132_deserialize(txt) + assert ch.name == chains.current_chain().name, 'wrong chain' + enc = SecretStash.encode(xprv=node) + + elif dtype == 'p': + # raw PSBT -- much bigger more complex + from auth import sign_transaction, TXN_INPUT_OFFSET + + psbt_len = len(raw) + + # copy into PSRAM + with SFFile(TXN_INPUT_OFFSET, max_size=psbt_len) as out: + out.write(raw) + + # This will take over UX w/ the signing process + # flags=None --> whether to finalize is decided based on psbt.is_complete + sign_transaction(psbt_len, flags=None) + return + + elif dtype == 'b': + # full system backup, including master: text lines + from backups import text_bk_parser, restore_tmp_from_dict_ll, restore_from_dict, extract_raw_secret + + vals = text_bk_parser(raw) + assert vals # empty? + + raw_sec, _ = extract_raw_secret(vals) + + from flow import has_secrets + + if has_secrets(): + # restores as tmp secret and/or offers to save to SeedVault + # need to remove key before I get into tmp seed settings + # so even if this errors out, new ktrx is needed + settings.remove_key("ktrx") + prob = await restore_tmp_from_dict_ll(vals, raw_sec) + else: + # we have no secret, so... reboot if it works, else errors shown, etc. + prob = await restore_from_dict(vals, raw_sec) + + if prob: + await ux_show_story(prob, title='FAILED') + else: + # force new rx key because this tfr worked + # only has effect if in master seed settings + settings.remove_key("ktrx") + return + + elif dtype in 'nv': + # all are JSON things + js = json.loads(raw) + + if dtype == 'v': + # one key export from a seed vault + # - watch for incompatibility here if we ever change VaultEntry + from seed import VaultEntry + rec = VaultEntry(*js) + enc = deserialize_secret(rec.encoded) + origin = rec.origin + label = rec.label + elif dtype == 'n': + # import secure note(s) + from notes import import_from_json, make_notes_menu, NoteContent + + settings.remove_key("ktrx") # force new rx key after this point + await import_from_json(dict(coldcard_notes=js)) + + await ux_dramatic_pause('Imported.', 2) + + # force them into notes submenu so they can see result right away + # - highlight to last note, which should be the just-added one(s) + goto_top_menu() + nm = await make_notes_menu() + nm.goto_idx(NoteContent.count()-1) + the_ux.push(nm) + + return + else: + raise ValueError(dtype) + + # key material is arriving; offer to use as main secret, or tmp, or seed vault? + settings.remove_key("ktrx") # force new rx key after this point + assert enc + + from seed import set_ephemeral_seed, set_seed_value + + if not has_se_secrets(): + # unit has nothing, so this will be the master seed + set_seed_value(encoded=enc) + ok = True + else: + ok = await set_ephemeral_seed(enc, origin=origin, label=label) + + if ok: + goto_top_menu() + +def noid_stretch(session_key, noid_key): + # TODO: measure timing of this on real Q + return ngu.hash.pbkdf2_sha512(session_key, noid_key, 5000)[0:32] + +def encode_payload(my_keypair, his_pubkey, noid_key, body, for_psbt=False): + # do all the encryption for sender + assert len(his_pubkey) == 33 + assert len(noid_key) == 5 + + # this can fail with ValueError: secp256k1_ec_pubkey_parse + # if the user has provided the wrong value for numeric password + # - better to catch this sooner in decrypt_rx_pubkey + session_key = my_keypair.ecdh_multiply(his_pubkey) + + # stretch noid key out -- will be slow + pk = noid_stretch(session_key, noid_key) + + b1 = aes256ctr.new(pk).cipher(body) + b1 += ngu.hash.sha256s(body)[-2:] + + b2 = aes256ctr.new(session_key).cipher(b1) + b2 += ngu.hash.sha256s(b1)[-2:] + + if for_psbt: + # no need to share pubkey for PSBT files + return b2 + + return my_keypair.pubkey().to_bytes() + b2 + +def decode_step1(my_keypair, his_pubkey, body): + # Do ECDH and remove top layer of encryption + try: + assert len(body) >= 3 + + session_key = my_keypair.ecdh_multiply(his_pubkey) + + rv = aes256ctr.new(session_key).cipher(body[:-2]) + chk = ngu.hash.sha256s(rv)[-2:] + + assert chk == body[-2:] # likely means wrong rx key, or truncation + except: + return None, None + + return session_key, rv + +def decode_step2(session_key, noid_key, body): + # After we have the noid key, can decode true payload + assert len(noid_key) == 5 + + pk = noid_stretch(session_key, noid_key) + + msg = aes256ctr.new(pk).cipher(body[:-2]) + chk = ngu.hash.sha256s(msg)[-2:] + + return msg if chk == body[-2:] else None + + +async def kt_incoming(type_code, payload): + # incoming BBQr was scanned (via main menu, etc) + + from pincodes import pa + if pa.hobbled_mode and type_code != 'E': + # only PSBT rx is supported in hobbled mode + # fail silently, this is second check, see decoders.py + return + + if type_code == 'R': + # they want to send to this guy + return await kt_start_send(payload) + + elif type_code == 'S': + # we are receiving something, let's try to decode + return await kt_decode_rx(False, payload) + + elif type_code == 'E': + # incoming PSBT! + return await kt_decode_rx(True, payload) + + else: + raise ValueError(type_code) + + +class SecretPickerMenu(MenuSystem): + def __init__(self, rx_pubkey): + self.rx_pubkey = rx_pubkey + + # this menu should be unreachable in hobbled mode. + from pincodes import pa + assert not pa.hobbled_mode + + from flow import word_based_seed, is_tmp, has_se_secrets + has_notes = bool(NoteContentBase.count()) + has_sv = bool(settings.get('seedvault', False)) + + # Q-only feature, so menu can be W I D E + # - in increasing order of importance & sensitivity! + # - pinned-virgin mode is supported, so might not have any secrets to share yet, + # but can do secret notes still + m = [ + MenuItem('Quick Text Message', f=self.quick_note), + MenuItem('Single Note / Password', predicate=has_notes, menu=self.pick_note_submenu), + MenuItem('Export All Notes & Passwords', predicate=has_notes, f=self.picked_note), + ] + + if has_sv: + m.append( MenuItem('From Seed Vault', menu=self.pick_vault_submenu) ) + + msg = None + if is_tmp(): + # tmp seed, or maybe bip39 is in effect + # - share the current master secret, not the real master + msg = 'Temp Secret (words)' if word_based_seed() else ( + 'XPRV from Words+Passphrase' if bip39_passphrase else 'Temp XPRV Secret') + elif has_se_secrets(): + # sharing real master secret + msg = 'Master Seed Words' if word_based_seed() else 'Master XPRV' + + if msg: + m.append( MenuItem(msg, f=self.share_master_secret) ) + m.append( MenuItem("Full COLDCARD Backup", f=self.share_full_backup) ) + + super().__init__(m) + + async def pick_vault_submenu(self, *a): + # pick a secret from seed vault + from seed import SeedVaultChooserMenu + rec = await SeedVaultChooserMenu.pick() + if rec: + await kt_do_send(self.rx_pubkey, 'v', obj=list(rec)) + + async def pick_note_submenu(self, *a): + # Make a submenu to select a single note/password + rv = [] + for note in NoteContentBase.get_all(): + rv.append(MenuItem('%d: %s' % (note.idx+1, note.title), f=self.picked_note, arg=note)) + + return rv + + async def quick_note(self, _, _2, item): + # accept a text string, and send as a note + from notes import NoteContent + txt = await ux_input_text('', max_len=100, + prompt='Enter your message', min_len=1, b39_complete=True, scan_ok=True, + placeholder='Attack at dawn.') + + if not txt: return + + n = NoteContent(dict(title="Quick Note", misc=txt)) + await kt_do_send(self.rx_pubkey, 'n', obj=[n.serialize()]) + + async def picked_note(self, _, _2, item): + # exporting note(s) + + if item.arg is None: + # export all + body = [n.serialize() for n in NoteContentBase.get_all()] + else: + # single note/password + body = [item.arg.serialize()] + + await kt_do_send(self.rx_pubkey, 'n', obj=body) + + async def share_full_backup(self, *a): + # context, and warn them + ch = await ux_show_story("Sending complete backup, including master secret, " + "seed vault (if any), miniscript wallets, notes/passwords, and all settings! " + "The receiving " + "COLDCARD must already have the master seed wiped to be able to install " + "everything, otherwise only master secret and miniscripts are saved into a tmp seed. " + "OK to proceed?") + if ch != 'y': return + + from backups import render_backup_contents + + dis.fullscreen("Buiding Backup...") + + # renders a text file, with rather a lot of comments; strip them + bkup = render_backup_contents(bypass_tmp=True) + out = [] + for ln in bkup.split('\n'): + if not ln: continue + if ln[0] == '#': continue + out.append(ln) + + await kt_do_send(self.rx_pubkey, 'b', raw=b'\n'.join(ln.encode() for ln in out)) + + async def share_master_secret(self, _, _2, item): + # altho menu items look different we are sharing same thing: + # - up to 72 bytes from secure elements + + dis.fullscreen("Wait...") + + with SensitiveValues(bypass_tmp=False, enforce_delta=True) as sv: + raw = bytearray(sv.secret) + xfp = xfp2str(sv.get_xfp()) + + # rtrim zeros + while raw[-1] == 0: + raw = raw[0:-1] + + summary = SecretStash.summary(raw[0]) + + from pincodes import pa + scale = 'your MASTER secret' if not pa.tmp_value else 'a temporary secret' + + msg = "Sharing %s [%s] (%s)." % (scale, xfp, summary) + msg += "\n\nWARNING: Allows full control over all associated Bitcoin!" + + if not await ux_confirm(msg): + blank_object(raw) + return + + await kt_do_send(self.rx_pubkey, 's', raw=raw) + + +async def kt_send_psbt(psbt, psbt_len): + # We just finished adding our signature to an incomplete PSBT. + # User wants to send to one or more other senders for them to complete signing. + + # who remains to sign? look at inputs + # all_xfps is set, no need to list one master xfp more than once - assuming CC can sign it all + assert psbt.active_miniscript + ms = psbt.active_miniscript + all_xfps = {x for x,*p in ms.to_descriptor().xfp_paths(skip_unspend_ik=True)} + + need = [x for x in psbt.miniscript_xfps_needed() if x in all_xfps] + # maybe it's not really a PSBT where we know the other signers? might be + # a weird coinjoin we don't fully understand + if not need: + await ux_show_story("No more signers?") + return + + # move out of PSRAM + from auth import TXN_OUTPUT_OFFSET + + with SFFile(TXN_OUTPUT_OFFSET, psbt_len) as fd: + bin_psbt = fd.read(psbt_len) + + my_xfp = settings.get('xfp') + + # if my_xfp in need: + # - we haven't signed yet? let's do that now .. except we've lost some of the + # data we need such as filename to save back into. + # - so just keep going instead... maybe they want to be last signer? + + # Make them pick a single next signer. It's not helpful to do multiple at once + # here, since we need signatures to be added serially so that last + # signer can do finalization. We don't have a general purpose combiner. + + async def done_cb(m, idx, item): + m.next_xfp = item.arg + the_ux.pop() + + ci = [] + next_signer = None + for idx, x in enumerate(all_xfps): + txt = '[%s] Co-signer #%d' % (xfp2str(x), idx+1) + f = done_cb + if x == my_xfp: + txt += ': YOU' + f = None + if x in need: + # we haven't signed ourselves yet, so allow that + from auth import sign_transaction, TXN_INPUT_OFFSET + + async def sign_now(*a): + # this will reset the UX stack: + # flags=None --> whether to finalize is decided based on psbt.is_complete + sign_transaction(psbt_len, flags=None) + + f = sign_now + + elif x not in need: + txt += ': DONE' + f = None + + mi = MenuItem(txt, f=f, arg=x) + + if x not in need: + # show check if we've got sig + mi.is_chosen = lambda: True + elif next_signer is None: + next_signer = idx + + ci.append(mi) + + m = MenuSystem(ci) + m.next_xfp = None + m.goto_idx(next_signer) # position cursor on next candidate + the_ux.push(m) + await m.interact() + + if m.next_xfp: + assert m.next_xfp != my_xfp + ri, rx_pubkey, kp = ms.kt_make_rxkey(m.next_xfp) + await kt_do_send(rx_pubkey, 'p', raw=bin_psbt, prefix=ri, kp=kp, + rx_label='[%s] co-signer' % xfp2str(m.next_xfp)) + + return True + + return None + +async def kt_send_file_psbt(*a): + # Menu item: choose a PSBT file from SD card, and send to co-signers. + # Heavy code re-use here. Need to find the multisig wallet associated w/ file, + # so we need to parse it and we must be one of the co-signers. + + from actions import is_psbt, file_picker + from auth import sign_psbt_file, TXN_INPUT_OFFSET + from version import MAX_TXN_LEN + from ux import import_export_prompt + from psbt import psbtObject + + # choose any PSBT from SD + picked = await import_export_prompt("PSBT", is_import=True, no_nfc=True, no_qr=True) + if picked == KEY_CANCEL: + return + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, + max_size=MAX_TXN_LEN, taster=is_psbt, **picked) + if not choices: + # error msg already shown + return + + if len(choices) == 1: + # single - skip the menu + label,path,fn = choices[0] + input_psbt = path + '/' + fn + else: + # multiples - make them pick one + input_psbt = await file_picker(choices=choices) + if not input_psbt: + return + + # read into PSRAM from wherever + psbt_len = await sign_psbt_file(input_psbt, just_read=True, **picked) + + dis.fullscreen("Validating...") + try: + dis.progress_sofar(1, 4) + with SFFile(TXN_INPUT_OFFSET, length=psbt_len, message='Reading...') as fd: + # NOTE: psbtObject captures the file descriptor and uses it later + psbt = psbtObject.read_psbt(fd) + + await psbt.validate() # might do UX: accept multisig import + + dis.progress_sofar(2, 4) + psbt.consider_inputs() + dis.progress_sofar(3, 4) + + except Exception as exc: + # not going to do full reporting here, use our other code for that! + await ux_show_story("Cannot validate PSBT?\n\n"+str(exc), "PSBT Load Failed") + return + finally: + dis.progress_bar_show(1) + + if not psbt.active_miniscript: + await ux_show_story("We are not part of this wallet.", "Cannot Teleport PSBT") + return + + await kt_send_psbt(psbt, psbt_len=psbt_len) + +# EOF diff --git a/shared/trick_pins.py b/shared/trick_pins.py index 1d7a19c6c..8dc89b5d1 100644 --- a/shared/trick_pins.py +++ b/shared/trick_pins.py @@ -12,6 +12,8 @@ from ux import ux_show_story, ux_confirm, ux_dramatic_pause, ux_enter_number, the_ux from stash import SecretStash from drv_entro import bip85_derive +from glob import settings + # see from mk4-bootloader/se2.h NUM_TRICKS = const(14) @@ -32,7 +34,7 @@ TC_XPRV_WALLET = const(0x0800) TC_DELTA_MODE = const(0x0400) TC_REBOOT = const(0x0200) -TC_RFU = const(0x0100) +TC_FW_DEFINED = const(0x0100) # for our use, not implemented in bootrom TC_BLANK_WALLET = const(0x0080) TC_COUNTDOWN = const(0x0040) # tc_arg = minutes of delay @@ -40,6 +42,10 @@ # tc_args encoding: # TC_WORD_WALLET -> BIP-85 index, 1001..1003 for 24 words, 2001..2003 for 12-words +# If TC_FW_DEFINED is true, then we can do anything with this PIN at the firmware +# level. First application is to unlock spending stuff. +TCA_SP_UNLOCK = const(0x0001) # spending policy unlock + # special "pin" used as catch-all for wrong pins WRONG_PIN_CODE = '!p' @@ -94,22 +100,6 @@ class TrickPinMgmt: def __init__(self): assert uctypes.sizeof(TRICK_SLOT_LAYOUT) == 128 - self.reload() - - def reload(self): - # we track known PINS as a dictionary: - # pin (in ascii) => (slot_num, tc_flags, arg) - from glob import settings - self.tp = settings.get('tp', {}) - - def save_record(self): - # commit changes back to settings - from glob import settings - if self.tp: - settings.set('tp', self.tp) - else: - settings.remove_key('tp') - settings.save() def roundtrip(self, method_num, slot_buf=None): from pincodes import pa @@ -129,26 +119,36 @@ def roundtrip(self, method_num, slot_buf=None): return rc + def get_all(self): + return settings.get("tp", {}) + + def commit(self, trick_pins): + settings.set("tp", trick_pins) + settings.save() + def clear_all(self): # get rid of them all self.roundtrip(0) - self.tp = {} - self.save_record() + settings.remove_key('tp') + settings.save() def forget_pin(self, pin): # forget about settings for a PIN - self.tp.pop(pin, None) - self.save_record() + t_pins = self.get_all() + t_pins.pop(pin, None) + self.commit(t_pins) def restore_pin(self, new_pin): # remember/restore PIN that we "forgot", return T if worked - b, slot = tp.get_by_pin(new_pin) + b, slot = self.get_by_pin(new_pin) if slot is None: return False record = (slot.slot_num, slot.tc_flags, 0xffff if slot.tc_flags & TC_DELTA_MODE else slot.tc_arg) - self.tp[new_pin] = record - self.save_record() + + t_pins = self.get_all() + t_pins[new_pin] = record + self.commit(t_pins) return True @@ -221,17 +221,18 @@ def update_slot(self, pin, new=False, new_pin=None, tc_flags=None, tc_arg=None, # pick a free slot sn = self.find_empty_slots(1 if not secret else 1+(len(secret)//32)) - if sn == None: + if sn is None: # we are full raise RuntimeError("no space left") slot.slot_num = sn + t_pins = self.get_all() if new_pin is not None: slot.pin_len = len(new_pin) slot.pin[0:slot.pin_len] = new_pin if new_pin != pin: - self.tp.pop(pin.decode(), None) + t_pins.pop(pin.decode(), None) pin = new_pin if tc_flags is not None: @@ -265,14 +266,18 @@ def update_slot(self, pin, new=False, new_pin=None, tc_flags=None, tc_arg=None, assert rc == 0 # record key details. - self.tp[pin.decode()] = record - self.save_record() + t_pins[pin.decode()] = record + self.commit(t_pins) return b, slot def all_tricks(self): # put them in order, with "wrong" last - return sorted(self.tp.keys(), key=lambda i: i if (i != WRONG_PIN_CODE) else 'Z') + return sorted(self.get_all().keys(), key=lambda i: i if (i != WRONG_PIN_CODE) else 'Z') + + def define_unlock_pin(self, new_pin): + # user is setting the bypass PIN for first time. + self.update_slot(new_pin.encode(), new=True, tc_flags=TC_FW_DEFINED, tc_arg=TCA_SP_UNLOCK) def was_countdown_pin(self): # was the trick pin just used? if so how much delay needed (or zero if not) @@ -284,24 +289,51 @@ def was_countdown_pin(self): else: return 0 + def was_sp_unlock(self): + # was a trick pin just used that enables acess to spending policy? + # - ok if it's also a trick PIN .. a wiping bypass for example + from pincodes import pa + tc_flags, tc_arg = pa.get_tc_values() + return bool(tc_flags & TC_FW_DEFINED) and (tc_arg == TCA_SP_UNLOCK) + + def has_sp_unlock(self): + # if spending policy defined, this PIN allows adjustment + # - not TRICK bypass choices, like ones that wipe + # - could be multiple, but only first returned. + for k, (sn,flags,arg) in self.get_all().items(): + if (flags == TC_FW_DEFINED) and (arg == TCA_SP_UNLOCK): + return k + return None + + def delete_sp_unlock_pins(self): + # remove all bypass pins, they are done w/ feature + for k, (sn,flags,arg) in self.get_all().items(): + if (flags & TC_FW_DEFINED) and (arg == TCA_SP_UNLOCK): + self.clear_slots([sn]) + self.forget_pin(k) + + def get_deltamode_pins(self): # iterate over all delta-mode PIN's defined. - for k, (sn,flags,args) in self.tp.items(): + for k, (sn,flags,args) in self.get_all().items(): if flags & TC_DELTA_MODE: yield k def get_duress_pins(self): # iterate over all duress wallets - for k, (sn,flags,args) in self.tp.items(): + for k, (sn,flags,args) in self.get_all().items(): if flags & (TC_WORD_WALLET | TC_XPRV_WALLET): yield k def check_new_main_pin(self, pin): # user is trying to change main PIN to new value; check for issues # - dups bad but also: delta mode pin might not work w/ longer main true pin + # - deciding whether TP already exists must be done via comms with SE2 + # as checking only self.tp is not sufficient for hidden TPs or after fast wipe # - return error msg or None assert isinstance(pin, str) - if pin in self.tp: + b, slot = self.get_by_pin(pin) + if slot is not None: return 'That PIN is already in use as a Trick PIN.' for d_pin in self.get_deltamode_pins(): @@ -319,8 +351,9 @@ def main_pin_has_changed(self, new_main_pin): def backup_duress_wallets(self, sv): # for backup file, yield (label, path, pairs-of-data) done = set() + t_pins = self.get_all() for pin in self.get_duress_pins(): - sn, flags, arg = self.tp[pin] + sn, flags, arg = t_pins[pin] if (flags, arg) in done: continue @@ -330,7 +363,7 @@ def backup_duress_wallets(self, sv): label = "Duress: BIP-85 Derived wallet" nwords = 12 if ((arg // 1000) == 2) else 24 path = "BIP85(words=%d, index=%d)" % (nwords, arg) - b, slot = tp.get_by_pin(pin) + b, slot = self.get_by_pin(pin) words = bip39.b2a_words(slot.xdata[0:(32 if nwords==24 else 16)]) d = [ ('duress_%d_words' % arg, words) ] @@ -369,10 +402,15 @@ def restore_backup(self, vals): # might need to construct a BIP-85 or XPRV secret to match path, new_secret = construct_duress_secret(flags, arg) - b, slot = tp.update_slot(pin.encode(), new=True, - tc_flags=flags, tc_arg=arg, secret=new_secret) - except Exception as exc: - sys.print_exception(exc) # not visible + self.update_slot(pin.encode(), new=True, tc_flags=flags, + tc_arg=arg, secret=new_secret) + except: pass + + @staticmethod + async def err_unique_pin(pin): + # standardized error UX + return await ux_show_story( + "That PIN (%s) is already in use. All PIN codes must be unique." % pin) tp = TrickPinMgmt() @@ -401,7 +439,6 @@ def construct(self): if bool(pa.tmp_value): return [MenuItem('Not Available')] - tp.reload() tricks = tp.all_tricks() if self.current_pin in tricks: @@ -489,7 +526,7 @@ async def done_picking(self, item, parents): tc_arg=tc_arg, secret=new_secret) await ux_dramatic_pause("Saved.", 1) except BaseException as exc: - sys.print_exception(exc) + # sys.print_exception(exc) await ux_show_story("Failed: %s" % exc) self.update_contents() @@ -518,8 +555,7 @@ async def get_new_pin(self, existing_pin=None): have.remove(existing_pin) if (new_pin == self.current_pin) or (new_pin in have): - await ux_show_story("That PIN (%s) is already in use. All PIN codes must be unique." % new_pin) - return + return await tp.err_unique_pin(new_pin) # check if we "forgot" this pin, and read it back if we did. # - important this is after the above checks so we don't reveal any trick pin used @@ -604,6 +640,9 @@ async def add_new(self, *a): For this mode only, trick PIN must be same length as true PIN and \ differ only in final 4 positions (ignoring dash).\ ''', flags=TC_DELTA_MODE), + StoryMenuItem('Policy Unlock', "Adds (another?) Spending Policy unlock PIN.", flags=TC_FW_DEFINED, arg=TCA_SP_UNLOCK), + StoryMenuItem('Policy Unlock & Wipe' if version.has_qwerty else 'P.U. & Wipe', + "Pretends correct Spending Policy unlock PIN given, but silently wipes seed before asking for main PIN.", flags=TC_FW_DEFINED|TC_WIPE, arg=TCA_SP_UNLOCK), ] m = MenuSystem(FirstMenu) m.goto_idx(1) @@ -632,23 +671,31 @@ async def set_any_wrong(self, *a): # xxxxxxxxxxxxxxxx MenuItem('[%s WRONG PIN]' % rel), StoryMenuItem('Wipe, Stop', "Seed is wiped and a message is shown.", - arg=num, flags=TC_WIPE), + arg=num, flags=TC_WIPE), StoryMenuItem('Wipe & Reboot', "Seed is wiped and Coldcard reboots without notice.", - arg=num, flags=TC_WIPE|TC_REBOOT), + arg=num, flags=TC_WIPE|TC_REBOOT), StoryMenuItem('Silent Wipe', "Seed is silently wiped and Coldcard acts as if PIN code was just wrong.", - arg=num, flags=TC_WIPE|TC_FAKE_OUT), - StoryMenuItem('Brick Self', "Become a brick instantly and forever.", flags=TC_BRICK, arg=num), - StoryMenuItem('Last Chance', "Wipe seed, then give one more try and then brick if wrong PIN.", arg=num, flags=TC_WIPE|TC_BRICK), - StoryMenuItem('Just Reboot', "Reboot when this happens. Doesn't do anything else.", arg=num, flags=TC_REBOOT), + arg=num, flags=TC_WIPE|TC_FAKE_OUT), + StoryMenuItem('Brick Self', "Become a brick instantly and forever.", + arg=num, flags=TC_BRICK,), + StoryMenuItem('Last Chance', "Wipe seed, then give one more try and then brick if wrong PIN.", + arg=num, flags=TC_WIPE|TC_BRICK), + StoryMenuItem('Just Reboot', "Reboot when this happens. Doesn't do anything else.", + arg=num, flags=TC_REBOOT), ]) m.goto_idx(1) the_ux.push(m) async def clear_all(self, m,l,item): + if not await ux_confirm("Remove ALL TRICK PIN codes and special wrong-pin handling?"): return + if tp.has_sp_unlock(): + if not await ux_confirm("You will not be able to bypass spending policy anymore."): + return + if any(tp.get_duress_pins()): if not await ux_confirm("Any funds on the duress wallet(s) have been moved already?"): return @@ -657,7 +704,7 @@ async def clear_all(self, m,l,item): m.update_contents() async def hide_pin(self, m,l, item): - pin, slot_num, flags = item.arg + pin, slot_num, flags, arg = item.arg if flags & TC_DELTA_MODE: await ux_show_story('''Delta mode PIN will be hidden if trick PIN menu is shown \ @@ -665,12 +712,14 @@ async def hide_pin(self, m,l, item): hiding this item.''') return - if pin != WRONG_PIN_CODE: + if (flags == TC_FW_DEFINED) and (arg == TCA_SP_UNLOCK): + msg = "It will still be possible to change or disable the spending policy if this PIN is known." + elif pin == WRONG_PIN_CODE: + msg = "This will hide what happens with wrong PINs from the menus but it will still be in effect." + else: msg = '''This will hide the PIN from the menus but it will still be in effect. You can restore it by trying to re-add the same PIN (%s) again later.''' % pin - else: - msg = "This will hide what happens with wrong PINs from the menus but it will still be in effect." if not await ux_confirm(msg): return @@ -706,16 +755,20 @@ async def change_pin(self, m,l, item): self.pop_submenu() # too lazy to get redraw right except BaseException as exc: - sys.print_exception(exc) + # sys.print_exception(exc) await ux_show_story("Failed: %s" % exc) async def delete_pin(self, m,l, item): - pin, slot_num, flags = item.arg + pin, slot_num, flags, arg = item.arg if flags & (TC_WORD_WALLET | TC_XPRV_WALLET): if not await ux_confirm("Any funds on this duress wallet have been moved already?"): return + if (flags == TC_FW_DEFINED) and (arg == TCA_SP_UNLOCK): + if not await ux_confirm("Changes to the spending policy will not be possible anymore."): + return + if pin == WRONG_PIN_CODE: msg = "Remove special handling of wrong PINs?" else: @@ -743,11 +796,9 @@ async def activate_wallet(self, m, l, item): ch = await ux_show_story('''\ This will temporarily load the secrets associated with this trick wallet \ -so you may perform transactions with it. Reboot the Coldcard to restore \ -normal operation.''') +so you may perform transactions with it.''') if ch != 'y': return - from pincodes import pa, AE_SECRET_LEN b, slot = tp.get_by_pin(pin) assert slot @@ -771,7 +822,7 @@ async def activate_wallet(self, m, l, item): # switch over to new secret! dis.fullscreen("Applying...") - await set_ephemeral_seed(encoded, meta=name) + await set_ephemeral_seed(encoded, origin=name) goto_top_menu() async def countdown_details(self, m, l, item): @@ -784,7 +835,7 @@ async def countdown_details(self, m, l, item): # "arg" can be out-of-date, if they edited timer value after parent was # rendered, where arg was captured into item.arg ... so don't use it. - cd_val = tp.tp[pin][2] + cd_val = tp.get_all()[pin][2] msg = 'Shows login countdown (%s)' % lgto_map.get(cd_val, '???').strip() if flags & TC_WIPE: @@ -800,16 +851,14 @@ async def countdown_details(self, m, l, item): def adjust_countdown_chooser(): # 'disabled' choice not appropriate for this case - ch = lgto_ch[1:] va = lgto_va[1:] def set_it(idx, text): new_val = va[idx] # save it try: - b, slot = tp.update_slot(pin.encode(), tc_flags=flags, tc_arg=new_val) - except BaseException as exc: - sys.print_exception(exc) + tp.update_slot(pin.encode(), tc_flags=flags, tc_arg=new_val) + except: pass return va.index(cd_val), lgto_ch[1:], set_it @@ -833,7 +882,8 @@ async def duress_details(self, m, l, item): if ch != '6': return b, s = tp.get_by_pin(pin) - if s == None: + if s is None: + title = None # could not find in SE2. Our settings vs. SE2 are not in sync. msg = "Not found in SE2. Delete and remake." else: @@ -845,21 +895,22 @@ async def duress_details(self, m, l, item): ch, pk = s.xdata[0:32], s.xdata[32:64] node.from_chaincode_privkey(ch, pk) - msg, *_ = render_master_secrets('xprv', None, node) + title, msg, *_ = render_master_secrets('xprv', None, node) elif flags & TC_WORD_WALLET: raw = s.xdata[0:(32 if nwords == 24 else 16)] - msg, *_ = render_master_secrets('words', raw, None) + title, msg, *_ = render_master_secrets('words', raw, None) else: raise ValueError(hex(flags)) - await ux_show_story(msg, sensitive=True) + await ux_show_story(msg, title=title, sensitive=True) async def pin_submenu(self, menu, label, item): # drill down into a sub-menu per existing PIN # - data display only, no editing; just clear and redo pin = item.arg - slot_num, flags, arg = tp.tp[pin] if (pin in tp.tp) else (-1, 0, 0) + t_pins = tp.get_all() + slot_num, flags, arg = t_pins[pin] if (pin in t_pins) else (-1, 0, 0) rv = [] @@ -878,6 +929,8 @@ async def pin_submenu(self, menu, label, item): rv.append(MenuItem("↳Pretends Wrong")) elif flags & TC_DELTA_MODE: rv.append(MenuItem("↳Delta Mode")) + elif (flags & TC_FW_DEFINED) and (arg == TCA_SP_UNLOCK): + rv.append(MenuItem("↳Unlock Policy")) # width issues on Mk4 for m, msg in [ (TC_WIPE, '↳Wipes seed'), @@ -891,8 +944,8 @@ async def pin_submenu(self, menu, label, item): rv.append(MenuItem("Activate Wallet", f=self.activate_wallet, arg=(pin, flags, arg))) rv.extend([ - MenuItem('Hide Trick', f=self.hide_pin, arg=(pin, slot_num, flags)), - MenuItem('Delete Trick', f=self.delete_pin, arg=(pin, slot_num, flags)), + MenuItem('Hide Trick', f=self.hide_pin, arg=(pin, slot_num, flags, arg)), + MenuItem('Delete Trick', f=self.delete_pin, arg=(pin, slot_num, flags, arg)), ]) if pin != WRONG_PIN_CODE: rv.append( @@ -903,6 +956,7 @@ async def pin_submenu(self, menu, label, item): class StoryMenuItem(MenuItem): def __init__(self, label, story, flags=0, **kws): + # arg= .. handled by super self.story = story self.flags = flags super().__init__(label, **kws) diff --git a/shared/usb.py b/shared/usb.py index 7dc1ea80f..fd4d6a413 100644 --- a/shared/usb.py +++ b/shared/usb.py @@ -2,16 +2,17 @@ # # usb.py - USB related things # -import ckcc, pyb, callgate, sys, ux, ngu, stash, aes256ctr +import ckcc, pyb, callgate, sys, ux, ngu, stash, aes256ctr, ujson from uasyncio import sleep_ms, core from uhashlib import sha256 -from public_constants import MAX_MSG_LEN, MAX_BLK_LEN, AFC_SCRIPT +from public_constants import MAX_MSG_LEN, MAX_BLK_LEN from public_constants import STXN_FLAGS_MASK from ustruct import pack, unpack_from from ckcc import watchpoint, is_simulator from utils import problem_file_line, call_later_ms from version import supports_hsm, is_devmode, MAX_TXN_LEN, MAX_UPLOAD_LEN -from exceptions import FramingError, CCBusyError, HSMDenied, HSMCMDDisabled +from exceptions import FramingError, CCBusyError, HSMDenied, HSMCMDDisabled, SpendPolicyViolation +from pincodes import pa # Unofficial, unpermissioned... numbers COINKITE_VID = 0xd13e @@ -52,8 +53,8 @@ 'smsg', # limited by policy 'blkc', 'hsts', # report status values 'stok', 'smok', # completion check: sign txn or msg - 'xpub', 'msck', # quick status checks - 'p2sh', 'show', # limited by HSM policy + 'xpub', # quick status checks + 'show', 'msas', # limited by HSM policy 'user', # auth HSM user, other user cmds not allowed 'gslr', # read storage locker; hsm mode only, limited usage }) @@ -68,6 +69,21 @@ "hsms", }) +# spending policy active: blacklist some commands +# - 'pass' may be allowed if 'okeys' is enabled +HOBBLED_CMDS = frozenset({ + 'enrl', # no new multisigs during policy enforcement + 'back', # no backups + 'bagi', 'dfu_', # just in case + + "user", # same as HSM_DISABLE_CMDS + "rmur", + "nwur", + "gslr", + "hsts", + "hsms", +}) + # singleton instance of USBHandler() handler = None @@ -117,6 +133,16 @@ def is_vcp_active(): return cur and ('VCP' in cur) and en + +def get_miniscript_by_name(name_bytes): + from wallet import MiniScriptWallet + + for w in MiniScriptWallet.iter_wallets(): + if w.name == str(name_bytes, 'ascii'): + return True, w + else: + return False, b'err_Miniscript wallet not found' + class USBHandler: def __init__(self): self.dev = pyb.USB_HID() @@ -169,6 +195,7 @@ async def usb_hid_recv(self): msg_len = 0 while 1: + success = False yield core._io_queue.queue_read(self.blockable) try: @@ -212,14 +239,14 @@ async def usb_hid_recv(self): # this saves memory over a simple slice (confirmed) args = memoryview(self.msg)[4:msg_len] resp = await self.handle(self.msg[0:4], args) - msg_len = 0 + success = True except CCBusyError: # auth UX is doing something else resp = b'busy' - msg_len = 0 + except SpendPolicyViolation: + resp = b'err_Spending policy in effect' except HSMDenied: resp = b'err_Not allowed in HSM mode' - msg_len = 0 except HSMCMDDisabled: # do NOT change below error msg as other applications depend on it resp = b'err_HSM commands disabled' @@ -227,16 +254,14 @@ async def usb_hid_recv(self): except (ValueError, AssertionError) as exc: # some limited invalid args feedback #print("USB request caused assert: ", end='') - #sys.print_exception(exc) + # sys.print_exception(exc) msg = str(exc) if not msg: msg = 'Assertion ' + problem_file_line(exc) resp = b'err_' + msg.encode()[0:80] - msg_len = 0 except MemoryError: # prefer to catch at higher layers, but sometimes can't resp = b'err_Out of RAM' - msg_len = 0 except FramingError as exc: raise exc except Exception as exc: @@ -245,9 +270,15 @@ async def usb_hid_recv(self): print("USB request caused this: ", end='') sys.print_exception(exc) resp = b'err_Confused ' + problem_file_line(exc) - msg_len = 0 - # aways send a reply if they get this far + if not success: + # do not let the progress screen hang on "Receiving..." + from ux import restore_menu + restore_menu() + + msg_len = 0 + + # always send a reply if they get this far await self.send_response(resp) except FramingError as exc: @@ -342,7 +373,7 @@ async def handle(self, cmd, args): except: raise FramingError('decode') - if cmd[0].isupper() and is_devmode: + if is_devmode and cmd[0].isupper(): # special hacky commands to support testing w/ the simulator try: from usb_test_commands import do_usb_command @@ -355,7 +386,18 @@ async def handle(self, cmd, args): if cmd not in HSM_WHITELIST: raise HSMDenied - if not settings.get('hsmcmd', False): + if pa.hobbled_mode: + # block some commands when we are hobbled. + if cmd in HOBBLED_CMDS: + raise SpendPolicyViolation + + if cmd in {'pwok', 'pass'}: + from ccc import sssp_spending_policy + if not sssp_spending_policy('okeys'): + raise SpendPolicyViolation + + elif not settings.get('hsmcmd', False): + # block these HSM-related command if not using feature if cmd in HSM_DISABLE_CMDS: raise HSMCMDDisabled @@ -432,39 +474,6 @@ async def handle(self, cmd, args): sign_msg(msg, subpath, addr_fmt) return None - if cmd == 'p2sh': - # show P2SH (probably multisig) address on screen (also provides it back) - # - must provide redeem script, and list of [xfp+path] - from auth import start_show_p2sh_address - - if hsm_active and not hsm_active.approve_address_share(is_p2sh=True): - raise HSMDenied - - # new multsig goodness, needs mapping from xfp->path and M values - addr_fmt, M, N, script_len = unpack_from('= 4, 'too short key path' + assert (length % 4) == 0, 'corrupt key path' + assert (length // 4) <= MAX_PATH_DEPTH, 'too deep' + class DecodeStreamer: def __init__(self): self.runt = bytearray() @@ -416,7 +421,7 @@ def check_firmware_hdr(hdr, binary_size): ok = (hw_compat & MK_4_OK) elif hw_label == 'q1': ok = (hw_compat & MK_Q1_OK) - + if not ok: return "That firmware doesn't support this version of Coldcard hardware (%s)."%hw_label @@ -431,7 +436,9 @@ def clean_shutdown(style=0): # wipe SPI flash and shutdown (wiping main memory) # - mk4: SPI flash not used, but NFC may hold data (PSRAM cleared by bootrom) # - bootrom wipes every byte of SRAM, so no need to repeat here - import callgate, version, uasyncio + # - style=2 => reboot and try login again + # - default is logout and (if applicable) power down. + import callgate # save if anything pending from glob import settings @@ -454,129 +461,87 @@ def clean_shutdown(style=0): callgate.show_logout(style) def call_later_ms(delay, cb, *args, **kws): - import uasyncio - async def doit(): await uasyncio.sleep_ms(delay) await cb(*args, **kws) uasyncio.create_task(doit()) -def txtlen(s): - # width of string in chars, accounting for - # double-wide characters which happen on Q. - rv = len(s) - - if DOUBLE_WIDE: - rv += sum(1 for ch in s if ch in DOUBLE_WIDE) - - return rv def word_wrap(ln, w): # Generate the lines needed to wrap one line into X "width"-long lines. # - tests in testing/test_unit.py - - if txtlen(ln) <= w: - yield ln + if ln and (ln[0] == OUT_CTRL_NOWRAP): + # no need to wrap this line - as requested by caller + yield ln[1:] return - while ln: + while True: + # ln_len considers DOUBLE_WIDTH chars + ln_len = 0 + sp = None + for idx, ch in enumerate(ln): + if ch == ' ': + # split point on space if possible + sp = idx + + ln_len += 1 + if ch in DOUBLE_WIDE: + ln_len += 1 + + if ln_len > w: + # if one of .,:; is last -> allow one more character + # even if only half visible on Mk4 + # on Q it's OK as (CHARS_W-1) is used as w + if ch in ".,:;": + idx += 1 + sp = None + + break - # find a space in (width) first part of remainder - sp = ln.rfind(' ', 0, w-1) + else: + yield ln + return + + if sp is None: + if ln[0] == OUT_CTRL_ADDRESS: + # special handling for lines w/ payment address in them + # - add same marker to newly split lines + addr = ln[1:] + # - 3 4-char groups on Mk4 + # - 6 4-char groups on Q + aw = 24 if version.has_qwerty else 12 + + pos = 0 + while pos < len(addr): + yield OUT_CTRL_ADDRESS + addr[pos:pos+aw] + pos += aw + return - if sp == -1: # bad-break the line - sp = min(txtlen(ln), w) - nsp = sp - if ln[nsp:nsp+1] == ' ': + sp = nsp = idx + if ln[sp:nsp+1] == " ": nsp += 1 else: # split on found space nsp = sp+1 left = ln[0:sp] - ln = ln[nsp:] - - if txtlen(left) + 1 + txtlen(ln) <= w: - # not clear when this would happen? final bit?? - left = left + ' ' + ln - ln = '' - yield left + ln = ln[nsp:] + if not ln: return -def parse_addr_fmt_str(addr_fmt): - # accepts strings and also integers if already parsed - from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH - - if addr_fmt in [AF_P2WPKH_P2SH, AF_P2WPKH, AF_CLASSIC]: - return addr_fmt - - addr_fmt = addr_fmt.lower() - if addr_fmt in ("p2sh-p2wpkh", "p2wpkh-p2sh"): - return AF_P2WPKH_P2SH - elif addr_fmt == "p2pkh": - return AF_CLASSIC - elif addr_fmt == "p2wpkh": - return AF_P2WPKH - else: - raise ValueError("Invalid address format: '%s'\n\n" - "Choose from p2pkh, p2wpkh, p2sh-p2wpkh." % addr_fmt) - -def parse_extended_key(ln, private=False): - # read an xpub/ypub/etc and return BIP-32 node and what chain it's on. - # - can handle any garbage line - # - returns (node, chain, addr_fmt) - # - people are using SLIP132 so we need this - node, chain, addr_fmt = None, None, None - if ln is None: - return node, chain, addr_fmt - - ln = ln.strip() - if private: - rgx = r'.prv[A-Za-z0-9]+' - else: - rgx = r'.pub[A-Za-z0-9]+' - - pat = ure.compile(rgx) - found = pat.search(ln) - # serialize, and note version code - try: - node, chain, addr_fmt, is_private = chains.slip32_deserialize(found.group(0)) - except: - pass - - return node, chain, addr_fmt - -def chunk_writer(fd, body): - from glob import dis - dis.fullscreen("Saving...") - body_len = len(body) - chunk = body_len // 10 - for idx, i in enumerate(range(0, body_len, chunk)): - fd.write(body[i:i + chunk]) - dis.progress_bar_show(idx / 10) - dis.progress_bar_show(1) - - -def addr_fmt_label(addr_fmt): - return { - AF_CLASSIC: "Classic P2PKH", - AF_P2WPKH_P2SH: "P2SH-Segwit", - AF_P2WPKH: "Segwit P2WPKH" - }[addr_fmt] - - -def pad_raw_secret(raw_sec_str): +def deserialize_secret(text_sec_str): # Chip can hold 72-bytes as a secret - # every secret has 0th byte as marker - # then secret and padded to zero to AE_SECRET_LEN + # - has 0th byte as marker, secret and zero padding to AE_SECRET_LEN + # - also does hex to binary conversion + # - converse of: SecretStash.storage_serialize() from pincodes import AE_SECRET_LEN raw = bytearray(AE_SECRET_LEN) - if len(raw_sec_str) % 2: - raw_sec_str += '0' - x = a2b_hex(raw_sec_str) + if len(text_sec_str) % 2: + text_sec_str += '0' + x = a2b_hex(text_sec_str) raw[0:len(x)] = x return raw @@ -615,11 +580,6 @@ def datetime_to_str(dt, fmt="%d-%02d-%02d %02d:%02d:%02d"): dts = fmt % (y, mo, d, h, mi, s) return dts + " UTC" -def censor_address(addr): - # We don't like to show the user multisig addresses because we cannot be certain - # they are valid and could actually be signed. And yet, dont blank too many - # spots or else an attacker could grind out a suitable replacement. - return addr[0:12] + '___' + addr[12+3:] def txid_from_fname(fname): if len(fname) >= 64: @@ -630,7 +590,7 @@ def txid_from_fname(fname): except: pass return None -def url_decode(u): +def url_unquote(u): # expand control chars from %XX and '+' # - equiv to urllib.parse.unquote_plus # - ure.sub is missing, so not being clever here. @@ -650,29 +610,38 @@ def url_decode(u): return u +def url_quote(u): + # convert non-text chars into %hex for URL usage + # - urllib.parse.quote() but w/o as much thought + return ''.join( (ch if 33 <= ord(ch) <= 127 else '%%%02x' % ord(ch)) \ + for ch in u) + def decode_bip21_text(got): # Assume text is a BIP-21 payment address (url), with amount, description # and url protocol prefix ... all optional except the address. # - also will detect correctly encoded & checksummed xpubs + # - always verifies checksum of data it finds proto, args, addr = None, None, None - # remove URL protocol: if present - if ':' in got: - proto, got = got.split(':', 1) - + # remove query params first - if any # looks like BIP-21 payment URL if '?' in got: - addr, args = got.split('?', 1) + got, args = got.split('?', 1) # full URL decode here, but assuming no repeated keys parts = args.split('&') args = dict() for p in parts: k, v = p.split('=', 1) - args[k] = url_decode(v) + args[k] = url_unquote(v) - # assume it's an bare address for now + # remove URL protocol: if present + if ':' in got: + proto, got = got.split(':', 1) + assert proto.lower() == "bitcoin" + + # assume it's a bare address for now if not addr: addr = got @@ -680,10 +649,12 @@ def decode_bip21_text(got): try: raw = ngu.codecs.b58_decode(addr) - # it's valid base58 - # an address, P2PKH or xpub (xprv checked above) + # It's valid base58: could be + # an address, P2PKH or xpub/xprv if addr[1:4] == 'pub': return 'xpub', (addr,) + if addr[1:4] == 'prv': + return 'xprv', (addr,) return 'addr', (proto, addr, args) except: @@ -701,4 +672,74 @@ def decode_bip21_text(got): def encode_seed_qr(words): return ''.join('%04d' % bip39.get_word_index(w) for w in words) +def show_single_address(addr): + # insert some metadata so display layer can do special rendering + # of addresses (based on hardware capabilities) + return OUT_CTRL_ADDRESS + addr + +def chunk_address(addr): + # useful to show payment addresses specially + return [addr[i:i+4] for i in range(0, len(addr), 4)] + +def cleanup_payment_address(s): + # Cleanup a payment address, or raise if bad checksum + # - later matching is string-based, so just doing basic syntax check here + # - must be checksumed-base58 or bech32 + try: + ngu.codecs.b58_decode(s) + assert len(s) < 40 # or else it's an xpub/xprv + return s + except: pass + + try: + ngu.codecs.segwit_decode(s) + return s.lower() + except: pass + + raise ValueError('bad address value: ' + s) + +def truncate_address(addr): + # Truncates address to width of screen, replacing middle chars + if not version.has_qwerty: + # - 16 chars screen width + # - but 2 lost at left (menu arrow, corner arrow) + # - want to show not truncated on right side + return addr[0:6] + '⋯' + addr[-6:] + else: + # tons of space on Q1 + return addr[0:12] + '⋯' + addr[-12:] + +def wipe_if_deltamode(): + # If in deltamode, give up and wipe self rather do + # a thing that might reveal true master secret... + from pincodes import pa + + if pa.is_deltamode(): + import callgate + callgate.fast_wipe() + +def chunk_checksum(fd, chunk=1024): + # reads from open file descriptor + md = sha256() + while True: + data = fd.read(chunk) + if not data: + break + md.update(data) + + return md.digest() + +def xor(*args): + # bit-wise xor between all args + vlen = len(args[0]) + # all have to be same length + assert all(len(e) == vlen for e in args) + rv = bytearray(vlen) + + for i in range(vlen): + for a in args: + rv[i] ^= a[i] + + return rv + # EOF diff --git a/shared/ux.py b/shared/ux.py index 813fa8094..36646ee67 100644 --- a/shared/ux.py +++ b/shared/ux.py @@ -6,8 +6,10 @@ from queues import QueueEmpty import utime, gc, version from utils import word_wrap -from charcodes import (KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_NFC, KEY_QR, - KEY_END, KEY_PAGE_UP, KEY_PAGE_DOWN, KEY_ENTER, KEY_CANCEL) +from version import has_qwerty, num_sd_slots, has_qr +from charcodes import (KEY_UP, KEY_DOWN, KEY_HOME, KEY_NFC, KEY_QR, KEY_END, KEY_PAGE_UP, + KEY_PAGE_DOWN, KEY_ENTER, KEY_CANCEL, OUT_CTRL_TITLE) + from exceptions import AbortInteraction DEFAULT_IDLE_TIMEOUT = const(4*3600) # (seconds) 4 hours @@ -15,21 +17,24 @@ # See ux_mk or ux_q1 for some display functions now if version.has_qwerty: from lcd_display import CHARS_W, CHARS_H - CH_PER_W = CHARS_W + # stories look nicer if we do not use the whole width + CH_PER_W = (CHARS_W - 1) STORY_H = CHARS_H - from ux_q1 import PressRelease, ux_enter_number, ux_input_numbers, ux_input_text, ux_show_pin - from ux_q1 import ux_login_countdown, ux_confirm, ux_dice_rolling, ux_render_words + from ux_q1 import PressRelease, ux_enter_number, ux_input_text, ux_show_pin + from ux_q1 import ux_login_countdown, ux_dice_rolling, ux_render_words from ux_q1 import ux_show_phish_words OK = "ENTER" X = "CANCEL" else: # How many characters can we fit on each line? How many lines? - # (using FontSmall) + # (using FontSmall) .. except it's an approximation since variable-width font. + # - 18 can work but rightmost spot is half-width. We allow . and , in that spot. + # - really should look at rendered-width of text CH_PER_W = 17 STORY_H = 5 - from ux_mk4 import PressRelease, ux_enter_number, ux_input_numbers, ux_input_text, ux_show_pin - from ux_mk4 import ux_login_countdown, ux_confirm, ux_dice_rolling, ux_render_words + from ux_mk4 import PressRelease, ux_enter_number, ux_input_text, ux_show_pin + from ux_mk4 import ux_login_countdown, ux_dice_rolling, ux_render_words from ux_mk4 import ux_show_phish_words OK = "OK" X = "X" @@ -169,7 +174,6 @@ def ux_poll_key(): return ch - async def ux_show_story(msg, title=None, escape=None, sensitive=False, strict_escape=False, hint_icons=None): # show a big long string, and wait for XY to continue @@ -181,8 +185,8 @@ async def ux_show_story(msg, title=None, escape=None, sensitive=False, lines = [] if title: - # kinda weak rendering but it works. - lines.append('\x01' + title) + # render the title line specially, see display/lcd_display.py + lines.append(OUT_CTRL_TITLE + title) if version.has_qwerty: # big screen always needs blank after title @@ -245,7 +249,21 @@ async def ux_show_story(msg, title=None, escape=None, sensitive=False, if ch in { KEY_NFC, KEY_QR }: return ch - +async def ux_confirm(msg, title="Are you SURE ?!?", confirm_key=None): + # confirmation screen, with stock title and Y=of course. + if not version.has_qwerty and len(title) > 12: + msg = title + "\n\n" + msg + title = None + + suffix = "" + if confirm_key: + suffix = ("\n\nPress (%s) to prove you read to the end of this message" + " and accept all consequences.") % confirm_key + + msg += suffix + r = await ux_show_story(msg, title=title, escape=confirm_key) + + return r == (confirm_key or 'y') async def idle_logout(): import glob @@ -321,14 +339,14 @@ def abort_and_push(m): the_ux.push(m) numpad.abort_ux() -async def show_qr_codes(addrs, is_alnum, start_n): +async def show_qr_codes(addrs, is_alnum, start_n, **kw): from qrs import QRDisplaySingle - o = QRDisplaySingle(addrs, is_alnum, start_n, sidebar=None) + o = QRDisplaySingle(addrs, is_alnum, start_n, **kw) await o.interact_bare() -async def show_qr_code(data, is_alnum=False, msg=None): +async def show_qr_code(data, is_alnum=False, msg=None, **kw): from qrs import QRDisplaySingle - o = QRDisplaySingle([data], is_alnum, msg=msg) + o = QRDisplaySingle([data], is_alnum, msg=msg, **kw) await o.interact_bare() async def ux_enter_bip32_index(prompt, can_cancel=False, unlimited=False): @@ -340,7 +358,6 @@ async def ux_enter_bip32_index(prompt, can_cancel=False, unlimited=False): return await ux_enter_number(prompt=prompt, max_value=max_value, can_cancel=can_cancel) def _import_prompt_builder(title, no_qr, no_nfc, slot_b_only=False): - from version import has_qwerty, num_sd_slots, has_qr from glob import NFC, VD prompt, escape = None, KEY_CANCEL+"x" @@ -376,16 +393,15 @@ def _import_prompt_builder(title, no_qr, no_nfc, slot_b_only=False): return prompt, escape -def export_prompt_builder(what_it_is, no_qr=False, no_nfc=False, key0=None, - force_prompt=False): +def export_prompt_builder(what_it_is, no_qr=False, no_nfc=False, key0=None, offer_kt=False, + force_prompt=False, txid=None): # Build the prompt for export # - key0 can be for special stuff - from version import has_qwerty, num_sd_slots, has_qr from glob import NFC, VD prompt, escape = None, KEY_CANCEL+"x" - if (NFC or VD) or (num_sd_slots>1) or key0 or force_prompt: + if (NFC or VD) or (num_sd_slots>1) or key0 or force_prompt or offer_kt or txid or (not no_qr): # no need to spam with another prompt, only option is SD card prompt = "Press (1) to save %s to SD Card" % what_it_is @@ -415,6 +431,14 @@ def export_prompt_builder(what_it_is, no_qr=False, no_nfc=False, key0=None, prompt += ", (4) to show QR code" escape += '4' + if txid: + prompt += ", (6) for QR Code of TXID" + escape += "6" + + if offer_kt: + prompt += ", (T) to " + offer_kt + escape += 't' + if key0: prompt += ', (0) ' + key0 escape += '0' @@ -456,17 +480,22 @@ def import_export_prompt_decode(ch): async def import_export_prompt(what_it_is, is_import=False, no_qr=False, no_nfc=False, title=None, intro='', footnotes='', - slot_b_only=False): + offer_kt=False, slot_b_only=False, force_prompt=False, + txid=None): + # Show story allowing user to select source for importing/exporting # - return either str(mode) OR dict(file_args) # - KEY_NFC or KEY_QR for those sources # - KEY_CANCEL for abort by user # - dict() => do file system thing, using file_args to control vdisk vs. SD vs slot_b + # - 't' => key teleport, but only offered with offer_kt is set (contetxt, and Q only) + from glob import NFC if is_import: prompt, escape = _import_prompt_builder(what_it_is, no_qr, no_nfc, slot_b_only) else: - prompt, escape = export_prompt_builder(what_it_is, no_qr, no_nfc) + prompt, escape = export_prompt_builder(what_it_is, no_qr, no_nfc, txid=txid, + force_prompt=force_prompt, offer_kt=offer_kt) # TODO: detect if we're only asking A or B, when just one card is inserted # - assume that's what they want to do @@ -476,8 +505,10 @@ async def import_export_prompt(what_it_is, is_import=False, no_qr=False, # they don't have NFC nor VD enabled, and no second slots... so will be file. return dict(force_vdisk=False, slot_b=None) else: - ch = await ux_show_story(intro+prompt+footnotes, escape=escape, title=title, - strict_escape=True) + hints = ("" if no_qr else KEY_QR) + (KEY_NFC if not no_nfc and NFC else "") + msg_lst = [i for i in (intro, prompt, footnotes) if i] + ch = await ux_show_story("\n\n".join(msg_lst), escape=escape, title=title, + strict_escape=True, hint_icons=hints) return import_export_prompt_decode(ch) diff --git a/shared/ux_mk4.py b/shared/ux_mk4.py index dac8bf515..feb37ad2d 100644 --- a/shared/ux_mk4.py +++ b/shared/ux_mk4.py @@ -58,17 +58,9 @@ async def wait(self): else: self.last_key = ch return ch - - -async def ux_confirm(msg): - # confirmation screen, with stock title and Y=of course. - from ux import ux_show_story - - resp = await ux_show_story("Are you SURE ?!?\n\n" + msg) - return resp == 'y' -async def ux_enter_number(prompt, max_value, can_cancel=False): +async def ux_enter_number(prompt, max_value, can_cancel=False, value=''): # return the decimal number which the user has entered # - default/blank value assumed to be zero # - clamps large values to the max @@ -80,7 +72,7 @@ async def ux_enter_number(prompt, max_value, can_cancel=False): press = PressRelease('1234567890y') y = 26 - value = '' + value = str(value) max_w = int(log(max_value, 10) + 1) dis.clear() @@ -122,8 +114,8 @@ async def ux_enter_number(prompt, max_value, can_cancel=False): # cleanup leading zeros and such value = str(min(int(value), max_value)) -async def ux_input_numbers(val, validate_func): - # collect a series of digits +async def ux_input_digits(val, prompt=None, maxlen=32): + # collect a series of digits. from glob import dis from display import FontTiny @@ -137,6 +129,11 @@ async def ux_input_numbers(val, validate_func): dis.clear() dis.text(None, -1, footer, FontTiny) + + if prompt: + dis.text(0, 0, prompt) + y += 8 + dis.save() while 1: @@ -161,7 +158,6 @@ async def ux_input_numbers(val, validate_func): ch = await press.wait() if ch == 'y': val += here - validate_func() return val elif ch == 'x': if here: @@ -170,7 +166,7 @@ async def ux_input_numbers(val, validate_func): # quit if they press X on empty screen return else: - if len(here) < 32: + if len(here) < maxlen: here += ch async def ux_input_text(pw, confirm_exit=True, hex_only=False, max_len=100, min_len=0, **_kws): @@ -287,7 +283,7 @@ def change(dx): ch = await press.wait() if ch == 'y': if len(pw) < min_len: - ch = await ux_show_story('Need %d characters at least. Press OK ' + ch = await ux_show_story('Need %d character(s) at least. Press OK ' 'to continue X to exit.' % min_len, escape="xy", strict_escape=True) if ch == "x": return diff --git a/shared/ux_q1.py b/shared/ux_q1.py index ba55f8382..1d98a841a 100644 --- a/shared/ux_q1.py +++ b/shared/ux_q1.py @@ -2,17 +2,18 @@ # # ux_q1.py - UX/UI interactions that are Q1 specific and use big screen, keyboard. # -import utime, gc, ngu, sys +import utime, gc, ngu, sys, bip39 import uasyncio as asyncio from uasyncio import sleep_ms from charcodes import * from lcd_display import CHARS_W, CHARS_H, CursorSpec, CURSOR_SOLID, CURSOR_OUTLINE from exceptions import AbortInteraction, QRDecodeExplained -import bip39 from decoders import decode_qr_result from ubinascii import hexlify as b2a_hex -from ubinascii import unhexlify as a2b_hex -from utils import problem_file_line +from ubinascii import b2a_base64 + +from utils import problem_file_line, show_single_address +from public_constants import MSG_SIGNING_MAX_LENGTH from glob import numpad # may be None depending on import order, careful class PressRelease: @@ -74,16 +75,8 @@ async def wait(self): else: self.last_key = ch return ch - -async def ux_confirm(msg): - # confirmation screen, with stock title and Y=of course. - from ux import ux_show_story - - resp = await ux_show_story(msg, title="Are you SURE ?!?") - return resp == 'y' - -async def ux_enter_number(prompt, max_value, can_cancel=False): +async def ux_enter_number(prompt, max_value, can_cancel=False, value=''): # return the decimal number which the user has entered # - default/blank value assumed to be zero # - clamps large values to the max @@ -93,7 +86,7 @@ async def ux_enter_number(prompt, max_value, can_cancel=False): # allow key repeat on X only? press = PressRelease() - value = '' + value = str(value) max_w = int(log(max_value, 10) + 1) dis.clear() @@ -122,6 +115,7 @@ async def ux_enter_number(prompt, max_value, can_cancel=False): elif ch == KEY_DELETE: if value: value = value[0:-1] + dis.text(0, 4, ' '*CHARS_W) elif ch == KEY_CLEAR: value = '' dis.text(0, 4, ' '*CHARS_W) @@ -138,11 +132,6 @@ async def ux_enter_number(prompt, max_value, can_cancel=False): # cleanup leading zeros and such value = str(min(int(value), max_value)) -async def ux_input_numbers(val, validate_func): - # collect a series of digits - # - not wanted on Q1; just get the digits mixed in w/ the text. - pass - async def ux_input_text(value, confirm_exit=False, hex_only=False, max_len=100, prompt='Enter value', min_len=0, b39_complete=False, scan_ok=False, placeholder=None, funct_keys=None, force_xy=None): @@ -159,7 +148,6 @@ async def ux_input_text(value, confirm_exit=False, hex_only=False, max_len=100, # to make longer single-line value onto screen # - confirm_exit default False here, because so easy to re-enter w/ qwerty, True on mk4 from glob import dis - from ux import ux_show_story MAX_LINES = 7 # without scroll can_scroll = False @@ -541,23 +529,22 @@ async def ux_login_countdown(sec): dis.busy_bar(0) -def ux_render_words(words, leading_blanks=1): +def ux_render_words(words, leading_blanks=0): # re-use word-list rendering code to show as a string in a story. # - because I want them all on-screen at once, and not simple to do that - buf = [bytearray(CHARS_W) for y in range(CHARS_H)] - rv = [''] * leading_blanks num_words = len(words) if num_words == 12: for y in range(6): + # no need to use NOWRAP here, will always fit (2 word columns) rv.append('%2d: %-8s %2d: %s' % (y+1, words[y], y+7, words[y+6])) else: lines = 6 if num_words == 18 else 8 for y in range(lines): - rv.append('%d:%-8s %2d:%-8s %2d:%s' % (y+1, words[y], - y+lines+1, words[y+lines], - y+(lines*2)+1, words[y+(lines*2)])) + rv.append(OUT_CTRL_NOWRAP+'%d:%-8s %2d:%-8s %2d:%s' % ( + y+1, words[y], y+lines+1, words[y+lines], + y+(lines*2)+1, words[y+(lines*2)])) return '\n'.join(rv) @@ -566,10 +553,21 @@ def ux_draw_words(y, num_words, words): # Draw seed words on single screen (hard) and return x/y position of start of each from glob import dis + if num_words == 2: + # simple version for first & last words, used only during login to spending policy + X = 14 + Y = y+1 + dis.text(X-7, Y, 'FIRST: %s' % words[0]) + dis.text(X-4, Y+1, '⋯') + dis.text(X-6, Y+2, 'LAST: %s' % words[-1]) + + return [ (X, Y), (X, Y+2) ] + if num_words == 12: cols = 2 xpos = [2, 18] else: + assert num_words in (18, 24) cols = 3 xpos = [0, 11, 23] @@ -596,14 +594,17 @@ def ux_draw_words(y, num_words, words): return rv -async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): +async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None, line2=None): # Accept a seed phrase, only # - replaces WordNestMenu on Q1 # - max word length is 8, min is 3 # - useful: simulator.py --q1 --eff --seq 'aa ee 4i ' from glob import dis + from ux import ux_confirm + + assert num_words and prompt - assert num_words and prompt and done_cb + not24 = (num_words != 24) def redraw_words(wrds=None): if not wrds: @@ -611,7 +612,15 @@ def redraw_words(wrds=None): dis.clear() dis.text(None, 0, prompt, invert=1) - p = ux_draw_words(2 if num_words != 24 else 1, num_words, wrds) + + Y = 2 if not24 else 1 + if line2 and not24: + # add second line, if provided, but only if words length < 24 + # currently only used to show backup filename during backup pwd entry + dis.text(None, 1, line2, invert=1) + Y += 1 + + p = ux_draw_words(Y, num_words, wrds) return wrds, p words, pos = redraw_words() @@ -693,8 +702,7 @@ def redraw_words(wrds=None): elif ch == KEY_CANCEL: if word_num >= 2: tmp = dis.save_state() - ok = await ux_confirm("Everything you've entered will be lost.") - if not ok: + if not await ux_confirm("Everything you've entered will be lost."): dis.restore_state(tmp) continue return None @@ -715,7 +723,7 @@ def redraw_words(wrds=None): maybe = [i for i in last_words if i.startswith(value)] if len(maybe) == 1: value = maybe[0] - elif len(maybe) == 0: + elif not maybe: if len(last_words) == 8: # 24 words case ll = ''.join(sorted(set([w[0] for w in last_words]))) err_msg = 'Final word starts with: ' + ll @@ -762,7 +770,10 @@ def redraw_words(wrds=None): else: err_msg = 'Next key: ' + nextchars - await done_cb(words) + if done_cb: + await done_cb(words) + + return words def ux_dice_rolling(): from glob import dis @@ -789,7 +800,7 @@ def __init__(self): pass @staticmethod - async def scan(prompt, line2=None): + async def scan(prompt, line2=None, enter_quits=False): # draw animation, while waiting for them to scan something # - CANCEL to abort # - returns a string, BBQr object or None. @@ -808,6 +819,8 @@ async def scan(prompt, line2=None): task = asyncio.create_task(SCAN.scan_once()) + escape = KEY_CANCEL + (KEY_ENTER if enter_quits else '') + ph = 0 while 1: if task.done(): @@ -819,9 +832,9 @@ async def scan(prompt, line2=None): ph = (ph + 1) % len(frames) # wait for key or 250ms animation delay - ch = await ux_wait_keydown(KEY_CANCEL, 250) + ch = await ux_wait_keydown(escape, 250) - if ch == KEY_CANCEL: + if ch and (ch in escape): data = None break @@ -833,14 +846,14 @@ async def scan(prompt, line2=None): return data - async def scan_general(self, prompt, convertor): + async def scan_general(self, prompt, convertor, line2=None, enter_quits=False): # Scan stuff, and parse it .. raise QRDecodeExplained if you don't like it # continues until something is accepted - problem = None + problem = line2 while 1: try: - got = await self.scan(prompt, line2=problem) + got = await self.scan(prompt, line2=problem, enter_quits=enter_quits) if got is None: return None @@ -850,7 +863,7 @@ async def scan_general(self, prompt, convertor): problem = str(exc) continue except Exception as exc: - #import sys; sys.print_exception(exc) + # import sys; sys.print_exception(exc) problem = "Unable to decode QR" continue @@ -880,9 +893,37 @@ def convertor(got): return await self.scan_general(prompt, convertor) + async def scan_for_addresses(self, prompt, line2=None): + # accept only payment addresses; strips BIP-21 junk that might be there + # - always a list result, might be size one + from utils import decode_bip21_text + + def addr_taster(got): + # could be muliple-line text file via BBQR or single line + got = decode_qr_result(got, expect_text=True) + + try: + rv = [] + for ln in got.split(): + what, args = decode_bip21_text(ln) + if what == 'addr': + rv.append(args[1]) + if rv: + return rv + except QRDecodeExplained: + raise + except: + pass + raise QRDecodeExplained("Not a payment address?") + + return await self.scan_general(prompt, addr_taster, line2=line2, enter_quits=True) - async def scan_anything(self, expect_secret=False, tmp=False): + + async def scan_anything(self, expect_secret=False, tmp=False, miniscript_wallet=None): # start a QR scan, and act on what we find, whatever it may be. + from ux import ux_show_story + from pincodes import pa + problem = None while 1: prompt = 'Scan any QR code, or CANCEL' if not expect_secret else \ @@ -895,89 +936,113 @@ async def scan_anything(self, expect_secret=False, tmp=False): # Figure out what we got. what, vals = decode_qr_result(got, expect_secret=expect_secret) + break except QRDecodeExplained as exc: problem = str(exc) continue - except Exception as exc: - import sys; sys.print_exception(exc) + except Exception: + # import sys; sys.print_exception(exc) problem = "Unable to decode QR" continue - if what == 'xprv': - from actions import import_extended_key_as_secret - text_xprv, = vals - await import_extended_key_as_secret(text_xprv, tmp) - return + if pa.hobbled_mode: + # block most imports in hobbled mode. + # - specific checks in place for teleport (PSBT is okay) + from ccc import sssp_spending_policy + whitelist = {'psbt', 'addr', 'vmsg', 'text', 'xpub', 'teleport' } - if what == 'words': - from seed import commit_new_words, set_ephemeral_seed_words # dirty API - words, = vals - if tmp: - await set_ephemeral_seed_words(words, 'From QR') - else: - await commit_new_words(words) + sv_ok = sssp_spending_policy('okeys') + if sv_ok: + # seed vault, and tmp seeds are okay with user, even in hobble mode + whitelist.update({'xprv', 'words'}) + if what not in whitelist: + await ux_show_story("Blocked when Spending Policy is in force.", title='Sorry') return - if what == 'psbt': - decoder, psbt_len, got = vals - await qr_psbt_sign(decoder, psbt_len, got) - return + if what == 'xprv': + from actions import import_extended_key_as_secret + text_xprv, = vals + await import_extended_key_as_secret(text_xprv, tmp) + return + + if what == 'words': + from seed import commit_new_words, set_ephemeral_seed_words # dirty API + words, = vals + if tmp: + await set_ephemeral_seed_words(words, 'From QR') + else: + await commit_new_words(words) - if what == 'txn': - bin_txn, = vals - await ux_visualize_txn(bin_txn) - return + return - if what == 'addr': - proto, addr, args = vals - await ux_visualize_bip21(proto, addr, args) - return + if what == 'psbt': + decoder, psbt_len, got = vals + await qr_psbt_sign(decoder, psbt_len, got, miniscript_wallet) - if what == "multi": - from auth import maybe_enroll_xpub - from ux import ux_show_story - ms_config, = vals - try: - maybe_enroll_xpub(config=ms_config) - except Exception as e: - await ux_show_story( - 'Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) - return + elif what == 'txn': + bin_txn, = vals + await ux_visualize_txn(bin_txn) - if what == "wif": - data, = vals - wif_str, key_pair, compressed, testnet = data - await ux_visualize_wif(wif_str, key_pair, compressed, testnet) - return + elif what == 'addr': + proto, addr, args = vals + await ux_visualize_bip21(proto, addr, args) - if what == 'text' or what == 'xpub': - # we couldn't really decode it. - txt, = vals - await ux_visualize_textqr(txt) - return + elif what == "minisc": + from auth import maybe_enroll_xpub + ms_config, = vals + try: + maybe_enroll_xpub(config=ms_config) + except Exception as e: + await ux_show_story( + 'Failed to import.\n\n%s\n%s' % (e, problem_file_line(e))) + return + + elif what == "wif": + data, = vals + wif_str, key_pair, compressed, testnet = data + await ux_visualize_wif(wif_str, key_pair, compressed, testnet) + + elif what == "vmsg": + data, = vals + from msgsign import verify_armored_signed_msg + await verify_armored_signed_msg(data) + + elif what == "smsg": + data, = vals + from auth import approve_msg_sign + from msgsign import msg_signing_done + await approve_msg_sign(None, None, None, + msg_sign_request=data, kill_menu=True, + approved_cb=msg_signing_done) + + elif what == 'text' or what == 'xpub': + # we couldn't really decode it. + txt, = vals + await ux_visualize_textqr(txt) + + elif what == 'teleport': + from teleport import kt_incoming + await kt_incoming(*vals) + + else: + await ux_show_story(what, title='Unhandled') - # not reached? - problem = 'Unhandled: ' + what - -async def qr_psbt_sign(decoder, psbt_len, raw): +async def qr_psbt_sign(decoder, psbt_len, raw, miniscript_wallet=None): # Got a PSBT coming in from QR scanner. Sign it. # - similar to auth.sign_psbt_file() - from auth import UserAuthorizedAction, ApproveTransaction, try_push_tx - from utils import CapsHexWriter - from glob import dis, PSRAM - from ux import show_qr_code, the_ux, ux_show_story - from ux_q1 import show_bbqr_codes + from auth import UserAuthorizedAction, ApproveTransaction + from ux import the_ux from sffile import SFFile - from auth import MAX_TXN_LEN, TXN_INPUT_OFFSET, TXN_OUTPUT_OFFSET + from auth import TXN_INPUT_OFFSET, psbt_encoding_taster if raw != 'PSRAM': # might already be in place - + # copy to PSRAM, and convert encoding at same time if isinstance(raw, str): raw = raw.encode() - # copy to PSRAM, and convert encoding at same time + _, output_encoder, _ = psbt_encoding_taster(raw[:10], psbt_len) total = 0 with SFFile(TXN_INPUT_OFFSET, max_size=psbt_len) as out: if not decoder: @@ -991,39 +1056,16 @@ async def qr_psbt_sign(decoder, psbt_len, raw): assert total <= psbt_len psbt_len = total - async def done(psbt): - dis.fullscreen("Wait...") - txid = None - - with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as psram: - - # save transaction, as hex into PSRAM - with CapsHexWriter(psram) as fd: - if psbt.is_complete(): - txid = psbt.finalize(fd) - else: - psbt.serialize(fd) - - data_len, sha = psram.tell(), fd.checksum.digest() - - UserAuthorizedAction.cleanup() - - # Show the result as a QR, perhaps many BBQr's - # - note: already HEX here! - here = PSRAM.read_at(TXN_OUTPUT_OFFSET, data_len) - if txid and await try_push_tx(a2b_hex(here), txid, sha): - return # success, exit - - try: - await show_qr_code(here.decode(), is_alnum=True, - msg=(txid or 'Partly Signed PSBT')) - except (ValueError, RuntimeError): - await show_bbqr_codes('T' if txid else 'P', here, - (txid or 'Partly Signed PSBT'), - already_hex=True) + else: + with SFFile(TXN_INPUT_OFFSET, max_size=psbt_len) as out: + taste = out.read(10) + _, output_encoder, _ = psbt_encoding_taster(taste, psbt_len) UserAuthorizedAction.cleanup() - UserAuthorizedAction.active_request = ApproveTransaction(psbt_len, approved_cb=done) + UserAuthorizedAction.active_request = ApproveTransaction( + psbt_len, input_method="qr", output_encoder=output_encoder, + miniscript_wallet=miniscript_wallet, + ) the_ux.push(UserAuthorizedAction.active_request) async def ux_visualize_txn(bin_txn): @@ -1056,7 +1098,7 @@ async def ux_visualize_txn(bin_txn): msg += '\n\nTxid:\n' + b2a_hex(txid).decode() except Exception as exc: - sys.print_exception(exc) + # sys.print_exception(exc) msg = "Unable to deserialize" await ux_show_story(msg, title="Signed Transaction") @@ -1068,7 +1110,7 @@ async def ux_visualize_bip21(proto, addr, args): # - validate address ownership on request from ux import ux_show_story - msg = addr + '\n\n' + msg = show_single_address(addr) + '\n\n' args = args or {} if 'amount' in args: @@ -1097,9 +1139,10 @@ async def ux_visualize_bip21(proto, addr, args): if ch == '1': from ownership import OWNERSHIP - await OWNERSHIP.search_ux(addr) + await OWNERSHIP.search_ux(addr, args) async def ux_visualize_wif(wif_str, kp, compressed, testnet): + # TODO: remove until we support signing w/ WIF keys IMHO from ux import ux_show_story msg = wif_str + "\n\n" msg += "chain: %s\n\n" % ("XTN" if testnet else "BTC") @@ -1107,15 +1150,48 @@ async def ux_visualize_wif(wif_str, kp, compressed, testnet): msg += "public key sec:\n" + b2a_hex(kp.pubkey().to_bytes(not compressed)).decode() + "\n\n" await ux_show_story(msg, title="WIF") -async def ux_visualize_textqr(txt, maxlen=200): +async def qr_msg_sign_done(signature, address, text): + from ux import ux_show_story + from msgsign import rfc_signature_template + from export import export_by_qr + + sig = b2a_base64(signature).decode('ascii').strip() + while True: + ch = await ux_show_story("Press ENTER to export signature QR only, " + "(0) to export full RFC template, " + "CANCEL if done.", escape="0") + if ch == "x": break + if ch == "y": + await export_by_qr(sig, "Signature", "U") + if ch == "0": + armored_str = "".join(rfc_signature_template(addr=address, msg=text, + sig=sig)) + await show_bbqr_codes("U", armored_str, "Armored MSG") + +async def qr_sign_msg(txt): + from msgsign import ux_sign_msg + + await ux_sign_msg(txt, approved_cb=qr_msg_sign_done, kill_menu=True) + +async def ux_visualize_textqr(txt, maxlen=MSG_SIGNING_MAX_LENGTH): # Show simple text. Don't crash on huge things, but be # able to show a full xpub. from ux import ux_show_story - if len(txt) > maxlen: + + txt_len = len(txt) + escape = "0" + if txt_len > maxlen: + escape = None txt = txt[0:maxlen] + '...' - await ux_show_story("%s\n\nAbove is text that was scanned. " - "We can't do any more with it." % txt, title="Simple Text") + msg = "%s\n\nAbove is text that was scanned. " % txt + if escape: + msg += " Press (0) to sign the text. " + + ch = await ux_show_story(title="Simple Text", msg=msg, escape=escape) + if escape and (ch == "0"): + await qr_sign_msg(txt) + async def show_bbqr_codes(type_code, data, msg, already_hex=False): # Compress, encode and split data, then show it animated... @@ -1128,10 +1204,13 @@ async def show_bbqr_codes(type_code, data, msg, already_hex=False): # - BUT: need zlib compress (not present) .. delayed for now from bbqr import TYPE_LABELS, int2base36, b32encode, num_qr_needed from glob import PSRAM, dis - from ux import ux_wait_keyup, ux_wait_keydown + from ux import ux_wait_keydown import uqr - assert not PSRAM.is_at(data, 0) # input data would be overwritten with our work + # put QR shenanigans at offset 1MB after TXN_OUTPUT_OFFSET + TMP_OFFSET = const(3 * 1024 * 1024) + + assert not PSRAM.is_at(data, TMP_OFFSET) # output data would be overwritten with our work assert type_code in TYPE_LABELS dis.fullscreen('Generating BBQr...', .1) @@ -1156,20 +1235,25 @@ async def show_bbqr_codes(type_code, data, msg, already_hex=False): # BBQr header hdr = 'B$' + encoding + type_code + int2base36(num_parts) + int2base36(pkt) - # encode the bytes assert pos < data_len, (pkt, pos, data_len) if already_hex: - # not encoding, just chars->bytes + # not encoding, just hex string hp = pos*2 - body = data[hp:hp+(part_size*2)].decode() + body = data[hp:hp+(part_size*2)] else: - # base32 encoding + # encode bytes to base32 encoding body = b32encode(data[pos:pos+part_size]) pos += part_size + # first packet, want to discover a working small value for QR version + if pkt == 0: + mnv = 10 if num_parts > 1 else 1 + else: + mnv = force_version + # do the hard work - qr_data = uqr.make(hdr+body, min_version=(10 if pkt == 0 else force_version), + qr_data = uqr.make(hdr+body, min_version=mnv, max_version=force_version, encoding=uqr.Mode_ALPHANUMERIC) # save the rendered QR @@ -1183,11 +1267,11 @@ async def show_bbqr_codes(type_code, data, msg, already_hex=False): else: _, _, raw = qr_data.packed() - PSRAM.write_at(qr_size * pkt, qr_size)[0:raw_qr_size] = raw + PSRAM.write_at(TMP_OFFSET + (qr_size * pkt), qr_size)[0:raw_qr_size] = raw del qr_data - dis.progress_bar_show((pkt+1) / num_parts) + dis.progress_sofar((pkt+1), num_parts) # display rate (plus time to send to display, etc) ms_per_each = 200 @@ -1199,7 +1283,7 @@ async def show_bbqr_codes(type_code, data, msg, already_hex=False): ch = None while not ch: for pkt in range(num_parts): - buf = PSRAM.read_at(qr_size * pkt, raw_qr_size) + buf = PSRAM.read_at(TMP_OFFSET + (qr_size * pkt), raw_qr_size) dis.draw_qr_display( (scan_w, w, buf), msg, True, None, None, False, partial_bar=((pkt, num_parts) if num_parts else None)) diff --git a/shared/vdisk.py b/shared/vdisk.py index 8b4fcab41..1ac66f98e 100644 --- a/shared/vdisk.py +++ b/shared/vdisk.py @@ -79,7 +79,7 @@ def mount(self, readonly=False): # corrupt or unformated? # XXX incomplete error handling here; needs work VBLKDEV.set_inserted(True) - sys.print_exception(exc) + # sys.print_exception(exc) return None @@ -93,7 +93,7 @@ def sample(self): return list(sorted(('/vdisk/'+fn, sz) for (fn,ty,_,sz) in os.ilistdir('/vdisk') if ty == 0x8000)) except BaseException as exc: - sys.print_exception(exc) + # sys.print_exception(exc) return [] finally: @@ -111,10 +111,10 @@ def import_file(self, filename, sz): return actual - def new_psbt(self, filename, sz): + def new_psbt(self, filename): # New incoming PSBT has been detected, start to sign it. from auth import sign_psbt_file - uasyncio.create_task(sign_psbt_file(filename, force_vdisk=True)) + uasyncio.create_task(sign_psbt_file(filename, force_vdisk=True, ux_abort=True)) def new_firmware(self, filename, sz): # potential new firmware file detected @@ -157,9 +157,9 @@ def host_done_handler(self): lfn = fn.lower() - if lfn.endswith('.psbt') and sz > 100: + if lfn.endswith('.psbt') and sz > 100 and ("-signed" not in lfn): self.ignore.add(fn) - self.new_psbt(fn, sz) + self.new_psbt(fn) break if lfn.endswith('.dfu') and sz > FW_MIN_LENGTH: diff --git a/shared/version.py b/shared/version.py index 1a528751a..015c38460 100644 --- a/shared/version.py +++ b/shared/version.py @@ -4,7 +4,8 @@ # # REMINDER: update simulator version of this file if API changes are made. # -from public_constants import MAX_TXN_LEN, MAX_UPLOAD_LEN +from public_constants import MAX_TXN_LEN_MK4 as MAX_TXN_LEN +from public_constants import MAX_UPLOAD_LEN_MK4 as MAX_UPLOAD_LEN def decode_firmware_header(hdr): from sigheader import FWH_PY_FORMAT @@ -76,14 +77,12 @@ def probe_system(): # run-once code to determine what hardware we are running on global hw_label, has_608, is_factory_mode, is_devmode, has_psram, is_edge global has_se2, mk_num, has_nfc, has_qr, num_sd_slots, has_qwerty, has_battery, supports_hsm - global MAX_UPLOAD_LEN, MAX_TXN_LEN from sigheader import RAM_BOOT_FLAGS, RBF_FACTORY_MODE import ckcc, callgate, machine hw_label = 'mk4' has_608 = True - nfc_presence_check() # hardware present; they might not be using it has_qr = False # QR scanner num_sd_slots = 1 # might have dual slots on Q1 mk_num = 4 @@ -91,7 +90,7 @@ def probe_system(): has_qwerty = False is_edge = False supports_hsm = True - has_nfc = True + has_nfc = nfc_presence_check() # hardware present; they might not use it. cpuid = ckcc.get_cpu_id() assert cpuid == 0x470 # STM32L4S5VI @@ -122,10 +121,8 @@ def probe_system(): # what firmware signing key did we boot with? are we in dev mode? is_devmode = get_is_devmode() - # increase size limits for mk4 - from public_constants import MAX_TXN_LEN_MK4, MAX_UPLOAD_LEN_MK4 - MAX_UPLOAD_LEN = MAX_UPLOAD_LEN_MK4 - MAX_TXN_LEN = MAX_TXN_LEN_MK4 + # newer, edge code in effect? + is_edge = (get_mpy_version()[1][-1] == 'X') probe_system() diff --git a/shared/wallet.py b/shared/wallet.py index 016b8a32b..4bc9f53bc 100644 --- a/shared/wallet.py +++ b/shared/wallet.py @@ -2,12 +2,33 @@ # # wallet.py - A place you find UTXO, addresses and descriptors. # -import chains -from descriptor import Descriptor -from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH -from stash import SensitiveValues + +import ngu, ujson, uio, chains, ure, version, stash +from binascii import hexlify as b2a_hex +from serializations import ser_string +from desc_utils import bip388_wallet_policy_to_descriptor, append_checksum, bip388_validate_policy, Key +from public_constants import AF_P2TR, AF_P2WSH, AF_CLASSIC, AF_P2SH, AF_P2WSH_P2SH +from menu import MenuSystem, MenuItem, start_chooser +from ux import ux_show_story, ux_confirm, ux_dramatic_pause, OK, X, ux_enter_bip32_index +from files import CardSlot, CardMissingError, needs_microsd +from utils import problem_file_line, xfp2str, to_ascii_printable, swab32, show_single_address +from charcodes import KEY_QR, KEY_CANCEL, KEY_NFC, KEY_ENTER +from glob import settings, DESC_CACHE + +# Arbitrary value, not 0 or 1, used to derive a pubkey from preshared xpub in Key Teleport +KT_RXPUBKEY_DERIV = const(20250317) + +# PSBT Xpub trust policies +TRUST_VERIFY = const(0) +TRUST_OFFER = const(1) +TRUST_PSBT = const(2) MAX_BIP32_IDX = (2 ** 31) - 1 +MAX_NAME_LEN = 30 # use (almost) full potential of Q screen + +class WalletOutOfSpace(RuntimeError): + pass + class WalletABC: # How to make this ABC useful without consuming memory/code space?? @@ -18,13 +39,13 @@ class WalletABC: # chain def yield_addresses(self, start_idx, count, change_idx=0): - # TODO: returns various tuples, with at least (idx, address, ...) + # returns various tuples, with at least (idx, address, ...) pass def render_address(self, change_idx, idx): # make one single address as text. - tmp = list(self.yield_addresses(idx, 1, change_idx=change_idx)) + tmp = list(self.yield_addresses(idx, 1, change_idx)) assert len(tmp) == 1 assert tmp[0][0] == idx @@ -41,17 +62,16 @@ def __init__(self, addr_fmt, path=None, account_idx=0, chain_name=None): # - path is optional, and then we use standard path for addr_fmt # - path can be overriden when we come here via address explorer - if addr_fmt == AF_P2WPKH: - n = 'Segwit P2WPKH' - prefix = path or 'm/84h/{coin_type}h/{account}h' - elif addr_fmt == AF_CLASSIC: - n = 'Classic P2PKH' - prefix = path or 'm/44h/{coin_type}h/{account}h' - elif addr_fmt == AF_P2WPKH_P2SH: - n = 'P2WPKH-in-P2SH' - prefix = path or 'm/49h/{coin_type}h/{account}h' - else: - raise ValueError(addr_fmt) + n = chains.addr_fmt_label(addr_fmt) + if not version.has_qwerty: + # Mk4 tiny display + # Classic P2PKH -> P2PKH + # Segwit P2WPKH -> P2WPKH + # P2SH-Segwit -> no change (should not be used that much) + n = n.split(" ")[-1] + + purpose = chains.af_to_bip44_purpose(addr_fmt) + prefix = path or 'm/%dh/{coin_type}h/{account}h' % purpose if chain_name: self.chain = chains.get_chain(chain_name) @@ -59,13 +79,13 @@ def __init__(self, addr_fmt, path=None, account_idx=0, chain_name=None): self.chain = chains.current_chain() if account_idx != 0: - n += ' Account#%d' % account_idx + rv = " Account#%d" if version.has_qwerty else " Acct#%d" + n += rv % account_idx if self.chain.ctype == 'XTN': - n += ' (Testnet)' + n += ' (Testnet)' if version.has_qwerty else " XTN" if self.chain.ctype == 'XRT': - n += ' (Regtest)' - + n += ' (Regtest)' if version.has_qwerty else " XRT" self.name = n self.addr_fmt = addr_fmt @@ -82,7 +102,6 @@ def __init__(self, addr_fmt, path=None, account_idx=0, chain_name=None): self._path = p - def yield_addresses(self, start_idx, count, change_idx=None): # Render a range of addresses. Slow to start, since accesses SE in general # - if count==1, don't derive any subkey, just do path. @@ -91,7 +110,7 @@ def yield_addresses(self, start_idx, count, change_idx=None): assert 0 <= change_idx <= 1 path += '/%d' % change_idx - with SensitiveValues() as sv: + with stash.SensitiveValues() as sv: node = sv.derive_path(path) if count is None: # special case - showing single, ignoring start_idx @@ -116,7 +135,7 @@ def yield_addresses(self, start_idx, count, change_idx=None): def render_address(self, change_idx, idx): # Optimized for a single address. path = self._path + '/%d/%d' % (change_idx, idx) - with SensitiveValues() as sv: + with stash.SensitiveValues() as sv: node = sv.derive_path(path) return self.chain.address(node, self.addr_fmt) @@ -125,11 +144,1310 @@ def render_path(self, change_idx, idx): return self._path + '/%d/%d' % (change_idx, idx) def to_descriptor(self): - from glob import settings + from descriptor import Descriptor, Key xfp = settings.get('xfp') xpub = settings.get('xpub') - keys = (xfp, self._path, xpub) - return Descriptor([keys], self.addr_fmt) + d = Descriptor(key=Key.from_cc_data(xfp, self._path, xpub), addr_fmt=self.addr_fmt) + return d + + +class MiniScriptWallet(WalletABC): + skey = "miniscript" + # optional: user can short-circuit many checks (system wide, one power-cycle only) + disable_checks = False + + def __init__(self, name, desc_tmplt, keys_info, af, ik_u=None, + desc=None, m_n=None, bip67=None, chain_type=None): + + assert 1 <= len(name) <= MAX_NAME_LEN, "name len" + + self.storage_idx = -1 + self.name = name + self.desc_tmplt = desc_tmplt + self.keys_info = keys_info + self.desc = desc + self.addr_fmt = af + self.ik_u = ik_u # internal key unspendable (taproot only) + + # below are basic multisig meta + # if m_n is not None, we are dealing with basic multisig + self.m_n = m_n + self.bip67 = bip67 + + # at this point all the keys are already validated + self.chain_type = chain_type or chains.current_chain().ctype + + def serialize(self): + opts = {"af": self.addr_fmt} + if self.ik_u is not None: + opts['ik_u'] = self.ik_u + if self.chain_type != "BTC": + opts['ct'] = self.chain_type + if self.m_n: + opts['m_n'] = self.m_n + opts['b67'] = self.bip67 + + return self.name, self.desc_tmplt, self.keys_info, opts + + @classmethod + def deserialize(cls, c, idx=-1): + # after deserialization - we lack loaded descriptor object + # we do not need it for everything + needs_migration = False + if len(c) == 4: + name, desc_tmplt, keys_info, opts = c + else: + # needs migration + name, desc_tmplt, keys_info, opts = miniscript_640_migrate(c) + needs_migration = True + + af = opts.get("af") + ct = opts.get("ct", "BTC") + ik_u = opts.get("ik_u", False) + m_n = opts.get("m_n", None) + b67 = opts.get("b67", None) + + rv = cls(name, desc_tmplt, keys_info, af, ik_u, m_n=m_n, + bip67=b67, chain_type=ct) + rv.storage_idx = idx + return rv, needs_migration + + @property + def chain(self): + return chains.get_chain(self.chain_type) + + @property + def key_chain(self): + return chains.get_chain("XTN" if self.chain_type == "XRT" else self.chain_type) + + @classmethod + def exists(cls): + # are there any wallets defined? + return bool(settings.get(cls.skey, [])) + + @classmethod + def iter_wallets(cls, name=None, addr_fmts=None): + # - this is only place we should be searching this list, please!! + lst = settings.get(cls.skey, []) + for idx in range(len(lst)): + w, migrate = cls.deserialize(lst[idx], idx) + if migrate: + if idx == 0: + from glob import dis + dis.fullscreen("Migrating...") + + lst[idx] = w.serialize() + settings.set(cls.skey, lst) + settings.save() + + if w.key_chain.ctype != chains.current_key_chain().ctype: + continue + if name and name != w.name: + continue + if addr_fmts and w.addr_fmt not in addr_fmts: + continue + + yield w + + def commit(self): + # data to save + # - important that this fails immediately when nvram overflows + obj = self.serialize() + + v = settings.get(self.skey, []) + orig = v.copy() + if not v or self.storage_idx == -1: + # create + self.storage_idx = len(v) + v.append(obj) + else: + # update in place + v[self.storage_idx] = obj + + settings.set(self.skey, v) + + # save now, rather than in background, so we can recover + # from out-of-space situation + try: + settings.save() + except: + # back out change; no longer sure of NVRAM state + try: + settings.set(self.skey, orig) + settings.save() + except: pass # give up on recovery + + raise WalletOutOfSpace + + def delete(self): + # remove saved entry + # - important: not expecting more than one instance of this class in memory + assert self.storage_idx >= 0 + lst = settings.get(self.skey, []) + try: + del lst[self.storage_idx] + if lst: + settings.set(self.skey, lst) + else: + settings.remove_key(self.skey) + + settings.save() # actual write + except IndexError: pass + self.storage_idx = -1 + + @classmethod + def get_trust_policy(cls): + which = settings.get('pms', None) + if which is None: + which = TRUST_VERIFY if cls.exists() else TRUST_OFFER + + return which + + @classmethod + def find_match(cls, xfp_paths, addr_fmt=None, M=None, N=None): + for rv in cls.iter_wallets(): + if addr_fmt is not None: + if rv.addr_fmt != addr_fmt: + continue + + if M and N: + if not rv.m_n: + continue + + m, n = rv.m_n + if m != M or n != N: + continue + + if rv.matching_subpaths(xfp_paths): + return rv + + return None + + def xfp_paths(self, skip_unspend_ik=False): + if not self.desc: + res = [] + for i, k_str in enumerate(self.keys_info): + if not i and self.ik_u and skip_unspend_ik: + continue + k = Key.from_string(k_str) + res.append(k.origin.psbt_derivation()) + return res + + return self.desc.xfp_paths(skip_unspend_ik=skip_unspend_ik) + + def matching_subpaths(self, xfp_paths): + my_xfp_paths = self.to_descriptor().xfp_paths() + + if len(xfp_paths) != len(my_xfp_paths): + return False + + for x in my_xfp_paths: + prefix_len = len(x) + for y in xfp_paths: + if x == y[:prefix_len]: + break + else: + return False + return True + + def subderivation_indexes(self, xfp_paths): + # we already know that they do match + my_xfp_paths = self.to_descriptor().xfp_paths() + res = set() + for x in my_xfp_paths: + prefix_len = len(x) + for y in xfp_paths: + if x == y[:prefix_len]: + to_derive = tuple(y[prefix_len:]) + res.add(to_derive) + + err = "derivation indexes" + assert res, err + if len(res) == 1: + branch, idx = list(res)[0] + else: + branch = [i[0] for i in res] + indexes = set([i[1] for i in res]) + assert len(indexes) == 1, err + idx = list(indexes)[0] + + return branch, idx + + def get_my_deriv(self): + # returns derivation path of the first "our" key in keys info vector + # used for signed exports only + str_xfp = xfp2str(settings.get('xfp')) + for ek in self.keys_info: + orig_end = ek.find("]") + if orig_end == -1: + continue # key without origin + + orig = ek[1:orig_end] + fp_end = orig.find("/") + if fp_end == -1: + master_fp = orig + fp_end = len(orig) + else: + master_fp = orig[:fp_end] + + if master_fp.upper() == str_xfp: + return "m" + orig[fp_end:] + + # didn't find any origin info + # BUT we know that our key is included (verified on import) + # therefore our key root key + return "m" + + def derive_desc(self, xfp_paths): + branch, idx = self.subderivation_indexes(xfp_paths) + derived_desc = self.desc.derive(branch).derive(idx) + return derived_desc + + def validate_script_pubkey(self, script_pubkey, xfp_paths, merkle_root=None): + derived_desc = self.derive_desc(xfp_paths) + derived_spk = derived_desc.script_pubkey() + assert derived_spk == script_pubkey, "spk mismatch\n\ncalc:\n%s\n\npsbt:\n%s" % ( + b2a_hex(derived_spk).decode(), b2a_hex(script_pubkey).decode() + ) + if merkle_root: + calc = derived_desc.tapscript.merkle_root + assert calc == merkle_root, "merkle root mismatch\n\ncalc:\n%s\n\npsbt:\n%s" % ( + b2a_hex(calc).decode(), b2a_hex(merkle_root).decode() + ) + return derived_desc + + def detail(self): + s = "Wallet Name:\n %s\n\n" % self.name + if self.m_n: + # basic multisig + M, N = self.m_n + s += "Policy: %d of %d\n\n" % (M, N) + + if M == N == 1: + s += 'The one signer must approve spends.' + elif M == N: + s += 'All %d co-signers must approve spends.' % N + elif M == 1: + s += 'Any signature from %d co-signers will approve spends.' % N + else: + s += '%d signatures, from %d possible co-signers, will be required to approve spends.' % (M, N) + + s += "\n\n" + + s += chains.addr_fmt_label(self.addr_fmt) + s += "\n\n" + self.desc_tmplt + return s + + async def show_detail(self, story="", offer_import=False): + story += self.detail() + story += "\n\nPress (1) to see extended public keys" + + if offer_import: + story += ", OK to approve, X to cancel." + + while True: + ch = await ux_show_story(story, escape="1") + if ch == "1": + await self.show_keys() + + elif (ch == "y") and offer_import: + return True + elif ch == "x": + return False + + async def show_keys(self): + msg = "" + for idx, k_str in enumerate(self.keys_info): + if idx: + msg += '\n---===---\n\n' + elif self.addr_fmt == AF_P2TR: + # index 0, taproot internal key + msg += "Taproot internal key:\n\n" + if self.ik_u: + msg += "(provably unspendable)\n\n" + + msg += '@%s:\n %s\n\n' % (idx, k_str) + + await ux_show_story(msg) + + def to_descriptor(self): + if self.desc is None: + # actual descriptor is not loaded, but was asked for + # fill policy - aka storage format - to actual descriptor + + if self.name in DESC_CACHE: + # loaded descriptor from cache + self.desc = DESC_CACHE[self.name] + else: + # print("loading... policy --> descriptor !!!") + # no need to validate already saved descriptor - was validated upon enroll + self.desc = self._from_bip388_wallet_policy(self.desc_tmplt, self.keys_info, + validate=False) + # cache len always 1 + DESC_CACHE.clear() + DESC_CACHE[self.name] = self.desc + + return self.desc + + @staticmethod + def _from_bip388_wallet_policy(desc_template, keys_info, validate=True): + desc_str = bip388_wallet_policy_to_descriptor( + desc_template.replace("/<0;1>/*", "/**"), + keys_info + ) + from descriptor import Descriptor + desc_obj = Descriptor.from_string(desc_str) + if validate: + desc_obj.validate(MiniScriptWallet.disable_checks) + return desc_obj + + @classmethod + def from_bip388_wallet_policy(cls, name, desc_template, keys_info): + bip388_validate_policy(desc_template, keys_info) + desc_obj = cls._from_bip388_wallet_policy(desc_template, keys_info) + msc = cls.from_descriptor_obj(name, desc_obj, desc_template, keys_info) + return msc + + @classmethod + def from_descriptor_obj(cls, name, desc_obj, desc_tmplt=None, keys_info=None): + if not desc_tmplt or not keys_info: + # BIP388 wasn't generated yet - generating from descriptor upon import/enroll + desc_tmplt, keys_info = desc_obj.bip388_wallet_policy() + # self-validation + bip388_validate_policy(desc_tmplt, keys_info) + + ik_u = desc_obj.key and desc_obj.key.is_provably_unspendable + af = desc_obj.addr_fmt + m_n = None + bip67 = None + if desc_obj.is_basic_multisig: + m_n = desc_obj.miniscript.m_n() + bip67 = desc_obj.is_sortedmulti + + return cls(name, desc_tmplt, keys_info, af, ik_u, desc_obj, m_n, bip67) + + @classmethod + def from_file(cls, config, name=None, bip388=False): + from descriptor import Descriptor + + if bip388: + # config is JSON wallet policy + wal = cls.from_bip388_wallet_policy(config["name"], config["desc_template"], + config["keys_info"]) + else: + if name is None: + desc_obj, cs = Descriptor.from_string(config.strip(), checksum=True) + name = cs + else: + name = to_ascii_printable(name) + desc_obj = Descriptor.from_string(config.strip()) + + desc_obj.validate(cls.disable_checks) + + wal = cls.from_descriptor_obj(name, desc_obj) + + return wal + + @classmethod + def import_from_psbt(cls, addr_fmt, M, N, xpubs_list): + # given the raw data from PSBT global header, offer the user + # the details, and/or bypass that all and just trust the data. + # - xpubs_list is a list of (xfp+path, binary BIP-32 xpub) + # - already know not in our records. + from descriptor import Descriptor + from miniscript import Sortedmulti, Number + + # build up an in-memory version of the wallet. + # - capture address format based on path used for my leg (if standards compliant) + + assert N == len(xpubs_list) + assert 1 <= M <= N <= 20, 'M/N range' + my_xfp = settings.get('xfp') + + has_mine = 0 + + keys = [] + for ek, xfp_pth in xpubs_list: + k = Key.from_psbt_xpub(ek, xfp_pth) + has_mine += k.validate(my_xfp, cls.disable_checks) + keys.append(k) + + assert has_mine == 1 # 'my key not included' + + name = 'PSBT-%d-of-%d' % (M, N) + # this will always create sortedmulti multisig (BIP-67) + # because BIP-174 came years after wide-spread acceptance of BIP-67 policy + desc_obj = Descriptor(miniscript=Sortedmulti(Number(M), *keys), + addr_fmt=addr_fmt) + return cls.from_descriptor_obj(name, desc_obj) + + def validate_psbt_xpubs(self, psbt_xpubs): + keys = set() + for ek, xfp_pth in psbt_xpubs: + key = Key.from_psbt_xpub(ek, xfp_pth) + key.validate(settings.get('xfp', 0), self.disable_checks) + keys.add(key) + + if not self.disable_checks: + assert set(self.to_descriptor().keys) == keys + + def ux_unique_name_msg(self, name=None): + return ("%s wallet with name '%s' already exists. All wallets MUST" + " have unique names.\n\n" % ("Multisig" if self.m_n else "Miniscript", name or self.name)) + + def find_duplicates(self): + for rv in self.iter_wallets(): + assert self.name != rv.name, self.ux_unique_name_msg() + + # optimization miniscript vs. multisig & different M/N multisigs + if self.m_n != rv.m_n: + # different M/N + continue + + err = "Duplicate wallet. Wallet '%s' is the same." % rv.name + if self.m_n: + # enrolling basic multisig wallet + if self.addr_fmt == rv.addr_fmt and sorted(self.keys_info) == sorted(rv.keys_info): + if self.bip67 != rv.bip67: + err += " BIP-67 clash." + err += "\n\n" + assert False, err + + else: + if self.desc_tmplt == rv.desc_tmplt and self.keys_info == rv.keys_info: + assert False, err + "\n\n" + + async def confirm_import(self): + # Return T if the user approves of this new wallet + try: + allow_import = True + self.find_duplicates() + story = "Create new %s wallet?\n\n" % ('multisig' if self.m_n else 'miniscript') + if self.m_n and not self.bip67: + story += ("WARNING: BIP-67 disabled! Unsorted multisig - " + "order of keys in descriptor/backup is crucial\n\n") + + except AssertionError as e: + story, allow_import = str(e), False + + if not await self.show_detail(story, offer_import=allow_import): + # user didn't like it, stop + return False + + # save new record + assert self.storage_idx == -1 + self.commit() + + # new wallet was imported, so cache its descriptor + assert self.desc + DESC_CACHE.clear() + DESC_CACHE[self.name] = self.desc + + await ux_dramatic_pause("Saved.", 2) + + return True + + def yield_addresses(self, start_idx, count, change_idx=0, scripts=False): + ch = chains.current_chain() + # change_idx work as boolean here - you cannot specify random change_idx + # as it is defined by descriptor + dd = self.to_descriptor().derive(None, change=bool(change_idx)) + idx = start_idx + while count: + if idx > MAX_BIP32_IDX: + break + # make the redeem script, convert into address + d = dd.derive(idx) + scr = d.miniscript.compile() if d.miniscript else None + addr = ch.render_address(d.script_pubkey(compiled_scr=scr)) + ders = script = None + if scripts: + ders = "" + for k in d.keys: + ders += "[%s]; " % str(k.origin) + + if d.tapscript: + # DFS ordered list of scripts + script = "" + for leaf_ver, scr, _ in d.tapscript._processed_tree: + script += b2a_hex(chains.tapscript_serialize(scr, leaf_ver)).decode() + "; " + else: + script = b2a_hex(ser_string(scr)).decode() + + yield idx, addr, ders, script + + idx += 1 + count -= 1 + + def make_addresses_msg(self, msg, start, n, change=0): + from glob import dis + + addrs = [] + + for idx, addr, *_ in self.yield_addresses(start, n, change): + msg += '.../%d =>\n' % idx # just idx, if derivations or scripts needed - export csv + addrs.append(addr) + msg += show_single_address(addr) + '\n\n' + dis.progress_sofar(idx - start + 1, n) + + return msg, addrs + + def generate_address_csv(self, start, n, change, saver=None): + scripts = settings.get("aemscsv", False) + header = ['Index', 'Payment Address'] + if scripts: + header += ['Script', 'Derivations'] + + yield '"' + '","'.join(header) + '"\n' + for idx, addr, ders, script in self.yield_addresses(start, n, change, scripts=scripts): + if saver: + saver(addr, idx) + + ln = '%d,"%s"' % (idx, addr) + if scripts: + ln += ',"%s"' % script + ln += ',"%s"' % ders + ln += '\n' + yield ln + + def to_string(self, checksum=True): + # policy filling - not possible to specify internal/external always multipath export + # only supported from bitcoin-core 29.0 + if self.desc_tmplt and self.keys_info: + desc = bip388_wallet_policy_to_descriptor(self.desc_tmplt, self.keys_info) + if checksum: + desc = append_checksum(desc) + return desc + + return self.desc.to_string() + + def bitcoin_core_serialize(self): + return [{ + "desc": self.to_string(), # policy fill + "active": True, + "timestamp": "now", + "range": [0, 100], + }] + + async def export_wallet_file(self, core=False, bip388=False, sign=True): + # do not load descriptor - just fill policy + # only with multipath format <0;1> + from glob import NFC, dis + from ux import import_export_prompt + + dis.fullscreen('Wait...') + + t = "Multisig" if self.m_n else "Miniscript" + + if core: + name = "Bitcoin Core %s" % t + fname_pattern = 'bitcoin-core-%s.txt' % self.name + msg = "importdescriptors cmd" + core_obj = self.bitcoin_core_serialize() + core_str = ujson.dumps(core_obj) + res = "importdescriptors '%s'\n" % core_str + elif bip388: + # policy as JSON + msg = self.name + name = "BIP-388 Wallet Policy" + fname_pattern = 'b388-%s.json' % self.name + res = ujson.dumps({"name": self.name, + "desc_template": self.desc_tmplt, + "keys_info": self.keys_info}) + else: + name = t + fname_pattern = '%s-%s.txt' % ("multi" if self.m_n else "minsc", self.name) + msg = self.name + res = self.to_string() + + ch = await import_export_prompt("%s file" % name) + if isinstance(ch, str): + if ch in "3"+KEY_NFC: + if bip388: + await NFC.share_json(res) + else: + await NFC.share_text(res) + elif ch == KEY_QR: + try: + from ux import show_qr_code + await show_qr_code(res, msg=msg) + except: + if version.has_qwerty: + from ux_q1 import show_bbqr_codes + await show_bbqr_codes('U', res, msg) + return + + try: + with CardSlot(**ch) as card: + fname, nice = card.pick_filename(fname_pattern) + + # do actual write + with open(fname, 'w+') as fp: + fp.write(res) + + if sign: + # sign with my key at the same path as first address of export + derive = self.get_my_deriv() + "/0/0" + from msgsign import write_sig_file + h = ngu.hash.sha256s(res.encode()) + sig_nice = write_sig_file([(h, fname)], derive, AF_CLASSIC) + + msg = '%s file written:\n\n%s' % (name, nice) + if sign: + msg += '\n\n%s signature file written:\n\n%s' % (name, sig_nice) + await ux_show_story(msg) + + except CardMissingError: + await needs_microsd() + return + except Exception as e: + await ux_show_story('Failed to write!\n\n%s\n%s' % (e, problem_file_line(e))) + return + + def xpubs_from_xfp(self, xfp): + # return list of XPUB's which match xfp + res = [] + desc = self.to_descriptor() + for k in desc.keys: + if k.origin and k.origin.cc_fp == xfp: + res.append(k) + elif swab32(k.node.my_fp()) == xfp: + res.append(k) + + assert res, "missing xfp %s" % xfp2str(xfp) + # returned is list of keys with corresponding master xfp + # key in list are lexicographically sorted based on their public keys + # lowest public key first + return sorted(res, key=lambda o: o.serialize()) + + def kt_make_rxkey(self, xfp): + # Derive the receiver's pubkey from preshared xpub and a special derivation + # - also provide the keypair we're using from our side of connection + # - returns 4 byte nonce which is sent un-encrypted, his_pubkey and my_keypair + ri = ngu.random.uniform(1<<28) + + # sorted lexicographically, always use the lowest pubkey from the list at index 0 + keys = self.xpubs_from_xfp(xfp) + k = keys[0] + k = k.derive(KT_RXPUBKEY_DERIV).derive(ri) + pubkey = k.node.pubkey() + + kp = self.kt_my_keypair(ri) + return ri.to_bytes(4, 'big'), pubkey, kp + + def kt_my_keypair(self, ri): + # Calc my keypair for sending PSBT files. + # + # sorted lexicographically, always use the lowest pubkey from the list at index 0 + keys = self.xpubs_from_xfp(settings.get('xfp')) + + subpath = "/%d/%d" % (KT_RXPUBKEY_DERIV, ri) + path = keys[0].origin.str_derivation() + subpath + with stash.SensitiveValues() as sv: + node = sv.derive_path(path) + kp = ngu.secp256k1.keypair(node.privkey()) + return kp + + @classmethod + def kt_search_rxkey(cls, payload): + # Construct the keypair for to be decryption + # - has to try pubkey each all the unique XFP for all co-signers in all wallets + # - checks checksum of ECDH unwrapped data to see if it's the right one + # - returns session key, decrypted first layer, and XFP of sender + from teleport import decode_step1 + + # this nonce is part of the derivation path so each txn gets new keys + ri = int.from_bytes(payload[0:4], 'big') + + my_xfp = settings.get('xfp') + + for msc in cls.iter_wallets(): + kp = msc.kt_my_keypair(ri) + for k in msc.to_descriptor().keys: + if k.origin.cc_fp == my_xfp: + continue + kk = k.derive(KT_RXPUBKEY_DERIV).derive(ri) + his_pubkey = kk.node.pubkey() + # if implied session key decodes the checksum, it is right + ses_key, body = decode_step1(kp, his_pubkey, payload[4:]) + if ses_key: + return ses_key, body, kk.origin.cc_fp + + return None, None, None + + async def export_electrum(self): + # Generate and save an Electrum JSON file. + from export import export_contents + + assert self.m_n, "not multisig" + M, N = self.m_n + + def doit(): + rv = dict(seed_version=17, use_encryption=False, + wallet_type='%dof%d' % (M, N)) + + ch = self.chain + + # the important stuff. + for idx, key in enumerate(self.to_descriptor().keys): + # CHALLENGE: we must do slip-132 format [yz]pubs here when not p2sh mode. + xp = ch.serialize_public(key.node, self.addr_fmt) + + rv['x%d/' % (idx + 1)] = {"hw_type":"coldcard", "type":"hardware", + "ckcc_xfp": key.origin.cc_fp, "xpub":xp, + "label":"Coldcard %s" % xfp2str(key.origin.cc_fp), + "derivation":key.origin.str_derivation()} + + # sign export with first p2pkh key + return ujson.dumps(rv), self.get_my_deriv() + "/0/0", AF_CLASSIC + + fname = '%s-%s.%s' % ("el", self.name.replace(" ", "_"), "json") + await export_contents('Electrum multisig wallet', doit, + fname, is_json=True) + +async def miniscript_delete(msc): + if not await ux_confirm("Delete miniscript wallet '%s'?\n\nFunds may be impacted." % msc.name): + await ux_dramatic_pause('Aborted.', 3) + return + + msc.delete() + await ux_dramatic_pause('Deleted.', 3) + +async def miniscript_wallet_delete(menu, label, item): + msc = item.arg + + await miniscript_delete(msc) + + from ux import the_ux + # pop stack + the_ux.pop() + + m = the_ux.top_of_stack() + m.update_contents() + +async def miniscript_wallet_rename(menu, label, item): + from glob import dis + from ux import ux_input_text, the_ux + + idx, msc = item.arg + new_name = await ux_input_text(msc.name, confirm_exit=False, + min_len=1, max_len=MAX_NAME_LEN) + + if not new_name: + return + + wallets = settings.get("miniscript", []) + names = [i[0] for i in wallets] + if new_name in names: + await ux_show_story(msc.ux_unique_name_msg(new_name), title="FAILED") + return + + dis.fullscreen("Saving...") + + # save it + wal = list(wallets[idx]) + wal[0] = new_name + # it will become list after JSON encode/decode anyways + wallets[idx] = wal + msc.name = new_name + settings.set("miniscript", wallets) + + # update label in sub-menu + menu.items[0].label = new_name + # and name in parent menu too + parent = the_ux.parent_of(menu) + if parent: + parent.update_contents() + +async def miniscript_wallet_detail(menu, label, item): + # show details of single multisig wallet + msc = item.arg + return await msc.show_detail() + +async def import_miniscript(*a): + # pick text file from SD card, import as multisig setup file + from actions import file_picker + from ux import import_export_prompt + + ch = await import_export_prompt("miniscript wallet file", is_import=True) + if isinstance(ch, str): + if ch == KEY_QR: + await import_miniscript_qr() + elif ch == KEY_NFC: + await import_miniscript_nfc() + return + + def possible(filename): + with open(filename, 'rt') as fd: + for ln in fd: + if "sh(" in ln or "wsh(" in ln or "tr(" in ln: + # descriptor import + return True + + fn = await file_picker(suffix=['.txt', '.json'], min_size=100, + taster=possible, **ch) + if not fn: return + + try: + with CardSlot(**ch) as card: + with open(fn, 'rt') as fp: + data = fp.read() + except CardMissingError: + await needs_microsd() + return + + from auth import maybe_enroll_xpub + try: + possible_name = (fn.split('/')[-1].split('.'))[0] if fn else None + maybe_enroll_xpub(config=data, name=possible_name) + except BaseException as e: + await ux_show_story('Failed to import miniscript.\n\n%s\n%s' % (e, problem_file_line(e))) + +async def import_miniscript_nfc(*a): + from glob import NFC + try: + return await NFC.import_miniscript_nfc() + except Exception as e: + await ux_show_story('Failed to import miniscript.\n\n%s\n%s' % (e, problem_file_line(e))) + +async def import_miniscript_qr(*a): + from auth import maybe_enroll_xpub + from ux_q1 import QRScannerInteraction + data = await QRScannerInteraction().scan_text('Scan Multisig/Miniscript from a QR code') + if not data: + # press pressed CANCEL + return + try: + maybe_enroll_xpub(config=data) + except Exception as e: + await ux_show_story('Failed to import miniscript.\n\n%s\n%s' % (e, problem_file_line(e))) + +async def miniscript_wallet_export(menu, label, item): + # create a text file with the details; ready for import to next Coldcard + msc = item.arg[0] + kwargs = item.arg[1] + await msc.export_wallet_file(**kwargs) + +async def miniscript_wallet_descriptors(menu, label, item): + # descriptor menu + msc = item.arg + if not msc: + return + + rv = [ + MenuItem('Export', f=miniscript_wallet_export, arg=(msc, {"core": False})), + MenuItem('Bitcoin Core', f=miniscript_wallet_export, arg=(msc, {"core": True})), + MenuItem('BIP-388 Policy', f=miniscript_wallet_export, arg=(msc, {"bip388":True})), + ] + return rv + +async def miniscript_sign_psbt(a, b, item): + from actions import _ready2sign + await _ready2sign(probe=False, miniscript_wallet=item.arg) + +async def make_miniscript_wallet_menu(menu, label, item): + # details, actions on single multisig wallet + idx, msc = item.arg + + rv = [ + MenuItem('"%s"' % msc.name, f=miniscript_wallet_detail, arg=msc), + MenuItem('View Details', f=miniscript_wallet_detail, arg=msc), + MenuItem('Descriptors', menu=miniscript_wallet_descriptors, arg=msc), + MenuItem('Sign PSBT', f=miniscript_sign_psbt, arg=msc), + MenuItem('Rename', f=miniscript_wallet_rename, arg=(idx, msc)), + MenuItem('Delete', f=miniscript_wallet_delete, arg=msc), + ] + if msc.m_n and msc.bip67: + # basic multisig but only sortedmulti + rv.append(MenuItem('Electrum Wallet', f=multisig_electrum_export, arg=msc)) + + return rv + + +class MiniscriptMenu(MenuSystem): + @classmethod + def construct(cls): + import version + from menu import ShortcutItem + from bsms import make_ms_wallet_bsms_menu + from multisig import create_ms_step1 + + rv = [] + for i, msc in enumerate(MiniScriptWallet.iter_wallets()): + rv.append(MenuItem('%s' % msc.name, menu=make_miniscript_wallet_menu, arg=(i,msc))) + + rv = rv or [MenuItem("(none setup yet)")] + + from glob import NFC + rv.append(MenuItem('Import', f=import_miniscript)) + rv.append(MenuItem('Export XPUB', f=export_miniscript_xpubs)) + rv.append(MenuItem('BSMS (BIP-129)', menu=make_ms_wallet_bsms_menu)) + rv.append(MenuItem('Create Airgapped', f=create_ms_step1)) + rv.append(MenuItem('Trust PSBT?', f=trust_psbt_menu)) + rv.append(MenuItem('Skip Checks?', f=disable_checks_menu)) + rv.append(ShortcutItem(KEY_NFC, predicate=lambda: NFC is not None, + f=import_miniscript_nfc)) + rv.append(ShortcutItem(KEY_QR, predicate=lambda: version.has_qwerty, + f=import_miniscript_qr)) + return rv + + def update_contents(self): + # Reconstruct the list of wallets on this dynamic menu, because + # we added or changed them and are showing that same menu again. + tmp = self.construct() + self.replace_items(tmp) + +async def make_miniscript_menu(*a): + # list of all multisig wallets, and high-level settings/actions + from pincodes import pa + + if pa.is_secret_blank(): + await ux_show_story("You must have wallet seed before creating miniscript wallets.") + return + + # 6.4.0 multisig migration is done in login_sequence + # this is duplicate for users that have multisig wallets stored in tmp seed settings + # executes upon entry to "Multisig/Miniscript" menu + await do_640_multisig_migration() + + rv = MiniscriptMenu.construct() + return MiniscriptMenu(rv) + + +def disable_checks_chooser(): + ch = ['Normal', 'Skip Checks'] + + def xset(idx, text): + MiniScriptWallet.disable_checks = bool(idx) + + return int(MiniScriptWallet.disable_checks), ch, xset + +async def disable_checks_menu(*a): + + if not MiniScriptWallet.disable_checks: + ch = await ux_show_story('''\ +With many different wallet vendors and implementors involved, it can \ +be hard to create a PSBT consistent with the many keys involved. \ +With this setting, you can \ +disable the more stringent verification checks your Coldcard normally provides. + +USE AT YOUR OWN RISK. These checks exist for good reason! Signed txn may \ +not be accepted by network. + +This settings lasts only until power down. + +Press (4) to confirm entering this DANGEROUS mode. +''', escape='4') + + if ch != '4': return + + start_chooser(disable_checks_chooser) + + +def psbt_xpubs_policy_chooser(): + # Chooser for trust policy + ch = ['Verify Only', 'Offer Import', 'Trust PSBT'] + + def xset(idx, text): + settings.set('pms', idx) + + return MiniScriptWallet.get_trust_policy(), ch, xset + +async def trust_psbt_menu(*a): + # show a story then go into chooser + + ch = await ux_show_story('''\ +This setting controls what the Coldcard does \ +with the co-signer public keys (XPUB) that may \ +be provided inside a PSBT file. Three choices: + +- Verify Only. Do not import the xpubs found, but do \ +verify the correct wallet already exists on the Coldcard. + +- Offer Import. If it's a new multisig wallet, offer to import \ +the details and store them as a new wallet in the Coldcard. + +- Trust PSBT. Use the wallet data in the PSBT as a temporary, +multisig wallet, and do not import it. This permits some \ +deniability and additional privacy. + +When the XPUB data is not provided in the PSBT, regardless of the above, \ +we require the appropriate multisig wallet to already exist \ +on the Coldcard. Default is to 'Offer' unless a multisig wallet already \ +exists, otherwise 'Verify'.''') + + if ch == 'x': return + start_chooser(psbt_xpubs_policy_chooser) + + +async def multisig_electrum_export(menu, label, item): + # create a JSON file that Electrum can use. Challenges: + # - file contains derivation paths for each co-signer to use + # - electrum is using BIP-43 with purpose=48 (purpose48_derivation) to make paths like: + # m/48h/1h/0h/2h + # - above is now called BIP-48 + # - other signers might not be coldcards (we don't know) + # solution: + # - when building air-gap, pick address type at that point, and matching path to suit + # - could check path prefix and addr_fmt make sense together, but meh. + msc = item.arg + await msc.export_electrum() + + +async def export_miniscript_xpubs(*a, xfp=None, alt_secret=None, skip_prompt=False): + # WAS: Create a single text file with lots of docs, and all possible useful xpub values. + # THEN: Just create the one-liner xpub export value they need/want to support BIP-45 + # NOW: Export JSON with one xpub per useful address type and semi-standard derivation path + # + # - consumer for this file is supposed to be ourselves, when we build on-device multisig. + # - however some 3rd parties are making use of it as well. + # - used for CCC feature now as well, but result looks just like normal export + # + xfp = xfp2str(xfp or settings.get('xfp', 0)) + chain = chains.current_chain() + + fname_pattern = 'ccxp-%s.json' % xfp + label = "Multisig XPUB" + + if not skip_prompt: + msg = '''\ +This feature creates a small file containing \ +the extended public keys (XPUB) you would need to join \ +a multisig wallet. + +Public keys for BIP-48 conformant paths are used: + +P2SH-P2WSH: + m/48h/{coin}h/{{acct}}h/1h +P2WSH: + m/48h/{coin}h/{{acct}}h/2h +P2TR: + m/48h/{coin}h/{{acct}}h/3h + +{ok} to continue. {x} to abort.'''.format(coin=chain.b44_cointype, ok=OK, x=X) + + ch = await ux_show_story(msg) + if ch != "y": + return + + acct = await ux_enter_bip32_index('Account Number:') or 0 + + def render(acct_num): + sign_der = None + with uio.StringIO() as fp: + fp.write('{\n') + with stash.SensitiveValues(secret=alt_secret) as sv: + for name, deriv, fmt in chains.MS_STD_DERIVATIONS: + if fmt == AF_P2SH and acct_num: + continue + dd = deriv.format(coin=chain.b44_cointype, acct_num=acct_num) + if fmt == AF_P2WSH: + sign_der = dd + "/0/0" + node = sv.derive_path(dd) + xp = chain.serialize_public(node, fmt) + fp.write(' "%s_deriv": "%s",\n' % (name, dd)) + fp.write(' "%s": "%s",\n' % (name, xp)) + xpub = chain.serialize_public(node) + fp.write(' "%s_key_exp": "%s",\n' % (name, "[%s/%s]%s" % (xfp, dd.replace("m/", ""), xpub))) + + fp.write(' "account": "%d",\n' % acct_num) + fp.write(' "xfp": "%s"\n}\n' % xfp) + return fp.getvalue(), sign_der, AF_CLASSIC + + from export import export_contents + await export_contents(label, lambda: render(acct), fname_pattern, + force_bbqr=True, is_json=True) + +## MIGRATION === + +def miniscript_640_migrate(old_serialization): + from ubinascii import unhexlify as a2b_hex + from ucollections import OrderedDict + from desc_utils import PROVABLY_UNSPENDABLE + + def remove_subderivation(str_key): + # find the end of origin derivation + orig_der_end = str_key.find(']') + if orig_der_end != -1: + orig_der = str_key[:orig_der_end + 1] + rest = str_key[orig_der_end + 1:] + else: + orig_der = "" + rest = str_key + + rest_split = rest.split("/") + subder = "/%s" % "/".join(rest_split[1:]) + return orig_der + rest_split[0], subder + + # last 4 members are irrelevant + name, ct, af, key, keys, policy, _, _, _, _ = old_serialization + + # standardize policy according to BIP-388 + policy = policy.replace("/<0;1>/*", "/**") + + # P2TR problem - policy here does not contain internal key (key) + # therefore numbering is wrong - needs to be x+1 to make place for internal key + # problem - keys can be duplicates with just subderivation different + + # deduplicate keys to become origin keys + keys = list(OrderedDict([(remove_subderivation(k)[0], None) for k in keys]).keys()) + + if key: + # taproot internal key + # will always be @0 + # need to check if this key is not already in policy somewhere + if "unspend(" in key: + # this is no longer supported - need to convert to xpub + end = key.find(")") + chain_code_str = key[8:end] + ik_u = True + ik_subder = key[end+1:] + n = ngu.hdnode.HDNode() + n.from_chaincode_pubkey(a2b_hex(chain_code_str), PROVABLY_UNSPENDABLE) + ik_key = chains.current_chain().serialize_public(n) + else: + ik_key, ik_subder = remove_subderivation(key) + ik_u = Key.from_string(ik_key).is_provably_unspendable + + if ik_subder == "/<0;1>/*": + ik_subder = "/**" + + # internal key can be used in script tree & can already be at its correct position + # i.e. first in the keys vector + ik_pos_incorrect = int(ik_key != keys[0]) + + keys_info = [] + for i in range(len(keys) - 1, -1, -1): + ph = "@%d" % i + assert policy.find(ph) != -1 + + res_key = keys[i] + + if af == AF_P2TR: + # to make space for internal key in policy we need to bump placeholder + if res_key == ik_key: + # this origin key is the same as internal key + # so it is @0 + policy = policy.replace(ph, "@0") + continue # no need to insert - will do later + else: + policy = policy.replace(ph, "@%d" % (i + ik_pos_incorrect)) + + keys_info.insert(0, res_key) + + new_opts = {"af": af} + + # policy in old version lacks script type + if af == AF_P2TR: + # handle internal key + keys_info.insert(0, ik_key) + desc_tmplt = "tr(@0%s,%s)" % (ik_subder, policy) + new_opts["ik_u"] = ik_u + + elif af == AF_P2WSH: + desc_tmplt = "wsh(" + policy + ")" + elif af == AF_P2WSH_P2SH: + desc_tmplt = "sh(wsh(" + policy + "))" + else: + desc_tmplt = "sh(" + policy + ")" + + if ct != "BTC": + new_opts['ct'] = ct + + # previous version had unbounded names, cut it + return name[:MAX_NAME_LEN], desc_tmplt, keys_info, new_opts + + +async def multisig_640_migration(multisig_wallets): + # all MultisigWallet needs to be converted to MiniscriptWallet + # this function just returns new list of migrated multisig wallets without + # changing any persisted settings data + from glob import dis + dis.fullscreen("Migrating...") + total = len(multisig_wallets) + + migrated_multi = [] + # first element is always name, whether migrated or not + # shorten to MAX_NAME_LEN that will be done to miniscript names upon migration + taken_names = [tup[0][:MAX_NAME_LEN] for tup in settings.get("miniscript", [])] + for i, ms in enumerate(multisig_wallets): + bip67 = 1 # default enabled, requires 5-element serialization to disable + if len(ms) == 5: + bip67 = ms[-1] + ms = ms[:-1] + + name, m_of_n, xpubs, opts = ms + ct = opts.get('ch', 'BTC') + af = opts.get('ft', AF_P2SH) + + if len(xpubs[0]) == 2: + common_prefix = opts.get('pp', None) + if not common_prefix: + common_prefix = 'm' + common_prefix = common_prefix.replace("'", "h") + xpubs = [(a, common_prefix, b) for a, b in xpubs] + else: + # new format decompression + if 'd' in opts: + derivs = [p.replace("'", "h") for p in opts.get('d')] + xpubs = [(a, derivs[b], c) for a, b, c in xpubs] + + keys_info = [] + for mfp, der, ek in xpubs: + xfp = xfp2str(mfp).lower() + if der == "m": + keys_info.append("[%s]%s" % (xfp, ek)) + else: + keys_info.append("[%s/%s]%s" % (xfp, der.replace("m/", ""), ek)) + + ms_type = "sortedmulti" if bip67 else "multi" + if af == AF_P2WSH: + desc_tmplt = "wsh(" + ms_type + "(%s))" + elif af == AF_P2WSH_P2SH: + desc_tmplt = "sh(wsh(" + ms_type + "(%s)))" + else: + desc_tmplt = "sh(" + ms_type + "(%s))" + + M, N = m_of_n + inner = "%d,%s" % (M, ",".join(["@%d/**" % i for i in range(M)])) + desc_tmplt = desc_tmplt % inner + + new_opts = { + "af": af, + "m_n": (M, N), + "b67": bip67 + } + if ct != "BTC": + new_opts['ct'] = ct + + # this should not happen as multisg names were limited to 20 chars max + name = name[:MAX_NAME_LEN] + if name in taken_names: + # name collision with miniscript + while name in taken_names: + suffix = str(ngu.random.uniform(100)) + if (len(name) + len(suffix)) > MAX_NAME_LEN: + # issue + name = name[:MAX_NAME_LEN-len(suffix)] + + name = name + suffix + + migrated_multi.append((name, desc_tmplt, keys_info, new_opts)) + dis.progress_sofar(i+1, total) + + return migrated_multi +async def do_640_multisig_migration(): + if not settings.get("multi_mig", 0): + ms = settings.get("multisig") + if ms: + # in version 6.4.0 EDGE + # MultisigWallet was removed & multisigs are now part of miniscript + migrated = await multisig_640_migration(ms) + msc = settings.get("miniscript", []) + settings.set("miniscript", msc + migrated) + # settings.remove_key("multisig") + settings.set("multi_mig", 1) + settings.save() # EOF diff --git a/shared/web2fa.py b/shared/web2fa.py new file mode 100644 index 000000000..b78fc1ddb --- /dev/null +++ b/shared/web2fa.py @@ -0,0 +1,176 @@ +# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# web2fa.py -- Bounce a shared secret off a Coinkite server to allow mobile app 2FA. +# +# +import ngu, ndef, aes256ctr +from utils import b2a_base64url, url_quote, B2A +from version import has_qr +from ux import show_qr_code, ux_show_story, X + +# Only Coldcard.com server knows private key for this pubkey. It protects +# the privacy of the values we send to the server. +# +# = 0231301ec4acec08c1c7d0181f4ffb8be70d693acccc86cccb8f00bf2e00fcabfd +SERVER_PUBKEY = b'\x02\x31\x30\x1e\xc4\xac\xec\x08\xc1\xc7\xd0\x18\x1f\x4f\xfb\x8b\xe7\x0d\x69\x3a\xcc\xcc\x86\xcc\xcb\x8f\x00\xbf\x2e\x00\xfc\xab\xfd' + +def encrypt_details(qs): + # encryption and base64 here + # - pick single-use ephemeral secp256k1 keypair + # - do ECDH to generate a shared secret based on known pubkey of server + # - AES-256-CTR encryption based on that + # - base64url encode result + + # pick a random key pair, just for this session + pair = ngu.secp256k1.keypair() + my_pubkey = pair.pubkey().to_bytes(False) # compressed format + + session_key = pair.ecdh_multiply(SERVER_PUBKEY) + del pair + + enc = aes256ctr.new(session_key).cipher + + return b2a_base64url(my_pubkey + enc(qs.encode('ascii'))) + +async def perform_web2fa(label, shared_secret): + + # send them to web, prompt for valid response. Return True if it all worked. + expect = await nfc_share_2fa_link(label, shared_secret) + if not expect: + # aborted at NFC step + return False + + if has_qr: + # Make them scan the result, for example: + # + # CCC-AUTH:E902B3DAF2D98040F3A5F556D7CCC7C22BF3D455C146C4D4C0F7CF8B7937C530 + # + from ux_q1 import QRScannerInteraction + from exceptions import QRDecodeExplained + + prefix = 'CCC-AUTH:' + scanner = QRScannerInteraction() + + def validate(got): + if not got.startswith(prefix): + raise QRDecodeExplained("QR isn't from our site") + if got != prefix+expect: + # probably attempted replay + raise QRDecodeExplained("Incorrect code?") + return got + + data = await scanner.scan_general('Scan QR shown from Web', validate) + if not data: + return False # pressed cancel + + # only one legal response possible, and already validated above + return data == (prefix+expect) + + else: + # + # Mk4 and other devices w/o QR scanner, require user to enter 8 digits + # + from ux_mk4 import ux_input_digits + + while 1: + got = await ux_input_digits('', maxlen=8, + prompt="8-digits From Web") + + if not got: + # abort if empty entry + return False + + if got == expect: + # good match + return True + + ch = await ux_show_story("You entered an incorrect code. You must" + " enter the digits shown after the correct" + " 2FA code is provided to the website." + " Try again or %s to stop." % X) + if ch == 'x': + return False + + # not reached + return False + + +async def web2fa_enroll(ss=None): + # + # Enroll: Pick a secret and test they have loaded it into their phone. + # + + # must have NFC tho + from flow import feature_requires_nfc + if not await feature_requires_nfc(): + # they don't want to proceed + return None + + # Pick a shared secret; 10 bytes, so encodes to 16 base32 chars + ss = ss or ngu.codecs.b32_encode(ngu.random.bytes(10)) + + # show a QR that app know how to use + # - problem: on Mk4, not really enough space: + # - can only show up to 42 chars, and secret is 16, required overhead is 23 => 39 min + # - can't fit any metadata, like username or our serial # in there + # - better on Q1 where no limitations for this size of QR + + nm = 'COLDCARD' if has_qr else 'CC' # must be url-safe + qr = 'otpauth://totp/{nm}?secret={ss}'.format(ss=ss, nm=nm) + + while 1: + # show QR for enroll + await show_qr_code(qr, is_alnum=False, msg="Import into 2FA Mobile App", + force_msg=True) + + # important: force them to prove they stored it correctly + ok = await perform_web2fa('Enroll: COLDCARD', ss) + if ok: break + + ch = await ux_show_story("That isn't correct. Please re-import and/or " + "try again or %s to give up." % X) + if ch == 'x': + return None + + return ss + +def make_web2fa_url(wallet_name, shared_secret): + # Build complex URL into our server w/ encrypted data + # - picking a nonce in the process + prefix = 'coldcard.com/2fa?' + + # random nonce: if we get this back, then server approves of TOTP answer + if has_qr: + # data for a QR + nonce = B2A(ngu.random.bytes(32)).upper() + else: + # 8 digits for human entry + nonce = '%08d' % ngu.random.uniform(1_0000_0000) + + # compose URL + qs = 'g=%s&ss=%s&nm=%s&q=%d' % (nonce, shared_secret, url_quote(wallet_name), has_qr) + + # encrypt that + qs = encrypt_details(qs) + + return nonce, prefix + qs + +async def nfc_share_2fa_link(wallet_name, shared_secret): + # + # Share complex NFC deeplink into 2fa backend; returns expected response-code. + # Next step is to prompt for that 8-digit code (mk4) or scan QR (Q) + # + from glob import NFC + assert NFC + + nonce, url = make_web2fa_url(wallet_name, shared_secret) + + n = ndef.ndefMaker() + n.add_url(url, https=True) + + aborted = await NFC.share_start(n, prompt="Tap for 2FA Authentication", + line2="Wallet: " + wallet_name) + + return None if aborted else nonce + +# EOF diff --git a/shared/xor_seed.py b/shared/xor_seed.py index deb7ab82a..0b2663c70 100644 --- a/shared/xor_seed.py +++ b/shared/xor_seed.py @@ -5,29 +5,16 @@ # - for secret spliting on paper # - all combination of partial XOR seed phrases are working wallets # -import stash, ngu, bip39, version +import ngu, bip39, version from ux import ux_show_story, the_ux, ux_confirm, ux_dramatic_pause from ux import show_qr_code, ux_render_words, OK -from seed import word_quiz, WordNestMenu, set_seed_value, set_ephemeral_seed +from seed import word_quiz, WordNestMenu, set_seed_value, set_ephemeral_seed, seed_vault_iter from glob import settings from menu import MenuSystem, MenuItem from actions import goto_top_menu -from utils import encode_seed_qr, pad_raw_secret +from utils import encode_seed_qr, deserialize_secret, xor from charcodes import KEY_QR - - -def xor(*args): - # bit-wise xor between all args - vlen = len(args[0]) - # all have to be same length - assert all(len(e) == vlen for e in args) - rv = bytearray(vlen) - - for i in range(vlen): - for a in args: - rv[i] ^= a[i] - - return rv +from stash import SecretStash, blank_object, SensitiveValues, numwords_to_len, len_to_numwords async def xor_split_start(*a): @@ -69,7 +56,7 @@ async def xor_split_start(*a): raw_secret = bytes(32) try: - with stash.SensitiveValues() as sv: + with SensitiveValues(enforce_delta=True) as sv: words = None if sv.mode == 'words': words = bip39.b2a_words(sv.raw).split(' ') @@ -77,7 +64,7 @@ async def xor_split_start(*a): # checksum of target result is useful chk_word = words[-1] - vlen = stash.numwords_to_len(len(words)) + vlen = numwords_to_len(len(words)) del words @@ -101,7 +88,7 @@ async def xor_split_start(*a): assert xor(*parts) == raw_secret # selftest finally: - stash.blank_object(raw_secret) + blank_object(raw_secret) word_parts = [bip39.b2a_words(p).split(' ') for p in parts] @@ -112,7 +99,8 @@ async def xor_split_start(*a): if await ux_confirm("Stop and forget those words?"): return continue - if ch == KEY_QR: + + if ch in "4"+KEY_QR: qrs = [] for wl in word_parts: qrs.append(encode_seed_qr(wl)) @@ -133,20 +121,22 @@ async def xor_split_start(*a): You have confirmed the details of the new split.''') # list of seed phrases -# stores encoded secret bytes (not word lists) +# - stores encoded secret bytes (not word lists) import_xor_parts = [] async def xor_all_done(data): # So we have another part, might be done or not. global import_xor_parts + chk_words = None + if data is None: # special case, needs something already in import_xor_parts - target_words = stash.len_to_numwords(len(import_xor_parts[0])) + target_words = len_to_numwords(len(import_xor_parts[0])) else: new_encoded = bip39.a2b_words(data) if isinstance(data, list) else data import_xor_parts.append(new_encoded) - target_words = stash.len_to_numwords(len(new_encoded)) + target_words = len_to_numwords(len(new_encoded)) XORWordNestMenu.pop_all() @@ -157,7 +147,7 @@ async def xor_all_done(data): if num_parts >= 2: chk_words = bip39.b2a_words(seed).split(' ') chk_word = chk_words[-1] - msg += "If you stop now, the %dth word of the XOR-combined seed phrase\nwill be:\n\n" % target_words + msg += "If you stop now, the %dth word of the XOR-combined seed phrase will be:\n\n" % target_words msg += "%d: %s\n\n" % (target_words, chk_word) if all((not x) for x in seed): @@ -181,7 +171,7 @@ async def xor_all_done(data): import_xor_parts.clear() # concern: we are contaminated w/ secrets elif chk_words and ch == KEY_QR: rv = encode_seed_qr(chk_words) - await show_qr_code(rv, True, msg="SeedQR") + await show_qr_code(rv, True, msg="SeedQR", is_secret=True) continue elif ch == '1': # do another list of words @@ -198,7 +188,7 @@ async def xor_all_done(data): from pincodes import pa from glob import dis - enc = stash.SecretStash.encode(seed_phrase=seed) + enc = SecretStash.encode(seed_phrase=seed) if pa.is_secret_blank(): # save it since they have no other secret @@ -212,17 +202,15 @@ async def xor_all_done(data): # only need XFPs for UI # xfps = [ # xfp2str(swab32( - # stash.SecretStash.decode(stash.SecretStash.encode(seed_phrase=i))[2].my_fp() + # SecretStash.decode(SecretStash.encode(seed_phrase=i))[2].my_fp() # )) # for i in enc_parts # ] - await set_ephemeral_seed( - enc, - meta='SeedXOR(%d parts, check: "%s")' % ( - num_parts, chk_word - ) - ) + await set_ephemeral_seed(enc, + origin='SeedXOR(%d parts, check: "%s")' % (num_parts, chk_word)) + goto_top_menu() + break class XORWordNestMenu(WordNestMenu): @@ -234,29 +222,33 @@ def tr_label(self): async def show_n_parts(parts, chk_word): num_parts = len(parts) seed_len = len(parts[0]) - msg = 'Record these %d lists of %d-words each:' % (num_parts, seed_len) + msg = '%d lists of %d-words each:' % (num_parts, seed_len) for n,words in enumerate(parts): msg += '\n\nPart %s:\n' % chr(65+n) - msg += ux_render_words(words, leading_blanks=0) + msg += ux_render_words(words) msg += ('\n\nThe correctly reconstructed seed phrase will have this final word,' ' which we recommend recording:\n\n%d: %s\n\n' % (seed_len, chk_word)) msg += 'Please check and double check your notes. There will be a test! ' + if not version.has_qwerty: + msg += 'Press (4) to view QR Codes. ' - return await ux_show_story(msg, sensitive=True) + # allow QR codes on both Mk4 & Q + return await ux_show_story(msg, title="Record these:", sensitive=True, escape="4", + hint_icons=KEY_QR) async def xor_restore_start(*a): # shown on import menu when no seed of any kind yet # - or operational system ch = await ux_show_story('''\ -To import a seed split using XOR, you must import all the parts. -It does not matter the order (A/B/C or C/A/B) and the Coldcard -cannot determine when you have all the parts. You may stop at -any time and you will have a valid wallet. Combined seed parts -have to be equal length. No way to combine seed parts of different -length. Press %s for 24 words XOR, press (1) for 12 words XOR, +To import a seed split using XOR, you must import all the parts. \ +It does not matter the order (A/B/C or C/A/B) and the Coldcard \ +cannot determine when you have all the parts. You may stop at \ +any time and you will have a valid wallet. Combined seed parts \ +have to be equal length.\n +Press %s for 24 words XOR, press (1) for 12 words XOR, \ or press (2) for 18 words XOR.''' % OK, escape="12") if ch == 'x': return @@ -266,8 +258,6 @@ async def xor_restore_start(*a): elif ch == "2": desired_num_words = 18 - curr_num_words = settings.get('words', desired_num_words) - global import_xor_parts import_xor_parts.clear() @@ -279,17 +269,22 @@ async def xor_restore_start(*a): msg = ("Since you have a seed already on this Coldcard, the reconstructed XOR seed will be " "temporary and not saved. Wipe the seed first if you want to commit the new value " "into the secure element.") - if curr_num_words == desired_num_words: + + curr_num_words = settings.get('words', desired_num_words) + if (curr_num_words == desired_num_words) and not pa.hobbled_mode: escape += "1" - msg += ("\nPress (1) to include this Coldcard's seed words into the XOR seed set, " + msg += ("\n\nPress (1) to include this Coldcard's seed words into the XOR seed set, " "or %s to continue without." % OK) ch = await ux_show_story(msg, escape=escape) - if ch == 'x': return + if ch == 'x': + return + if ch == '1': + assert not pa.hobbled_mode dis.fullscreen("Wait...") - with stash.SensitiveValues() as sv: + with SensitiveValues(enforce_delta=True) as sv: if sv.mode == 'words': # needs copy here [:] otherwise rewritten with zeros in __exit__ import_xor_parts.append(sv.raw[:]) @@ -297,17 +292,22 @@ async def xor_restore_start(*a): # Add from Seed Vault? # filter only those that are correct length and type from seed vault opt = [] - for i, (xfp_str, hex_str, _, _) in enumerate(settings.master_get("seeds", [])): - raw = pad_raw_secret(hex_str) - if raw[0] & 0x80: - # seed phrase - sk = raw[1:1 + stash.len_from_marker(raw[0])] - if stash.len_to_numwords(len(sk)) == desired_num_words: - opt.append((i, xfp_str, sk)) + for i, rec in enumerate(seed_vault_iter()): + raw = deserialize_secret(rec.encoded) + + nw = SecretStash.is_words(raw) + if nw and nw == desired_num_words: + # it is words, and right length + sk = SecretStash.decode_words(raw, bin_mode=True) + opt.append((i, rec.xfp, sk)) + + blank_object(raw) + if opt: escape = "2" msg = ("Seed Vault is enabled. %d stored seeds have suitable type and length." - "\n\nPress (2) to add from Seed Vault, press %s to continue normally.") % (len(opt), OK) + "\n\nPress (2) to add from Seed Vault and then (1) to select seeds," + " press %s to continue normally.") % (len(opt), OK) ch = await ux_show_story(msg, escape=escape) if ch == 'x': return if ch == "2": diff --git a/shared/zevvpeep.py b/shared/zevvpeep.py index 624bd5eec..be67f32d8 100644 --- a/shared/zevvpeep.py +++ b/shared/zevvpeep.py @@ -36,8 +36,8 @@ class FontSmall(FontBase): _bboxes = [None, (0, -3, 7, 14, 0), (0, -3, 7, 14, 4), (0, -3, 7, 14, 5), (0, -3, 7, 14, 7), (0, -3, 7, 14, 9), (0, -3, 7, 14, 10), (0, -3, 7, 14, 11), (0, -3, 7, 14, 12), (0, -3, 7, 14, 13), (0, -3, - 7, 14, 14), (0, 0, 8, 8, 8), (0, 0, 11, 8, 16), (0, 0, 11, 9, 18), - (0, 0, 14, 10, 20)] + 7, 14, 14), (0, 0, 5, 2, 2), (0, 0, 8, 8, 8), (0, 0, 11, 8, 16), (0, + 0, 11, 9, 18), (0, 0, 14, 10, 20)] _code_points = [ (range(32, 127), [1, 2, 14, 20, 31, 43, 55, 67, 73, 87, 101, 111, 122, @@ -48,11 +48,11 @@ class FontSmall(FontBase): 755, 767, 779, 791, 803, 815, 827, 842, 854, 866, 880, 892, 904, 916, 928, 940, 954, 968, 980, 992, 1004, 1016, 1028, 1040, 1052, 1067, 1079, 1093, 1106, 1120]), -(range(8226, 8227), [1126]), # • -(range(8592, 8593), [1145]), # ← -(range(8594, 8595), [1166]), # → -(range(8627, 8628), [1187]), # ↳ -(range(8943, 8944), [1208]), # ⋯ +(range(8201, 8202), [1126]), # +(range(8226, 8227), [1129]), # • +(range(8592, 8595), [1148, 0, 1169]), # ← → +(range(8627, 8628), [1190]), # ↳ +(range(8943, 8944), [1211]), # ⋯ ] _bitmaps = b"""\ @@ -63,7 +63,7 @@ class FontSmall(FontBase): \x10\x09\x04\x08\x10\x10\x20\x20\x20\x20\x20\x10\x10\x08\x04\x09\x20\x10\ \x08\x08\x04\x04\x04\x04\x04\x08\x08\x10\x20\x05\x00\x00\x00\x00\x24\x18\ \x7e\x18\x24\x06\x00\x00\x00\x08\x08\x08\x3e\x08\x08\x08\x09\x00\x00\x00\ -\x00\x00\x00\x00\x00\x00\x18\x30\x20\x40\x0b\x00\x00\x00\x00\x00\x00\x3e\ +\x00\x00\x00\x00\x00\x00\x18\x30\x20\x40\x0c\x00\x00\x00\x00\x00\x00\x3e\ \x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x38\x10\x08\x02\x02\x04\ \x04\x08\x08\x10\x10\x20\x20\x40\x40\x07\x00\x18\x24\x42\x42\x4a\x52\x42\ \x42\x24\x18\x07\x00\x08\x18\x28\x48\x08\x08\x08\x08\x08\x08\x07\x00\x3c\ @@ -118,13 +118,13 @@ class FontSmall(FontBase): \x3a\x02\x02\x42\x3c\x07\x00\x00\x00\x00\x7e\x02\x04\x08\x10\x20\x7e\x09\ \x06\x08\x08\x08\x08\x08\x30\x08\x08\x08\x08\x08\x06\x08\x10\x10\x10\x10\ \x10\x10\x10\x10\x10\x10\x10\x10\x09\x60\x10\x10\x10\x10\x10\x0c\x10\x10\ -\x10\x10\x10\x60\x03\x00\x00\x32\x4a\x44\x0d\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x03\x80\x03\x80\x03\x80\x00\x00\x0e\x00\x00\x00\x00\x00\x00\ -\x00\x00\x08\x00\x18\x00\x3f\xf8\x18\x00\x08\x00\x00\x00\x0e\x00\x00\x00\ -\x00\x00\x00\x00\x00\x00\x20\x00\x30\x3f\xf8\x00\x30\x00\x20\x00\x00\x0e\ -\x00\x00\x10\x00\x10\x00\x10\x00\x10\x20\x10\x30\x1f\xf8\x00\x30\x00\x20\ -\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2a\xa0\x00\ -\x00\ +\x10\x10\x10\x60\x03\x00\x00\x32\x4a\x44\x0b\x00\x00\x0e\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x03\x80\x03\x80\x03\x80\x00\x00\x0f\x00\x00\x00\ +\x00\x00\x00\x00\x00\x08\x00\x18\x00\x3f\xf8\x18\x00\x08\x00\x00\x00\x0f\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x30\x3f\xf8\x00\x30\x00\x20\ +\x00\x00\x0f\x00\x00\x10\x00\x10\x00\x10\x00\x10\x20\x10\x30\x1f\xf8\x00\ +\x30\x00\x20\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x2a\xa0\x00\x00\ """ diff --git a/stm32/.gitignore b/stm32/.gitignore index d1542d386..c7b33a6ae 100644 --- a/stm32/.gitignore +++ b/stm32/.gitignore @@ -9,7 +9,7 @@ firmware.lss firmware-signed.* firmware.elf file_time.c -*-RC1-coldcard.dfu +*-RC1-*.dfu RC2-*.dfu # somewhat useful binary snapshots diff --git a/stm32/COLDCARD_MK4/file_time.c b/stm32/COLDCARD_MK4/file_time.c index 6dde832ab..8cda6664c 100644 --- a/stm32/COLDCARD_MK4/file_time.c +++ b/stm32/COLDCARD_MK4/file_time.c @@ -1,13 +1,13 @@ -// (c) Copyright 2020-2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +// (c) Copyright 2020-2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. // // AUTO-generated. // -// built: 2024-09-12 -// version: 5.4.0 +// built: 2025-11-05 +// version: 6.4.0X // #include // this overrides ports/stm32/fatfs_port.c uint32_t get_fattime(void) { - return 0x592c2880UL; + return 0x5b653080UL; } diff --git a/stm32/COLDCARD_MK4/psramdisk.c b/stm32/COLDCARD_MK4/psramdisk.c index b1fc0d690..bcfa9ffc9 100644 --- a/stm32/COLDCARD_MK4/psramdisk.c +++ b/stm32/COLDCARD_MK4/psramdisk.c @@ -501,28 +501,31 @@ static void psram_init_vfs(fs_user_mount_t *vfs, bool readonly) { // psram_memset4() // - static inline void -psram_memset4(void *dest_addr, uint32_t value, uint32_t byte_len) + static void +psram_memset4(void *dest_addr, uint32_t byte_len) { // Fast, aligned, and bug-fixing memset // - PSRAM can starve the internal bus with too many writes, too fast // - leads to a weird crash where SRAM bus (at least) is locked up, but flash works + // - and/or just call w/ interrupts off for reliable non-crashing behaviour uint32_t *dest = (uint32_t *)dest_addr; for(; byte_len; byte_len-=4, dest++) { - *dest = value; - - asm("nop; nop; nop;"); // tested value, do not reduce + *dest = 0x12345678; } } +// mp_obj_t psram_wipe_and_setup() +// mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self) { // Erase and reformat filesystem // - you probably should unmount it, before calling this // Wipe contents for security. - psram_memset4(PSRAM_TOP_BASE, 0x12345678, BLOCK_SIZE * BLOCK_COUNT); + mp_uint_t before = disable_irq(); + psram_memset4(PSRAM_TOP_BASE, BLOCK_SIZE * BLOCK_COUNT); + enable_irq(before); // Build obj to handle blockdev protocol fs_user_mount_t vfs = {0}; diff --git a/stm32/COLDCARD_Q1/file_time.c b/stm32/COLDCARD_Q1/file_time.c index c4f85d15d..fcc45e124 100644 --- a/stm32/COLDCARD_Q1/file_time.c +++ b/stm32/COLDCARD_Q1/file_time.c @@ -1,13 +1,13 @@ -// (c) Copyright 2020-2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +// (c) Copyright 2020-2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. // // AUTO-generated. // -// built: 2024-09-12 -// version: 1.3.0Q +// built: 2025-11-05 +// version: 6.4.0QX // #include // this overrides ports/stm32/fatfs_port.c uint32_t get_fattime(void) { - return 0x592c0860UL; + return 0x5b653080UL; } diff --git a/stm32/MK4-Makefile b/stm32/MK4-Makefile index 02b543de9..609f34974 100644 --- a/stm32/MK4-Makefile +++ b/stm32/MK4-Makefile @@ -12,14 +12,14 @@ HW_MODEL = mk4 PARENT_MKFILE = MK4-Makefile # This is release of the bootloader that will be built into the factory.dfu -BOOTLOADER_VERSION = 3.2.0 +BOOTLOADER_VERSION = 3.2.1 BOOTLOADER_DIR = mk4-bootloader LATEST_RELEASE = $(shell ls -t1 ../releases/*-mk4-*.dfu | head -1) # Our version for this release. # - caution, the bootrom will not accept version < 3.0.0 -VERSION_STRING = 5.4.0 +VERSION_STRING = 6.4.0X # keep near top, because defined default target (all) include shared.mk diff --git a/stm32/Q1-Makefile b/stm32/Q1-Makefile index 323e8d431..23dd07857 100644 --- a/stm32/Q1-Makefile +++ b/stm32/Q1-Makefile @@ -10,13 +10,13 @@ HW_MODEL = q1 PARENT_MKFILE = Q1-Makefile # This is release of the bootloader that will be built into the factory.dfu -BOOTLOADER_VERSION = 1.0.4 +BOOTLOADER_VERSION = 1.1.0 BOOTLOADER_DIR = q1-bootloader LATEST_RELEASE = $(shell ls -t1 ../releases/*-q1-*.dfu | head -1) # Our version for this release. -VERSION_STRING = 1.3.0Q +VERSION_STRING = 6.4.0QX # Remove this closer to shipping. #$(warning "Forcing debug build") diff --git a/stm32/bootloader/README.md b/stm32/bootloader/README.md index 1b629de4d..13cbb4268 100644 --- a/stm32/bootloader/README.md +++ b/stm32/bootloader/README.md @@ -19,7 +19,7 @@ your key storage per-system unique. - the most helpful file here is `bootloader.lss` which is generated in build process -- using OpenOCD is prefered for lower level code like this (not GDB) +- using OpenOCD is preferred for lower level code like this (not GDB) - `stm32l4x.cpu arm disassemble 0x000008 10 thumb` is very helpful @@ -140,7 +140,7 @@ Mk4: ## Re-do Bag Number -- cannot writes ones, and then change flash cells; have to remain unprogrammed +- cannot write ones, and then change flash cells; have to remain unprogrammed dfu-util -d 0483:df11 -a 0 -s 0x0801c000:8192 -U pairing.bin diff --git a/stm32/mk4-bootloader/ae.c b/stm32/mk4-bootloader/ae.c index a55e34ccf..df632ed14 100644 --- a/stm32/mk4-bootloader/ae.c +++ b/stm32/mk4-bootloader/ae.c @@ -1726,6 +1726,7 @@ ae_read_config_byte(int offset) uint8_t tmp[4]; ae_read_config_word(offset, tmp); + // BUG: didnt check for failure, in which case we will return un-inited values return tmp[offset % 4]; } diff --git a/stm32/mk4-bootloader/pins.c b/stm32/mk4-bootloader/pins.c index bba8272d3..25ae42380 100644 --- a/stm32/mk4-bootloader/pins.c +++ b/stm32/mk4-bootloader/pins.c @@ -718,7 +718,8 @@ pin_login_attempt(pinAttempt_t *args) args->num_fails = 0; args->attempts_left = MAX_TARGET_ATTEMPTS; - if(check_all_zeros(slot.xdata, 32) || (slot.tc_flags & TC_WIPE)) { + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + if(check_all_zeros(slot.xdata, 32) || wipe) { args->state_flags |= PA_ZERO_SECRET; } diff --git a/stm32/mk4-bootloader/releases/3.2.1.txt b/stm32/mk4-bootloader/releases/3.2.1.txt new file mode 100644 index 000000000..8f3fdd6b6 --- /dev/null +++ b/stm32/mk4-bootloader/releases/3.2.1.txt @@ -0,0 +1,4 @@ +0904b790af34c8acd8e3156cd5b4e818ae09e93611e90c673a7953fec67802d0 bootloader.dfu +7c7acbb849d17721f9a53b613d631f8bb8ed3b49c2bf5e1a413511c7d9105775 bootloader.bin +e71a730d2025bfcc0bf334614c60022e8df3d847c7c6a53f172aace004d69553 bootloader.lss +3.2.1 time=20250415.090935 git=master@adcf2c8e diff --git a/stm32/mk4-bootloader/releases/3.2.1/bootloader.bin b/stm32/mk4-bootloader/releases/3.2.1/bootloader.bin new file mode 100644 index 000000000..964e8174b Binary files /dev/null and b/stm32/mk4-bootloader/releases/3.2.1/bootloader.bin differ diff --git a/stm32/mk4-bootloader/releases/3.2.1/bootloader.dfu b/stm32/mk4-bootloader/releases/3.2.1/bootloader.dfu new file mode 100644 index 000000000..13784b589 Binary files /dev/null and b/stm32/mk4-bootloader/releases/3.2.1/bootloader.dfu differ diff --git a/stm32/mk4-bootloader/releases/3.2.1/bootloader.lss b/stm32/mk4-bootloader/releases/3.2.1/bootloader.lss new file mode 100644 index 000000000..a67725ce3 --- /dev/null +++ b/stm32/mk4-bootloader/releases/3.2.1/bootloader.lss @@ -0,0 +1,34612 @@ + +bootloader.elf: file format elf32-littlearm + +Sections: +Idx Name Size VMA LMA File off Algn + 0 .text 0000ea48 08000000 08000000 00010000 2**8 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 1 .relocate 00000150 2009e000 0800ea48 0002e000 2**2 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 2 .bss 000002e8 2009e150 0800eb98 0002e150 2**2 + ALLOC + 3 .stack 00000800 2009e438 0800ee80 0002e150 2**0 + ALLOC + 4 .debug_info 0002bc2d 00000000 00000000 0002e150 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 5 .debug_abbrev 00005f7a 00000000 00000000 00059d7d 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 6 .debug_loc 00014618 00000000 00000000 0005fcf7 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 7 .debug_aranges 000010c8 00000000 00000000 0007430f 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 8 .debug_ranges 00002148 00000000 00000000 000753d7 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 9 .debug_macro 00032533 00000000 00000000 0007751f 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 10 .debug_line 0001de5f 00000000 00000000 000a9a52 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 11 .debug_str 0011cbb0 00000000 00000000 000c78b1 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 12 .comment 00000049 00000000 00000000 001e4461 2**0 + CONTENTS, READONLY + 13 .ARM.attributes 00000032 00000000 00000000 001e44aa 2**0 + CONTENTS, READONLY + 14 .debug_frame 000036f4 00000000 00000000 001e44dc 2**2 + CONTENTS, READONLY, DEBUGGING, OCTETS + +Disassembly of section .text: + +08000000 <_sfixed>: + 8000000: 200a0000 .word 0x200a0000 + 8000004: 080000b5 .word 0x080000b5 + 8000008: 0800001d .word 0x0800001d + 800000c: 0800001f .word 0x0800001f + 8000010: 08000021 .word 0x08000021 + 8000014: 08000023 .word 0x08000023 + 8000018: 08000025 .word 0x08000025 + +0800001c : + 800001c: be01 bkpt 0x0001 + +0800001e : + 800001e: be02 bkpt 0x0002 + +08000020 : + 8000020: be03 bkpt 0x0003 + +08000022 : + 8000022: be04 bkpt 0x0004 + +08000024 : + 8000024: be05 bkpt 0x0005 + 8000026: e7fe b.n 8000026 + +08000028 : + ... + 8000040: 08000305 .word 0x08000305 + +08000044 : + 8000044: 00000200 .word 0x00000200 + ... + 8000060: 20296328 .word 0x20296328 + 8000064: 79706f43 .word 0x79706f43 + 8000068: 68676972 .word 0x68676972 + 800006c: 30322074 .word 0x30322074 + 8000070: 322d3831 .word 0x322d3831 + 8000074: 20323230 .word 0x20323230 + 8000078: 43207962 .word 0x43207962 + 800007c: 6b6e696f .word 0x6b6e696f + 8000080: 20657469 .word 0x20657469 + 8000084: 2e636e49 .word 0x2e636e49 + 8000088: 0a200a20 .word 0x0a200a20 + 800008c: 73696854 .word 0x73696854 + 8000090: 61707320 .word 0x61707320 + 8000094: 66206563 .word 0x66206563 + 8000098: 7220726f .word 0x7220726f + 800009c: 21746e65 .word 0x21746e65 + 80000a0: 73754a20 .word 0x73754a20 + 80000a4: 42312074 .word 0x42312074 + 80000a8: 792f4354 .word 0x792f4354 + 80000ac: 2e726165 .word 0x2e726165 + 80000b0: 0a200a20 .word 0x0a200a20 + +080000b4 : + 80000b4: f000 f816 bl 80000e4 + 80000b8: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 80000bc: f04f 0100 mov.w r1, #0 + 80000c0: f04f 0200 mov.w r2, #0 + 80000c4: f04f 0300 mov.w r3, #0 + 80000c8: f000 f91c bl 8000304 + 80000cc: f248 0120 movw r1, #32800 ; 0x8020 + 80000d0: ea4f 3101 mov.w r1, r1, lsl #12 + 80000d4: 6808 ldr r0, [r1, #0] + 80000d6: 4685 mov sp, r0 + 80000d8: f04f 0001 mov.w r0, #1 + 80000dc: f8d1 e004 ldr.w lr, [r1, #4] + 80000e0: 4770 bx lr + ... + +080000e4 : + void +firewall_setup(void) +{ + // This is critical: without the clock enabled to "SYSCFG" we + // can't tell the FW is enabled or not! Enabling it would also not work + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000e4: 4b1b ldr r3, [pc, #108] ; (8000154 ) +{ + 80000e6: b500 push {lr} + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000e8: 6e1a ldr r2, [r3, #96] ; 0x60 + 80000ea: f042 0201 orr.w r2, r2, #1 + 80000ee: 661a str r2, [r3, #96] ; 0x60 + 80000f0: 6e1b ldr r3, [r3, #96] ; 0x60 +{ + 80000f2: b08b sub sp, #44 ; 0x2c + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000f4: f003 0301 and.w r3, r3, #1 + 80000f8: 9300 str r3, [sp, #0] + 80000fa: 9b00 ldr r3, [sp, #0] + + if(__HAL_FIREWALL_IS_ENABLED()) { + 80000fc: 4b16 ldr r3, [pc, #88] ; (8000158 ) + 80000fe: 685b ldr r3, [r3, #4] + 8000100: 07db lsls r3, r3, #31 + 8000102: d524 bpl.n 800014e + // REMINDERS: + // - cannot debug anything in boot loader w/ firewall enabled (no readback, no bkpt) + // - when RDP=2, this protection still important or else python can read pairing secret + // - in factory mode (RDP!=2), it's nice to have this disabled so we can debug still + // - could look at RDP level here, but it would be harder to completely reset the bag number! + if(check_all_ones_raw(rom_secrets->bag_number, sizeof(rom_secrets->bag_number))) { + 8000104: 4815 ldr r0, [pc, #84] ; (800015c ) + 8000106: 2120 movs r1, #32 + 8000108: f002 faae bl 8002668 + 800010c: b9f8 cbnz r0, 800014e + // for debug builds, never enable firewall + return; +#endif + + extern int firewall_starts; // see startup.S ... aligned@256 (0x08000300) + uint32_t start = (uint32_t)&firewall_starts; + 800010e: 4b14 ldr r3, [pc, #80] ; (8000160 ) + uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE); + 8000110: 4a14 ldr r2, [pc, #80] ; (8000164 ) + // but sensitive stuff is still there (which would allow bypass) + // - so it's important to enable option bytes to set write-protect flash of entire bootloader + // - to disable debug and complete protection, must enable write-protect "level 2" (RDP=2) + // + + FIREWALL_InitTypeDef init = { + 8000112: 9302 str r3, [sp, #8] + uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE); + 8000114: 1ad3 subs r3, r2, r3 + FIREWALL_InitTypeDef init = { + 8000116: e9cd 3203 strd r3, r2, [sp, #12] + 800011a: f44f 4380 mov.w r3, #16384 ; 0x4000 + 800011e: e9cd 3005 strd r3, r0, [sp, #20] + 8000122: e9cd 0007 strd r0, r0, [sp, #28] + 8000126: 9009 str r0, [sp, #36] ; 0x24 + .VDataSegmentLength = 0, + .VolatileDataExecution = 0, + .VolatileDataShared = 0, + }; + + int rv = HAL_FIREWALL_Config((FIREWALL_InitTypeDef *)&init); + 8000128: a802 add r0, sp, #8 + 800012a: f000 f821 bl 8000170 + if(rv) { + 800012e: b110 cbz r0, 8000136 + INCONSISTENT("fw"); + 8000130: 480d ldr r0, [pc, #52] ; (8000168 ) + 8000132: f000 fc89 bl 8000a48 + } + + __HAL_FIREWALL_PREARM_DISABLE(); + 8000136: 4b0d ldr r3, [pc, #52] ; (800016c ) + 8000138: 6a1a ldr r2, [r3, #32] + 800013a: f022 0201 bic.w r2, r2, #1 + 800013e: 621a str r2, [r3, #32] + 8000140: 6a1b ldr r3, [r3, #32] + 8000142: f003 0301 and.w r3, r3, #1 + 8000146: 9301 str r3, [sp, #4] + 8000148: 9b01 ldr r3, [sp, #4] + HAL_FIREWALL_EnableFirewall(); + 800014a: f000 f88b bl 8000264 +} + 800014e: b00b add sp, #44 ; 0x2c + 8000150: f85d fb04 ldr.w pc, [sp], #4 + 8000154: 40021000 .word 0x40021000 + 8000158: 40010000 .word 0x40010000 + 800015c: 0801c050 .word 0x0801c050 + 8000160: 08000300 .word 0x08000300 + 8000164: 0801c000 .word 0x0801c000 + 8000168: 0800d700 .word 0x0800d700 + 800016c: 40011c00 .word 0x40011c00 + +08000170 : + * @param fw_init: Firewall initialization structure + * @note The API returns HAL_ERROR if the Firewall is already enabled. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_FIREWALL_Config(FIREWALL_InitTypeDef * fw_init) +{ + 8000170: b573 push {r0, r1, r4, r5, r6, lr} + /* Check the Firewall initialization structure allocation */ + if(fw_init == NULL) + 8000172: b910 cbnz r0, 800017a + { + return HAL_ERROR; + 8000174: 2001 movs r0, #1 + /* Set Firewall Configuration Register VDE and VDS bits + (volatile data execution and shared configuration) */ + MODIFY_REG(FIREWALL->CR, FW_CR_VDS|FW_CR_VDE, fw_init->VolatileDataExecution|fw_init->VolatileDataShared); + + return HAL_OK; +} + 8000176: b002 add sp, #8 + 8000178: bd70 pop {r4, r5, r6, pc} + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 800017a: 4b19 ldr r3, [pc, #100] ; (80001e0 ) + 800017c: 6e1a ldr r2, [r3, #96] ; 0x60 + 800017e: f042 0280 orr.w r2, r2, #128 ; 0x80 + 8000182: 661a str r2, [r3, #96] ; 0x60 + 8000184: 6e1b ldr r3, [r3, #96] ; 0x60 + 8000186: f003 0380 and.w r3, r3, #128 ; 0x80 + 800018a: 9301 str r3, [sp, #4] + 800018c: 9b01 ldr r3, [sp, #4] + if (__HAL_FIREWALL_IS_ENABLED() != RESET) + 800018e: 4b15 ldr r3, [pc, #84] ; (80001e4 ) + 8000190: 685b ldr r3, [r3, #4] + 8000192: 07db lsls r3, r3, #31 + 8000194: d5ee bpl.n 8000174 + if (fw_init->CodeSegmentLength != 0U) + 8000196: 6841 ldr r1, [r0, #4] + if (fw_init->NonVDataSegmentLength < 0x100U) + 8000198: 68c2 ldr r2, [r0, #12] + if (fw_init->CodeSegmentLength != 0U) + 800019a: b109 cbz r1, 80001a0 + if (fw_init->NonVDataSegmentLength < 0x100U) + 800019c: 2aff cmp r2, #255 ; 0xff + 800019e: d9e9 bls.n 8000174 + WRITE_REG(FIREWALL->CSSA, (FW_CSSA_ADD & fw_init->CodeSegmentStartAddress)); + 80001a0: 6803 ldr r3, [r0, #0] + 80001a2: 4e11 ldr r6, [pc, #68] ; (80001e8 ) + if (fw_init->VDataSegmentLength != 0U) + 80001a4: 6944 ldr r4, [r0, #20] + WRITE_REG(FIREWALL->CSSA, (FW_CSSA_ADD & fw_init->CodeSegmentStartAddress)); + 80001a6: ea03 0506 and.w r5, r3, r6 + 80001aa: 4b10 ldr r3, [pc, #64] ; (80001ec ) + 80001ac: 601d str r5, [r3, #0] + WRITE_REG(FIREWALL->CSL, (FW_CSL_LENG & fw_init->CodeSegmentLength)); + 80001ae: 4d10 ldr r5, [pc, #64] ; (80001f0 ) + 80001b0: 4029 ands r1, r5 + 80001b2: 6059 str r1, [r3, #4] + WRITE_REG(FIREWALL->NVDSSA, (FW_NVDSSA_ADD & fw_init->NonVDataSegmentStartAddress)); + 80001b4: 6881 ldr r1, [r0, #8] + WRITE_REG(FIREWALL->NVDSL, (FW_NVDSL_LENG & fw_init->NonVDataSegmentLength)); + 80001b6: 402a ands r2, r5 + WRITE_REG(FIREWALL->NVDSSA, (FW_NVDSSA_ADD & fw_init->NonVDataSegmentStartAddress)); + 80001b8: 4031 ands r1, r6 + 80001ba: 6099 str r1, [r3, #8] + WRITE_REG(FIREWALL->NVDSL, (FW_NVDSL_LENG & fw_init->NonVDataSegmentLength)); + 80001bc: 60da str r2, [r3, #12] + WRITE_REG(FIREWALL->VDSSA, (FW_VDSSA_ADD & fw_init->VDataSegmentStartAddress)); + 80001be: 6901 ldr r1, [r0, #16] + 80001c0: 4a0c ldr r2, [pc, #48] ; (80001f4 ) + 80001c2: 4011 ands r1, r2 + WRITE_REG(FIREWALL->VDSL, (FW_VDSL_LENG & fw_init->VDataSegmentLength)); + 80001c4: 4022 ands r2, r4 + WRITE_REG(FIREWALL->VDSSA, (FW_VDSSA_ADD & fw_init->VDataSegmentStartAddress)); + 80001c6: 6119 str r1, [r3, #16] + WRITE_REG(FIREWALL->VDSL, (FW_VDSL_LENG & fw_init->VDataSegmentLength)); + 80001c8: 615a str r2, [r3, #20] + MODIFY_REG(FIREWALL->CR, FW_CR_VDS|FW_CR_VDE, fw_init->VolatileDataExecution|fw_init->VolatileDataShared); + 80001ca: e9d0 2006 ldrd r2, r0, [r0, #24] + 80001ce: 6a19 ldr r1, [r3, #32] + 80001d0: 4302 orrs r2, r0 + 80001d2: f021 0106 bic.w r1, r1, #6 + 80001d6: 430a orrs r2, r1 + 80001d8: 621a str r2, [r3, #32] + return HAL_OK; + 80001da: 2000 movs r0, #0 + 80001dc: e7cb b.n 8000176 + 80001de: bf00 nop + 80001e0: 40021000 .word 0x40021000 + 80001e4: 40010000 .word 0x40010000 + 80001e8: 00ffff00 .word 0x00ffff00 + 80001ec: 40011c00 .word 0x40011c00 + 80001f0: 003fff00 .word 0x003fff00 + 80001f4: 0003ffc0 .word 0x0003ffc0 + +080001f8 : +void HAL_FIREWALL_GetConfig(FIREWALL_InitTypeDef * fw_config) +{ + + /* Enable Firewall clock, in case no Firewall configuration has been carried + out up to this point */ + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 80001f8: 4b15 ldr r3, [pc, #84] ; (8000250 ) + 80001fa: 6e1a ldr r2, [r3, #96] ; 0x60 +{ + 80001fc: b573 push {r0, r1, r4, r5, r6, lr} + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 80001fe: f042 0280 orr.w r2, r2, #128 ; 0x80 + 8000202: 661a str r2, [r3, #96] ; 0x60 + 8000204: 6e1b ldr r3, [r3, #96] ; 0x60 + + /* Retrieve code segment protection setting */ + fw_config->CodeSegmentStartAddress = (READ_REG(FIREWALL->CSSA) & FW_CSSA_ADD); + 8000206: 4e13 ldr r6, [pc, #76] ; (8000254 ) + fw_config->CodeSegmentLength = (READ_REG(FIREWALL->CSL) & FW_CSL_LENG); + 8000208: 4d13 ldr r5, [pc, #76] ; (8000258 ) + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 800020a: f003 0380 and.w r3, r3, #128 ; 0x80 + 800020e: 9301 str r3, [sp, #4] + 8000210: 9b01 ldr r3, [sp, #4] + fw_config->CodeSegmentStartAddress = (READ_REG(FIREWALL->CSSA) & FW_CSSA_ADD); + 8000212: 4b12 ldr r3, [pc, #72] ; (800025c ) + 8000214: 681a ldr r2, [r3, #0] + 8000216: 4032 ands r2, r6 + 8000218: 6002 str r2, [r0, #0] + fw_config->CodeSegmentLength = (READ_REG(FIREWALL->CSL) & FW_CSL_LENG); + 800021a: 685c ldr r4, [r3, #4] + 800021c: 402c ands r4, r5 + 800021e: 6044 str r4, [r0, #4] + + /* Retrieve non volatile data segment protection setting */ + fw_config->NonVDataSegmentStartAddress = (READ_REG(FIREWALL->NVDSSA) & FW_NVDSSA_ADD); + 8000220: 6899 ldr r1, [r3, #8] + fw_config->NonVDataSegmentLength = (READ_REG(FIREWALL->NVDSL) & FW_NVDSL_LENG); + + /* Retrieve volatile data segment protection setting */ + fw_config->VDataSegmentStartAddress = (READ_REG(FIREWALL->VDSSA) & FW_VDSSA_ADD); + 8000222: 4c0f ldr r4, [pc, #60] ; (8000260 ) + fw_config->NonVDataSegmentStartAddress = (READ_REG(FIREWALL->NVDSSA) & FW_NVDSSA_ADD); + 8000224: 4031 ands r1, r6 + 8000226: 6081 str r1, [r0, #8] + fw_config->NonVDataSegmentLength = (READ_REG(FIREWALL->NVDSL) & FW_NVDSL_LENG); + 8000228: 68da ldr r2, [r3, #12] + 800022a: 402a ands r2, r5 + 800022c: 60c2 str r2, [r0, #12] + fw_config->VDataSegmentStartAddress = (READ_REG(FIREWALL->VDSSA) & FW_VDSSA_ADD); + 800022e: 6919 ldr r1, [r3, #16] + 8000230: 4021 ands r1, r4 + 8000232: 6101 str r1, [r0, #16] + fw_config->VDataSegmentLength = (READ_REG(FIREWALL->VDSL) & FW_VDSL_LENG); + 8000234: 695a ldr r2, [r3, #20] + 8000236: 4022 ands r2, r4 + 8000238: 6142 str r2, [r0, #20] + + /* Retrieve volatile data execution setting */ + fw_config->VolatileDataExecution = (READ_REG(FIREWALL->CR) & FW_CR_VDE); + 800023a: 6a1a ldr r2, [r3, #32] + 800023c: f002 0204 and.w r2, r2, #4 + 8000240: 6182 str r2, [r0, #24] + + /* Retrieve volatile data shared setting */ + fw_config->VolatileDataShared = (READ_REG(FIREWALL->CR) & FW_CR_VDS); + 8000242: 6a1b ldr r3, [r3, #32] + 8000244: f003 0302 and.w r3, r3, #2 + 8000248: 61c3 str r3, [r0, #28] + + return; +} + 800024a: b002 add sp, #8 + 800024c: bd70 pop {r4, r5, r6, pc} + 800024e: bf00 nop + 8000250: 40021000 .word 0x40021000 + 8000254: 00ffff00 .word 0x00ffff00 + 8000258: 003fff00 .word 0x003fff00 + 800025c: 40011c00 .word 0x40011c00 + 8000260: 0003ffc0 .word 0x0003ffc0 + +08000264 : + * @retval None + */ +void HAL_FIREWALL_EnableFirewall(void) +{ + /* Clears FWDIS bit of SYSCFG CFGR1 register */ + CLEAR_BIT(SYSCFG->CFGR1, SYSCFG_CFGR1_FWDIS); + 8000264: 4a02 ldr r2, [pc, #8] ; (8000270 ) + 8000266: 6853 ldr r3, [r2, #4] + 8000268: f023 0301 bic.w r3, r3, #1 + 800026c: 6053 str r3, [r2, #4] + +} + 800026e: 4770 bx lr + 8000270: 40010000 .word 0x40010000 + +08000274 : + * @retval None + */ +void HAL_FIREWALL_EnablePreArmFlag(void) +{ + /* Set FPA bit */ + SET_BIT(FIREWALL->CR, FW_CR_FPA); + 8000274: 4a02 ldr r2, [pc, #8] ; (8000280 ) + 8000276: 6a13 ldr r3, [r2, #32] + 8000278: f043 0301 orr.w r3, r3, #1 + 800027c: 6213 str r3, [r2, #32] +} + 800027e: 4770 bx lr + 8000280: 40011c00 .word 0x40011c00 + +08000284 : + * @retval None + */ +void HAL_FIREWALL_DisablePreArmFlag(void) +{ + /* Clear FPA bit */ + CLEAR_BIT(FIREWALL->CR, FW_CR_FPA); + 8000284: 4a02 ldr r2, [pc, #8] ; (8000290 ) + 8000286: 6a13 ldr r3, [r2, #32] + 8000288: f023 0301 bic.w r3, r3, #1 + 800028c: 6213 str r3, [r2, #32] +} + 800028e: 4770 bx lr + 8000290: 40011c00 .word 0x40011c00 + ... + +08000300 <_firewall_start>: + 8000300: 0f193a11 .word 0x0f193a11 + +08000304 : + 8000304: f24e 0900 movw r9, #57344 ; 0xe000 + 8000308: f2c2 0909 movt r9, #8201 ; 0x2009 + 800030c: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 8000310: 44ca add sl, r9 + +08000312 : + 8000312: f849 ab04 str.w sl, [r9], #4 + 8000316: 45d1 cmp r9, sl + 8000318: d1fb bne.n 8000312 + 800031a: 46ea mov sl, sp + 800031c: 46cd mov sp, r9 + 800031e: e92d 4400 stmdb sp!, {sl, lr} + +08000322 : + 8000322: f000 f841 bl 80003a8 + 8000326: e8bd 4400 ldmia.w sp!, {sl, lr} + 800032a: 46d5 mov sp, sl + 800032c: f24e 0900 movw r9, #57344 ; 0xe000 + 8000330: f2c2 0909 movt r9, #8201 ; 0x2009 + 8000334: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 8000338: 44ca add sl, r9 + +0800033a : + 800033a: f849 0b04 str.w r0, [r9], #4 + 800033e: 45d1 cmp r9, sl + 8000340: d1fb bne.n 800033a + 8000342: 4770 bx lr + +08000344 <__NVIC_SystemReset>: + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); + 8000344: f3bf 8f4f dsb sy +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8000348: 4905 ldr r1, [pc, #20] ; (8000360 <__NVIC_SystemReset+0x1c>) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800034a: 4b06 ldr r3, [pc, #24] ; (8000364 <__NVIC_SystemReset+0x20>) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 800034c: 68ca ldr r2, [r1, #12] + 800034e: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8000352: 4313 orrs r3, r2 + 8000354: 60cb str r3, [r1, #12] + 8000356: f3bf 8f4f dsb sy + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + 800035a: bf00 nop + for(;;) /* wait until reset */ + 800035c: e7fd b.n 800035a <__NVIC_SystemReset+0x16> + 800035e: bf00 nop + 8000360: e000ed00 .word 0xe000ed00 + 8000364: 05fa0004 .word 0x05fa0004 + +08000368 : +good_addr(const uint8_t *b, int minlen, int len, bool readonly) +{ + uint32_t x = (uint32_t)b; + + if(minlen) { + if(!b) return EFAULT; // gave no buffer + 8000368: b198 cbz r0, 8000392 + if(len < minlen) return ERANGE; // too small + 800036a: 4291 cmp r1, r2 + 800036c: dc13 bgt.n 8000396 + } + + if((x >= SRAM1_BASE) && ((x+len) <= BL_SRAM_BASE)) { + 800036e: f1b0 5f00 cmp.w r0, #536870912 ; 0x20000000 + 8000372: d303 bcc.n 800037c + 8000374: 490b ldr r1, [pc, #44] ; (80003a4 ) + 8000376: 4402 add r2, r0 + 8000378: 428a cmp r2, r1 + 800037a: d90e bls.n 800039a + // ok: it's inside the SRAM areas, up to where we start + return 0; + } + + if(!readonly) { + 800037c: b17b cbz r3, 800039e + return EPERM; + } + + if((x >= FIRMWARE_START) && (x - FIRMWARE_START) < FW_MAX_LENGTH_MK4) { + 800037e: f100 4077 add.w r0, r0, #4143972352 ; 0xf7000000 + 8000382: f500 007e add.w r0, r0, #16646144 ; 0xfe0000 + // inside flash of main firmware (happens for QSTR's) + return 0; + } + + return EACCES; + 8000386: f5b0 1ff0 cmp.w r0, #1966080 ; 0x1e0000 + 800038a: bf34 ite cc + 800038c: 2000 movcc r0, #0 + 800038e: 200d movcs r0, #13 + 8000390: 4770 bx lr + if(!b) return EFAULT; // gave no buffer + 8000392: 200e movs r0, #14 + 8000394: 4770 bx lr + if(len < minlen) return ERANGE; // too small + 8000396: 2022 movs r0, #34 ; 0x22 + 8000398: 4770 bx lr + return 0; + 800039a: 2000 movs r0, #0 + 800039c: 4770 bx lr + return EPERM; + 800039e: 2001 movs r0, #1 +} + 80003a0: 4770 bx lr + 80003a2: bf00 nop + 80003a4: 2009e000 .word 0x2009e000 + +080003a8 : +// + __attribute__ ((used)) + int +firewall_dispatch(int method_num, uint8_t *buf_io, int len_in, + uint32_t arg2, uint32_t incoming_sp, uint32_t incoming_lr) +{ + 80003a8: b570 push {r4, r5, r6, lr} + 80003aa: b09e sub sp, #120 ; 0x78 + 80003ac: 460d mov r5, r1 + 80003ae: 9c23 ldr r4, [sp, #140] ; 0x8c + 80003b0: 9301 str r3, [sp, #4] + __ASM volatile ("cpsid i" : : : "memory"); + 80003b2: b672 cpsid i + // in case the caller didn't already, but would just lead to a crash anyway + __disable_irq(); + + // "1=any code executed outside the protected segment will close the Firewall" + // "0=.. will reset the processor" + __HAL_FIREWALL_PREARM_DISABLE(); + 80003b4: 4ba5 ldr r3, [pc, #660] ; (800064c ) + 80003b6: 6a19 ldr r1, [r3, #32] + 80003b8: f021 0101 bic.w r1, r1, #1 + 80003bc: 6219 str r1, [r3, #32] + 80003be: 6a1b ldr r3, [r3, #32] + 80003c0: f003 0301 and.w r3, r3, #1 + 80003c4: 9302 str r3, [sp, #8] + // using read/write in place. + // - use arg2 use when a simple number is needed; never a pointer! + // - mpy may provide a pointer to flash if we give it a qstr or small value, and if + // we're reading only, that's fine. + + if(len_in > 1024) { // arbitrary max, increase as needed + 80003c6: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + __HAL_FIREWALL_PREARM_DISABLE(); + 80003ca: 9b02 ldr r3, [sp, #8] + if(len_in > 1024) { // arbitrary max, increase as needed + 80003cc: f300 82e3 bgt.w 8000996 + + // Use these macros +#define REQUIRE_IN_ONLY(x) if((rv = good_addr(buf_io, (x), len_in, true))) { goto fail; } +#define REQUIRE_OUT(x) if((rv = good_addr(buf_io, (x), len_in, false))) { goto fail; } + + switch(method_num) { + 80003d0: 3001 adds r0, #1 + 80003d2: 281c cmp r0, #28 + 80003d4: f200 81b6 bhi.w 8000744 + 80003d8: e8df f010 tbh [pc, r0, lsl #1] + 80003dc: 001d02f9 .word 0x001d02f9 + 80003e0: 00800034 .word 0x00800034 + 80003e4: 00d100bd .word 0x00d100bd + 80003e8: 01f300f2 .word 0x01f300f2 + 80003ec: 01b401b4 .word 0x01b401b4 + 80003f0: 01b401b4 .word 0x01b401b4 + 80003f4: 00fa01b4 .word 0x00fa01b4 + 80003f8: 01b401b4 .word 0x01b401b4 + 80003fc: 01240105 .word 0x01240105 + 8000400: 01670154 .word 0x01670154 + 8000404: 01f701ab .word 0x01f701ab + 8000408: 025f0206 .word 0x025f0206 + 800040c: 02b702a2 .word 0x02b702a2 + 8000410: 02cf02bf .word 0x02cf02bf + 8000414: 02eb .short 0x02eb + case 0: { + REQUIRE_OUT(64); + 8000416: 2300 movs r3, #0 + 8000418: 2140 movs r1, #64 ; 0x40 + 800041a: 4628 mov r0, r5 + 800041c: 9200 str r2, [sp, #0] + 800041e: f7ff ffa3 bl 8000368 + 8000422: 4604 mov r4, r0 + 8000424: bb48 cbnz r0, 800047a + + // Return my version string + memset(buf_io, 0, len_in); + 8000426: 4601 mov r1, r0 + 8000428: 9a00 ldr r2, [sp, #0] + 800042a: 4628 mov r0, r5 + 800042c: f00d f922 bl 800d674 + strlcpy((char *)buf_io, version_string, len_in); + 8000430: 9a00 ldr r2, [sp, #0] + 8000432: 4987 ldr r1, [pc, #540] ; (8000650 ) + 8000434: 4628 mov r0, r5 + 8000436: f00d f93b bl 800d6b0 + + rv = strlen(version_string); + 800043a: 4885 ldr r0, [pc, #532] ; (8000650 ) + 800043c: f00d f94d bl 800d6da + ae_setup(); + ae_keep_alive(); + switch(arg2) { + default: + case 0: // read state + rv = ae_get_gpio(); + 8000440: 4604 mov r4, r0 + break; + 8000442: e01a b.n 800047a + REQUIRE_OUT(32); + 8000444: 2300 movs r3, #0 + 8000446: 2120 movs r1, #32 + 8000448: 4628 mov r0, r5 + 800044a: f7ff ff8d bl 8000368 + 800044e: 4604 mov r4, r0 + 8000450: b998 cbnz r0, 800047a + sha256_init(&ctx); + 8000452: a80b add r0, sp, #44 ; 0x2c + 8000454: f005 f81a bl 800548c + sha256_update(&ctx, (void *)&arg2, 4); + 8000458: 2204 movs r2, #4 + 800045a: eb0d 0102 add.w r1, sp, r2 + 800045e: a80b add r0, sp, #44 ; 0x2c + 8000460: f005 f822 bl 80054a8 + sha256_update(&ctx, (void *)BL_FLASH_BASE, BL_FLASH_SIZE); + 8000464: f04f 6100 mov.w r1, #134217728 ; 0x8000000 + 8000468: a80b add r0, sp, #44 ; 0x2c + 800046a: f44f 32e0 mov.w r2, #114688 ; 0x1c000 + 800046e: f005 f81b bl 80054a8 + sha256_final(&ctx, buf_io); + 8000472: 4629 mov r1, r5 + 8000474: a80b add r0, sp, #44 ; 0x2c + 8000476: f005 f85d bl 8005534 + +fail: + + // Precaution: we don't want to leave SE1 authorized for any specific keys, + // perhaps due to an error path we didn't see. Always reset the chip. + ae_reset_chip(); + 800047a: f002 fa95 bl 80029a8 + + // Unlikely it matters, but clear flash memory cache. + __HAL_FLASH_DATA_CACHE_DISABLE(); + 800047e: 4b75 ldr r3, [pc, #468] ; (8000654 ) + 8000480: 681a ldr r2, [r3, #0] + 8000482: f422 6280 bic.w r2, r2, #1024 ; 0x400 + 8000486: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_RESET(); + 8000488: 681a ldr r2, [r3, #0] + 800048a: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800048e: 601a str r2, [r3, #0] + 8000490: 681a ldr r2, [r3, #0] + 8000492: f422 5280 bic.w r2, r2, #4096 ; 0x1000 + 8000496: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); + 8000498: 681a ldr r2, [r3, #0] + 800049a: f442 6280 orr.w r2, r2, #1024 ; 0x400 + 800049e: 601a str r2, [r3, #0] + + // .. and instruction memory (flash cache too?) + __HAL_FLASH_INSTRUCTION_CACHE_DISABLE(); + 80004a0: 681a ldr r2, [r3, #0] + 80004a2: f422 7200 bic.w r2, r2, #512 ; 0x200 + 80004a6: 601a str r2, [r3, #0] + __HAL_FLASH_INSTRUCTION_CACHE_RESET(); + 80004a8: 681a ldr r2, [r3, #0] + 80004aa: f442 6200 orr.w r2, r2, #2048 ; 0x800 + 80004ae: 601a str r2, [r3, #0] + 80004b0: 681a ldr r2, [r3, #0] + 80004b2: f422 6200 bic.w r2, r2, #2048 ; 0x800 + 80004b6: 601a str r2, [r3, #0] + __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); + 80004b8: 681a ldr r2, [r3, #0] + 80004ba: f442 7200 orr.w r2, r2, #512 ; 0x200 + 80004be: 601a str r2, [r3, #0] + + // authorize return from firewall into user's code + __HAL_FIREWALL_PREARM_ENABLE(); + 80004c0: f5a3 3382 sub.w r3, r3, #66560 ; 0x10400 + + return rv; +} + 80004c4: 4620 mov r0, r4 + __HAL_FIREWALL_PREARM_ENABLE(); + 80004c6: 6a1a ldr r2, [r3, #32] + 80004c8: f042 0201 orr.w r2, r2, #1 + 80004cc: 621a str r2, [r3, #32] + 80004ce: 6a1b ldr r3, [r3, #32] + 80004d0: f003 0301 and.w r3, r3, #1 + 80004d4: 930b str r3, [sp, #44] ; 0x2c + 80004d6: 9b0b ldr r3, [sp, #44] ; 0x2c +} + 80004d8: b01e add sp, #120 ; 0x78 + 80004da: bd70 pop {r4, r5, r6, pc} +// Write bag number (probably a string) +void flash_save_bag_number(const uint8_t new_number[32]); + +// Are we operating in level2? +static inline bool flash_is_security_level2(void) { + rng_delay(); + 80004dc: f002 f94e bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 80004e0: 4b5c ldr r3, [pc, #368] ; (8000654 ) + 80004e2: 6a1b ldr r3, [r3, #32] + 80004e4: b2db uxtb r3, r3 + 80004e6: f1a3 02cc sub.w r2, r3, #204 ; 0xcc + 80004ea: 4255 negs r5, r2 + 80004ec: 4155 adcs r5, r2 + switch(arg2) { + 80004ee: 9a01 ldr r2, [sp, #4] + 80004f0: 2a02 cmp r2, #2 + 80004f2: d01c beq.n 800052e + 80004f4: 2a03 cmp r2, #3 + 80004f6: d01f beq.n 8000538 + 80004f8: 2a01 cmp r2, #1 + 80004fa: d013 beq.n 8000524 + if(secure) { + 80004fc: 2bcc cmp r3, #204 ; 0xcc + 80004fe: f000 8216 beq.w 800092e + puts("Die: DFU"); + 8000502: 4855 ldr r0, [pc, #340] ; (8000658 ) + scr = screen_upgrading; // was screen_dfu, but limited audience + 8000504: 4c55 ldr r4, [pc, #340] ; (800065c ) + puts("Die: DFU"); + 8000506: f004 fc51 bl 8004dac + bool secure = flash_is_security_level2(); + 800050a: 2500 movs r5, #0 + oled_setup(); + 800050c: f000 fc0a bl 8000d24 + oled_show(scr); + 8000510: 4620 mov r0, r4 + 8000512: f000 fc97 bl 8000e44 + wipe_all_sram(); + 8000516: f000 fa77 bl 8000a08 + psram_wipe(); + 800051a: f004 fd6f bl 8004ffc + if(secure) { + 800051e: b18d cbz r5, 8000544 + LOCKUP_FOREVER(); + 8000520: bf30 wfi + 8000522: e7fd b.n 8000520 + puts("Die: Downgrade"); + 8000524: 484e ldr r0, [pc, #312] ; (8000660 ) + scr = screen_downgrade; + 8000526: 4c4f ldr r4, [pc, #316] ; (8000664 ) + puts("Die: Downgrade"); + 8000528: f004 fc40 bl 8004dac + break; + 800052c: e7ee b.n 800050c + puts("Die: Blankish"); + 800052e: 484e ldr r0, [pc, #312] ; (8000668 ) + scr = screen_blankish; + 8000530: 4c4e ldr r4, [pc, #312] ; (800066c ) + puts("Die: Blankish"); + 8000532: f004 fc3b bl 8004dac + break; + 8000536: e7e9 b.n 800050c + puts("Die: Brick"); + 8000538: 484d ldr r0, [pc, #308] ; (8000670 ) + scr = screen_brick; + 800053a: 4c4e ldr r4, [pc, #312] ; (8000674 ) + puts("Die: Brick"); + 800053c: f004 fc36 bl 8004dac + secure = true; // no point going into DFU, if even possible + 8000540: 2501 movs r5, #1 + break; + 8000542: e7e3 b.n 800050c + memcpy(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)); + 8000544: 494c ldr r1, [pc, #304] ; (8000678 ) + 8000546: 4a4d ldr r2, [pc, #308] ; (800067c ) + 8000548: 6808 ldr r0, [r1, #0] + 800054a: 6849 ldr r1, [r1, #4] + 800054c: 4613 mov r3, r2 + 800054e: c303 stmia r3!, {r0, r1} + dfu_flag->screen = scr; + 8000550: 6094 str r4, [r2, #8] + NVIC_SystemReset(); + 8000552: f7ff fef7 bl 8000344 <__NVIC_SystemReset> + switch(arg2) { + 8000556: 9b01 ldr r3, [sp, #4] + 8000558: f033 0302 bics.w r3, r3, #2 + 800055c: d102 bne.n 8000564 + oled_show(screen_logout); + 800055e: 4848 ldr r0, [pc, #288] ; (8000680 ) + 8000560: f000 fc70 bl 8000e44 + wipe_all_sram(); + 8000564: f000 fa50 bl 8000a08 + psram_wipe(); + 8000568: f004 fd48 bl 8004ffc + if(arg2 == 2) { + 800056c: 9b01 ldr r3, [sp, #4] + 800056e: 2b02 cmp r3, #2 + 8000570: d103 bne.n 800057a + delay_ms(100); + 8000572: 2064 movs r0, #100 ; 0x64 + 8000574: f003 f9c0 bl 80038f8 + 8000578: e7eb b.n 8000552 + LOCKUP_FOREVER(); + 800057a: bf30 wfi + 800057c: e7fd b.n 800057a + ae_setup(); + 800057e: f002 fa21 bl 80029c4 + ae_keep_alive(); + 8000582: f002 fa51 bl 8002a28 + switch(arg2) { + 8000586: 9b01 ldr r3, [sp, #4] + 8000588: 2b02 cmp r3, #2 + 800058a: d00a beq.n 80005a2 + 800058c: 2b03 cmp r3, #3 + 800058e: d00a beq.n 80005a6 + 8000590: 2b01 cmp r3, #1 + 8000592: d002 beq.n 800059a + rv = ae_get_gpio(); + 8000594: f002 ffc6 bl 8003524 + 8000598: e752 b.n 8000440 + rv = ae_set_gpio(0); + 800059a: 2000 movs r0, #0 + rv = ae_set_gpio(1); + 800059c: f002 ff94 bl 80034c8 + 80005a0: e74e b.n 8000440 + 80005a2: 2001 movs r0, #1 + 80005a4: e7fa b.n 800059c + checksum_flash(fw_digest, world_digest, 0); + 80005a6: 2200 movs r2, #0 + 80005a8: a90b add r1, sp, #44 ; 0x2c + 80005aa: a803 add r0, sp, #12 + 80005ac: f001 fa44 bl 8001a38 + rv = ae_set_gpio_secure(world_digest); + 80005b0: a80b add r0, sp, #44 ; 0x2c + 80005b2: f002 ff9f bl 80034f4 + 80005b6: 4604 mov r4, r0 + oled_show(screen_blankish); + 80005b8: 482c ldr r0, [pc, #176] ; (800066c ) + 80005ba: f000 fc43 bl 8000e44 + break; + 80005be: e75c b.n 800047a + ae_setup(); + 80005c0: f002 fa00 bl 80029c4 + rv = (ae_pair_unlock() != 0); + 80005c4: f002 fbf4 bl 8002db0 + 80005c8: 1e04 subs r4, r0, #0 + 80005ca: bf18 it ne + 80005cc: 2401 movne r4, #1 + break; + 80005ce: e754 b.n 800047a + REQUIRE_OUT(1); + 80005d0: 2300 movs r3, #0 + 80005d2: 2101 movs r1, #1 + 80005d4: 4628 mov r0, r5 + 80005d6: f7ff fec7 bl 8000368 + 80005da: 4604 mov r4, r0 + 80005dc: 2800 cmp r0, #0 + 80005de: f47f af4c bne.w 800047a + buf_io[0] = 0; // NOT SUPPORTED on Mk4 + 80005e2: 7028 strb r0, [r5, #0] + break; + 80005e4: e749 b.n 800047a + if(len_in != 4 && len_in != 32 && len_in != 72) { + 80005e6: 2a04 cmp r2, #4 + 80005e8: d004 beq.n 80005f4 + 80005ea: 2a20 cmp r2, #32 + 80005ec: d002 beq.n 80005f4 + 80005ee: 2a48 cmp r2, #72 ; 0x48 + 80005f0: f040 81d1 bne.w 8000996 + REQUIRE_OUT(4); + 80005f4: 2300 movs r3, #0 + 80005f6: 2104 movs r1, #4 + 80005f8: 4628 mov r0, r5 + 80005fa: 9200 str r2, [sp, #0] + 80005fc: f7ff feb4 bl 8000368 + 8000600: 4604 mov r4, r0 + 8000602: 2800 cmp r0, #0 + 8000604: f47f af39 bne.w 800047a + ae_setup(); + 8000608: f002 f9dc bl 80029c4 + if(ae_read_data_slot(arg2 & 0xf, buf_io, len_in)) { + 800060c: 9801 ldr r0, [sp, #4] + 800060e: 9a00 ldr r2, [sp, #0] + 8000610: 4629 mov r1, r5 + 8000612: f000 000f and.w r0, r0, #15 + 8000616: f002 ff11 bl 800343c + if(rv) { + 800061a: 2800 cmp r0, #0 + 800061c: f000 80d1 beq.w 80007c2 + rv = EIO; + 8000620: 2405 movs r4, #5 + 8000622: e72a b.n 800047a + REQUIRE_OUT(MAX_PIN_LEN); + 8000624: 2300 movs r3, #0 + 8000626: 2120 movs r1, #32 + 8000628: 4628 mov r0, r5 + 800062a: f7ff fe9d bl 8000368 + 800062e: 4604 mov r4, r0 + 8000630: 2800 cmp r0, #0 + 8000632: f47f af22 bne.w 800047a + if((arg2 < 1) || (arg2 > MAX_PIN_LEN)) { + 8000636: 9901 ldr r1, [sp, #4] + 8000638: 1e4b subs r3, r1, #1 + 800063a: 2b1f cmp r3, #31 + 800063c: f200 81ab bhi.w 8000996 + if(pin_prefix_words((char *)buf_io, arg2, (uint32_t *)buf_io)) { + 8000640: 462a mov r2, r5 + 8000642: 4628 mov r0, r5 + 8000644: f003 fc5c bl 8003f00 + 8000648: e7e7 b.n 800061a + 800064a: bf00 nop + 800064c: 40011c00 .word 0x40011c00 + 8000650: 0800e720 .word 0x0800e720 + 8000654: 40022000 .word 0x40022000 + 8000658: 0800d706 .word 0x0800d706 + 800065c: 0800e18b .word 0x0800e18b + 8000660: 0800d70f .word 0x0800d70f + 8000664: 0800da7a .word 0x0800da7a + 8000668: 0800d71e .word 0x0800d71e + 800066c: 0800d7de .word 0x0800d7de + 8000670: 0800d72c .word 0x0800d72c + 8000674: 0800d80b .word 0x0800d80b + 8000678: 0800d737 .word 0x0800d737 + 800067c: 20008000 .word 0x20008000 + 8000680: 0800db96 .word 0x0800db96 + REQUIRE_OUT(32); + 8000684: 2300 movs r3, #0 + 8000686: 2120 movs r1, #32 + 8000688: 4628 mov r0, r5 + 800068a: f7ff fe6d bl 8000368 + 800068e: 4604 mov r4, r0 + 8000690: 2800 cmp r0, #0 + 8000692: f47f aef2 bne.w 800047a + memset(buf_io, 0x55, 32); // to help show errors + 8000696: 2220 movs r2, #32 + 8000698: 2155 movs r1, #85 ; 0x55 + 800069a: 4628 mov r0, r5 + 800069c: f00c ffea bl 800d674 + rng_buffer(buf_io, 32); + 80006a0: 2120 movs r1, #32 + 80006a2: 4628 mov r0, r5 + 80006a4: f002 f854 bl 8002750 + break; + 80006a8: e6e7 b.n 800047a + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2); + 80006aa: 2300 movs r3, #0 + 80006ac: f44f 718c mov.w r1, #280 ; 0x118 + 80006b0: 4628 mov r0, r5 + 80006b2: 9200 str r2, [sp, #0] + 80006b4: f7ff fe58 bl 8000368 + 80006b8: 4604 mov r4, r0 + 80006ba: 2800 cmp r0, #0 + 80006bc: f47f aedd bne.w 800047a + switch(arg2) { + 80006c0: e9dd 2300 ldrd r2, r3, [sp] + 80006c4: 2b08 cmp r3, #8 + 80006c6: d83d bhi.n 8000744 + 80006c8: e8df f003 tbb [pc, r3] + 80006cc: 110d0905 .word 0x110d0905 + 80006d0: 221d1915 .word 0x221d1915 + 80006d4: 26 .byte 0x26 + 80006d5: 00 .byte 0x00 + rv = pin_setup_attempt(args); + 80006d6: 4628 mov r0, r5 + 80006d8: f003 fc30 bl 8003f3c + 80006dc: e6b0 b.n 8000440 + rv = pin_delay(args); + 80006de: 4628 mov r0, r5 + 80006e0: f003 fc9a bl 8004018 + 80006e4: e6ac b.n 8000440 + rv = pin_login_attempt(args); + 80006e6: 4628 mov r0, r5 + 80006e8: f003 fc98 bl 800401c + 80006ec: e6a8 b.n 8000440 + rv = pin_change(args); + 80006ee: 4628 mov r0, r5 + 80006f0: f003 fda2 bl 8004238 + 80006f4: e6a4 b.n 8000440 + rv = pin_fetch_secret(args); + 80006f6: 4628 mov r0, r5 + 80006f8: f003 fe56 bl 80043a8 + 80006fc: e6a0 b.n 8000440 + rv = pin_firmware_greenlight(args); + 80006fe: 4628 mov r0, r5 + 8000700: f004 f812 bl 8004728 + 8000704: e69c b.n 8000440 + rv = pin_long_secret(args, NULL); + 8000706: 2100 movs r1, #0 + rv = pin_long_secret(args, &buf_io[PIN_ATTEMPT_SIZE_V2]); + 8000708: 4628 mov r0, r5 + 800070a: f003 ff4f bl 80045ac + 800070e: e697 b.n 8000440 + rv = pin_firmware_upgrade(args); + 8000710: 4628 mov r0, r5 + 8000712: f004 f849 bl 80047a8 + 8000716: e693 b.n 8000440 + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2 + AE_LONG_SECRET_LEN); + 8000718: 2300 movs r3, #0 + 800071a: f44f 712e mov.w r1, #696 ; 0x2b8 + 800071e: 4628 mov r0, r5 + 8000720: f7ff fe22 bl 8000368 + 8000724: 4604 mov r4, r0 + 8000726: 2800 cmp r0, #0 + 8000728: f47f aea7 bne.w 800047a + rv = pin_long_secret(args, &buf_io[PIN_ATTEMPT_SIZE_V2]); + 800072c: f505 718c add.w r1, r5, #280 ; 0x118 + 8000730: e7ea b.n 8000708 + switch(arg2) { + 8000732: 9b01 ldr r3, [sp, #4] + 8000734: 2b64 cmp r3, #100 ; 0x64 + 8000736: d041 beq.n 80007bc + 8000738: d806 bhi.n 8000748 + 800073a: 2b01 cmp r3, #1 + 800073c: d01e beq.n 800077c + 800073e: 2b02 cmp r3, #2 + 8000740: d028 beq.n 8000794 + 8000742: b13b cbz r3, 8000754 + 8000744: 2402 movs r4, #2 + 8000746: e698 b.n 800047a + 8000748: 2b65 cmp r3, #101 ; 0x65 + 800074a: d03c beq.n 80007c6 + 800074c: 2b66 cmp r3, #102 ; 0x66 + 800074e: d1f9 bne.n 8000744 + flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this. + 8000750: 20cc movs r0, #204 ; 0xcc + 8000752: e034 b.n 80007be + REQUIRE_OUT(32); + 8000754: 2120 movs r1, #32 + 8000756: 4628 mov r0, r5 + 8000758: f7ff fe06 bl 8000368 + 800075c: 4604 mov r4, r0 + 800075e: 2800 cmp r0, #0 + 8000760: f47f ae8b bne.w 800047a + memcpy(buf_io, rom_secrets->bag_number, 32); + 8000764: 4aa1 ldr r2, [pc, #644] ; (80009ec ) + 8000766: 4ea2 ldr r6, [pc, #648] ; (80009f0 ) + 8000768: 4613 mov r3, r2 + 800076a: cb03 ldmia r3!, {r0, r1} + 800076c: 42b3 cmp r3, r6 + 800076e: 6028 str r0, [r5, #0] + 8000770: 6069 str r1, [r5, #4] + 8000772: 461a mov r2, r3 + 8000774: f105 0508 add.w r5, r5, #8 + 8000778: d1f6 bne.n 8000768 + 800077a: e67e b.n 800047a + REQUIRE_IN_ONLY(32); + 800077c: 2120 movs r1, #32 + 800077e: 4628 mov r0, r5 + 8000780: f7ff fdf2 bl 8000368 + 8000784: 4604 mov r4, r0 + 8000786: 2800 cmp r0, #0 + 8000788: f47f ae77 bne.w 800047a + flash_save_bag_number(buf_io); + 800078c: 4628 mov r0, r5 + 800078e: f001 fcf9 bl 8002184 + break; + 8000792: e672 b.n 800047a + REQUIRE_OUT(1); + 8000794: 2300 movs r3, #0 + 8000796: 2101 movs r1, #1 + 8000798: 4628 mov r0, r5 + 800079a: f7ff fde5 bl 8000368 + 800079e: 4604 mov r4, r0 + 80007a0: 2800 cmp r0, #0 + 80007a2: f47f ae6a bne.w 800047a + rng_delay(); + 80007a6: f001 ffe9 bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 80007aa: 4b92 ldr r3, [pc, #584] ; (80009f4 ) + 80007ac: 6a1b ldr r3, [r3, #32] + 80007ae: b2db uxtb r3, r3 + buf_io[0] = (flash_is_security_level2() ? 2 : 0xff); + 80007b0: 2bcc cmp r3, #204 ; 0xcc + 80007b2: bf0c ite eq + 80007b4: 2302 moveq r3, #2 + 80007b6: 23ff movne r3, #255 ; 0xff + buf_io[0] = 32; + 80007b8: 702b strb r3, [r5, #0] + break; + 80007ba: e65e b.n 800047a + flash_lockdown_hard(OB_RDP_LEVEL_0); // wipes contents of flash (1->0) + 80007bc: 20aa movs r0, #170 ; 0xaa + flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this. + 80007be: f001 fde3 bl 8002388 + int rv = 0; + 80007c2: 2400 movs r4, #0 + break; + 80007c4: e659 b.n 800047a + flash_lockdown_hard(OB_RDP_LEVEL_1); // Can only do 0->1 (experiments) + 80007c6: 20bb movs r0, #187 ; 0xbb + 80007c8: e7f9 b.n 80007be + REQUIRE_OUT(128); + 80007ca: 2300 movs r3, #0 + 80007cc: 2180 movs r1, #128 ; 0x80 + 80007ce: 4628 mov r0, r5 + 80007d0: f7ff fdca bl 8000368 + 80007d4: 4604 mov r4, r0 + 80007d6: 2800 cmp r0, #0 + 80007d8: f47f ae4f bne.w 800047a + ae_setup(); + 80007dc: f002 f8f2 bl 80029c4 + rv = ae_config_read(buf_io); + 80007e0: 4628 mov r0, r5 + 80007e2: f002 fef0 bl 80035c6 + 80007e6: e718 b.n 800061a + switch(arg2) { + 80007e8: 9b01 ldr r3, [sp, #4] + 80007ea: 2b03 cmp r3, #3 + 80007ec: d8aa bhi.n 8000744 + 80007ee: e8df f003 tbb [pc, r3] + 80007f2: 0f02 .short 0x0f02 + 80007f4: 441d .short 0x441d + REQUIRE_OUT(8); + 80007f6: 2300 movs r3, #0 + 80007f8: 2108 movs r1, #8 + 80007fa: 4628 mov r0, r5 + 80007fc: f7ff fdb4 bl 8000368 + 8000800: 4604 mov r4, r0 + 8000802: 2800 cmp r0, #0 + 8000804: f47f ae39 bne.w 800047a + get_min_version(buf_io); + 8000808: 4628 mov r0, r5 + 800080a: f001 f9a5 bl 8001b58 + break; + 800080e: e634 b.n 800047a + REQUIRE_IN_ONLY(8); + 8000810: 2301 movs r3, #1 + 8000812: 2108 movs r1, #8 + 8000814: 4628 mov r0, r5 + 8000816: f7ff fda7 bl 8000368 + 800081a: 4604 mov r4, r0 + 800081c: 2800 cmp r0, #0 + 800081e: f47f ae2c bne.w 800047a + rv = check_is_downgrade(buf_io, NULL); + 8000822: 4601 mov r1, r0 + 8000824: 4628 mov r0, r5 + 8000826: f001 f9b7 bl 8001b98 + 800082a: e609 b.n 8000440 + REQUIRE_IN_ONLY(8); + 800082c: 2301 movs r3, #1 + 800082e: 2108 movs r1, #8 + 8000830: 4628 mov r0, r5 + 8000832: f7ff fd99 bl 8000368 + 8000836: 4604 mov r4, r0 + 8000838: 2800 cmp r0, #0 + 800083a: f47f ae1e bne.w 800047a + if(buf_io[0] < 0x10 || buf_io[0] >= 0x40) { + 800083e: 782b ldrb r3, [r5, #0] + 8000840: 3b10 subs r3, #16 + rv = ERANGE; + 8000842: 2b2f cmp r3, #47 ; 0x2f + } if(check_is_downgrade(buf_io, NULL)) { + 8000844: 4601 mov r1, r0 + 8000846: 4628 mov r0, r5 + rv = ERANGE; + 8000848: bf88 it hi + 800084a: 2422 movhi r4, #34 ; 0x22 + } if(check_is_downgrade(buf_io, NULL)) { + 800084c: f001 f9a4 bl 8001b98 + 8000850: 2800 cmp r0, #0 + 8000852: f040 80c8 bne.w 80009e6 + get_min_version(min); + 8000856: a80b add r0, sp, #44 ; 0x2c + 8000858: f001 f97e bl 8001b58 + if(memcmp(min, buf_io, 8) == 0) { + 800085c: 2208 movs r2, #8 + 800085e: 4629 mov r1, r5 + 8000860: a80b add r0, sp, #44 ; 0x2c + 8000862: f00c fecf bl 800d604 + 8000866: 2800 cmp r0, #0 + 8000868: f000 80bd beq.w 80009e6 + if(record_highwater_version(buf_io)) { + 800086c: 4628 mov r0, r5 + 800086e: f001 fda5 bl 80023bc + rv = ENOMEM; + 8000872: 2800 cmp r0, #0 + 8000874: bf18 it ne + 8000876: 240c movne r4, #12 + 8000878: e5ff b.n 800047a + REQUIRE_OUT(4); + 800087a: 2300 movs r3, #0 + 800087c: 2104 movs r1, #4 + 800087e: 4628 mov r0, r5 + 8000880: f7ff fd72 bl 8000368 + 8000884: 4604 mov r4, r0 + 8000886: 2800 cmp r0, #0 + 8000888: f47f adf7 bne.w 800047a + ae_setup(); + 800088c: f002 f89a bl 80029c4 + rv = ae_get_counter((uint32_t *)buf_io, 0) ? EIO: 0; + 8000890: 4621 mov r1, r4 + 8000892: 4628 mov r0, r5 + 8000894: f002 fc87 bl 80031a6 + 8000898: e6bf b.n 800061a + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2 + sizeof(trick_slot_t)); + 800089a: 2300 movs r3, #0 + 800089c: f44f 71cc mov.w r1, #408 ; 0x198 + 80008a0: 4628 mov r0, r5 + 80008a2: f7ff fd61 bl 8000368 + 80008a6: 4604 mov r4, r0 + 80008a8: 2800 cmp r0, #0 + 80008aa: f47f ade6 bne.w 800047a + rv = pin_check_logged_in(args, &trick_mode); + 80008ae: a90b add r1, sp, #44 ; 0x2c + 80008b0: 4628 mov r0, r5 + 80008b2: f003 fc8f bl 80041d4 + if(rv) goto fail; + 80008b6: 4604 mov r4, r0 + 80008b8: 2800 cmp r0, #0 + 80008ba: f47f adde bne.w 800047a + if(trick_mode) { + 80008be: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 80008c2: b10b cbz r3, 80008c8 + mcu_key_clear(NULL); + 80008c4: f001 fdc8 bl 8002458 + switch(arg2) { + 80008c8: 9b01 ldr r3, [sp, #4] + 80008ca: 2b01 cmp r3, #1 + trick_slot_t *slot = (trick_slot_t *)(&buf_io[PIN_ATTEMPT_SIZE_V2]); + 80008cc: f505 728c add.w r2, r5, #280 ; 0x118 + switch(arg2) { + 80008d0: d00c beq.n 80008ec + 80008d2: 2b02 cmp r3, #2 + 80008d4: d01b beq.n 800090e + 80008d6: 2b00 cmp r3, #0 + 80008d8: f47f af34 bne.w 8000744 + if(!trick_mode) { + 80008dc: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 80008e0: 2b00 cmp r3, #0 + 80008e2: f47f adca bne.w 800047a + se2_clear_tricks(); + 80008e6: f007 fa31 bl 8007d4c + 80008ea: e5c6 b.n 800047a + if(trick_mode) { + 80008ec: f89d 102c ldrb.w r1, [sp, #44] ; 0x2c + 80008f0: 2900 cmp r1, #0 + 80008f2: f47f af27 bne.w 8000744 + if(slot->pin_len > 16) { + 80008f6: f8d5 1170 ldr.w r1, [r5, #368] ; 0x170 + 80008fa: 2910 cmp r1, #16 + 80008fc: dc4b bgt.n 8000996 + if(se2_test_trick_pin(slot->pin, slot->pin_len, slot, true)) { + 80008fe: f505 70b0 add.w r0, r5, #352 ; 0x160 + 8000902: f007 fa89 bl 8007e18 + 8000906: 2800 cmp r0, #0 + 8000908: f47f adb7 bne.w 800047a + 800090c: e71a b.n 8000744 + if(!trick_mode) { + 800090e: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 8000912: 2b00 cmp r3, #0 + 8000914: f47f adb1 bne.w 800047a + rv = se2_save_trick(slot); + 8000918: 4610 mov r0, r2 + 800091a: f007 fb9d bl 8008058 + 800091e: e58f b.n 8000440 + if(arg2 == 0xBeef) { + 8000920: 9b01 ldr r3, [sp, #4] + 8000922: f64b 62ef movw r2, #48879 ; 0xbeef + 8000926: 4293 cmp r3, r2 + 8000928: d103 bne.n 8000932 + fast_wipe(); + 800092a: f001 fe87 bl 800263c + rv = EPERM; + 800092e: 2401 movs r4, #1 + 8000930: e5a3 b.n 800047a + } else if(arg2 == 0xDead) { + 8000932: f64d 62ad movw r2, #57005 ; 0xdead + 8000936: 4293 cmp r3, r2 + 8000938: d1f9 bne.n 800092e + mcu_key_clear(NULL); + 800093a: 2000 movs r0, #0 + 800093c: f001 fd8c bl 8002458 + oled_show(screen_wiped); + 8000940: 482d ldr r0, [pc, #180] ; (80009f8 ) + 8000942: f000 fa7f bl 8000e44 + LOCKUP_FOREVER(); + 8000946: bf30 wfi + 8000948: e7fd b.n 8000946 + if(arg2 == 0xDead) fast_brick(); + 800094a: 9a01 ldr r2, [sp, #4] + 800094c: f64d 63ad movw r3, #57005 ; 0xdead + 8000950: 429a cmp r2, r3 + 8000952: d1ec bne.n 800092e + 8000954: f001 fe44 bl 80025e0 + 8000958: e7e9 b.n 800092e + REQUIRE_OUT(8); + 800095a: 2300 movs r3, #0 + 800095c: 2108 movs r1, #8 + 800095e: 4628 mov r0, r5 + 8000960: f7ff fd02 bl 8000368 + 8000964: 4604 mov r4, r0 + 8000966: 2800 cmp r0, #0 + 8000968: f47f ad87 bne.w 800047a + mcu_key_usage(avail, consumed, total); + 800096c: f105 0208 add.w r2, r5, #8 + 8000970: 1d29 adds r1, r5, #4 + 8000972: 4628 mov r0, r5 + 8000974: f001 fd9e bl 80024b4 + break; + 8000978: e57f b.n 800047a + REQUIRE_OUT(33); + 800097a: 2300 movs r3, #0 + 800097c: 2121 movs r1, #33 ; 0x21 + 800097e: 4628 mov r0, r5 + 8000980: f7ff fcf2 bl 8000368 + 8000984: 4604 mov r4, r0 + 8000986: 2800 cmp r0, #0 + 8000988: f47f ad77 bne.w 800047a + switch(arg2) { + 800098c: 9b01 ldr r3, [sp, #4] + 800098e: 2b01 cmp r3, #1 + 8000990: d003 beq.n 800099a + 8000992: 2b02 cmp r3, #2 + 8000994: d008 beq.n 80009a8 + rv = ERANGE; + 8000996: 2422 movs r4, #34 ; 0x22 + 8000998: e56f b.n 800047a + ae_setup(); + 800099a: f002 f813 bl 80029c4 + ae_secure_random(&buf_io[1]); + 800099e: 1c68 adds r0, r5, #1 + 80009a0: f002 fb78 bl 8003094 + buf_io[0] = 32; + 80009a4: 2320 movs r3, #32 + 80009a6: e707 b.n 80007b8 + se2_read_rng(&buf_io[1]); + 80009a8: 1c68 adds r0, r5, #1 + 80009aa: f007 fd39 bl 8008420 + buf_io[0] = 8; + 80009ae: 2308 movs r3, #8 + 80009b0: e702 b.n 80007b8 + REQUIRE_OUT(80); + 80009b2: 2300 movs r3, #0 + 80009b4: 2150 movs r1, #80 ; 0x50 + 80009b6: 4628 mov r0, r5 + 80009b8: f7ff fcd6 bl 8000368 + 80009bc: 4604 mov r4, r0 + 80009be: 2800 cmp r0, #0 + 80009c0: f47f ad5b bne.w 800047a + strcpy((char *)buf_io, "ATECC608B\nDS28C36B"); + 80009c4: 490d ldr r1, [pc, #52] ; (80009fc ) + 80009c6: 4628 mov r0, r5 + 80009c8: f00c fe6a bl 800d6a0 + break; + 80009cc: e555 b.n 800047a + if(incoming_lr <= BL_FLASH_BASE || incoming_lr >= (uint32_t)&firewall_starts) { + 80009ce: f1b4 6f00 cmp.w r4, #134217728 ; 0x8000000 + 80009d2: d902 bls.n 80009da + 80009d4: 4b0a ldr r3, [pc, #40] ; (8000a00 ) + 80009d6: 429c cmp r4, r3 + 80009d8: d302 bcc.n 80009e0 + fatal_error("LR"); + 80009da: 480a ldr r0, [pc, #40] ; (8000a04 ) + 80009dc: f000 f834 bl 8000a48 + system_startup(); + 80009e0: f000 f890 bl 8000b04 + break; + 80009e4: e6ed b.n 80007c2 + rv = EAGAIN; + 80009e6: 240b movs r4, #11 + 80009e8: e547 b.n 800047a + 80009ea: bf00 nop + 80009ec: 0801c050 .word 0x0801c050 + 80009f0: 0801c070 .word 0x0801c070 + 80009f4: 40022000 .word 0x40022000 + 80009f8: 0800e310 .word 0x0800e310 + 80009fc: 0800d740 .word 0x0800d740 + 8000a00: 08000300 .word 0x08000300 + 8000a04: 0800d753 .word 0x0800d753 + +08000a08 : +// + static inline void +memset4(uint32_t *dest, uint32_t value, uint32_t byte_len) +{ + for(; byte_len; byte_len-=4, dest++) { + *dest = value; + 8000a08: 4a0a ldr r2, [pc, #40] ; (8000a34 ) + for(; byte_len; byte_len-=4, dest++) { + 8000a0a: 490b ldr r1, [pc, #44] ; (8000a38 ) + +// wipe_all_sram() +// + void +wipe_all_sram(void) +{ + 8000a0c: f04f 5300 mov.w r3, #536870912 ; 0x20000000 + *dest = value; + 8000a10: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a14: 428b cmp r3, r1 + 8000a16: d1fb bne.n 8000a10 + 8000a18: 4908 ldr r1, [pc, #32] ; (8000a3c ) + 8000a1a: f04f 5380 mov.w r3, #268435456 ; 0x10000000 + *dest = value; + 8000a1e: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a22: 428b cmp r3, r1 + 8000a24: d1fb bne.n 8000a1e + 8000a26: 4b06 ldr r3, [pc, #24] ; (8000a40 ) + 8000a28: 4906 ldr r1, [pc, #24] ; (8000a44 ) + *dest = value; + 8000a2a: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a2e: 428b cmp r3, r1 + 8000a30: d1fb bne.n 8000a2a + STATIC_ASSERT((SRAM3_BASE + SRAM3_SIZE) - BL_SRAM_BASE == 8192); + + memset4((void *)SRAM1_BASE, noise, SRAM1_SIZE_MAX); + memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE); + memset4((void *)SRAM3_BASE, noise, SRAM3_SIZE - (BL_SRAM_BASE - SRAM3_BASE)); +} + 8000a32: 4770 bx lr + 8000a34: deadbeef .word 0xdeadbeef + 8000a38: 20030000 .word 0x20030000 + 8000a3c: 10010000 .word 0x10010000 + 8000a40: 20040000 .word 0x20040000 + 8000a44: 20042000 .word 0x20042000 + +08000a48 : + +// fatal_error(const char *msg) +// + void __attribute__((noreturn)) +fatal_error(const char *msgvoid) +{ + 8000a48: b508 push {r3, lr} + oled_setup(); + 8000a4a: f000 f96b bl 8000d24 + oled_show(screen_fatal); + 8000a4e: 4802 ldr r0, [pc, #8] ; (8000a58 ) + 8000a50: f000 f9f8 bl 8000e44 + BREAKPOINT; +#endif + + // Maybe should do a reset after a delay, like with + // the watchdog timer or something. + LOCKUP_FOREVER(); + 8000a54: bf30 wfi + 8000a56: e7fd b.n 8000a54 + 8000a58: 0800db52 .word 0x0800db52 + +08000a5c : + +// fatal_mitm() +// + void __attribute__((noreturn)) +fatal_mitm(void) +{ + 8000a5c: b508 push {r3, lr} + oled_setup(); + 8000a5e: f000 f961 bl 8000d24 + oled_show(screen_mitm); + 8000a62: 4803 ldr r0, [pc, #12] ; (8000a70 ) + 8000a64: f000 f9ee bl 8000e44 + +#ifdef RELEASE + wipe_all_sram(); + 8000a68: f7ff ffce bl 8000a08 +#endif + + LOCKUP_FOREVER(); + 8000a6c: bf30 wfi + 8000a6e: e7fd b.n 8000a6c + 8000a70: 0800dc56 .word 0x0800dc56 + +08000a74 : + +// enter_dfu() +// + void __attribute__((noreturn)) +enter_dfu(void) +{ + 8000a74: b507 push {r0, r1, r2, lr} + puts("enter_dfu()"); + 8000a76: 481f ldr r0, [pc, #124] ; (8000af4 ) + 8000a78: f004 f998 bl 8004dac + + // clear the green light, if set + ae_setup(); + 8000a7c: f001 ffa2 bl 80029c4 + ae_set_gpio(0); + 8000a80: 2000 movs r0, #0 + 8000a82: f002 fd21 bl 80034c8 + + // Reset huge parts of the chip + __HAL_RCC_APB1_FORCE_RESET(); + 8000a86: 4b1c ldr r3, [pc, #112] ; (8000af8 ) + 8000a88: f04f 31ff mov.w r1, #4294967295 ; 0xffffffff + __HAL_RCC_APB1_RELEASE_RESET(); + 8000a8c: 2200 movs r2, #0 + __HAL_RCC_APB1_FORCE_RESET(); + 8000a8e: 6399 str r1, [r3, #56] ; 0x38 + 8000a90: 63d9 str r1, [r3, #60] ; 0x3c + __HAL_RCC_APB1_RELEASE_RESET(); + 8000a92: 639a str r2, [r3, #56] ; 0x38 + 8000a94: 63da str r2, [r3, #60] ; 0x3c + + __HAL_RCC_APB2_FORCE_RESET(); + 8000a96: 6419 str r1, [r3, #64] ; 0x40 + __HAL_RCC_APB2_RELEASE_RESET(); + 8000a98: 641a str r2, [r3, #64] ; 0x40 + + __HAL_RCC_AHB1_FORCE_RESET(); + 8000a9a: 6299 str r1, [r3, #40] ; 0x28 + __HAL_RCC_AHB1_RELEASE_RESET(); + 8000a9c: 629a str r2, [r3, #40] ; 0x28 + // But not this; it borks things. + __HAL_RCC_AHB2_FORCE_RESET(); + __HAL_RCC_AHB2_RELEASE_RESET(); +#endif + + __HAL_RCC_AHB3_FORCE_RESET(); + 8000a9e: 6319 str r1, [r3, #48] ; 0x30 + __HAL_RCC_AHB3_RELEASE_RESET(); + 8000aa0: 631a str r2, [r3, #48] ; 0x30 + + __HAL_FIREWALL_PREARM_ENABLE(); + 8000aa2: f5a3 4374 sub.w r3, r3, #62464 ; 0xf400 + 8000aa6: 6a1a ldr r2, [r3, #32] + 8000aa8: f042 0201 orr.w r2, r2, #1 + 8000aac: 621a str r2, [r3, #32] + 8000aae: 6a1b ldr r3, [r3, #32] + 8000ab0: f003 0301 and.w r3, r3, #1 + 8000ab4: 9301 str r3, [sp, #4] + 8000ab6: 9b01 ldr r3, [sp, #4] + + // Wipe all of memory SRAM, just in case + // there is some way to trick us into DFU + // after sensitive content in place. + wipe_all_sram(); + 8000ab8: f7ff ffa6 bl 8000a08 + rng_delay(); + 8000abc: f001 fe5e bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000ac0: 4b0e ldr r3, [pc, #56] ; (8000afc ) + 8000ac2: 6a1b ldr r3, [r3, #32] + 8000ac4: b2db uxtb r3, r3 + + if(flash_is_security_level2()) { + 8000ac6: 2bcc cmp r3, #204 ; 0xcc + 8000ac8: d101 bne.n 8000ace + // cannot do DFU in RDP=2, so just die. Helps to preserve screen + LOCKUP_FOREVER(); + 8000aca: bf30 wfi + 8000acc: e7fd b.n 8000aca + } + + // Reset clocks. + HAL_RCC_DeInit(); + 8000ace: f007 fde5 bl 800869c + + // move system ROM into 0x0 + __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); + 8000ad2: 4a0b ldr r2, [pc, #44] ; (8000b00 ) + 8000ad4: 6813 ldr r3, [r2, #0] + 8000ad6: f023 0307 bic.w r3, r3, #7 + 8000ada: f043 0301 orr.w r3, r3, #1 + 8000ade: 6013 str r3, [r2, #0] + + // need this here?! + asm("nop; nop; nop; nop;"); + 8000ae0: bf00 nop + 8000ae2: bf00 nop + 8000ae4: bf00 nop + 8000ae6: bf00 nop + + // simulate a reset vector + __ASM volatile ("movs r0, #0\n" + 8000ae8: 2000 movs r0, #0 + 8000aea: 6803 ldr r3, [r0, #0] + 8000aec: f383 8808 msr MSP, r3 + 8000af0: 6843 ldr r3, [r0, #4] + 8000af2: 4798 blx r3 + "ldr r3, [r0, #4]\n" + "blx r3" + : : : "r0", "r3"); // also SP + + // NOT-REACHED. + __builtin_unreachable(); + 8000af4: 0800d756 .word 0x0800d756 + 8000af8: 40021000 .word 0x40021000 + 8000afc: 40022000 .word 0x40022000 + 8000b00: 40010000 .word 0x40010000 + +08000b04 : +{ + 8000b04: b510 push {r4, lr} + system_init0(); + 8000b06: f001 f985 bl 8001e14 + clocks_setup(); + 8000b0a: f001 f9a5 bl 8001e58 + rng_setup(); // needs to be super early + 8000b0e: f001 fdf3 bl 80026f8 + rng_delay(); + 8000b12: f001 fe33 bl 800277c + if(!check_all_ones(rom_secrets->bag_number, sizeof(rom_secrets->bag_number)) + 8000b16: 4838 ldr r0, [pc, #224] ; (8000bf8 ) + 8000b18: 2120 movs r1, #32 + 8000b1a: f001 fdb1 bl 8002680 + 8000b1e: b948 cbnz r0, 8000b34 + rng_delay(); + 8000b20: f001 fe2c bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000b24: 4b35 ldr r3, [pc, #212] ; (8000bfc ) + 8000b26: 6a1b ldr r3, [r3, #32] + 8000b28: b2db uxtb r3, r3 + && !flash_is_security_level2() + 8000b2a: 2bcc cmp r3, #204 ; 0xcc + 8000b2c: d002 beq.n 8000b34 + flash_lockdown_hard(OB_RDP_LEVEL_2); + 8000b2e: 20cc movs r0, #204 ; 0xcc + 8000b30: f001 fc2a bl 8002388 + gpio_setup(); + 8000b34: f002 fef0 bl 8003918 + uint32_t reset_reason = RCC->CSR; + 8000b38: 4c31 ldr r4, [pc, #196] ; (8000c00 ) + console_setup(); + 8000b3a: f004 f85d bl 8004bf8 + puts2(BOOT_BANNER); + 8000b3e: 4831 ldr r0, [pc, #196] ; (8000c04 ) + 8000b40: f004 f8a6 bl 8004c90 + puts(version_string); + 8000b44: 4830 ldr r0, [pc, #192] ; (8000c08 ) + 8000b46: f004 f931 bl 8004dac + uint32_t reset_reason = RCC->CSR; + 8000b4a: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + if(reset_reason & RCC_CSR_FWRSTF) { + 8000b4e: 01db lsls r3, r3, #7 + 8000b50: d502 bpl.n 8000b58 + puts(">FIREWALLED<"); + 8000b52: 482e ldr r0, [pc, #184] ; (8000c0c ) + 8000b54: f004 f92a bl 8004dac + SET_BIT(RCC->CSR, RCC_CSR_RMVF); + 8000b58: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8000b5c: f443 0300 orr.w r3, r3, #8388608 ; 0x800000 + 8000b60: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) { + 8000b64: 4c2a ldr r4, [pc, #168] ; (8000c10 ) + pin_setup0(); + 8000b66: f003 f935 bl 8003dd4 + rng_delay(); + 8000b6a: f001 fe07 bl 800277c + oled_setup(); + 8000b6e: f000 f8d9 bl 8000d24 + if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) { + 8000b72: 4928 ldr r1, [pc, #160] ; (8000c14 ) + 8000b74: 2208 movs r2, #8 + 8000b76: 4620 mov r0, r4 + 8000b78: f00c fd44 bl 800d604 + 8000b7c: b928 cbnz r0, 8000b8a + dfu_flag->magic[0] = 0; + 8000b7e: 7020 strb r0, [r4, #0] + oled_show(dfu_flag->screen); + 8000b80: 68a0 ldr r0, [r4, #8] + 8000b82: f000 f95f bl 8000e44 + enter_dfu(); + 8000b86: f7ff ff75 bl 8000a74 + rng_delay(); + 8000b8a: f001 fdf7 bl 800277c + oled_show_progress(screen_verify, 0); + 8000b8e: 2100 movs r1, #0 + 8000b90: 4821 ldr r0, [pc, #132] ; (8000c18 ) + 8000b92: f000 f999 bl 8000ec8 + wipe_all_sram(); + 8000b96: f7ff ff37 bl 8000a08 + ae_setup(); + 8000b9a: f001 ff13 bl 80029c4 + ae_set_gpio(0); // turn light red + 8000b9e: 2000 movs r0, #0 + 8000ba0: f002 fc92 bl 80034c8 + se2_setup(); + 8000ba4: f007 f88c bl 8007cc0 + se2_probe(); + 8000ba8: f006 fe10 bl 80077cc + flash_setup(); + 8000bac: f001 fb56 bl 800225c + psram_setup(); + 8000bb0: f004 f934 bl 8004e1c + if(ae_pair_unlock() != 0) { + 8000bb4: f002 f8fc bl 8002db0 + 8000bb8: b138 cbz r0, 8000bca + oled_show(screen_brick); + 8000bba: 4818 ldr r0, [pc, #96] ; (8000c1c ) + 8000bbc: f000 f942 bl 8000e44 + puts("pair-bricked"); + 8000bc0: 4817 ldr r0, [pc, #92] ; (8000c20 ) + 8000bc2: f004 f8f3 bl 8004dac + LOCKUP_FOREVER(); + 8000bc6: bf30 wfi + 8000bc8: e7fd b.n 8000bc6 + puts2("Verify: "); + 8000bca: 4816 ldr r0, [pc, #88] ; (8000c24 ) + 8000bcc: f004 f860 bl 8004c90 + bool main_ok = verify_firmware(); + 8000bd0: f001 f8a4 bl 8001d1c + if(main_ok) { + 8000bd4: b120 cbz r0, 8000be0 +} + 8000bd6: e8bd 4010 ldmia.w sp!, {r4, lr} + oled_show(screen_blankish); + 8000bda: 4813 ldr r0, [pc, #76] ; (8000c28 ) + 8000bdc: f000 b932 b.w 8000e44 + psram_recover_firmware(); + 8000be0: f004 fa6a bl 80050b8 + rng_delay(); + 8000be4: f001 fdca bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000be8: 4b04 ldr r3, [pc, #16] ; (8000bfc ) + 8000bea: 6a1b ldr r3, [r3, #32] + 8000bec: b2db uxtb r3, r3 + if(!flash_is_security_level2()) { + 8000bee: 2bcc cmp r3, #204 ; 0xcc + 8000bf0: d1c9 bne.n 8000b86 + while(1) sdcard_recovery(); + 8000bf2: f004 fc11 bl 8005418 + 8000bf6: e7fc b.n 8000bf2 + 8000bf8: 0801c050 .word 0x0801c050 + 8000bfc: 40022000 .word 0x40022000 + 8000c00: 40021000 .word 0x40021000 + 8000c04: 0800d762 .word 0x0800d762 + 8000c08: 0800e720 .word 0x0800e720 + 8000c0c: 0800d776 .word 0x0800d776 + 8000c10: 20008000 .word 0x20008000 + 8000c14: 0800d737 .word 0x0800d737 + 8000c18: 0800e242 .word 0x0800e242 + 8000c1c: 0800d80b .word 0x0800d80b + 8000c20: 0800d783 .word 0x0800d783 + 8000c24: 0800d790 .word 0x0800d790 + 8000c28: 0800d7de .word 0x0800d7de + +08000c2c : + static inline void +write_bytes(int len, const uint8_t *buf) +{ +#ifndef DISABLE_OLED + // send via SPI(1) + HAL_SPI_Transmit(&spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); + 8000c2c: b282 uxth r2, r0 + 8000c2e: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8000c32: 4801 ldr r0, [pc, #4] ; (8000c38 ) + 8000c34: f000 bc06 b.w 8001444 + 8000c38: 2009e154 .word 0x2009e154 + +08000c3c : + +// oled_write_cmd() +// + void +oled_write_cmd(uint8_t cmd) +{ + 8000c3c: b507 push {r0, r1, r2, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c3e: 2201 movs r2, #1 +{ + 8000c40: f88d 0007 strb.w r0, [sp, #7] + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c44: 2110 movs r1, #16 + 8000c46: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c4a: f000 fb5b bl 8001304 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 0); + 8000c4e: 2200 movs r2, #0 + 8000c50: f44f 7180 mov.w r1, #256 ; 0x100 + 8000c54: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c58: f000 fb54 bl 8001304 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000c5c: 2200 movs r2, #0 + 8000c5e: 2110 movs r1, #16 + 8000c60: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c64: f000 fb4e bl 8001304 + + write_bytes(1, &cmd); + 8000c68: f10d 0107 add.w r1, sp, #7 + 8000c6c: 2001 movs r0, #1 + 8000c6e: f7ff ffdd bl 8000c2c + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c72: 2201 movs r2, #1 + 8000c74: 2110 movs r1, #16 + 8000c76: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c7a: f000 fb43 bl 8001304 +} + 8000c7e: b003 add sp, #12 + 8000c80: f85d fb04 ldr.w pc, [sp], #4 + +08000c84 : + +// oled_write_cmd_sequence() +// + void +oled_write_cmd_sequence(int len, const uint8_t *cmds) +{ + 8000c84: b570 push {r4, r5, r6, lr} + 8000c86: 4605 mov r5, r0 + 8000c88: 460e mov r6, r1 + for(int i=0; i + oled_write_cmd(cmds[i]); + } +} + 8000c90: bd70 pop {r4, r5, r6, pc} + oled_write_cmd(cmds[i]); + 8000c92: 5d30 ldrb r0, [r6, r4] + 8000c94: f7ff ffd2 bl 8000c3c + for(int i=0; i + +08000c9c : + +// oled_write_data() +// + void +oled_write_data(int len, const uint8_t *pixels) +{ + 8000c9c: b538 push {r3, r4, r5, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c9e: 2201 movs r2, #1 +{ + 8000ca0: 4604 mov r4, r0 + 8000ca2: 460d mov r5, r1 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000ca4: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000ca8: 2110 movs r1, #16 + 8000caa: f000 fb2b bl 8001304 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 1); + 8000cae: 2201 movs r2, #1 + 8000cb0: f44f 7180 mov.w r1, #256 ; 0x100 + 8000cb4: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cb8: f000 fb24 bl 8001304 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000cbc: 2200 movs r2, #0 + 8000cbe: 2110 movs r1, #16 + 8000cc0: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cc4: f000 fb1e bl 8001304 + + write_bytes(len, pixels); + 8000cc8: 4629 mov r1, r5 + 8000cca: 4620 mov r0, r4 + 8000ccc: f7ff ffae bl 8000c2c + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); +} + 8000cd0: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000cd4: 2201 movs r2, #1 + 8000cd6: 2110 movs r1, #16 + 8000cd8: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cdc: f000 bb12 b.w 8001304 + +08000ce0 : +// +// Just setup SPI, do not reset display, etc. +// + void +oled_spi_setup(void) +{ + 8000ce0: b538 push {r3, r4, r5, lr} +#ifndef DISABLE_OLED + // might already be setup + if(spi_port.Instance == SPI1) return; + 8000ce2: 4c0e ldr r4, [pc, #56] ; (8000d1c ) + 8000ce4: 4d0e ldr r5, [pc, #56] ; (8000d20 ) + 8000ce6: 6823 ldr r3, [r4, #0] + 8000ce8: 42ab cmp r3, r5 + 8000cea: d016 beq.n 8000d1a + + memset(&spi_port, 0, sizeof(spi_port)); + 8000cec: f104 0008 add.w r0, r4, #8 + 8000cf0: 225c movs r2, #92 ; 0x5c + 8000cf2: 2100 movs r1, #0 + 8000cf4: f00c fcbe bl 800d674 + + spi_port.Instance = SPI1; + + // see SPI_InitTypeDef + spi_port.Init.Mode = SPI_MODE_MASTER; + 8000cf8: f44f 7382 mov.w r3, #260 ; 0x104 + 8000cfc: 6063 str r3, [r4, #4] + spi_port.Init.Direction = SPI_DIRECTION_2LINES; + spi_port.Init.DataSize = SPI_DATASIZE_8BIT; + 8000cfe: f44f 63e0 mov.w r3, #1792 ; 0x700 + 8000d02: 60e3 str r3, [r4, #12] + spi_port.Init.CLKPolarity = SPI_POLARITY_LOW; + spi_port.Init.CLKPhase = SPI_PHASE_1EDGE; + spi_port.Init.NSS = SPI_NSS_SOFT; + spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // conservative + 8000d04: f44f 7000 mov.w r0, #512 ; 0x200 + 8000d08: 2318 movs r3, #24 + 8000d0a: e9c4 0306 strd r0, r3, [r4, #24] + spi_port.Instance = SPI1; + 8000d0e: 6025 str r5, [r4, #0] + spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB; + spi_port.Init.TIMode = SPI_TIMODE_DISABLED; + spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; + + HAL_SPI_Init(&spi_port); + 8000d10: 4620 mov r0, r4 +#endif +} + 8000d12: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + HAL_SPI_Init(&spi_port); + 8000d16: f000 bb37 b.w 8001388 +} + 8000d1a: bd38 pop {r3, r4, r5, pc} + 8000d1c: 2009e154 .word 0x2009e154 + 8000d20: 40013000 .word 0x40013000 + +08000d24 : +// +// Ok to call this lots. +// + void +oled_setup(void) +{ + 8000d24: b530 push {r4, r5, lr} + puts("oled disabled");return; // disable so I can use MCO +#endif + + static uint32_t inited; + + if(inited == 0x238a572F) { + 8000d26: 4b2c ldr r3, [pc, #176] ; (8000dd8 ) + 8000d28: 4a2c ldr r2, [pc, #176] ; (8000ddc ) + 8000d2a: 6819 ldr r1, [r3, #0] + 8000d2c: 4291 cmp r1, r2 +{ + 8000d2e: b089 sub sp, #36 ; 0x24 + if(inited == 0x238a572F) { + 8000d30: d050 beq.n 8000dd4 + return; + } + inited = 0x238a572F; + 8000d32: 601a str r2, [r3, #0] + + // enable some internal clocks + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8000d34: 4b2a ldr r3, [pc, #168] ; (8000de0 ) + __HAL_RCC_SPI1_CLK_ENABLE(); + + // simple pins + GPIO_InitTypeDef setup = { + 8000d36: 4d2b ldr r5, [pc, #172] ; (8000de4 ) + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8000d38: 6cda ldr r2, [r3, #76] ; 0x4c + 8000d3a: f042 0201 orr.w r2, r2, #1 + 8000d3e: 64da str r2, [r3, #76] ; 0x4c + 8000d40: 6cda ldr r2, [r3, #76] ; 0x4c + 8000d42: f002 0201 and.w r2, r2, #1 + 8000d46: 9201 str r2, [sp, #4] + 8000d48: 9a01 ldr r2, [sp, #4] + __HAL_RCC_SPI1_CLK_ENABLE(); + 8000d4a: 6e1a ldr r2, [r3, #96] ; 0x60 + 8000d4c: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8000d50: 661a str r2, [r3, #96] ; 0x60 + 8000d52: 6e1b ldr r3, [r3, #96] ; 0x60 + 8000d54: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 8000d58: 9302 str r3, [sp, #8] + 8000d5a: 9b02 ldr r3, [sp, #8] + GPIO_InitTypeDef setup = { + 8000d5c: cd0f ldmia r5!, {r0, r1, r2, r3} + 8000d5e: ac03 add r4, sp, #12 + 8000d60: c40f stmia r4!, {r0, r1, r2, r3} + 8000d62: 682b ldr r3, [r5, #0] + 8000d64: 6023 str r3, [r4, #0] + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = 0, + }; + HAL_GPIO_Init(GPIOA, &setup); + 8000d66: a903 add r1, sp, #12 + 8000d68: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000d6c: f000 f950 bl 8001010 + + // starting values + HAL_GPIO_WritePin(GPIOA, RESET_PIN | CS_PIN | DC_PIN, 1); + 8000d70: 2201 movs r2, #1 + 8000d72: f44f 71a8 mov.w r1, #336 ; 0x150 + 8000d76: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000d7a: f000 fac3 bl 8001304 + + // SPI pins + setup.Pin = SPI_SCK | SPI_MOSI; + setup.Mode = GPIO_MODE_AF_PP; + 8000d7e: 22a0 movs r2, #160 ; 0xa0 + 8000d80: 2302 movs r3, #2 + 8000d82: e9cd 2303 strd r2, r3, [sp, #12] + setup.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(GPIOA, &setup); + 8000d86: a903 add r1, sp, #12 + setup.Alternate = GPIO_AF5_SPI1; + 8000d88: 2305 movs r3, #5 + HAL_GPIO_Init(GPIOA, &setup); + 8000d8a: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + setup.Alternate = GPIO_AF5_SPI1; + 8000d8e: 9307 str r3, [sp, #28] + HAL_GPIO_Init(GPIOA, &setup); + 8000d90: f000 f93e bl 8001010 + + // lock the RESET pin so that St's DFU code doesn't clear screen + // it might be trying to use it a MISO signal for SPI loading + HAL_GPIO_LockPin(GPIOA, RESET_PIN | CS_PIN | DC_PIN); + 8000d94: f44f 71a8 mov.w r1, #336 ; 0x150 + 8000d98: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000d9c: f000 fabb bl 8001316 + + // 10ms low-going pulse on reset pin + delay_ms(1); + 8000da0: 2001 movs r0, #1 + 8000da2: f002 fda9 bl 80038f8 + HAL_GPIO_WritePin(GPIOA, RESET_PIN, 0); + 8000da6: 2200 movs r2, #0 + 8000da8: 2140 movs r1, #64 ; 0x40 + 8000daa: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000dae: f000 faa9 bl 8001304 + delay_ms(10); + 8000db2: 200a movs r0, #10 + 8000db4: f002 fda0 bl 80038f8 + HAL_GPIO_WritePin(GPIOA, RESET_PIN, 1); + 8000db8: 2201 movs r2, #1 + 8000dba: 2140 movs r1, #64 ; 0x40 + 8000dbc: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000dc0: f000 faa0 bl 8001304 + + oled_spi_setup(); + 8000dc4: f7ff ff8c bl 8000ce0 + // this code: + // '0x37c', '0x1700', '0x603' + //SPI1->CR1 = 0x354; + + // write a sequence to reset things + oled_write_cmd_sequence(sizeof(reset_commands), reset_commands); + 8000dc8: 4907 ldr r1, [pc, #28] ; (8000de8 ) + 8000dca: 2019 movs r0, #25 + 8000dcc: f7ff ff5a bl 8000c84 + + rng_delay(); + 8000dd0: f001 fcd4 bl 800277c +} + 8000dd4: b009 add sp, #36 ; 0x24 + 8000dd6: bd30 pop {r4, r5, pc} + 8000dd8: 2009e150 .word 0x2009e150 + 8000ddc: 238a572f .word 0x238a572f + 8000de0: 40021000 .word 0x40021000 + 8000de4: 0800d79c .word 0x0800d79c + 8000de8: 0800d7bf .word 0x0800d7bf + +08000dec : +// +// No decompression. +// + void +oled_show_raw(uint32_t len, const uint8_t *pixels) +{ + 8000dec: b538 push {r3, r4, r5, lr} + 8000dee: 4604 mov r4, r0 + 8000df0: 460d mov r5, r1 + oled_setup(); + 8000df2: f7ff ff97 bl 8000d24 + + oled_write_cmd_sequence(sizeof(before_show), before_show); + 8000df6: 4912 ldr r1, [pc, #72] ; (8000e40 ) + 8000df8: 2006 movs r0, #6 + 8000dfa: f7ff ff43 bl 8000c84 + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000dfe: 2201 movs r2, #1 + 8000e00: 2110 movs r1, #16 + 8000e02: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e06: f000 fa7d bl 8001304 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 1); + 8000e0a: 2201 movs r2, #1 + 8000e0c: f44f 7180 mov.w r1, #256 ; 0x100 + 8000e10: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e14: f000 fa76 bl 8001304 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000e18: 2200 movs r2, #0 + 8000e1a: 2110 movs r1, #16 + 8000e1c: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e20: f000 fa70 bl 8001304 + + write_bytes(len, pixels); + 8000e24: 4629 mov r1, r5 + 8000e26: 4620 mov r0, r4 + 8000e28: f7ff ff00 bl 8000c2c + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000e2c: 2201 movs r2, #1 + 8000e2e: 2110 movs r1, #16 + 8000e30: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e34: f000 fa66 bl 8001304 + rng_delay(); +} + 8000e38: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + rng_delay(); + 8000e3c: f001 bc9e b.w 800277c + 8000e40: 0800d7b9 .word 0x0800d7b9 + +08000e44 : +// +// Perform simple RLE decompression. +// + void +oled_show(const uint8_t *pixels) +{ + 8000e44: b530 push {r4, r5, lr} + 8000e46: b0a1 sub sp, #132 ; 0x84 + 8000e48: 4604 mov r4, r0 + oled_setup(); + 8000e4a: f7ff ff6b bl 8000d24 + + oled_write_cmd_sequence(sizeof(before_show), before_show); + 8000e4e: 491d ldr r1, [pc, #116] ; (8000ec4 ) + 8000e50: 2006 movs r0, #6 + 8000e52: f7ff ff17 bl 8000c84 + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000e56: 2201 movs r2, #1 + 8000e58: 2110 movs r1, #16 + 8000e5a: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e5e: f000 fa51 bl 8001304 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 1); + 8000e62: 2201 movs r2, #1 + 8000e64: f44f 7180 mov.w r1, #256 ; 0x100 + 8000e68: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e6c: f000 fa4a bl 8001304 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000e70: 2200 movs r2, #0 + 8000e72: 2110 movs r1, #16 + 8000e74: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e78: f000 fa44 bl 8001304 + uint8_t buf[127]; + const uint8_t *p = pixels; + + // NOTE: must also update code in oled_show_progress, which dups this heavily. + while(1) { + uint8_t hdr = *(p++); + 8000e7c: 7823 ldrb r3, [r4, #0] + if(!hdr) break; + 8000e7e: b1b3 cbz r3, 8000eae + + uint8_t len = hdr & 0x7f; + 8000e80: f003 057f and.w r5, r3, #127 ; 0x7f + if(hdr & 0x80) { + 8000e84: 061b lsls r3, r3, #24 + 8000e86: d50b bpl.n 8000ea0 + uint8_t hdr = *(p++); + 8000e88: 3401 adds r4, #1 + // random bytes follow + memcpy(buf, p, len); + 8000e8a: 4621 mov r1, r4 + 8000e8c: 462a mov r2, r5 + 8000e8e: 4668 mov r0, sp + 8000e90: f00c fbc8 bl 800d624 + p += len; + 8000e94: 442c add r4, r5 + // repeat same byte + memset(buf, *p, len); + p++; + } + + write_bytes(len, buf); + 8000e96: 4669 mov r1, sp + 8000e98: 4628 mov r0, r5 + 8000e9a: f7ff fec7 bl 8000c2c + while(1) { + 8000e9e: e7ed b.n 8000e7c + memset(buf, *p, len); + 8000ea0: 7861 ldrb r1, [r4, #1] + 8000ea2: 462a mov r2, r5 + 8000ea4: 4668 mov r0, sp + 8000ea6: f00c fbe5 bl 800d674 + p++; + 8000eaa: 3402 adds r4, #2 + 8000eac: e7f3 b.n 8000e96 + } + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000eae: 2201 movs r2, #1 + 8000eb0: 2110 movs r1, #16 + 8000eb2: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000eb6: f000 fa25 bl 8001304 + rng_delay(); + 8000eba: f001 fc5f bl 800277c +} + 8000ebe: b021 add sp, #132 ; 0x84 + 8000ec0: bd30 pop {r4, r5, pc} + 8000ec2: bf00 nop + 8000ec4: 0800d7b9 .word 0x0800d7b9 + +08000ec8 : +// +// Perform simple RLE decompression, and add a bar on final screen line. +// + void +oled_show_progress(const uint8_t *pixels, int progress) +{ + 8000ec8: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8000ecc: b0a1 sub sp, #132 ; 0x84 + 8000ece: 460d mov r5, r1 + 8000ed0: 4606 mov r6, r0 + oled_setup(); + 8000ed2: f7ff ff27 bl 8000d24 + + oled_write_cmd_sequence(sizeof(before_show), before_show); + 8000ed6: 493b ldr r1, [pc, #236] ; (8000fc4 ) + 8000ed8: 2006 movs r0, #6 + 8000eda: f7ff fed3 bl 8000c84 + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000ede: 2201 movs r2, #1 + 8000ee0: 2110 movs r1, #16 + 8000ee2: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000ee6: f000 fa0d bl 8001304 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 1); + 8000eea: 2201 movs r2, #1 + 8000eec: f44f 7180 mov.w r1, #256 ; 0x100 + 8000ef0: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000ef4: f000 fa06 bl 8001304 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000ef8: 2110 movs r1, #16 + 8000efa: 2200 movs r2, #0 + 8000efc: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000f00: f000 fa00 bl 8001304 + + uint8_t buf[127]; + const uint8_t *p = pixels; + + const uint16_t p_start = 896; + uint32_t p_count = 1280 * progress / 1000; + 8000f04: f44f 61a0 mov.w r1, #1280 ; 0x500 + 8000f08: 434d muls r5, r1 + 8000f0a: 2400 movs r4, #0 + 8000f0c: f44f 717a mov.w r1, #1000 ; 0x3e8 + 8000f10: fb95 f5f1 sdiv r5, r5, r1 + + if(p_count > 128) p_count = 128; + 8000f14: 2d80 cmp r5, #128 ; 0x80 + 8000f16: bf28 it cs + 8000f18: 2580 movcs r5, #128 ; 0x80 + uint32_t p_count = 1280 * progress / 1000; + 8000f1a: 46a0 mov r8, r4 + + bool last_line = false; + + uint16_t offset = 0; + while(1) { + uint8_t hdr = *(p++); + 8000f1c: 7833 ldrb r3, [r6, #0] + if(hdr == 0) break; + 8000f1e: 2b00 cmp r3, #0 + 8000f20: d045 beq.n 8000fae + + uint8_t len = hdr & 0x7f; + 8000f22: f003 097f and.w r9, r3, #127 ; 0x7f + if(hdr & 0x80) { + 8000f26: 061b lsls r3, r3, #24 + 8000f28: d524 bpl.n 8000f74 + uint8_t hdr = *(p++); + 8000f2a: 3601 adds r6, #1 + // random bytes follow + memcpy(buf, p, len); + 8000f2c: 4631 mov r1, r6 + 8000f2e: 464a mov r2, r9 + 8000f30: 4668 mov r0, sp + 8000f32: f00c fb77 bl 800d624 + p += len; + 8000f36: 444e add r6, r9 + // repeat same byte + memset(buf, *p, len); + p++; + } + + if(!last_line && (offset+len) >= p_start) { + 8000f38: f1b8 0f00 cmp.w r8, #0 + 8000f3c: d117 bne.n 8000f6e + 8000f3e: eb04 0309 add.w r3, r4, r9 + 8000f42: f5b3 7f60 cmp.w r3, #896 ; 0x380 + 8000f46: db29 blt.n 8000f9c + last_line = true; + + // adjust so we're aligned w/ last line + int h = p_start - offset; + if(h) { + 8000f48: f5d4 7460 rsbs r4, r4, #896 ; 0x380 + 8000f4c: d00d beq.n 8000f6a + write_bytes(h, buf); + 8000f4e: 4669 mov r1, sp + 8000f50: 4620 mov r0, r4 + memmove(buf, buf+h, len-h); + 8000f52: eba9 0904 sub.w r9, r9, r4 + write_bytes(h, buf); + 8000f56: f7ff fe69 bl 8000c2c + memmove(buf, buf+h, len-h); + 8000f5a: 464a mov r2, r9 + 8000f5c: eb0d 0104 add.w r1, sp, r4 + 8000f60: 4668 mov r0, sp + 8000f62: f00c fb6d bl 800d640 + len -= h; + 8000f66: fa5f f989 uxtb.w r9, r9 + offset += h; + 8000f6a: f44f 7460 mov.w r4, #896 ; 0x380 + } + } + + if(last_line) { + 8000f6e: 466b mov r3, sp + while(1) { + 8000f70: 462f mov r7, r5 + 8000f72: e00c b.n 8000f8e + memset(buf, *p, len); + 8000f74: 7871 ldrb r1, [r6, #1] + 8000f76: 464a mov r2, r9 + 8000f78: 4668 mov r0, sp + 8000f7a: f00c fb7b bl 800d674 + p++; + 8000f7e: 3602 adds r6, #2 + 8000f80: e7da b.n 8000f38 + for(int j=0; (p_count > 0) && (j 0) && (j + 8000f90: 1bea subs r2, r5, r7 + 8000f92: 454a cmp r2, r9 + 8000f94: dbf5 blt.n 8000f82 + 8000f96: f04f 0801 mov.w r8, #1 + 8000f9a: e000 b.n 8000f9e + 8000f9c: 462f mov r7, r5 + } + } + + write_bytes(len, buf); + 8000f9e: 4669 mov r1, sp + 8000fa0: 4648 mov r0, r9 + offset += len; + 8000fa2: 444c add r4, r9 + write_bytes(len, buf); + 8000fa4: f7ff fe42 bl 8000c2c + offset += len; + 8000fa8: b2a4 uxth r4, r4 + while(1) { + 8000faa: 463d mov r5, r7 + 8000fac: e7b6 b.n 8000f1c + } + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000fae: 2201 movs r2, #1 + 8000fb0: 2110 movs r1, #16 + 8000fb2: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000fb6: f000 f9a5 bl 8001304 + rng_delay(); + 8000fba: f001 fbdf bl 800277c +} + 8000fbe: b021 add sp, #132 ; 0x84 + 8000fc0: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 8000fc4: 0800d7b9 .word 0x0800d7b9 + +08000fc8 : + +// oled_factory_busy() +// + void +oled_factory_busy(void) +{ + 8000fc8: b510 push {r4, lr} + 8000fca: b0a0 sub sp, #128 ; 0x80 + 8000fcc: 466a mov r2, sp + 8000fce: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8000fd2: 4614 mov r4, r2 + }; + uint8_t data[128]; + + for(int x=0; x<128; x++) { + // each byte here is a vertical column, 8 pixels tall, MSB at bottom + data[x] = (1<<(7 - (x%8))); + 8000fd4: 2001 movs r0, #1 + 8000fd6: f003 0107 and.w r1, r3, #7 + for(int x=0; x<128; x++) { + 8000fda: 3b01 subs r3, #1 + data[x] = (1<<(7 - (x%8))); + 8000fdc: fa00 f101 lsl.w r1, r0, r1 + for(int x=0; x<128; x++) { + 8000fe0: f113 0f81 cmn.w r3, #129 ; 0x81 + data[x] = (1<<(7 - (x%8))); + 8000fe4: f802 1b01 strb.w r1, [r2], #1 + for(int x=0; x<128; x++) { + 8000fe8: d1f5 bne.n 8000fd6 + } + + oled_write_cmd_sequence(sizeof(setup), setup); + 8000fea: 4907 ldr r1, [pc, #28] ; (8001008 ) + 8000fec: 2006 movs r0, #6 + 8000fee: f7ff fe49 bl 8000c84 + oled_write_data(sizeof(data), data); + 8000ff2: 4621 mov r1, r4 + 8000ff4: 2080 movs r0, #128 ; 0x80 + 8000ff6: f7ff fe51 bl 8000c9c + oled_write_cmd_sequence(sizeof(animate), animate); + 8000ffa: 4904 ldr r1, [pc, #16] ; (800100c ) + 8000ffc: 2009 movs r0, #9 + 8000ffe: f7ff fe41 bl 8000c84 +} + 8001002: b020 add sp, #128 ; 0x80 + 8001004: bd10 pop {r4, pc} + 8001006: bf00 nop + 8001008: 0800d7d8 .word 0x0800d7d8 + 800100c: 0800d7b0 .word 0x0800d7b0 + +08001010 : + * @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains + * the configuration information for the specified GPIO peripheral. + * @retval None + */ +void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) +{ + 8001010: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + /*--------------------- EXTI Mode Configuration ------------------------*/ + /* Configure the External Interrupt or event for the current IO */ + if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) + { + /* Enable SYSCFG Clock */ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 8001014: f8df 81b4 ldr.w r8, [pc, #436] ; 80011cc + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + SYSCFG->EXTICR[position >> 2] = temp; + + /* Clear EXTI line configuration */ + temp = EXTI->IMR1; + 8001018: 4c6a ldr r4, [pc, #424] ; (80011c4 ) + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 800101a: f8df 91b4 ldr.w r9, [pc, #436] ; 80011d0 +{ + 800101e: b085 sub sp, #20 + uint32_t position = 0x00; + 8001020: 2300 movs r3, #0 + while (((GPIO_Init->Pin) >> position) != RESET) + 8001022: 680a ldr r2, [r1, #0] + 8001024: fa32 f503 lsrs.w r5, r2, r3 + 8001028: d102 bne.n 8001030 + } + } + + position++; + } +} + 800102a: b005 add sp, #20 + 800102c: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + iocurrent = (GPIO_Init->Pin) & (1U << position); + 8001030: 2701 movs r7, #1 + 8001032: 409f lsls r7, r3 + if(iocurrent) + 8001034: 403a ands r2, r7 + 8001036: f000 80b4 beq.w 80011a2 + if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) + 800103a: 684d ldr r5, [r1, #4] + 800103c: f025 0a10 bic.w sl, r5, #16 + 8001040: f1ba 0f02 cmp.w sl, #2 + 8001044: d116 bne.n 8001074 + temp = GPIOx->AFR[position >> 3]; + 8001046: ea4f 0ed3 mov.w lr, r3, lsr #3 + 800104a: eb00 0e8e add.w lr, r0, lr, lsl #2 + temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 800104e: f003 0b07 and.w fp, r3, #7 + temp = GPIOx->AFR[position >> 3]; + 8001052: f8de 6020 ldr.w r6, [lr, #32] + temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 8001056: ea4f 0b8b mov.w fp, fp, lsl #2 + 800105a: f04f 0c0f mov.w ip, #15 + 800105e: fa0c fc0b lsl.w ip, ip, fp + 8001062: ea26 0c0c bic.w ip, r6, ip + temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07) * 4)); + 8001066: 690e ldr r6, [r1, #16] + 8001068: fa06 f60b lsl.w r6, r6, fp + 800106c: ea46 060c orr.w r6, r6, ip + GPIOx->AFR[position >> 3] = temp; + 8001070: f8ce 6020 str.w r6, [lr, #32] + temp = GPIOx->MODER; + 8001074: f8d0 b000 ldr.w fp, [r0] + temp &= ~(GPIO_MODER_MODE0 << (position * 2)); + 8001078: ea4f 0e43 mov.w lr, r3, lsl #1 + 800107c: f04f 0c03 mov.w ip, #3 + 8001080: fa0c fc0e lsl.w ip, ip, lr + 8001084: ea6f 060c mvn.w r6, ip + 8001088: ea2b 0b0c bic.w fp, fp, ip + temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2)); + 800108c: f005 0c03 and.w ip, r5, #3 + 8001090: fa0c fc0e lsl.w ip, ip, lr + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 8001094: f10a 3aff add.w sl, sl, #4294967295 ; 0xffffffff + temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2)); + 8001098: ea4c 0c0b orr.w ip, ip, fp + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 800109c: f1ba 0f01 cmp.w sl, #1 + temp &= ~(GPIO_MODER_MODE0 << (position * 2)); + 80010a0: 9601 str r6, [sp, #4] + GPIOx->MODER = temp; + 80010a2: f8c0 c000 str.w ip, [r0] + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 80010a6: d815 bhi.n 80010d4 + temp = GPIOx->OSPEEDR; + 80010a8: f8d0 c008 ldr.w ip, [r0, #8] + temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2)); + 80010ac: ea06 0c0c and.w ip, r6, ip + temp |= (GPIO_Init->Speed << (position * 2)); + 80010b0: 68ce ldr r6, [r1, #12] + 80010b2: fa06 fa0e lsl.w sl, r6, lr + 80010b6: ea4a 0c0c orr.w ip, sl, ip + GPIOx->OSPEEDR = temp; + 80010ba: f8c0 c008 str.w ip, [r0, #8] + temp = GPIOx->OTYPER; + 80010be: f8d0 c004 ldr.w ip, [r0, #4] + temp &= ~(GPIO_OTYPER_OT0 << position) ; + 80010c2: ea2c 0707 bic.w r7, ip, r7 + temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4) << position); + 80010c6: f3c5 1c00 ubfx ip, r5, #4, #1 + 80010ca: fa0c fc03 lsl.w ip, ip, r3 + 80010ce: ea4c 0707 orr.w r7, ip, r7 + GPIOx->OTYPER = temp; + 80010d2: 6047 str r7, [r0, #4] + temp = GPIOx->PUPDR; + 80010d4: 68c7 ldr r7, [r0, #12] + temp &= ~(GPIO_PUPDR_PUPD0 << (position * 2)); + 80010d6: 9e01 ldr r6, [sp, #4] + 80010d8: 4037 ands r7, r6 + temp |= ((GPIO_Init->Pull) << (position * 2)); + 80010da: 688e ldr r6, [r1, #8] + 80010dc: fa06 f60e lsl.w r6, r6, lr + 80010e0: 433e orrs r6, r7 + GPIOx->PUPDR = temp; + 80010e2: 60c6 str r6, [r0, #12] + if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) + 80010e4: 00ee lsls r6, r5, #3 + 80010e6: d55c bpl.n 80011a2 + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80010e8: f8d8 6060 ldr.w r6, [r8, #96] ; 0x60 + 80010ec: f046 0601 orr.w r6, r6, #1 + 80010f0: f8c8 6060 str.w r6, [r8, #96] ; 0x60 + 80010f4: f8d8 6060 ldr.w r6, [r8, #96] ; 0x60 + 80010f8: f023 0703 bic.w r7, r3, #3 + 80010fc: f107 4780 add.w r7, r7, #1073741824 ; 0x40000000 + 8001100: f006 0601 and.w r6, r6, #1 + 8001104: f507 3780 add.w r7, r7, #65536 ; 0x10000 + 8001108: 9603 str r6, [sp, #12] + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 800110a: f003 0c03 and.w ip, r3, #3 + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 800110e: 9e03 ldr r6, [sp, #12] + temp = SYSCFG->EXTICR[position >> 2]; + 8001110: f8d7 a008 ldr.w sl, [r7, #8] + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001114: f04f 0e0f mov.w lr, #15 + 8001118: ea4f 0c8c mov.w ip, ip, lsl #2 + 800111c: fa0e f60c lsl.w r6, lr, ip + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 8001120: f1b0 4f90 cmp.w r0, #1207959552 ; 0x48000000 + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001124: ea2a 0e06 bic.w lr, sl, r6 + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 8001128: d03d beq.n 80011a6 + 800112a: 4e27 ldr r6, [pc, #156] ; (80011c8 ) + 800112c: 42b0 cmp r0, r6 + 800112e: d03c beq.n 80011aa + 8001130: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001134: 42b0 cmp r0, r6 + 8001136: d03a beq.n 80011ae + 8001138: f506 6680 add.w r6, r6, #1024 ; 0x400 + 800113c: 42b0 cmp r0, r6 + 800113e: d038 beq.n 80011b2 + 8001140: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001144: 42b0 cmp r0, r6 + 8001146: d036 beq.n 80011b6 + 8001148: f506 6680 add.w r6, r6, #1024 ; 0x400 + 800114c: 42b0 cmp r0, r6 + 800114e: d034 beq.n 80011ba + 8001150: 4548 cmp r0, r9 + 8001152: d034 beq.n 80011be + 8001154: f506 6600 add.w r6, r6, #2048 ; 0x800 + 8001158: 42b0 cmp r0, r6 + 800115a: bf0c ite eq + 800115c: 2607 moveq r6, #7 + 800115e: 2608 movne r6, #8 + 8001160: fa06 f60c lsl.w r6, r6, ip + 8001164: ea46 060e orr.w r6, r6, lr + SYSCFG->EXTICR[position >> 2] = temp; + 8001168: 60be str r6, [r7, #8] + temp = EXTI->IMR1; + 800116a: 6826 ldr r6, [r4, #0] + temp &= ~((uint32_t)iocurrent); + 800116c: 43d7 mvns r7, r2 + if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT) + 800116e: f415 3f80 tst.w r5, #65536 ; 0x10000 + temp &= ~((uint32_t)iocurrent); + 8001172: bf0c ite eq + 8001174: 403e andeq r6, r7 + temp |= iocurrent; + 8001176: 4316 orrne r6, r2 + EXTI->IMR1 = temp; + 8001178: 6026 str r6, [r4, #0] + temp = EXTI->EMR1; + 800117a: 6866 ldr r6, [r4, #4] + if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT) + 800117c: f415 3f00 tst.w r5, #131072 ; 0x20000 + temp &= ~((uint32_t)iocurrent); + 8001180: bf0c ite eq + 8001182: 403e andeq r6, r7 + temp |= iocurrent; + 8001184: 4316 orrne r6, r2 + EXTI->EMR1 = temp; + 8001186: 6066 str r6, [r4, #4] + temp = EXTI->RTSR1; + 8001188: 68a6 ldr r6, [r4, #8] + if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE) + 800118a: f415 1f80 tst.w r5, #1048576 ; 0x100000 + temp &= ~((uint32_t)iocurrent); + 800118e: bf0c ite eq + 8001190: 403e andeq r6, r7 + temp |= iocurrent; + 8001192: 4316 orrne r6, r2 + EXTI->RTSR1 = temp; + 8001194: 60a6 str r6, [r4, #8] + temp = EXTI->FTSR1; + 8001196: 68e6 ldr r6, [r4, #12] + if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE) + 8001198: 02ad lsls r5, r5, #10 + temp &= ~((uint32_t)iocurrent); + 800119a: bf54 ite pl + 800119c: 403e andpl r6, r7 + temp |= iocurrent; + 800119e: 4316 orrmi r6, r2 + EXTI->FTSR1 = temp; + 80011a0: 60e6 str r6, [r4, #12] + position++; + 80011a2: 3301 adds r3, #1 + 80011a4: e73d b.n 8001022 + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 80011a6: 2600 movs r6, #0 + 80011a8: e7da b.n 8001160 + 80011aa: 2601 movs r6, #1 + 80011ac: e7d8 b.n 8001160 + 80011ae: 2602 movs r6, #2 + 80011b0: e7d6 b.n 8001160 + 80011b2: 2603 movs r6, #3 + 80011b4: e7d4 b.n 8001160 + 80011b6: 2604 movs r6, #4 + 80011b8: e7d2 b.n 8001160 + 80011ba: 2605 movs r6, #5 + 80011bc: e7d0 b.n 8001160 + 80011be: 2606 movs r6, #6 + 80011c0: e7ce b.n 8001160 + 80011c2: bf00 nop + 80011c4: 40010400 .word 0x40010400 + 80011c8: 48000400 .word 0x48000400 + 80011cc: 40021000 .word 0x40021000 + 80011d0: 48001800 .word 0x48001800 + +080011d4 : + * @param GPIO_Pin: specifies the port bit to be written. + * This parameter can be one of GPIO_PIN_x where x can be (0..15). + * @retval None + */ +void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) +{ + 80011d4: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + { + tmp = ((uint32_t)0x0F) << (4 * (position & 0x03)); + SYSCFG->EXTICR[position >> 2] &= ~tmp; + + /* Clear EXTI line configuration */ + EXTI->IMR1 &= ~((uint32_t)iocurrent); + 80011d8: 4c43 ldr r4, [pc, #268] ; (80012e8 ) + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 80011da: f8df a114 ldr.w sl, [pc, #276] ; 80012f0 + 80011de: f8df b114 ldr.w fp, [pc, #276] ; 80012f4 + uint32_t position = 0x00; + 80011e2: 2200 movs r2, #0 + iocurrent = (GPIO_Pin) & (1U << position); + 80011e4: f04f 0901 mov.w r9, #1 + while ((GPIO_Pin >> position) != RESET) + 80011e8: fa31 f302 lsrs.w r3, r1, r2 + 80011ec: d101 bne.n 80011f2 + } + } + + position++; + } +} + 80011ee: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + iocurrent = (GPIO_Pin) & (1U << position); + 80011f2: fa09 f802 lsl.w r8, r9, r2 + if (iocurrent) + 80011f6: ea18 0c01 ands.w ip, r8, r1 + 80011fa: d064 beq.n 80012c6 + GPIOx->MODER |= (GPIO_MODER_MODE0 << (position * 2)); + 80011fc: 6805 ldr r5, [r0, #0] + 80011fe: 2303 movs r3, #3 + 8001200: 0056 lsls r6, r2, #1 + 8001202: fa03 f606 lsl.w r6, r3, r6 + GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 8001206: fa22 fe03 lsr.w lr, r2, r3 + GPIOx->MODER |= (GPIO_MODER_MODE0 << (position * 2)); + 800120a: 4335 orrs r5, r6 + 800120c: eb00 0e8e add.w lr, r0, lr, lsl #2 + 8001210: 6005 str r5, [r0, #0] + GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 8001212: f8de 5020 ldr.w r5, [lr, #32] + 8001216: f002 0707 and.w r7, r2, #7 + 800121a: 462b mov r3, r5 + 800121c: 00bf lsls r7, r7, #2 + 800121e: 250f movs r5, #15 + 8001220: fa05 f707 lsl.w r7, r5, r7 + 8001224: ea23 0707 bic.w r7, r3, r7 + 8001228: f8ce 7020 str.w r7, [lr, #32] + GPIOx->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2)); + 800122c: 6887 ldr r7, [r0, #8] + 800122e: ea27 0706 bic.w r7, r7, r6 + 8001232: 6087 str r7, [r0, #8] + GPIOx->OTYPER &= ~(GPIO_OTYPER_OT0 << position) ; + 8001234: 6847 ldr r7, [r0, #4] + 8001236: ea27 0708 bic.w r7, r7, r8 + 800123a: 6047 str r7, [r0, #4] + GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPD0 << (position * 2)); + 800123c: 68c7 ldr r7, [r0, #12] + 800123e: ea27 0606 bic.w r6, r7, r6 + 8001242: 60c6 str r6, [r0, #12] + tmp = SYSCFG->EXTICR[position >> 2]; + 8001244: f022 0603 bic.w r6, r2, #3 + 8001248: f106 4680 add.w r6, r6, #1073741824 ; 0x40000000 + 800124c: f506 3680 add.w r6, r6, #65536 ; 0x10000 + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001250: f002 0703 and.w r7, r2, #3 + tmp = SYSCFG->EXTICR[position >> 2]; + 8001254: f8d6 e008 ldr.w lr, [r6, #8] + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001258: 00bf lsls r7, r7, #2 + 800125a: 40bd lsls r5, r7 + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 800125c: f1b0 4f90 cmp.w r0, #1207959552 ; 0x48000000 + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001260: ea05 0e0e and.w lr, r5, lr + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 8001264: d031 beq.n 80012ca + 8001266: 4b21 ldr r3, [pc, #132] ; (80012ec ) + 8001268: 4298 cmp r0, r3 + 800126a: d030 beq.n 80012ce + 800126c: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001270: 4298 cmp r0, r3 + 8001272: d02e beq.n 80012d2 + 8001274: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001278: 4298 cmp r0, r3 + 800127a: d02c beq.n 80012d6 + 800127c: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001280: 4298 cmp r0, r3 + 8001282: d02a beq.n 80012da + 8001284: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001288: 4298 cmp r0, r3 + 800128a: d028 beq.n 80012de + 800128c: 4550 cmp r0, sl + 800128e: d028 beq.n 80012e2 + 8001290: 4558 cmp r0, fp + 8001292: bf0c ite eq + 8001294: 2307 moveq r3, #7 + 8001296: 2308 movne r3, #8 + 8001298: 40bb lsls r3, r7 + 800129a: 4573 cmp r3, lr + 800129c: d113 bne.n 80012c6 + SYSCFG->EXTICR[position >> 2] &= ~tmp; + 800129e: 68b3 ldr r3, [r6, #8] + 80012a0: ea23 0505 bic.w r5, r3, r5 + 80012a4: 60b5 str r5, [r6, #8] + EXTI->IMR1 &= ~((uint32_t)iocurrent); + 80012a6: 6823 ldr r3, [r4, #0] + 80012a8: ea23 030c bic.w r3, r3, ip + 80012ac: 6023 str r3, [r4, #0] + EXTI->EMR1 &= ~((uint32_t)iocurrent); + 80012ae: 6863 ldr r3, [r4, #4] + 80012b0: ea23 030c bic.w r3, r3, ip + 80012b4: 6063 str r3, [r4, #4] + EXTI->RTSR1 &= ~((uint32_t)iocurrent); + 80012b6: 68a3 ldr r3, [r4, #8] + 80012b8: ea23 030c bic.w r3, r3, ip + 80012bc: 60a3 str r3, [r4, #8] + EXTI->FTSR1 &= ~((uint32_t)iocurrent); + 80012be: 68e3 ldr r3, [r4, #12] + 80012c0: ea23 030c bic.w r3, r3, ip + 80012c4: 60e3 str r3, [r4, #12] + position++; + 80012c6: 3201 adds r2, #1 + 80012c8: e78e b.n 80011e8 + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 80012ca: 2300 movs r3, #0 + 80012cc: e7e4 b.n 8001298 + 80012ce: 2301 movs r3, #1 + 80012d0: e7e2 b.n 8001298 + 80012d2: 2302 movs r3, #2 + 80012d4: e7e0 b.n 8001298 + 80012d6: 2303 movs r3, #3 + 80012d8: e7de b.n 8001298 + 80012da: 2304 movs r3, #4 + 80012dc: e7dc b.n 8001298 + 80012de: 2305 movs r3, #5 + 80012e0: e7da b.n 8001298 + 80012e2: 2306 movs r3, #6 + 80012e4: e7d8 b.n 8001298 + 80012e6: bf00 nop + 80012e8: 40010400 .word 0x40010400 + 80012ec: 48000400 .word 0x48000400 + 80012f0: 48001800 .word 0x48001800 + 80012f4: 48001c00 .word 0x48001c00 + +080012f8 : + GPIO_PinState bitstatus; + + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET) + 80012f8: 6903 ldr r3, [r0, #16] + 80012fa: 4219 tst r1, r3 + else + { + bitstatus = GPIO_PIN_RESET; + } + return bitstatus; +} + 80012fc: bf14 ite ne + 80012fe: 2001 movne r0, #1 + 8001300: 2000 moveq r0, #0 + 8001302: 4770 bx lr + +08001304 : +{ + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + assert_param(IS_GPIO_PIN_ACTION(PinState)); + + if(PinState != GPIO_PIN_RESET) + 8001304: b10a cbz r2, 800130a + { + GPIOx->BSRR = (uint32_t)GPIO_Pin; + 8001306: 6181 str r1, [r0, #24] + 8001308: 4770 bx lr + } + else + { + GPIOx->BRR = (uint32_t)GPIO_Pin; + 800130a: 6281 str r1, [r0, #40] ; 0x28 + } +} + 800130c: 4770 bx lr + +0800130e : +void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + GPIOx->ODR ^= GPIO_Pin; + 800130e: 6943 ldr r3, [r0, #20] + 8001310: 4059 eors r1, r3 + 8001312: 6141 str r1, [r0, #20] +} + 8001314: 4770 bx lr + +08001316 : + * @param GPIO_Pin: specifies the port bits to be locked. + * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). + * @retval None + */ +HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + 8001316: b082 sub sp, #8 + __IO uint32_t tmp = GPIO_LCKR_LCKK; + 8001318: f44f 3380 mov.w r3, #65536 ; 0x10000 + 800131c: 9301 str r3, [sp, #4] + /* Check the parameters */ + assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx)); + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + /* Apply lock key write sequence */ + tmp |= GPIO_Pin; + 800131e: 9b01 ldr r3, [sp, #4] + 8001320: 430b orrs r3, r1 + 8001322: 9301 str r3, [sp, #4] + /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */ + GPIOx->LCKR = tmp; + 8001324: 9b01 ldr r3, [sp, #4] + 8001326: 61c3 str r3, [r0, #28] + /* Reset LCKx bit(s): LCKK='0' + LCK[15-0] */ + GPIOx->LCKR = GPIO_Pin; + 8001328: 61c1 str r1, [r0, #28] + /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */ + GPIOx->LCKR = tmp; + 800132a: 9b01 ldr r3, [sp, #4] + 800132c: 61c3 str r3, [r0, #28] + /* Read LCKK bit*/ + tmp = GPIOx->LCKR; + 800132e: 69c3 ldr r3, [r0, #28] + 8001330: 9301 str r3, [sp, #4] + + if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET) + 8001332: 69c0 ldr r0, [r0, #28] + 8001334: f480 3080 eor.w r0, r0, #65536 ; 0x10000 + } + else + { + return HAL_ERROR; + } +} + 8001338: f3c0 4000 ubfx r0, r0, #16, #1 + 800133c: b002 add sp, #8 + 800133e: 4770 bx lr + +08001340 : + UNUSED(GPIO_Pin); + + /* NOTE: This function should not be modified, when the callback is needed, + the HAL_GPIO_EXTI_Callback could be implemented in the user file + */ +} + 8001340: 4770 bx lr + ... + +08001344 : + if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) + 8001344: 4a04 ldr r2, [pc, #16] ; (8001358 ) + 8001346: 6951 ldr r1, [r2, #20] + 8001348: 4201 tst r1, r0 +{ + 800134a: b508 push {r3, lr} + if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) + 800134c: d002 beq.n 8001354 + __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); + 800134e: 6150 str r0, [r2, #20] + HAL_GPIO_EXTI_Callback(GPIO_Pin); + 8001350: f7ff fff6 bl 8001340 +} + 8001354: bd08 pop {r3, pc} + 8001356: bf00 nop + 8001358: 40010400 .word 0x40010400 + +0800135c : +static HAL_StatusTypeDef SPI_WaitFifoStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Fifo, uint32_t State, + uint32_t Timeout, uint32_t Tickstart) +{ + __IO uint8_t tmpreg; + + while ((hspi->Instance->SR & Fifo) != State) + 800135c: 6803 ldr r3, [r0, #0] +static HAL_StatusTypeDef SPI_EndRxTxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart) + 800135e: b082 sub sp, #8 + while ((hspi->Instance->SR & Fifo) != State) + 8001360: 689a ldr r2, [r3, #8] + 8001362: f412 5fc0 tst.w r2, #6144 ; 0x1800 + 8001366: d1fb bne.n 8001360 + * @retval HAL status + */ +static HAL_StatusTypeDef SPI_WaitFlagStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Flag, uint32_t State, + uint32_t Timeout, uint32_t Tickstart) +{ + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001368: 689a ldr r2, [r3, #8] + 800136a: 0612 lsls r2, r2, #24 + 800136c: d4fc bmi.n 8001368 + while ((hspi->Instance->SR & Fifo) != State) + 800136e: 6898 ldr r0, [r3, #8] + 8001370: f410 60c0 ands.w r0, r0, #1536 ; 0x600 + 8001374: d101 bne.n 800137a +} + 8001376: b002 add sp, #8 + 8001378: 4770 bx lr + tmpreg = *((__IO uint8_t *)&hspi->Instance->DR); + 800137a: 7b1a ldrb r2, [r3, #12] + 800137c: b2d2 uxtb r2, r2 + 800137e: f88d 2007 strb.w r2, [sp, #7] + UNUSED(tmpreg); + 8001382: f89d 2007 ldrb.w r2, [sp, #7] + 8001386: e7f2 b.n 800136e + +08001388 : +{ + 8001388: b5f0 push {r4, r5, r6, r7, lr} + if (hspi == NULL) + 800138a: 2800 cmp r0, #0 + 800138c: d054 beq.n 8001438 + if (hspi->State == HAL_SPI_STATE_RESET) + 800138e: f890 305d ldrb.w r3, [r0, #93] ; 0x5d + if (hspi->Init.TIMode == SPI_TIMODE_DISABLE) + 8001392: f8d0 c024 ldr.w ip, [r0, #36] ; 0x24 + if (hspi->State == HAL_SPI_STATE_RESET) + 8001396: f003 02ff and.w r2, r3, #255 ; 0xff + 800139a: b90b cbnz r3, 80013a0 + hspi->Lock = HAL_UNLOCKED; + 800139c: f880 205c strb.w r2, [r0, #92] ; 0x5c + __HAL_SPI_DISABLE(hspi); + 80013a0: 6801 ldr r1, [r0, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80013a2: 68c2 ldr r2, [r0, #12] + hspi->State = HAL_SPI_STATE_BUSY; + 80013a4: 2302 movs r3, #2 + 80013a6: f880 305d strb.w r3, [r0, #93] ; 0x5d + __HAL_SPI_DISABLE(hspi); + 80013aa: 680b ldr r3, [r1, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80013ac: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + __HAL_SPI_DISABLE(hspi); + 80013b0: f023 0340 bic.w r3, r3, #64 ; 0x40 + 80013b4: 600b str r3, [r1, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80013b6: f04f 0300 mov.w r3, #0 + 80013ba: d83f bhi.n 800143c + frxth = SPI_RXFIFO_THRESHOLD_QF; + 80013bc: f44f 5580 mov.w r5, #4096 ; 0x1000 + if ((hspi->Init.DataSize != SPI_DATASIZE_16BIT) && (hspi->Init.DataSize != SPI_DATASIZE_8BIT)) + 80013c0: d000 beq.n 80013c4 + hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + 80013c2: 6283 str r3, [r0, #40] ; 0x28 + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_DATASIZE) + 80013c4: 6b03 ldr r3, [r0, #48] ; 0x30 + 80013c6: b92b cbnz r3, 80013d4 + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80013c8: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + hspi->Init.CRCLength = SPI_CRC_LENGTH_16BIT; + 80013cc: bf8c ite hi + 80013ce: 2302 movhi r3, #2 + hspi->Init.CRCLength = SPI_CRC_LENGTH_8BIT; + 80013d0: 2301 movls r3, #1 + 80013d2: 6303 str r3, [r0, #48] ; 0x30 + WRITE_REG(hspi->Instance->CR1, (hspi->Init.Mode | hspi->Init.Direction | + 80013d4: e9d0 3701 ldrd r3, r7, [r0, #4] + 80013d8: 433b orrs r3, r7 + 80013da: 6907 ldr r7, [r0, #16] + 80013dc: 6984 ldr r4, [r0, #24] + 80013de: 6a86 ldr r6, [r0, #40] ; 0x28 + 80013e0: 433b orrs r3, r7 + 80013e2: 6947 ldr r7, [r0, #20] + 80013e4: 433b orrs r3, r7 + 80013e6: 69c7 ldr r7, [r0, #28] + 80013e8: 433b orrs r3, r7 + 80013ea: 6a07 ldr r7, [r0, #32] + 80013ec: 433b orrs r3, r7 + 80013ee: 4333 orrs r3, r6 + 80013f0: f404 7700 and.w r7, r4, #512 ; 0x200 + 80013f4: 433b orrs r3, r7 + 80013f6: 600b str r3, [r1, #0] + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT) + 80013f8: 6b03 ldr r3, [r0, #48] ; 0x30 + 80013fa: 2b02 cmp r3, #2 + hspi->Instance->CR1 |= SPI_CR1_CRCL; + 80013fc: bf02 ittt eq + 80013fe: 680b ldreq r3, [r1, #0] + 8001400: f443 6300 orreq.w r3, r3, #2048 ; 0x800 + 8001404: 600b streq r3, [r1, #0] + WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode | + 8001406: 6b43 ldr r3, [r0, #52] ; 0x34 + 8001408: ea4c 0202 orr.w r2, ip, r2 + 800140c: 0c24 lsrs r4, r4, #16 + 800140e: 431a orrs r2, r3 + 8001410: f004 0404 and.w r4, r4, #4 + 8001414: 4322 orrs r2, r4 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001416: f5b6 5f00 cmp.w r6, #8192 ; 0x2000 + WRITE_REG(hspi->Instance->CRCPR, hspi->Init.CRCPolynomial); + 800141a: bf08 it eq + 800141c: 6ac3 ldreq r3, [r0, #44] ; 0x2c + WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode | + 800141e: ea45 0502 orr.w r5, r5, r2 + 8001422: 604d str r5, [r1, #4] + hspi->State = HAL_SPI_STATE_READY; + 8001424: f04f 0201 mov.w r2, #1 + WRITE_REG(hspi->Instance->CRCPR, hspi->Init.CRCPolynomial); + 8001428: bf08 it eq + 800142a: 610b streq r3, [r1, #16] + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 800142c: 2300 movs r3, #0 + 800142e: 6603 str r3, [r0, #96] ; 0x60 + hspi->State = HAL_SPI_STATE_READY; + 8001430: f880 205d strb.w r2, [r0, #93] ; 0x5d + return HAL_OK; + 8001434: 4618 mov r0, r3 +} + 8001436: bdf0 pop {r4, r5, r6, r7, pc} + return HAL_ERROR; + 8001438: 2001 movs r0, #1 + 800143a: e7fc b.n 8001436 + frxth = SPI_RXFIFO_THRESHOLD_HF; + 800143c: 461d mov r5, r3 + if ((hspi->Init.DataSize != SPI_DATASIZE_16BIT) && (hspi->Init.DataSize != SPI_DATASIZE_8BIT)) + 800143e: f5b2 6f70 cmp.w r2, #3840 ; 0xf00 + 8001442: e7bd b.n 80013c0 + +08001444 : +{ + 8001444: e92d 41f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, lr} + 8001448: 461e mov r6, r3 + __HAL_LOCK(hspi); + 800144a: f890 305c ldrb.w r3, [r0, #92] ; 0x5c + 800144e: 2b01 cmp r3, #1 +{ + 8001450: 4604 mov r4, r0 + 8001452: 460d mov r5, r1 + 8001454: 4690 mov r8, r2 + __HAL_LOCK(hspi); + 8001456: f000 809c beq.w 8001592 + 800145a: 2301 movs r3, #1 + 800145c: f880 305c strb.w r3, [r0, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8001460: f005 fe44 bl 80070ec + if (hspi->State != HAL_SPI_STATE_READY) + 8001464: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + 8001468: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 800146a: 4607 mov r7, r0 + if (hspi->State != HAL_SPI_STATE_READY) + 800146c: b2d8 uxtb r0, r3 + 800146e: f040 808e bne.w 800158e + if ((pData == NULL) || (Size == 0U)) + 8001472: 2d00 cmp r5, #0 + 8001474: d07a beq.n 800156c + 8001476: f1b8 0f00 cmp.w r8, #0 + 800147a: d077 beq.n 800156c + hspi->State = HAL_SPI_STATE_BUSY_TX; + 800147c: 2303 movs r3, #3 + 800147e: f884 305d strb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001482: 68a3 ldr r3, [r4, #8] + SPI_1LINE_TX(hspi); + 8001484: 6822 ldr r2, [r4, #0] + hspi->pTxBuffPtr = (uint8_t *)pData; + 8001486: 63a5 str r5, [r4, #56] ; 0x38 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001488: 2100 movs r1, #0 + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 800148a: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 800148e: 6621 str r1, [r4, #96] ; 0x60 + hspi->TxXferCount = Size; + 8001490: f8a4 803e strh.w r8, [r4, #62] ; 0x3e + hspi->RxXferCount = 0U; + 8001494: f8a4 1046 strh.w r1, [r4, #70] ; 0x46 + SPI_1LINE_TX(hspi); + 8001498: bf08 it eq + 800149a: 6813 ldreq r3, [r2, #0] + hspi->TxXferSize = Size; + 800149c: f8a4 803c strh.w r8, [r4, #60] ; 0x3c + SPI_1LINE_TX(hspi); + 80014a0: bf08 it eq + 80014a2: f443 4380 orreq.w r3, r3, #16384 ; 0x4000 + hspi->RxISR = NULL; + 80014a6: e9c4 1113 strd r1, r1, [r4, #76] ; 0x4c + hspi->pRxBuffPtr = (uint8_t *)NULL; + 80014aa: 6421 str r1, [r4, #64] ; 0x40 + hspi->RxXferSize = 0U; + 80014ac: f8a4 1044 strh.w r1, [r4, #68] ; 0x44 + SPI_1LINE_TX(hspi); + 80014b0: bf08 it eq + 80014b2: 6013 streq r3, [r2, #0] + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80014b4: 6aa3 ldr r3, [r4, #40] ; 0x28 + 80014b6: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 80014ba: d107 bne.n 80014cc + SPI_RESET_CRC(hspi); + 80014bc: 6813 ldr r3, [r2, #0] + 80014be: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 80014c2: 6013 str r3, [r2, #0] + 80014c4: 6813 ldr r3, [r2, #0] + 80014c6: f443 5300 orr.w r3, r3, #8192 ; 0x2000 + 80014ca: 6013 str r3, [r2, #0] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 80014cc: 6813 ldr r3, [r2, #0] + 80014ce: 0659 lsls r1, r3, #25 + __HAL_SPI_ENABLE(hspi); + 80014d0: bf5e ittt pl + 80014d2: 6813 ldrpl r3, [r2, #0] + 80014d4: f043 0340 orrpl.w r3, r3, #64 ; 0x40 + 80014d8: 6013 strpl r3, [r2, #0] + if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U)) + 80014da: 6863 ldr r3, [r4, #4] + 80014dc: b11b cbz r3, 80014e6 + 80014de: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80014e0: b29b uxth r3, r3 + 80014e2: 2b01 cmp r3, #1 + 80014e4: d110 bne.n 8001508 + if (hspi->TxXferCount > 1U) + 80014e6: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80014e8: b29b uxth r3, r3 + 80014ea: 2b01 cmp r3, #1 + 80014ec: d905 bls.n 80014fa + hspi->Instance->DR = *((uint16_t *)pData); + 80014ee: f835 3b02 ldrh.w r3, [r5], #2 + 80014f2: 60d3 str r3, [r2, #12] + hspi->TxXferCount -= 2U; + 80014f4: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80014f6: 3b02 subs r3, #2 + 80014f8: e004 b.n 8001504 + *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++); + 80014fa: f815 3b01 ldrb.w r3, [r5], #1 + 80014fe: 7313 strb r3, [r2, #12] + hspi->TxXferCount--; + 8001500: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 8001502: 3b01 subs r3, #1 + 8001504: b29b uxth r3, r3 + 8001506: 87e3 strh r3, [r4, #62] ; 0x3e + while (hspi->TxXferCount > 0U) + 8001508: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 800150a: b29b uxth r3, r3 + 800150c: b9e3 cbnz r3, 8001548 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 800150e: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8001510: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 8001514: bf01 itttt eq + 8001516: 6822 ldreq r2, [r4, #0] + 8001518: 6813 ldreq r3, [r2, #0] + 800151a: f443 5380 orreq.w r3, r3, #4096 ; 0x1000 + 800151e: 6013 streq r3, [r2, #0] + if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK) + 8001520: 4620 mov r0, r4 + 8001522: f7ff ff1b bl 800135c + 8001526: b108 cbz r0, 800152c + hspi->ErrorCode = HAL_SPI_ERROR_FLAG; + 8001528: 2320 movs r3, #32 + 800152a: 6623 str r3, [r4, #96] ; 0x60 + if (hspi->Init.Direction == SPI_DIRECTION_2LINES) + 800152c: 68a3 ldr r3, [r4, #8] + 800152e: b933 cbnz r3, 800153e + __HAL_SPI_CLEAR_OVRFLAG(hspi); + 8001530: 9301 str r3, [sp, #4] + 8001532: 6823 ldr r3, [r4, #0] + 8001534: 68da ldr r2, [r3, #12] + 8001536: 9201 str r2, [sp, #4] + 8001538: 689b ldr r3, [r3, #8] + 800153a: 9301 str r3, [sp, #4] + 800153c: 9b01 ldr r3, [sp, #4] + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 800153e: 6e20 ldr r0, [r4, #96] ; 0x60 + errorcode = HAL_BUSY; + 8001540: 3800 subs r0, #0 + 8001542: bf18 it ne + 8001544: 2001 movne r0, #1 +error: + 8001546: e011 b.n 800156c + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) + 8001548: 6823 ldr r3, [r4, #0] + 800154a: 689a ldr r2, [r3, #8] + 800154c: 0792 lsls r2, r2, #30 + 800154e: d50b bpl.n 8001568 + if (hspi->TxXferCount > 1U) + 8001550: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001552: b292 uxth r2, r2 + 8001554: 2a01 cmp r2, #1 + 8001556: d903 bls.n 8001560 + hspi->Instance->DR = *((uint16_t *)pData); + 8001558: f835 2b02 ldrh.w r2, [r5], #2 + 800155c: 60da str r2, [r3, #12] + 800155e: e7c9 b.n 80014f4 + *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++); + 8001560: f815 2b01 ldrb.w r2, [r5], #1 + 8001564: 731a strb r2, [r3, #12] + hspi->TxXferCount--; + 8001566: e7cb b.n 8001500 + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 8001568: b94e cbnz r6, 800157e + errorcode = HAL_TIMEOUT; + 800156a: 2003 movs r0, #3 + hspi->State = HAL_SPI_STATE_READY; + 800156c: 2301 movs r3, #1 + 800156e: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 8001572: 2300 movs r3, #0 + 8001574: f884 305c strb.w r3, [r4, #92] ; 0x5c +} + 8001578: b002 add sp, #8 + 800157a: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 800157e: 1c73 adds r3, r6, #1 + 8001580: d0c2 beq.n 8001508 + 8001582: f005 fdb3 bl 80070ec + 8001586: 1bc0 subs r0, r0, r7 + 8001588: 42b0 cmp r0, r6 + 800158a: d3bd bcc.n 8001508 + 800158c: e7ed b.n 800156a + errorcode = HAL_BUSY; + 800158e: 2002 movs r0, #2 + 8001590: e7ec b.n 800156c + __HAL_LOCK(hspi); + 8001592: 2002 movs r0, #2 + 8001594: e7f0 b.n 8001578 + +08001596 : + * @param Timeout: Timeout duration + * @retval HAL status + */ +HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, + uint32_t Timeout) +{ + 8001596: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + 800159a: 461e mov r6, r3 + uint32_t tmp = 0U, tmp1 = 0U; +#if (USE_SPI_CRC != 0U) + __IO uint16_t tmpreg = 0U; + 800159c: 2300 movs r3, #0 + 800159e: f8ad 3006 strh.w r3, [sp, #6] + + /* Check Direction parameter */ + assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction)); + + /* Process Locked */ + __HAL_LOCK(hspi); + 80015a2: f890 305c ldrb.w r3, [r0, #92] ; 0x5c +{ + 80015a6: f8dd 8028 ldr.w r8, [sp, #40] ; 0x28 + __HAL_LOCK(hspi); + 80015aa: 2b01 cmp r3, #1 +{ + 80015ac: 4604 mov r4, r0 + 80015ae: 460d mov r5, r1 + 80015b0: 4617 mov r7, r2 + __HAL_LOCK(hspi); + 80015b2: f000 8124 beq.w 80017fe + 80015b6: 2301 movs r3, #1 + 80015b8: f880 305c strb.w r3, [r0, #92] ; 0x5c + + /* Init tickstart for timeout management*/ + tickstart = HAL_GetTick(); + 80015bc: f005 fd96 bl 80070ec + + tmp = hspi->State; + 80015c0: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + tmp1 = hspi->Init.Mode; + 80015c4: 6861 ldr r1, [r4, #4] + + if (!((tmp == HAL_SPI_STATE_READY) || \ + 80015c6: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 80015c8: 4681 mov r9, r0 + tmp = hspi->State; + 80015ca: b2da uxtb r2, r3 + if (!((tmp == HAL_SPI_STATE_READY) || \ + 80015cc: d00a beq.n 80015e4 + 80015ce: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 80015d2: f040 8112 bne.w 80017fa + ((tmp1 == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp == HAL_SPI_STATE_BUSY_RX)))) + 80015d6: 68a3 ldr r3, [r4, #8] + 80015d8: 2b00 cmp r3, #0 + 80015da: f040 810e bne.w 80017fa + 80015de: 2a04 cmp r2, #4 + 80015e0: f040 810b bne.w 80017fa + { + errorcode = HAL_BUSY; + goto error; + } + + if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)) + 80015e4: b955 cbnz r5, 80015fc + { + errorcode = HAL_ERROR; + 80015e6: 2101 movs r1, #1 + { + errorcode = HAL_ERROR; + } + +error : + hspi->State = HAL_SPI_STATE_READY; + 80015e8: 2301 movs r3, #1 + 80015ea: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 80015ee: 2300 movs r3, #0 + 80015f0: f884 305c strb.w r3, [r4, #92] ; 0x5c + return errorcode; +} + 80015f4: 4608 mov r0, r1 + 80015f6: b003 add sp, #12 + 80015f8: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)) + 80015fc: 2f00 cmp r7, #0 + 80015fe: d0f2 beq.n 80015e6 + 8001600: 2e00 cmp r6, #0 + 8001602: d0f0 beq.n 80015e6 + if (hspi->State != HAL_SPI_STATE_BUSY_RX) + 8001604: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001608: 6aa2 ldr r2, [r4, #40] ; 0x28 + hspi->pRxBuffPtr = (uint8_t *)pRxData; + 800160a: 6427 str r7, [r4, #64] ; 0x40 + if (hspi->State != HAL_SPI_STATE_BUSY_RX) + 800160c: 2b04 cmp r3, #4 + hspi->State = HAL_SPI_STATE_BUSY_TX_RX; + 800160e: bf1c itt ne + 8001610: 2305 movne r3, #5 + 8001612: f884 305d strbne.w r3, [r4, #93] ; 0x5d + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001616: 2300 movs r3, #0 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001618: f5b2 5f00 cmp.w r2, #8192 ; 0x2000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 800161c: 6623 str r3, [r4, #96] ; 0x60 + hspi->TxISR = NULL; + 800161e: e9c4 3313 strd r3, r3, [r4, #76] ; 0x4c + hspi->RxXferCount = Size; + 8001622: f8a4 6046 strh.w r6, [r4, #70] ; 0x46 + SPI_RESET_CRC(hspi); + 8001626: 6823 ldr r3, [r4, #0] + hspi->RxXferSize = Size; + 8001628: f8a4 6044 strh.w r6, [r4, #68] ; 0x44 + hspi->pTxBuffPtr = (uint8_t *)pTxData; + 800162c: 63a5 str r5, [r4, #56] ; 0x38 + hspi->TxXferCount = Size; + 800162e: 87e6 strh r6, [r4, #62] ; 0x3e + hspi->TxXferSize = Size; + 8001630: 87a6 strh r6, [r4, #60] ; 0x3c + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001632: d107 bne.n 8001644 + SPI_RESET_CRC(hspi); + 8001634: 681a ldr r2, [r3, #0] + 8001636: f422 5200 bic.w r2, r2, #8192 ; 0x2000 + 800163a: 601a str r2, [r3, #0] + 800163c: 681a ldr r2, [r3, #0] + 800163e: f442 5200 orr.w r2, r2, #8192 ; 0x2000 + 8001642: 601a str r2, [r3, #0] + if ((hspi->Init.DataSize > SPI_DATASIZE_8BIT) || (hspi->RxXferCount > 1U)) + 8001644: 68e2 ldr r2, [r4, #12] + 8001646: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + 800164a: d804 bhi.n 8001656 + 800164c: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001650: b292 uxth r2, r2 + 8001652: 2a01 cmp r2, #1 + 8001654: d94e bls.n 80016f4 + CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 8001656: 685a ldr r2, [r3, #4] + 8001658: f422 5280 bic.w r2, r2, #4096 ; 0x1000 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 800165c: 605a str r2, [r3, #4] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 800165e: 681a ldr r2, [r3, #0] + 8001660: 0650 lsls r0, r2, #25 + __HAL_SPI_ENABLE(hspi); + 8001662: bf5e ittt pl + 8001664: 681a ldrpl r2, [r3, #0] + 8001666: f042 0240 orrpl.w r2, r2, #64 ; 0x40 + 800166a: 601a strpl r2, [r3, #0] + if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U)) + 800166c: b119 cbz r1, 8001676 + 800166e: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001670: b292 uxth r2, r2 + 8001672: 2a01 cmp r2, #1 + 8001674: d10a bne.n 800168c + if (hspi->TxXferCount > 1U) + 8001676: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001678: b292 uxth r2, r2 + 800167a: 2a01 cmp r2, #1 + 800167c: d93e bls.n 80016fc + hspi->Instance->DR = *((uint16_t *)pTxData); + 800167e: f835 2b02 ldrh.w r2, [r5], #2 + 8001682: 60da str r2, [r3, #12] + hspi->TxXferCount -= 2U; + 8001684: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 8001686: 3b02 subs r3, #2 + 8001688: b29b uxth r3, r3 + 800168a: 87e3 strh r3, [r4, #62] ; 0x3e + txallowed = 1U; + 800168c: 2601 movs r6, #1 + while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)) + 800168e: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 8001690: b29b uxth r3, r3 + 8001692: 2b00 cmp r3, #0 + 8001694: d138 bne.n 8001708 + 8001696: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 800169a: b29b uxth r3, r3 + 800169c: 2b00 cmp r3, #0 + 800169e: d133 bne.n 8001708 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80016a0: 6aa2 ldr r2, [r4, #40] ; 0x28 + if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))) + 80016a2: 6823 ldr r3, [r4, #0] + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80016a4: f5b2 5f00 cmp.w r2, #8192 ; 0x2000 + 80016a8: d10d bne.n 80016c6 + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 80016aa: 689a ldr r2, [r3, #8] + 80016ac: 07d1 lsls r1, r2, #31 + 80016ae: d5fc bpl.n 80016aa + if (hspi->Init.DataSize == SPI_DATASIZE_16BIT) + 80016b0: 68e2 ldr r2, [r4, #12] + 80016b2: f5b2 6f70 cmp.w r2, #3840 ; 0xf00 + 80016b6: f040 8092 bne.w 80017de + tmpreg = hspi->Instance->DR; + 80016ba: 68da ldr r2, [r3, #12] + 80016bc: b292 uxth r2, r2 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80016be: f8ad 2006 strh.w r2, [sp, #6] + UNUSED(tmpreg); + 80016c2: f8bd 2006 ldrh.w r2, [sp, #6] + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)) + 80016c6: 6899 ldr r1, [r3, #8] + 80016c8: f011 0110 ands.w r1, r1, #16 + 80016cc: d007 beq.n 80016de + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC); + 80016ce: 6e22 ldr r2, [r4, #96] ; 0x60 + 80016d0: f042 0202 orr.w r2, r2, #2 + 80016d4: 6622 str r2, [r4, #96] ; 0x60 + __HAL_SPI_CLEAR_CRCERRFLAG(hspi); + 80016d6: f64f 72ef movw r2, #65519 ; 0xffef + 80016da: 609a str r2, [r3, #8] + errorcode = HAL_ERROR; + 80016dc: 2101 movs r1, #1 + if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK) + 80016de: 4620 mov r0, r4 + 80016e0: f7ff fe3c bl 800135c + 80016e4: b108 cbz r0, 80016ea + hspi->ErrorCode = HAL_SPI_ERROR_FLAG; + 80016e6: 2320 movs r3, #32 + 80016e8: 6623 str r3, [r4, #96] ; 0x60 + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 80016ea: 6e23 ldr r3, [r4, #96] ; 0x60 + 80016ec: 2b00 cmp r3, #0 + 80016ee: f47f af7a bne.w 80015e6 + 80016f2: e779 b.n 80015e8 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 80016f4: 685a ldr r2, [r3, #4] + 80016f6: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 80016fa: e7af b.n 800165c + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 80016fc: f815 2b01 ldrb.w r2, [r5], #1 + 8001700: 731a strb r2, [r3, #12] + hspi->TxXferCount--; + 8001702: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 8001704: 3b01 subs r3, #1 + 8001706: e7bf b.n 8001688 + if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))) + 8001708: 2e00 cmp r6, #0 + 800170a: d030 beq.n 800176e + 800170c: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 800170e: b29b uxth r3, r3 + 8001710: 2b00 cmp r3, #0 + 8001712: d02c beq.n 800176e + 8001714: 6823 ldr r3, [r4, #0] + 8001716: 689a ldr r2, [r3, #8] + 8001718: 0792 lsls r2, r2, #30 + 800171a: d528 bpl.n 800176e + if (hspi->TxXferCount > 1U) + 800171c: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 800171e: b292 uxth r2, r2 + 8001720: 2a01 cmp r2, #1 + hspi->Instance->DR = *((uint16_t *)pTxData); + 8001722: bf8b itete hi + 8001724: f835 2b02 ldrhhi.w r2, [r5], #2 + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 8001728: f815 2b01 ldrbls.w r2, [r5], #1 + hspi->Instance->DR = *((uint16_t *)pTxData); + 800172c: 60da strhi r2, [r3, #12] + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 800172e: 731a strbls r2, [r3, #12] + hspi->TxXferCount -= 2U; + 8001730: bf8b itete hi + 8001732: 8fe3 ldrhhi r3, [r4, #62] ; 0x3e + hspi->TxXferCount--; + 8001734: 8fe3 ldrhls r3, [r4, #62] ; 0x3e + hspi->TxXferCount -= 2U; + 8001736: 3b02 subhi r3, #2 + hspi->TxXferCount--; + 8001738: f103 33ff addls.w r3, r3, #4294967295 ; 0xffffffff + 800173c: b29b uxth r3, r3 + 800173e: 87e3 strh r3, [r4, #62] ; 0x3e + if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)) + 8001740: 8fe6 ldrh r6, [r4, #62] ; 0x3e + 8001742: b2b6 uxth r6, r6 + 8001744: b996 cbnz r6, 800176c + 8001746: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8001748: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 800174c: d10f bne.n 800176e + if (((hspi->Instance->CR1 & SPI_CR1_MSTR) == 0U) && ((hspi->Instance->CR2 & SPI_CR2_NSSP) == SPI_CR2_NSSP)) + 800174e: 6823 ldr r3, [r4, #0] + 8001750: 681a ldr r2, [r3, #0] + 8001752: 0756 lsls r6, r2, #29 + 8001754: d406 bmi.n 8001764 + 8001756: 685a ldr r2, [r3, #4] + 8001758: 0710 lsls r0, r2, #28 + SET_BIT(hspi->Instance->CR1, SPI_CR1_SSM); + 800175a: bf42 ittt mi + 800175c: 681a ldrmi r2, [r3, #0] + 800175e: f442 7200 orrmi.w r2, r2, #512 ; 0x200 + 8001762: 601a strmi r2, [r3, #0] + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 8001764: 681a ldr r2, [r3, #0] + 8001766: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800176a: 601a str r2, [r3, #0] + txallowed = 0U; + 800176c: 2600 movs r6, #0 + if ((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE))) + 800176e: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 8001772: b29b uxth r3, r3 + 8001774: b1e3 cbz r3, 80017b0 + 8001776: 6821 ldr r1, [r4, #0] + 8001778: 688b ldr r3, [r1, #8] + 800177a: f013 0301 ands.w r3, r3, #1 + 800177e: d017 beq.n 80017b0 + if (hspi->RxXferCount > 1U) + 8001780: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001784: b292 uxth r2, r2 + 8001786: 2a01 cmp r2, #1 + 8001788: d91f bls.n 80017ca + *((uint16_t *)pRxData) = hspi->Instance->DR; + 800178a: 68ca ldr r2, [r1, #12] + 800178c: f827 2b02 strh.w r2, [r7], #2 + hspi->RxXferCount -= 2U; + 8001790: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001794: 3a02 subs r2, #2 + 8001796: b292 uxth r2, r2 + 8001798: f8a4 2046 strh.w r2, [r4, #70] ; 0x46 + if (hspi->RxXferCount <= 1U) + 800179c: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 80017a0: b292 uxth r2, r2 + 80017a2: 2a01 cmp r2, #1 + 80017a4: d803 bhi.n 80017ae + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 80017a6: 684a ldr r2, [r1, #4] + 80017a8: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 80017ac: 604a str r2, [r1, #4] + txallowed = 1U; + 80017ae: 461e mov r6, r3 + if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout)) + 80017b0: f1b8 3fff cmp.w r8, #4294967295 ; 0xffffffff + 80017b4: f43f af6b beq.w 800168e + 80017b8: f005 fc98 bl 80070ec + 80017bc: eba0 0009 sub.w r0, r0, r9 + 80017c0: 4540 cmp r0, r8 + 80017c2: f4ff af64 bcc.w 800168e + errorcode = HAL_TIMEOUT; + 80017c6: 2103 movs r1, #3 + 80017c8: e70e b.n 80015e8 + (*(uint8_t *)pRxData++) = *(__IO uint8_t *)&hspi->Instance->DR; + 80017ca: 7b0a ldrb r2, [r1, #12] + 80017cc: f807 2b01 strb.w r2, [r7], #1 + hspi->RxXferCount--; + 80017d0: f8b4 1046 ldrh.w r1, [r4, #70] ; 0x46 + 80017d4: 3901 subs r1, #1 + 80017d6: b289 uxth r1, r1 + 80017d8: f8a4 1046 strh.w r1, [r4, #70] ; 0x46 + 80017dc: e7e7 b.n 80017ae + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80017de: 7b1a ldrb r2, [r3, #12] + 80017e0: f8ad 2006 strh.w r2, [sp, #6] + UNUSED(tmpreg); + 80017e4: f8bd 2006 ldrh.w r2, [sp, #6] + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT) + 80017e8: 6b22 ldr r2, [r4, #48] ; 0x30 + 80017ea: 2a02 cmp r2, #2 + 80017ec: f47f af6b bne.w 80016c6 + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 80017f0: 689a ldr r2, [r3, #8] + 80017f2: 07d2 lsls r2, r2, #31 + 80017f4: d5fc bpl.n 80017f0 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80017f6: 7b1a ldrb r2, [r3, #12] + 80017f8: e761 b.n 80016be + errorcode = HAL_BUSY; + 80017fa: 2102 movs r1, #2 + 80017fc: e6f4 b.n 80015e8 + __HAL_LOCK(hspi); + 80017fe: 2102 movs r1, #2 + 8001800: e6f8 b.n 80015f4 + +08001802 : +{ + 8001802: e92d 41ff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, lr} + 8001806: 461f mov r7, r3 + __IO uint16_t tmpreg = 0U; + 8001808: 2300 movs r3, #0 + 800180a: f8ad 300e strh.w r3, [sp, #14] + if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES)) + 800180e: 6843 ldr r3, [r0, #4] + 8001810: f5b3 7f82 cmp.w r3, #260 ; 0x104 +{ + 8001814: 4604 mov r4, r0 + 8001816: 460e mov r6, r1 + 8001818: 4615 mov r5, r2 + if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES)) + 800181a: d10c bne.n 8001836 + 800181c: 6883 ldr r3, [r0, #8] + 800181e: b953 cbnz r3, 8001836 + hspi->State = HAL_SPI_STATE_BUSY_RX; + 8001820: 2304 movs r3, #4 + 8001822: f880 305d strb.w r3, [r0, #93] ; 0x5d + return HAL_SPI_TransmitReceive(hspi, pData, pData, Size, Timeout); + 8001826: 4613 mov r3, r2 + 8001828: 9700 str r7, [sp, #0] + 800182a: 460a mov r2, r1 + 800182c: f7ff feb3 bl 8001596 +} + 8001830: b004 add sp, #16 + 8001832: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + __HAL_LOCK(hspi); + 8001836: f894 305c ldrb.w r3, [r4, #92] ; 0x5c + 800183a: 2b01 cmp r3, #1 + 800183c: f000 80dd beq.w 80019fa + 8001840: 2301 movs r3, #1 + 8001842: f884 305c strb.w r3, [r4, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8001846: f005 fc51 bl 80070ec + if (hspi->State != HAL_SPI_STATE_READY) + 800184a: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + 800184e: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 8001850: 4680 mov r8, r0 + if (hspi->State != HAL_SPI_STATE_READY) + 8001852: b2d8 uxtb r0, r3 + 8001854: f040 80cf bne.w 80019f6 + if ((pData == NULL) || (Size == 0U)) + 8001858: 2e00 cmp r6, #0 + 800185a: f000 8092 beq.w 8001982 + 800185e: 2d00 cmp r5, #0 + 8001860: f000 808f beq.w 8001982 + hspi->State = HAL_SPI_STATE_BUSY_RX; + 8001864: 2304 movs r3, #4 + 8001866: f884 305d strb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 800186a: 6aa3 ldr r3, [r4, #40] ; 0x28 + hspi->RxXferSize = Size; + 800186c: f8a4 5044 strh.w r5, [r4, #68] ; 0x44 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001870: 2100 movs r1, #0 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001872: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001876: 6621 str r1, [r4, #96] ; 0x60 + hspi->TxISR = NULL; + 8001878: e9c4 1113 strd r1, r1, [r4, #76] ; 0x4c + hspi->RxXferCount = Size; + 800187c: f8a4 5046 strh.w r5, [r4, #70] ; 0x46 + hspi->pRxBuffPtr = (uint8_t *)pData; + 8001880: 6426 str r6, [r4, #64] ; 0x40 + SPI_RESET_CRC(hspi); + 8001882: 6825 ldr r5, [r4, #0] + hspi->pTxBuffPtr = (uint8_t *)NULL; + 8001884: 63a1 str r1, [r4, #56] ; 0x38 + hspi->TxXferSize = 0U; + 8001886: 87a1 strh r1, [r4, #60] ; 0x3c + hspi->TxXferCount = 0U; + 8001888: 87e1 strh r1, [r4, #62] ; 0x3e + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 800188a: d10d bne.n 80018a8 + SPI_RESET_CRC(hspi); + 800188c: 682b ldr r3, [r5, #0] + 800188e: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 8001892: 602b str r3, [r5, #0] + 8001894: 682b ldr r3, [r5, #0] + 8001896: f443 5300 orr.w r3, r3, #8192 ; 0x2000 + 800189a: 602b str r3, [r5, #0] + hspi->RxXferCount--; + 800189c: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 80018a0: 3b01 subs r3, #1 + 80018a2: b29b uxth r3, r3 + 80018a4: f8a4 3046 strh.w r3, [r4, #70] ; 0x46 + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80018a8: 68e3 ldr r3, [r4, #12] + 80018aa: f5b3 6fe0 cmp.w r3, #1792 ; 0x700 + CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 80018ae: 686b ldr r3, [r5, #4] + 80018b0: bf8c ite hi + 80018b2: f423 5380 bichi.w r3, r3, #4096 ; 0x1000 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 80018b6: f443 5380 orrls.w r3, r3, #4096 ; 0x1000 + 80018ba: 606b str r3, [r5, #4] + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 80018bc: 68a3 ldr r3, [r4, #8] + 80018be: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + SPI_1LINE_RX(hspi); + 80018c2: bf02 ittt eq + 80018c4: 682b ldreq r3, [r5, #0] + 80018c6: f423 4380 biceq.w r3, r3, #16384 ; 0x4000 + 80018ca: 602b streq r3, [r5, #0] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 80018cc: 682b ldr r3, [r5, #0] + 80018ce: 0658 lsls r0, r3, #25 + 80018d0: d403 bmi.n 80018da + __HAL_SPI_ENABLE(hspi); + 80018d2: 682b ldr r3, [r5, #0] + 80018d4: f043 0340 orr.w r3, r3, #64 ; 0x40 + 80018d8: 602b str r3, [r5, #0] + while (hspi->RxXferCount > 0U) + 80018da: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) + 80018de: 6822 ldr r2, [r4, #0] + while (hspi->RxXferCount > 0U) + 80018e0: b29b uxth r3, r3 + 80018e2: 2b00 cmp r3, #0 + 80018e4: d13e bne.n 8001964 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80018e6: 6aa3 ldr r3, [r4, #40] ; 0x28 + 80018e8: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 80018ec: d11c bne.n 8001928 + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 80018ee: 6813 ldr r3, [r2, #0] + 80018f0: f443 5380 orr.w r3, r3, #4096 ; 0x1000 + 80018f4: 6013 str r3, [r2, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 80018f6: 6893 ldr r3, [r2, #8] + 80018f8: 07df lsls r7, r3, #31 + 80018fa: d5fc bpl.n 80018f6 + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80018fc: 68e3 ldr r3, [r4, #12] + 80018fe: f5b3 6fe0 cmp.w r3, #1792 ; 0x700 + (*(uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 8001902: bf95 itete ls + 8001904: 7b13 ldrbls r3, [r2, #12] + *((uint16_t *)pData) = hspi->Instance->DR; + 8001906: 68d3 ldrhi r3, [r2, #12] + (*(uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 8001908: 7033 strbls r3, [r6, #0] + *((uint16_t *)pData) = hspi->Instance->DR; + 800190a: 8033 strhhi r3, [r6, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 800190c: 6823 ldr r3, [r4, #0] + 800190e: 689a ldr r2, [r3, #8] + 8001910: 07d6 lsls r6, r2, #31 + 8001912: d5fc bpl.n 800190e + if (hspi->Init.DataSize == SPI_DATASIZE_16BIT) + 8001914: 68e1 ldr r1, [r4, #12] + 8001916: f5b1 6f70 cmp.w r1, #3840 ; 0xf00 + 800191a: d142 bne.n 80019a2 + tmpreg = hspi->Instance->DR; + 800191c: 68db ldr r3, [r3, #12] + 800191e: b29b uxth r3, r3 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 8001920: f8ad 300e strh.w r3, [sp, #14] + UNUSED(tmpreg); + 8001924: f8bd 300e ldrh.w r3, [sp, #14] + * @param Tickstart: tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef SPI_EndRxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart) +{ + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001928: 6861 ldr r1, [r4, #4] + 800192a: 6823 ldr r3, [r4, #0] + 800192c: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 8001930: d10a bne.n 8001948 + 8001932: 68a2 ldr r2, [r4, #8] + 8001934: f5b2 4f00 cmp.w r2, #32768 ; 0x8000 + 8001938: d002 beq.n 8001940 + || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY))) + 800193a: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + 800193e: d103 bne.n 8001948 + { + /* Disable SPI peripheral */ + __HAL_SPI_DISABLE(hspi); + 8001940: 681a ldr r2, [r3, #0] + 8001942: f022 0240 bic.w r2, r2, #64 ; 0x40 + 8001946: 601a str r2, [r3, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001948: 689a ldr r2, [r3, #8] + 800194a: 0610 lsls r0, r2, #24 + 800194c: d4fc bmi.n 8001948 + { + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG); + return HAL_TIMEOUT; + } + + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 800194e: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 8001952: d036 beq.n 80019c2 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)) + 8001954: 689a ldr r2, [r3, #8] + 8001956: 06d2 lsls r2, r2, #27 + 8001958: d445 bmi.n 80019e6 + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 800195a: 6e20 ldr r0, [r4, #96] ; 0x60 + errorcode = HAL_BUSY; + 800195c: 3800 subs r0, #0 + 800195e: bf18 it ne + 8001960: 2001 movne r0, #1 +error : + 8001962: e00e b.n 8001982 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) + 8001964: 6893 ldr r3, [r2, #8] + 8001966: 07d9 lsls r1, r3, #31 + 8001968: d509 bpl.n 800197e + (* (uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 800196a: 7b13 ldrb r3, [r2, #12] + 800196c: f806 3b01 strb.w r3, [r6], #1 + hspi->RxXferCount--; + 8001970: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 8001974: 3b01 subs r3, #1 + 8001976: b29b uxth r3, r3 + 8001978: f8a4 3046 strh.w r3, [r4, #70] ; 0x46 + 800197c: e7ad b.n 80018da + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 800197e: b93f cbnz r7, 8001990 + errorcode = HAL_TIMEOUT; + 8001980: 2003 movs r0, #3 + hspi->State = HAL_SPI_STATE_READY; + 8001982: 2301 movs r3, #1 + 8001984: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 8001988: 2300 movs r3, #0 + 800198a: f884 305c strb.w r3, [r4, #92] ; 0x5c + return errorcode; + 800198e: e74f b.n 8001830 + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 8001990: 1c7b adds r3, r7, #1 + 8001992: d0a2 beq.n 80018da + 8001994: f005 fbaa bl 80070ec + 8001998: eba0 0008 sub.w r0, r0, r8 + 800199c: 42b8 cmp r0, r7 + 800199e: d39c bcc.n 80018da + 80019a0: e7ee b.n 8001980 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80019a2: 7b1a ldrb r2, [r3, #12] + 80019a4: f8ad 200e strh.w r2, [sp, #14] + if ((hspi->Init.DataSize == SPI_DATASIZE_8BIT) && (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT)) + 80019a8: f5b1 6fe0 cmp.w r1, #1792 ; 0x700 + UNUSED(tmpreg); + 80019ac: f8bd 200e ldrh.w r2, [sp, #14] + if ((hspi->Init.DataSize == SPI_DATASIZE_8BIT) && (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT)) + 80019b0: d1ba bne.n 8001928 + 80019b2: 6b22 ldr r2, [r4, #48] ; 0x30 + 80019b4: 2a02 cmp r2, #2 + 80019b6: d1b7 bne.n 8001928 + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 80019b8: 689a ldr r2, [r3, #8] + 80019ba: 07d5 lsls r5, r2, #31 + 80019bc: d5fc bpl.n 80019b8 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80019be: 7b1b ldrb r3, [r3, #12] + 80019c0: e7ae b.n 8001920 + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 80019c2: 68a2 ldr r2, [r4, #8] + 80019c4: f5b2 4f00 cmp.w r2, #32768 ; 0x8000 + 80019c8: d002 beq.n 80019d0 + || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY))) + 80019ca: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + 80019ce: d1c1 bne.n 8001954 + while ((hspi->Instance->SR & Fifo) != State) + 80019d0: 689a ldr r2, [r3, #8] + 80019d2: f412 6fc0 tst.w r2, #1536 ; 0x600 + 80019d6: d0bd beq.n 8001954 + tmpreg = *((__IO uint8_t *)&hspi->Instance->DR); + 80019d8: 7b1a ldrb r2, [r3, #12] + 80019da: b2d2 uxtb r2, r2 + 80019dc: f88d 200d strb.w r2, [sp, #13] + UNUSED(tmpreg); + 80019e0: f89d 200d ldrb.w r2, [sp, #13] + 80019e4: e7f4 b.n 80019d0 + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC); + 80019e6: 6e22 ldr r2, [r4, #96] ; 0x60 + 80019e8: f042 0202 orr.w r2, r2, #2 + 80019ec: 6622 str r2, [r4, #96] ; 0x60 + __HAL_SPI_CLEAR_CRCERRFLAG(hspi); + 80019ee: f64f 72ef movw r2, #65519 ; 0xffef + 80019f2: 609a str r2, [r3, #8] + 80019f4: e7b1 b.n 800195a + errorcode = HAL_BUSY; + 80019f6: 2002 movs r0, #2 + 80019f8: e7c3 b.n 8001982 + __HAL_LOCK(hspi); + 80019fa: 2002 movs r0, #2 + 80019fc: e718 b.n 8001830 + ... + +08001a00 : + +// checksum_more() +// + static void +checksum_more(SHA256_CTX *ctx, uint32_t *total, const uint8_t *addr, int len) +{ + 8001a00: b5f8 push {r3, r4, r5, r6, r7, lr} + 8001a02: 460c mov r4, r1 + // mk4 has hardware hash engine, and no DFU button + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001a04: 6809 ldr r1, [r1, #0] +{ + 8001a06: 461d mov r5, r3 + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001a08: 2364 movs r3, #100 ; 0x64 +{ + 8001a0a: 4617 mov r7, r2 + 8001a0c: 4606 mov r6, r0 + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001a0e: 4359 muls r1, r3 + puts2("Verify %0x"); + puthex2(percent); + putchar('\n'); +#endif + + oled_show_progress(screen_verify, percent); + 8001a10: 4807 ldr r0, [pc, #28] ; (8001a30 ) + 8001a12: 4b08 ldr r3, [pc, #32] ; (8001a34 ) + 8001a14: fbb1 f1f3 udiv r1, r1, r3 + 8001a18: f7ff fa56 bl 8000ec8 + + sha256_update(ctx, addr, len); + 8001a1c: 462a mov r2, r5 + 8001a1e: 4639 mov r1, r7 + 8001a20: 4630 mov r0, r6 + 8001a22: f003 fd41 bl 80054a8 + *total += len; + 8001a26: 6823 ldr r3, [r4, #0] + 8001a28: 442b add r3, r5 + 8001a2a: 6023 str r3, [r4, #0] +} + 8001a2c: bdf8 pop {r3, r4, r5, r6, r7, pc} + 8001a2e: bf00 nop + 8001a30: 0800e242 .word 0x0800e242 + 8001a34: 0018541c .word 0x0018541c + +08001a38 : + +// checksum_flash() +// + void +checksum_flash(uint8_t fw_digest[32], uint8_t world_digest[32], uint32_t fw_length) +{ + 8001a38: b570 push {r4, r5, r6, lr} + 8001a3a: b09c sub sp, #112 ; 0x70 + 8001a3c: 4606 mov r6, r0 + 8001a3e: 460d mov r5, r1 + 8001a40: 4614 mov r4, r2 + const uint8_t *start = (const uint8_t *)FIRMWARE_START; + + rng_delay(); + 8001a42: f000 fe9b bl 800277c + + SHA256_CTX ctx; + uint32_t total_len = 0; + 8001a46: 2300 movs r3, #0 + 8001a48: 9300 str r3, [sp, #0] + + if(fw_length == 0) { + 8001a4a: 2c00 cmp r4, #0 + 8001a4c: d15f bne.n 8001b0e + uint8_t first[32]; + sha256_init(&ctx); + 8001a4e: a809 add r0, sp, #36 ; 0x24 + 8001a50: f003 fd1c bl 800548c + + // use length from header in flash + fw_length = FW_HDR->firmware_length; + 8001a54: 4b36 ldr r3, [pc, #216] ; (8001b30 ) + + // start of firmware (just after we end) to header + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001a56: 4a37 ldr r2, [pc, #220] ; (8001b34 ) + fw_length = FW_HDR->firmware_length; + 8001a58: f8d3 4098 ldr.w r4, [r3, #152] ; 0x98 + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001a5c: 4669 mov r1, sp + 8001a5e: f44f 537f mov.w r3, #16320 ; 0x3fc0 + 8001a62: a809 add r0, sp, #36 ; 0x24 + 8001a64: f7ff ffcc bl 8001a00 + + // from after header to end + checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE, + 8001a68: 4a33 ldr r2, [pc, #204] ; (8001b38 ) + 8001a6a: f5a4 4380 sub.w r3, r4, #16384 ; 0x4000 + 8001a6e: 4669 mov r1, sp + 8001a70: a809 add r0, sp, #36 ; 0x24 + 8001a72: f7ff ffc5 bl 8001a00 + fw_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + + sha256_final(&ctx, first); + 8001a76: a901 add r1, sp, #4 + 8001a78: a809 add r0, sp, #36 ; 0x24 + 8001a7a: f003 fd5b bl 8005534 + + // double SHA256 + sha256_single(first, sizeof(first), fw_digest); + 8001a7e: 4632 mov r2, r6 + 8001a80: 2120 movs r1, #32 + 8001a82: a801 add r0, sp, #4 + 8001a84: f003 fd6a bl 800555c + // fw_digest should already be populated by caller + total_len = fw_length - 64; + } + + // start over, and get the rest of flash. All of it. + sha256_init(&ctx); + 8001a88: a809 add r0, sp, #36 ; 0x24 + 8001a8a: f003 fcff bl 800548c + + // .. and chain in what we have so far + sha256_update(&ctx, fw_digest, 32); + 8001a8e: 2220 movs r2, #32 + 8001a90: 4631 mov r1, r6 + 8001a92: a809 add r0, sp, #36 ; 0x24 + 8001a94: f003 fd08 bl 80054a8 + + // Bootloader, including pairing secret area, but excluding MCU keys. + const uint8_t *base = (const uint8_t *)BL_FLASH_BASE; + checksum_more(&ctx, &total_len, base, ((uint8_t *)MCU_KEYS)-base); + 8001a98: f44f 33f0 mov.w r3, #122880 ; 0x1e000 + 8001a9c: f04f 6200 mov.w r2, #134217728 ; 0x8000000 + 8001aa0: 4669 mov r1, sp + 8001aa2: a809 add r0, sp, #36 ; 0x24 + 8001aa4: f7ff ffac bl 8001a00 + + // Probably-blank area after firmware, and filesystem area. + // Important: firmware images (fw_length) must be aligned with flash erase unit size (4k). + const uint8_t *fs = start + fw_length; + const uint8_t *last = base + MAIN_FLASH_SIZE; + checksum_more(&ctx, &total_len, fs, last-fs); + 8001aa8: f104 6200 add.w r2, r4, #134217728 ; 0x8000000 + 8001aac: f5c4 13b0 rsb r3, r4, #1441792 ; 0x160000 + 8001ab0: f502 3200 add.w r2, r2, #131072 ; 0x20000 + 8001ab4: 4669 mov r1, sp + 8001ab6: a809 add r0, sp, #36 ; 0x24 + 8001ab8: f7ff ffa2 bl 8001a00 + + rng_delay(); + 8001abc: f000 fe5e bl 800277c + + // OTP area + checksum_more(&ctx, &total_len, (void *)0x1fff7000, 0x400); + 8001ac0: 4a1e ldr r2, [pc, #120] ; (8001b3c ) + 8001ac2: f44f 6380 mov.w r3, #1024 ; 0x400 + 8001ac6: 4669 mov r1, sp + 8001ac8: a809 add r0, sp, #36 ; 0x24 + 8001aca: f7ff ff99 bl 8001a00 + + // "just in case" ... the option bytes (2 banks) + checksum_more(&ctx, &total_len, (void *)0x1fff7800, 0x28); + 8001ace: 4a1c ldr r2, [pc, #112] ; (8001b40 ) + 8001ad0: 2328 movs r3, #40 ; 0x28 + 8001ad2: 4669 mov r1, sp + 8001ad4: a809 add r0, sp, #36 ; 0x24 + 8001ad6: f7ff ff93 bl 8001a00 + checksum_more(&ctx, &total_len, (void *)0x1ffff800, 0x28); + 8001ada: 4a1a ldr r2, [pc, #104] ; (8001b44 ) + 8001adc: 2328 movs r3, #40 ; 0x28 + 8001ade: 4669 mov r1, sp + 8001ae0: a809 add r0, sp, #36 ; 0x24 + 8001ae2: f7ff ff8d bl 8001a00 + + // System ROM (they say it can't change, but clearly + // implemented as flash cells) + checksum_more(&ctx, &total_len, (void *)0x1fff0000, 0x7000); + 8001ae6: 4a18 ldr r2, [pc, #96] ; (8001b48 ) + 8001ae8: f44f 43e0 mov.w r3, #28672 ; 0x7000 + 8001aec: 4669 mov r1, sp + 8001aee: a809 add r0, sp, #36 ; 0x24 + 8001af0: f7ff ff86 bl 8001a00 + + // device serial number, just for kicks + checksum_more(&ctx, &total_len, (void *)0x1fff7590, 12); + 8001af4: 4a15 ldr r2, [pc, #84] ; (8001b4c ) + 8001af6: 230c movs r3, #12 + 8001af8: 4669 mov r1, sp + 8001afa: a809 add r0, sp, #36 ; 0x24 + 8001afc: f7ff ff80 bl 8001a00 + + ASSERT(total_len == TOTAL_CHECKSUM_LEN); + 8001b00: 4b13 ldr r3, [pc, #76] ; (8001b50 ) + 8001b02: 9a00 ldr r2, [sp, #0] + 8001b04: 429a cmp r2, r3 + 8001b06: d006 beq.n 8001b16 + 8001b08: 4812 ldr r0, [pc, #72] ; (8001b54 ) + 8001b0a: f7fe ff9d bl 8000a48 + total_len = fw_length - 64; + 8001b0e: f1a4 0340 sub.w r3, r4, #64 ; 0x40 + 8001b12: 9300 str r3, [sp, #0] + 8001b14: e7b8 b.n 8001a88 + + sha256_final(&ctx, world_digest); + 8001b16: 4629 mov r1, r5 + 8001b18: a809 add r0, sp, #36 ; 0x24 + 8001b1a: f003 fd0b bl 8005534 + + // double SHA256 (a bitcoin fetish) + sha256_single(world_digest, 32, world_digest); + 8001b1e: 462a mov r2, r5 + 8001b20: 2120 movs r1, #32 + 8001b22: 4628 mov r0, r5 + 8001b24: f003 fd1a bl 800555c + + rng_delay(); + 8001b28: f000 fe28 bl 800277c +} + 8001b2c: b01c add sp, #112 ; 0x70 + 8001b2e: bd70 pop {r4, r5, r6, pc} + 8001b30: 08023f00 .word 0x08023f00 + 8001b34: 08020000 .word 0x08020000 + 8001b38: 08024000 .word 0x08024000 + 8001b3c: 1fff7000 .word 0x1fff7000 + 8001b40: 1fff7800 .word 0x1fff7800 + 8001b44: 1ffff800 .word 0x1ffff800 + 8001b48: 1fff0000 .word 0x1fff0000 + 8001b4c: 1fff7590 .word 0x1fff7590 + 8001b50: 0018541c .word 0x0018541c + 8001b54: 0800e3e0 .word 0x0800e3e0 + +08001b58 : +// Scan the OTP area and determine what the current min-version (timestamp) +// we can allow. All zeros if any if okay. +// + void +get_min_version(uint8_t min_version[8]) +{ + 8001b58: b570 push {r4, r5, r6, lr} + 8001b5a: 4604 mov r4, r0 + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + 8001b5c: 4d0c ldr r5, [pc, #48] ; (8001b90 ) + + rng_delay(); + memset(min_version, 0, 8); + + for(int i=0; i) + rng_delay(); + 8001b60: f000 fe0c bl 800277c + memset(min_version, 0, 8); + 8001b64: 2300 movs r3, #0 + 8001b66: 6023 str r3, [r4, #0] + 8001b68: 6063 str r3, [r4, #4] + // is it programmed? + if(otp[0] == 0xff) continue; + + // is it a timestamp value? + if(otp[0] >= 0x40) continue; + if(otp[0] < 0x10) continue; + 8001b6a: 782b ldrb r3, [r5, #0] + 8001b6c: 3b10 subs r3, #16 + 8001b6e: 2b2f cmp r3, #47 ; 0x2f + 8001b70: d80a bhi.n 8001b88 + + if(memcmp(otp, min_version, 8) > 0) { + 8001b72: 4621 mov r1, r4 + 8001b74: 2208 movs r2, #8 + 8001b76: 4628 mov r0, r5 + 8001b78: f00b fd44 bl 800d604 + 8001b7c: 2800 cmp r0, #0 + memcpy(min_version, otp, 8); + 8001b7e: bfc1 itttt gt + 8001b80: 462b movgt r3, r5 + 8001b82: cb03 ldmiagt r3!, {r0, r1} + 8001b84: 6020 strgt r0, [r4, #0] + 8001b86: 6061 strgt r1, [r4, #4] + for(int i=0; i + } + } +} + 8001b8e: bd70 pop {r4, r5, r6, pc} + 8001b90: 1fff7000 .word 0x1fff7000 + 8001b94: 1fff7400 .word 0x1fff7400 + +08001b98 : + +// check_is_downgrade() +// + bool +check_is_downgrade(const uint8_t timestamp[8], const char *version) +{ + 8001b98: b513 push {r0, r1, r4, lr} + 8001b9a: 4604 mov r4, r0 +#ifndef FOR_Q1_ONLY + if(version) { + 8001b9c: b129 cbz r1, 8001baa + int major = (version[1] == '.') ? (version[0]-'0') : 10; + 8001b9e: 784b ldrb r3, [r1, #1] + 8001ba0: 2b2e cmp r3, #46 ; 0x2e + 8001ba2: d102 bne.n 8001baa + if(major < 3) { + 8001ba4: 780b ldrb r3, [r1, #0] + 8001ba6: 2b32 cmp r3, #50 ; 0x32 + 8001ba8: d90a bls.n 8001bc0 + } +#endif + + // look at FW_HDR->timestamp and compare to a growing list in main flash OTP + uint8_t min[8]; + get_min_version(min); + 8001baa: 4668 mov r0, sp + 8001bac: f7ff ffd4 bl 8001b58 + + return (memcmp(timestamp, min, 8) < 0); + 8001bb0: 2208 movs r2, #8 + 8001bb2: 4669 mov r1, sp + 8001bb4: 4620 mov r0, r4 + 8001bb6: f00b fd25 bl 800d604 + 8001bba: 0fc0 lsrs r0, r0, #31 +} + 8001bbc: b002 add sp, #8 + 8001bbe: bd10 pop {r4, pc} + return true; + 8001bc0: 2001 movs r0, #1 + 8001bc2: e7fb b.n 8001bbc + +08001bc4 : + +// warn_fishy_firmware() +// + void +warn_fishy_firmware(const uint8_t *pixels) +{ + 8001bc4: b538 push {r3, r4, r5, lr} + 8001bc6: 4605 mov r5, r0 + const int wait = 100; +#else + const int wait = 10; +#endif + + for(int i=0; i < wait; i++) { + 8001bc8: 2400 movs r4, #0 + oled_show_progress(pixels, (i*100)/wait); + 8001bca: 4621 mov r1, r4 + 8001bcc: 4628 mov r0, r5 + 8001bce: f7ff f97b bl 8000ec8 + for(int i=0; i < wait; i++) { + 8001bd2: 3401 adds r4, #1 + + delay_ms(250); + 8001bd4: 20fa movs r0, #250 ; 0xfa + 8001bd6: f001 fe8f bl 80038f8 + for(int i=0; i < wait; i++) { + 8001bda: 2c64 cmp r4, #100 ; 0x64 + 8001bdc: d1f5 bne.n 8001bca + } +} + 8001bde: bd38 pop {r3, r4, r5, pc} + +08001be0 : + +// verify_header() +// + bool +verify_header(const coldcardFirmwareHeader_t *hdr) +{ + 8001be0: b510 push {r4, lr} + 8001be2: 4604 mov r4, r0 + rng_delay(); + 8001be4: f000 fdca bl 800277c + + if(hdr->magic_value != FW_HEADER_MAGIC) goto fail; + 8001be8: 6822 ldr r2, [r4, #0] + 8001bea: 4b0b ldr r3, [pc, #44] ; (8001c18 ) + 8001bec: 429a cmp r2, r3 + 8001bee: d110 bne.n 8001c12 + if(hdr->version_string[0] == 0x0) goto fail; + 8001bf0: 7b20 ldrb r0, [r4, #12] + 8001bf2: b168 cbz r0, 8001c10 + if(hdr->timestamp[0] >= 0x40) goto fail; // 22 yr product lifetime + 8001bf4: 7923 ldrb r3, [r4, #4] + 8001bf6: 2b3f cmp r3, #63 ; 0x3f + 8001bf8: d80b bhi.n 8001c12 + if(hdr->firmware_length < FW_MIN_LENGTH) goto fail; + 8001bfa: 69a3 ldr r3, [r4, #24] + 8001bfc: f5a3 2380 sub.w r3, r3, #262144 ; 0x40000 + 8001c00: f5b3 1fd0 cmp.w r3, #1703936 ; 0x1a0000 + 8001c04: d205 bcs.n 8001c12 + if(hdr->firmware_length >= FW_MAX_LENGTH_MK4) goto fail; + if(hdr->pubkey_num >= NUM_KNOWN_PUBKEYS) goto fail; + 8001c06: 6960 ldr r0, [r4, #20] + 8001c08: 2805 cmp r0, #5 + 8001c0a: bf8c ite hi + 8001c0c: 2000 movhi r0, #0 + 8001c0e: 2001 movls r0, #1 + + return true; +fail: + return false; +} + 8001c10: bd10 pop {r4, pc} + return false; + 8001c12: 2000 movs r0, #0 + 8001c14: e7fc b.n 8001c10 + 8001c16: bf00 nop + 8001c18: cc001234 .word 0xcc001234 + +08001c1c : +// +// Given double-sha256 over the firmware bytes, check the signature. +// + bool +verify_signature(const coldcardFirmwareHeader_t *hdr, const uint8_t fw_check[32]) +{ + 8001c1c: b530 push {r4, r5, lr} + // this takes a few ms at least, not fast. + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001c1e: 6943 ldr r3, [r0, #20] + 8001c20: 4d0b ldr r5, [pc, #44] ; (8001c50 ) +{ + 8001c22: b085 sub sp, #20 + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001c24: eb05 1583 add.w r5, r5, r3, lsl #6 +{ + 8001c28: 4604 mov r4, r0 + 8001c2a: 9103 str r1, [sp, #12] + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001c2c: f004 fe5a bl 80068e4 + 8001c30: f104 0340 add.w r3, r4, #64 ; 0x40 + 8001c34: 9903 ldr r1, [sp, #12] + 8001c36: 9000 str r0, [sp, #0] + 8001c38: 2220 movs r2, #32 + 8001c3a: 4628 mov r0, r5 + 8001c3c: f005 f8d9 bl 8006df2 + 8001c40: 4604 mov r4, r0 + hdr->signature, uECC_secp256k1()); + + //puts(ok ? "Sig ok" : "Sig fail"); + rng_delay(); + 8001c42: f000 fd9b bl 800277c + + return ok; +} + 8001c46: 1e20 subs r0, r4, #0 + 8001c48: bf18 it ne + 8001c4a: 2001 movne r0, #1 + 8001c4c: b005 add sp, #20 + 8001c4e: bd30 pop {r4, r5, pc} + 8001c50: 0800e45a .word 0x0800e45a + +08001c54 : +// Check hdr, and even signature of protential new firmware in PSRAM. +// Returns checksum needed for 608 +// + bool +verify_firmware_in_ram(const uint8_t *start, uint32_t len, uint8_t world_check[32]) +{ + 8001c54: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + const coldcardFirmwareHeader_t *hdr = (const coldcardFirmwareHeader_t *) + 8001c58: f500 567e add.w r6, r0, #16256 ; 0x3f80 +{ + 8001c5c: b09c sub sp, #112 ; 0x70 + 8001c5e: 4605 mov r5, r0 + (start + FW_HEADER_OFFSET); + uint8_t fw_digest[32]; + + // check basics like verison, hw compat, etc + if(!verify_header(hdr)) goto fail; + 8001c60: 4630 mov r0, r6 +{ + 8001c62: 4617 mov r7, r2 + if(!verify_header(hdr)) goto fail; + 8001c64: f7ff ffbc bl 8001be0 + 8001c68: 4604 mov r4, r0 + 8001c6a: b150 cbz r0, 8001c82 + + if(check_is_downgrade(hdr->timestamp, (const char *)hdr->version_string)) { + 8001c6c: f106 010c add.w r1, r6, #12 + 8001c70: 1d30 adds r0, r6, #4 + 8001c72: f7ff ff91 bl 8001b98 + 8001c76: 4604 mov r4, r0 + 8001c78: b138 cbz r0, 8001c8a + puts("downgrade"); + 8001c7a: 481e ldr r0, [pc, #120] ; (8001cf4 ) + 8001c7c: f003 f896 bl 8004dac + + checksum_flash(fw_digest, world_check, hdr->firmware_length); + + return true; +fail: + return false; + 8001c80: 2400 movs r4, #0 +} + 8001c82: 4620 mov r0, r4 + 8001c84: b01c add sp, #112 ; 0x70 + 8001c86: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + rng_delay(); + 8001c8a: f000 fd77 bl 800277c + hdr->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + 8001c8e: f505 5840 add.w r8, r5, #12288 ; 0x3000 + sha256_init(&ctx); + 8001c92: a809 add r0, sp, #36 ; 0x24 + uint32_t total_len = 0; + 8001c94: 9400 str r4, [sp, #0] + sha256_init(&ctx); + 8001c96: f003 fbf9 bl 800548c + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001c9a: f44f 537f mov.w r3, #16320 ; 0x3fc0 + 8001c9e: 462a mov r2, r5 + 8001ca0: 4669 mov r1, sp + 8001ca2: a809 add r0, sp, #36 ; 0x24 + 8001ca4: f7ff feac bl 8001a00 + hdr->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + 8001ca8: f8d8 3f98 ldr.w r3, [r8, #3992] ; 0xf98 + checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE, + 8001cac: f505 4280 add.w r2, r5, #16384 ; 0x4000 + 8001cb0: f5a3 4380 sub.w r3, r3, #16384 ; 0x4000 + 8001cb4: 4669 mov r1, sp + 8001cb6: a809 add r0, sp, #36 ; 0x24 + 8001cb8: f7ff fea2 bl 8001a00 + sha256_final(&ctx, fw_digest); + 8001cbc: a901 add r1, sp, #4 + 8001cbe: a809 add r0, sp, #36 ; 0x24 + 8001cc0: f003 fc38 bl 8005534 + sha256_single(fw_digest, 32, fw_digest); + 8001cc4: aa01 add r2, sp, #4 + 8001cc6: 4610 mov r0, r2 + 8001cc8: 2120 movs r1, #32 + 8001cca: f003 fc47 bl 800555c + rng_delay(); + 8001cce: f000 fd55 bl 800277c + if(!verify_signature(hdr, fw_digest)) { + 8001cd2: a901 add r1, sp, #4 + 8001cd4: 4630 mov r0, r6 + 8001cd6: f7ff ffa1 bl 8001c1c + 8001cda: 4604 mov r4, r0 + 8001cdc: b918 cbnz r0, 8001ce6 + puts("sig fail"); + 8001cde: 4806 ldr r0, [pc, #24] ; (8001cf8 ) + 8001ce0: f003 f864 bl 8004dac + goto fail; + 8001ce4: e7cd b.n 8001c82 + checksum_flash(fw_digest, world_check, hdr->firmware_length); + 8001ce6: f8d8 2f98 ldr.w r2, [r8, #3992] ; 0xf98 + 8001cea: 4639 mov r1, r7 + 8001cec: a801 add r0, sp, #4 + 8001cee: f7ff fea3 bl 8001a38 + return true; + 8001cf2: e7c6 b.n 8001c82 + 8001cf4: 0800e3e7 .word 0x0800e3e7 + 8001cf8: 0800e3f1 .word 0x0800e3f1 + +08001cfc : +// - don't set the light at this point. +// - requires bootloader to have been unchanged since world_check recorded (debug issue) +// + bool +verify_world_checksum(const uint8_t world_check[32]) +{ + 8001cfc: b507 push {r0, r1, r2, lr} + 8001cfe: 9001 str r0, [sp, #4] + ae_setup(); + 8001d00: f000 fe60 bl 80029c4 + ae_pair_unlock(); + 8001d04: f001 f854 bl 8002db0 + + return (ae_checkmac_hard(KEYNUM_firmware, world_check) == 0); + 8001d08: 9901 ldr r1, [sp, #4] + 8001d0a: 200e movs r0, #14 + 8001d0c: f001 f9de bl 80030cc +} + 8001d10: fab0 f080 clz r0, r0 + 8001d14: 0940 lsrs r0, r0, #5 + 8001d16: b003 add sp, #12 + 8001d18: f85d fb04 ldr.w pc, [sp], #4 + +08001d1c : + +// verify_firmware() +// + bool +verify_firmware(void) +{ + 8001d1c: b570 push {r4, r5, r6, lr} + STATIC_ASSERT(sizeof(coldcardFirmwareHeader_t) == FW_HEADER_SIZE); + + rng_delay(); + + // watch for unprogrammed header. and some + if(FW_HDR->version_string[0] == 0xff) goto blank; + 8001d1e: 4e2a ldr r6, [pc, #168] ; (8001dc8 ) +{ + 8001d20: b090 sub sp, #64 ; 0x40 + rng_delay(); + 8001d22: f000 fd2b bl 800277c + if(FW_HDR->version_string[0] == 0xff) goto blank; + 8001d26: f896 308c ldrb.w r3, [r6, #140] ; 0x8c + 8001d2a: 2bff cmp r3, #255 ; 0xff + 8001d2c: d107 bne.n 8001d3e + puts("corrupt firmware"); + oled_show(screen_corrupt); + return false; + +blank: + puts("no firmware"); + 8001d2e: 4827 ldr r0, [pc, #156] ; (8001dcc ) + puts("corrupt firmware"); + 8001d30: f003 f83c bl 8004dac + oled_show(screen_corrupt); + 8001d34: 4826 ldr r0, [pc, #152] ; (8001dd0 ) + 8001d36: f7ff f885 bl 8000e44 + return false; + 8001d3a: 2400 movs r4, #0 + 8001d3c: e030 b.n 8001da0 + if(!verify_header(FW_HDR)) goto fail; + 8001d3e: 4825 ldr r0, [pc, #148] ; (8001dd4 ) + 8001d40: f7ff ff4e bl 8001be0 + 8001d44: 2800 cmp r0, #0 + 8001d46: d03c beq.n 8001dc2 + rng_delay(); + 8001d48: f000 fd18 bl 800277c + checksum_flash(fw_check, world_check, 0); + 8001d4c: 2200 movs r2, #0 + 8001d4e: a908 add r1, sp, #32 + 8001d50: 4668 mov r0, sp + 8001d52: f7ff fe71 bl 8001a38 + rng_delay(); + 8001d56: f000 fd11 bl 800277c + if(!verify_signature(FW_HDR, fw_check)) goto fail; + 8001d5a: 481e ldr r0, [pc, #120] ; (8001dd4 ) + 8001d5c: 4669 mov r1, sp + 8001d5e: f7ff ff5d bl 8001c1c + 8001d62: 4604 mov r4, r0 + 8001d64: b368 cbz r0, 8001dc2 + int not_green = ae_set_gpio_secure(world_check); + 8001d66: a808 add r0, sp, #32 + 8001d68: f001 fbc4 bl 80034f4 + 8001d6c: 4605 mov r5, r0 + rng_delay(); + 8001d6e: f000 fd05 bl 800277c + rng_delay(); + 8001d72: f000 fd03 bl 800277c + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8001d76: 4b18 ldr r3, [pc, #96] ; (8001dd8 ) + 8001d78: 6a1b ldr r3, [r3, #32] + 8001d7a: b2db uxtb r3, r3 + if(!flash_is_security_level2() && not_green) { + 8001d7c: 2bcc cmp r3, #204 ; 0xcc + 8001d7e: d008 beq.n 8001d92 + 8001d80: b18d cbz r5, 8001da6 + oled_show_progress(screen_verify, 100); + 8001d82: 4816 ldr r0, [pc, #88] ; (8001ddc ) + 8001d84: 2164 movs r1, #100 ; 0x64 + 8001d86: f7ff f89f bl 8000ec8 + puts("Factory boot"); + 8001d8a: 4815 ldr r0, [pc, #84] ; (8001de0 ) + puts("Good firmware"); + 8001d8c: f003 f80e bl 8004dac + 8001d90: e006 b.n 8001da0 + } else if(not_green) { + 8001d92: b145 cbz r5, 8001da6 + puts("WARN: Red light"); + 8001d94: 4813 ldr r0, [pc, #76] ; (8001de4 ) + 8001d96: f003 f809 bl 8004dac + warn_fishy_firmware(screen_red_light); + 8001d9a: 4813 ldr r0, [pc, #76] ; (8001de8 ) + warn_fishy_firmware(screen_devmode); + 8001d9c: f7ff ff12 bl 8001bc4 + oled_show(screen_corrupt); + + return false; +} + 8001da0: 4620 mov r0, r4 + 8001da2: b010 add sp, #64 ; 0x40 + 8001da4: bd70 pop {r4, r5, r6, pc} + } else if(FW_HDR->pubkey_num == 0) { + 8001da6: f8d6 3094 ldr.w r3, [r6, #148] ; 0x94 + 8001daa: b923 cbnz r3, 8001db6 + puts("WARN: Unsigned firmware"); + 8001dac: 480f ldr r0, [pc, #60] ; (8001dec ) + 8001dae: f002 fffd bl 8004dac + warn_fishy_firmware(screen_devmode); + 8001db2: 480f ldr r0, [pc, #60] ; (8001df0 ) + 8001db4: e7f2 b.n 8001d9c + oled_show_progress(screen_verify, 100); + 8001db6: 4809 ldr r0, [pc, #36] ; (8001ddc ) + 8001db8: 2164 movs r1, #100 ; 0x64 + 8001dba: f7ff f885 bl 8000ec8 + puts("Good firmware"); + 8001dbe: 480d ldr r0, [pc, #52] ; (8001df4 ) + 8001dc0: e7e4 b.n 8001d8c + puts("corrupt firmware"); + 8001dc2: 480d ldr r0, [pc, #52] ; (8001df8 ) + 8001dc4: e7b4 b.n 8001d30 + 8001dc6: bf00 nop + 8001dc8: 08023f00 .word 0x08023f00 + 8001dcc: 0800e3fa .word 0x0800e3fa + 8001dd0: 0800d875 .word 0x0800d875 + 8001dd4: 08023f80 .word 0x08023f80 + 8001dd8: 40022000 .word 0x40022000 + 8001ddc: 0800e242 .word 0x0800e242 + 8001de0: 0800e406 .word 0x0800e406 + 8001de4: 0800e413 .word 0x0800e413 + 8001de8: 0800dd72 .word 0x0800dd72 + 8001dec: 0800e423 .word 0x0800e423 + 8001df0: 0800d932 .word 0x0800d932 + 8001df4: 0800e43b .word 0x0800e43b + 8001df8: 0800e449 .word 0x0800e449 + +08001dfc : + void +systick_setup(void) +{ + const uint32_t ticks = HCLK_FREQUENCY/1000; + + SysTick->LOAD = (ticks - 1); + 8001dfc: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 8001e00: 4a03 ldr r2, [pc, #12] ; (8001e10 ) + 8001e02: 615a str r2, [r3, #20] + SysTick->VAL = 0; + 8001e04: 2200 movs r2, #0 + 8001e06: 619a str r2, [r3, #24] + SysTick->CTRL = SYSTICK_CLKSOURCE_HCLK | SysTick_CTRL_ENABLE_Msk; + 8001e08: 2205 movs r2, #5 + 8001e0a: 611a str r2, [r3, #16] +} + 8001e0c: 4770 bx lr + 8001e0e: bf00 nop + 8001e10: 0001d4bf .word 0x0001d4bf + +08001e14 : + SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; +#endif + + /* FPU settings ------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 20U)|(3UL << 22U)); /* set CP10 and CP11 Full Access */ + 8001e14: 4a0e ldr r2, [pc, #56] ; (8001e50 ) + 8001e16: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 8001e1a: f443 0370 orr.w r3, r3, #15728640 ; 0xf00000 + 8001e1e: f8c2 3088 str.w r3, [r2, #136] ; 0x88 +#endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set MSION bit */ + RCC->CR |= RCC_CR_MSION; + 8001e22: 4b0c ldr r3, [pc, #48] ; (8001e54 ) + 8001e24: 681a ldr r2, [r3, #0] + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000U; + 8001e26: 2100 movs r1, #0 + RCC->CR |= RCC_CR_MSION; + 8001e28: f042 0201 orr.w r2, r2, #1 + 8001e2c: 601a str r2, [r3, #0] + RCC->CFGR = 0x00000000U; + 8001e2e: 6099 str r1, [r3, #8] + + /* Reset HSEON, CSSON , HSION, and PLLON bits */ + RCC->CR &= 0xEAF6FFFFU; + 8001e30: 681a ldr r2, [r3, #0] + 8001e32: f022 52a8 bic.w r2, r2, #352321536 ; 0x15000000 + 8001e36: f422 2210 bic.w r2, r2, #589824 ; 0x90000 + 8001e3a: 601a str r2, [r3, #0] + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x00001000U; + 8001e3c: f44f 5280 mov.w r2, #4096 ; 0x1000 + 8001e40: 60da str r2, [r3, #12] + + /* Reset HSEBYP bit */ + RCC->CR &= 0xFFFBFFFFU; + 8001e42: 681a ldr r2, [r3, #0] + 8001e44: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 8001e48: 601a str r2, [r3, #0] + + /* Disable all interrupts */ + RCC->CIER = 0x00000000U; + 8001e4a: 6199 str r1, [r3, #24] +} + 8001e4c: 4770 bx lr + 8001e4e: bf00 nop + 8001e50: e000ed00 .word 0xe000ed00 + 8001e54: 40021000 .word 0x40021000 + +08001e58 : + +// clocks_setup() +// + void +clocks_setup(void) +{ + 8001e58: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + + // setup power supplies + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); + + // Configure LSE Drive Capability + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); + 8001e5c: 4c41 ldr r4, [pc, #260] ; (8001f64 ) +{ + 8001e5e: b0c1 sub sp, #260 ; 0x104 + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); + 8001e60: 2000 movs r0, #0 + 8001e62: f005 f959 bl 8007118 + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); + 8001e66: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8001e6a: f023 0318 bic.w r3, r3, #24 + 8001e6e: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + + // Enable HSE Oscillator and activate PLL with HSE as source + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + 8001e72: 2201 movs r2, #1 + 8001e74: f44f 3380 mov.w r3, #65536 ; 0x10000 + 8001e78: e9cd 230a strd r2, r3, [sp, #40] ; 0x28 + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + RCC_OscInitStruct.MSIState = RCC_MSI_OFF; + + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 8001e7c: 2703 movs r7, #3 + + // Select PLL as system clock source and configure + // the HCLK, PCLK1 and PCLK2 clocks dividers + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK + 8001e7e: 230f movs r3, #15 + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + 8001e80: 2500 movs r5, #0 + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 8001e82: 2602 movs r6, #2 + | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + 8001e84: e9cd 3705 strd r3, r7, [sp, #20] + + RCC_OscInitStruct.PLL.PLLM = CKCC_CLK_PLLM; + RCC_OscInitStruct.PLL.PLLN = CKCC_CLK_PLLN; + RCC_OscInitStruct.PLL.PLLP = CKCC_CLK_PLLP; + 8001e88: f04f 0807 mov.w r8, #7 + 8001e8c: 233c movs r3, #60 ; 0x3c + RCC_OscInitStruct.PLL.PLLQ = CKCC_CLK_PLLQ; + 8001e8e: f04f 0905 mov.w r9, #5 + + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + HAL_RCC_OscConfig(&RCC_OscInitStruct); + 8001e92: a80a add r0, sp, #40 ; 0x28 + RCC_OscInitStruct.PLL.PLLP = CKCC_CLK_PLLP; + 8001e94: e9cd 3817 strd r3, r8, [sp, #92] ; 0x5c + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 8001e98: e9cd 6714 strd r6, r7, [sp, #80] ; 0x50 + RCC_OscInitStruct.PLL.PLLR = CKCC_CLK_PLLR; + 8001e9c: e9cd 9619 strd r9, r6, [sp, #100] ; 0x64 + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + 8001ea0: e9cd 5507 strd r5, r5, [sp, #28] + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + 8001ea4: 950c str r5, [sp, #48] ; 0x30 + RCC_OscInitStruct.MSIState = RCC_MSI_OFF; + 8001ea6: 9510 str r5, [sp, #64] ; 0x40 + RCC_OscInitStruct.PLL.PLLM = CKCC_CLK_PLLM; + 8001ea8: 9616 str r6, [sp, #88] ; 0x58 + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + 8001eaa: 9509 str r5, [sp, #36] ; 0x24 + HAL_RCC_OscConfig(&RCC_OscInitStruct); + 8001eac: f006 fcdc bl 8008868 + + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); + 8001eb0: 4649 mov r1, r9 + 8001eb2: a805 add r0, sp, #20 + 8001eb4: f006 ff86 bl 8008dc4 + + // DIS-able MSI-Hardware auto calibration mode with LSE + CLEAR_BIT(RCC->CR, RCC_CR_MSIPLLEN); + 8001eb8: 6823 ldr r3, [r4, #0] + 8001eba: f023 0304 bic.w r3, r3, #4 + 8001ebe: 6023 str r3, [r4, #0] + + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1|RCC_PERIPHCLK_I2C2 + 8001ec0: 4b29 ldr r3, [pc, #164] ; (8001f68 ) + 8001ec2: 931b str r3, [sp, #108] ; 0x6c + + // PLLSAI is used to clock USB, ADC, I2C1 and RNG. The frequency is + // HSE(8MHz)/PLLM(2)*PLLSAI1N(24)/PLLSAIQ(2) = 48MHz. + // + PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; + PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; + 8001ec4: f04f 5380 mov.w r3, #268435456 ; 0x10000000 + 8001ec8: 933b str r3, [sp, #236] ; 0xec + PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1; + 8001eca: f04f 6380 mov.w r3, #67108864 ; 0x4000000 + 8001ece: 9338 str r3, [sp, #224] ; 0xe0 + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLLSAI1; + 8001ed0: 933a str r3, [sp, #232] ; 0xe8 + + PeriphClkInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; + PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 2; + PeriphClkInitStruct.PLLSAI1.PLLSAI1N = 24; + 8001ed2: 2318 movs r3, #24 + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + 8001ed4: f44f 7240 mov.w r2, #768 ; 0x300 + PeriphClkInitStruct.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; + 8001ed8: e9cd 381e strd r3, r8, [sp, #120] ; 0x78 + PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + |RCC_PLLSAI1_48M2CLK + |RCC_PLLSAI1_ADC1CLK; + + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + 8001edc: a81b add r0, sp, #108 ; 0x6c + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + 8001ede: 4b23 ldr r3, [pc, #140] ; (8001f6c ) + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + 8001ee0: 923f str r2, [sp, #252] ; 0xfc + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + 8001ee2: 9322 str r3, [sp, #136] ; 0x88 + PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 2; + 8001ee4: e9cd 761c strd r7, r6, [sp, #112] ; 0x70 + PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; + 8001ee8: e9cd 6620 strd r6, r6, [sp, #128] ; 0x80 + PeriphClkInitStruct.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; + 8001eec: 9531 str r5, [sp, #196] ; 0xc4 + PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; + 8001eee: 9536 str r5, [sp, #216] ; 0xd8 + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + 8001ef0: f007 fa8c bl 800940c + + __HAL_RCC_RTC_ENABLE(); + 8001ef4: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8001ef8: f443 4300 orr.w r3, r3, #32768 ; 0x8000 + 8001efc: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + __HAL_RCC_HASH_CLK_ENABLE(); // for SHA256 + 8001f00: 6ce3 ldr r3, [r4, #76] ; 0x4c + 8001f02: f443 3300 orr.w r3, r3, #131072 ; 0x20000 + 8001f06: 64e3 str r3, [r4, #76] ; 0x4c + 8001f08: 6ce3 ldr r3, [r4, #76] ; 0x4c + 8001f0a: f403 3300 and.w r3, r3, #131072 ; 0x20000 + 8001f0e: 9301 str r3, [sp, #4] + 8001f10: 9b01 ldr r3, [sp, #4] + __HAL_RCC_SPI1_CLK_ENABLE(); // for OLED + 8001f12: 6e23 ldr r3, [r4, #96] ; 0x60 + 8001f14: f443 5380 orr.w r3, r3, #4096 ; 0x1000 + 8001f18: 6623 str r3, [r4, #96] ; 0x60 + 8001f1a: 6e23 ldr r3, [r4, #96] ; 0x60 + 8001f1c: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 8001f20: 9302 str r3, [sp, #8] + 8001f22: 9b02 ldr r3, [sp, #8] + //__HAL_RCC_SPI2_CLK_ENABLE(); // for SPI flash + __HAL_RCC_DMAMUX1_CLK_ENABLE(); // (need this) because code missing in mpy? + 8001f24: 6ca3 ldr r3, [r4, #72] ; 0x48 + 8001f26: f043 0304 orr.w r3, r3, #4 + 8001f2a: 64a3 str r3, [r4, #72] ; 0x48 + 8001f2c: 6ca3 ldr r3, [r4, #72] ; 0x48 + 8001f2e: f003 0304 and.w r3, r3, #4 + 8001f32: 9303 str r3, [sp, #12] + 8001f34: 9b03 ldr r3, [sp, #12] + + // for SE2 + __HAL_RCC_I2C2_CLK_ENABLE(); + 8001f36: 6da3 ldr r3, [r4, #88] ; 0x58 + 8001f38: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 8001f3c: 65a3 str r3, [r4, #88] ; 0x58 + 8001f3e: 6da3 ldr r3, [r4, #88] ; 0x58 + 8001f40: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 8001f44: 9304 str r3, [sp, #16] + 8001f46: 9b04 ldr r3, [sp, #16] + __HAL_RCC_I2C2_FORCE_RESET(); + 8001f48: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8001f4a: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 8001f4e: 63a3 str r3, [r4, #56] ; 0x38 + __HAL_RCC_I2C2_RELEASE_RESET(); + 8001f50: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8001f52: f423 0380 bic.w r3, r3, #4194304 ; 0x400000 + 8001f56: 63a3 str r3, [r4, #56] ; 0x38 + + // setup SYSTICK, but we don't have the irq hooked up and not using HAL + // but we use it in polling mode for delay_ms() + systick_setup(); + 8001f58: f7ff ff50 bl 8001dfc + +} + 8001f5c: b041 add sp, #260 ; 0x104 + 8001f5e: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 8001f62: bf00 nop + 8001f64: 40021000 .word 0x40021000 + 8001f68: 00066880 .word 0x00066880 + 8001f6c: 01110000 .word 0x01110000 + +08001f70 : + } else { + + // write changes to OB flash bytes + + // Set OPTSTRT bit + SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT); + 8001f70: 4b13 ldr r3, [pc, #76] ; (8001fc0 ) + 8001f72: 695a ldr r2, [r3, #20] + 8001f74: f442 3200 orr.w r2, r2, #131072 ; 0x20000 + 8001f78: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { + 8001f7a: 691a ldr r2, [r3, #16] + 8001f7c: 03d2 lsls r2, r2, #15 + 8001f7e: d4fc bmi.n 8001f7a + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); + 8001f80: 6919 ldr r1, [r3, #16] + if(error) { + 8001f82: 4a10 ldr r2, [pc, #64] ; (8001fc4 ) + 8001f84: 4211 tst r1, r2 + 8001f86: d104 bne.n 8001f92 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { + 8001f88: 691a ldr r2, [r3, #16] + 8001f8a: 07d0 lsls r0, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); + 8001f8c: bf44 itt mi + 8001f8e: 2201 movmi r2, #1 + 8001f90: 611a strmi r2, [r3, #16] + + /// Wait for update to complete + _flash_wait_done(); + + // lock OB again. + SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK); + 8001f92: 4b0b ldr r3, [pc, #44] ; (8001fc0 ) + 8001f94: 695a ldr r2, [r3, #20] + 8001f96: f042 4280 orr.w r2, r2, #1073741824 ; 0x40000000 + 8001f9a: 615a str r2, [r3, #20] + + // include "launch" to make them take effect NOW + SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH); + 8001f9c: 695a ldr r2, [r3, #20] + 8001f9e: f042 6200 orr.w r2, r2, #134217728 ; 0x8000000 + 8001fa2: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { + 8001fa4: 691a ldr r2, [r3, #16] + 8001fa6: 03d1 lsls r1, r2, #15 + 8001fa8: d4fc bmi.n 8001fa4 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); + 8001faa: 6919 ldr r1, [r3, #16] + if(error) { + 8001fac: 4a05 ldr r2, [pc, #20] ; (8001fc4 ) + 8001fae: 4211 tst r1, r2 + 8001fb0: d104 bne.n 8001fbc + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { + 8001fb2: 691a ldr r2, [r3, #16] + 8001fb4: 07d2 lsls r2, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); + 8001fb6: bf44 itt mi + 8001fb8: 2201 movmi r2, #1 + 8001fba: 611a strmi r2, [r3, #16] + + _flash_wait_done(); + } +} + 8001fbc: 4770 bx lr + 8001fbe: bf00 nop + 8001fc0: 40022000 .word 0x40022000 + 8001fc4: 0002c3fa .word 0x0002c3fa + +08001fc8 : +{ + 8001fc8: b507 push {r0, r1, r2, lr} + memcpy(&_srelocate, &_etext, ((uint32_t)&_erelocate)-(uint32_t)&_srelocate); + 8001fca: 4809 ldr r0, [pc, #36] ; (8001ff0 ) + 8001fcc: 4a09 ldr r2, [pc, #36] ; (8001ff4 ) + 8001fce: 490a ldr r1, [pc, #40] ; (8001ff8 ) + 8001fd0: 1a12 subs r2, r2, r0 + 8001fd2: f00b fb27 bl 800d624 + __HAL_RCC_FLASH_CLK_ENABLE(); + 8001fd6: 4b09 ldr r3, [pc, #36] ; (8001ffc ) + 8001fd8: 6c9a ldr r2, [r3, #72] ; 0x48 + 8001fda: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8001fde: 649a str r2, [r3, #72] ; 0x48 + 8001fe0: 6c9b ldr r3, [r3, #72] ; 0x48 + 8001fe2: f403 7380 and.w r3, r3, #256 ; 0x100 + 8001fe6: 9301 str r3, [sp, #4] + 8001fe8: 9b01 ldr r3, [sp, #4] +} + 8001fea: b003 add sp, #12 + 8001fec: f85d fb04 ldr.w pc, [sp], #4 + 8001ff0: 2009e000 .word 0x2009e000 + 8001ff4: 2009e150 .word 0x2009e150 + 8001ff8: 0800ea48 .word 0x0800ea48 + 8001ffc: 40021000 .word 0x40021000 + +08002000 : + SET_BIT(FLASH->CR, FLASH_CR_LOCK); + 8002000: 4a02 ldr r2, [pc, #8] ; (800200c ) + 8002002: 6953 ldr r3, [r2, #20] + 8002004: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 8002008: 6153 str r3, [r2, #20] +} + 800200a: 4770 bx lr + 800200c: 40022000 .word 0x40022000 + +08002010 : +{ + 8002010: b508 push {r3, lr} + if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) { + 8002012: 4b08 ldr r3, [pc, #32] ; (8002034 ) + 8002014: 695a ldr r2, [r3, #20] + 8002016: 2a00 cmp r2, #0 + 8002018: da0a bge.n 8002030 + WRITE_REG(FLASH->KEYR, FLASH_KEY1); + 800201a: 4a07 ldr r2, [pc, #28] ; (8002038 ) + 800201c: 609a str r2, [r3, #8] + WRITE_REG(FLASH->KEYR, FLASH_KEY2); + 800201e: f102 3288 add.w r2, r2, #2290649224 ; 0x88888888 + 8002022: 609a str r2, [r3, #8] + if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) { + 8002024: 695b ldr r3, [r3, #20] + 8002026: 2b00 cmp r3, #0 + 8002028: da02 bge.n 8002030 + INCONSISTENT("failed to unlock"); + 800202a: 4804 ldr r0, [pc, #16] ; (800203c ) + 800202c: f7fe fd0c bl 8000a48 +} + 8002030: bd08 pop {r3, pc} + 8002032: bf00 nop + 8002034: 40022000 .word 0x40022000 + 8002038: 45670123 .word 0x45670123 + 800203c: 0800d700 .word 0x0800d700 + +08002040 : +{ + 8002040: b510 push {r4, lr} + if(!lock) { + 8002042: b980 cbnz r0, 8002066 + if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) { + 8002044: 4c0a ldr r4, [pc, #40] ; (8002070 ) + 8002046: 6963 ldr r3, [r4, #20] + 8002048: 005a lsls r2, r3, #1 + 800204a: d510 bpl.n 800206e + flash_unlock(); + 800204c: f7ff ffe0 bl 8002010 + WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1); + 8002050: 4b08 ldr r3, [pc, #32] ; (8002074 ) + 8002052: 60e3 str r3, [r4, #12] + WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2); + 8002054: f103 3344 add.w r3, r3, #1145324612 ; 0x44444444 + 8002058: 60e3 str r3, [r4, #12] + if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) { + 800205a: 6963 ldr r3, [r4, #20] + 800205c: 005b lsls r3, r3, #1 + 800205e: d506 bpl.n 800206e + INCONSISTENT("failed to OB unlock"); + 8002060: 4805 ldr r0, [pc, #20] ; (8002078 ) + 8002062: f7fe fcf1 bl 8000a48 +} + 8002066: e8bd 4010 ldmia.w sp!, {r4, lr} + 800206a: f7ff bf81 b.w 8001f70 + 800206e: bd10 pop {r4, pc} + 8002070: 40022000 .word 0x40022000 + 8002074: 08192a3b .word 0x08192a3b + 8002078: 0800d700 .word 0x0800d700 + +0800207c : + +// pick_pairing_secret() +// + static void +pick_pairing_secret(void) +{ + 800207c: b570 push {r4, r5, r6, lr} + 800207e: f5ad 6d85 sub.w sp, sp, #1064 ; 0x428 + // important the RNG works here. ok to call setup multiple times. + rng_setup(); + 8002082: f000 fb39 bl 80026f8 + 8002086: 24c8 movs r4, #200 ; 0xc8 +#else + // Demo to anyone watching that the RNG is working, but likely only + // to be seen by production team during initial powerup. + uint8_t tmp[1024]; + for(int i=0; i<200; i++) { + rng_buffer(tmp, sizeof(tmp)); + 8002088: f44f 6180 mov.w r1, #1024 ; 0x400 + 800208c: a80a add r0, sp, #40 ; 0x28 + 800208e: f000 fb5f bl 8002750 + + oled_show_raw(sizeof(tmp), (void *)tmp); + 8002092: a90a add r1, sp, #40 ; 0x28 + 8002094: f44f 6080 mov.w r0, #1024 ; 0x400 + 8002098: f7fe fea8 bl 8000dec + for(int i=0; i<200; i++) { + 800209c: 3c01 subs r4, #1 + 800209e: d1f3 bne.n 8002088 + } + + oled_factory_busy(); + 80020a0: f7fe ff92 bl 8000fc8 +#endif + + // .. but don't use those numbers, because those are semi-public now. + uint32_t secret[8]; + for(int i=0; i<8; i++) { + 80020a4: ad02 add r5, sp, #8 + oled_factory_busy(); + 80020a6: 462e mov r6, r5 + secret[i] = rng_sample(); + 80020a8: f000 fb14 bl 80026d4 + for(int i=0; i<8; i++) { + 80020ac: 3401 adds r4, #1 + 80020ae: 2c08 cmp r4, #8 + secret[i] = rng_sample(); + 80020b0: f846 0b04 str.w r0, [r6], #4 + for(int i=0; i<8; i++) { + 80020b4: d1f8 bne.n 80020a8 + } + + // enforce policy that first word is not all ones (so it never + // looks like unprogrammed flash). + while(secret[0] == ~0) { + 80020b6: 682b ldr r3, [r5, #0] + 80020b8: 3301 adds r3, #1 + 80020ba: d00c beq.n 80020d6 + + // Write pairing secret into flash + { + uint32_t dest = (uint32_t)&rom_secrets->pairing_secret; + + flash_unlock(); + 80020bc: f7ff ffa8 bl 8002010 + uint32_t dest = (uint32_t)&rom_secrets->pairing_secret; + 80020c0: 4c16 ldr r4, [pc, #88] ; (800211c ) + for(int i=0; i<8; i+=2, dest += 8) { + 80020c2: 4e17 ldr r6, [pc, #92] ; (8002120 ) + uint64_t val = (((uint64_t)secret[i]) << 32) | secret[i+1]; + + if(flash_burn(dest, val)) { + 80020c4: e9d5 3200 ldrd r3, r2, [r5] + 80020c8: 4620 mov r0, r4 + 80020ca: f00b fb11 bl 800d6f0 <__flash_burn_veneer> + 80020ce: b130 cbz r0, 80020de + INCONSISTENT("flash fail"); + 80020d0: 4814 ldr r0, [pc, #80] ; (8002124 ) + 80020d2: f7fe fcb9 bl 8000a48 + secret[0] = rng_sample(); + 80020d6: f000 fafd bl 80026d4 + 80020da: 6028 str r0, [r5, #0] + 80020dc: e7eb b.n 80020b6 + for(int i=0; i<8; i+=2, dest += 8) { + 80020de: 3408 adds r4, #8 + 80020e0: 42b4 cmp r4, r6 + 80020e2: f105 0508 add.w r5, r5, #8 + 80020e6: d1ed bne.n 80020c4 + } + } + flash_lock(); + 80020e8: f7ff ff8a bl 8002000 + + sizeof(rom_secrets->mcu_hmac_key); + + STATIC_ASSERT(offsetof(rom_secrets_t, hash_cache_secret) % 8 == 0); + STATIC_ASSERT(blen % 8 == 0); + + flash_unlock(); + 80020ec: f7ff ff90 bl 8002010 + uint32_t dest = (uint32_t)&rom_secrets->hash_cache_secret; + 80020f0: 4c0d ldr r4, [pc, #52] ; (8002128 ) + for(int i=0; i) + uint64_t val = ((uint64_t)rng_sample() << 32) | rng_sample(); + 80020f4: f000 faee bl 80026d4 + 80020f8: 9001 str r0, [sp, #4] + 80020fa: f000 faeb bl 80026d4 + + if(flash_burn(dest, val)) { + 80020fe: 9b01 ldr r3, [sp, #4] + uint64_t val = ((uint64_t)rng_sample() << 32) | rng_sample(); + 8002100: 4602 mov r2, r0 + if(flash_burn(dest, val)) { + 8002102: 4620 mov r0, r4 + 8002104: f00b faf4 bl 800d6f0 <__flash_burn_veneer> + 8002108: 2800 cmp r0, #0 + 800210a: d1e1 bne.n 80020d0 + for(int i=0; i + INCONSISTENT("flash fail"); + } + } + flash_lock(); + 8002112: f7ff ff75 bl 8002000 + } + +} + 8002116: f50d 6d85 add.w sp, sp, #1064 ; 0x428 + 800211a: bd70 pop {r4, r5, r6, pc} + 800211c: 0801c000 .word 0x0801c000 + 8002120: 0801c020 .word 0x0801c020 + 8002124: 0800d700 .word 0x0800d700 + 8002128: 0801c070 .word 0x0801c070 + 800212c: 0801c0b0 .word 0x0801c0b0 + +08002130 : +// +// Write the serial number of ATECC608 into flash forever. +// + void +flash_save_ae_serial(const uint8_t serial[9]) +{ + 8002130: b51f push {r0, r1, r2, r3, r4, lr} + 8002132: 4602 mov r2, r0 + uint64_t tmp[2]; + memset(&tmp, 0x0, sizeof(tmp)); + 8002134: 2300 movs r3, #0 + memcpy(&tmp, serial, 9); + 8002136: 6800 ldr r0, [r0, #0] + 8002138: 6851 ldr r1, [r2, #4] + 800213a: 7a12 ldrb r2, [r2, #8] + memset(&tmp, 0x0, sizeof(tmp)); + 800213c: e9cd 3302 strd r3, r3, [sp, #8] + memcpy(&tmp, serial, 9); + 8002140: 466b mov r3, sp + 8002142: c303 stmia r3!, {r0, r1} + 8002144: 701a strb r2, [r3, #0] + + flash_setup0(); + 8002146: f7ff ff3f bl 8001fc8 + flash_unlock(); + 800214a: f7ff ff61 bl 8002010 + + if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[0], tmp[0])) { + 800214e: e9dd 2300 ldrd r2, r3, [sp] + 8002152: 4809 ldr r0, [pc, #36] ; (8002178 ) + 8002154: f00b facc bl 800d6f0 <__flash_burn_veneer> + 8002158: b110 cbz r0, 8002160 + INCONSISTENT("fail1"); + 800215a: 4808 ldr r0, [pc, #32] ; (800217c ) + 800215c: f7fe fc74 bl 8000a48 + } + if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[1], tmp[1])) { + 8002160: e9dd 2302 ldrd r2, r3, [sp, #8] + 8002164: 4806 ldr r0, [pc, #24] ; (8002180 ) + 8002166: f00b fac3 bl 800d6f0 <__flash_burn_veneer> + 800216a: 2800 cmp r0, #0 + 800216c: d1f5 bne.n 800215a + INCONSISTENT("fail2"); + } + + flash_lock(); +} + 800216e: b005 add sp, #20 + 8002170: f85d eb04 ldr.w lr, [sp], #4 + flash_lock(); + 8002174: f7ff bf44 b.w 8002000 + 8002178: 0801c040 .word 0x0801c040 + 800217c: 0800d700 .word 0x0800d700 + 8002180: 0801c048 .word 0x0801c048 + +08002184 : +// +// Write bag number (probably a string) +// + void +flash_save_bag_number(const uint8_t new_number[32]) +{ + 8002184: b570 push {r4, r5, r6, lr} + 8002186: b088 sub sp, #32 + uint32_t dest = (uint32_t)&rom_secrets->bag_number[0]; + uint64_t tmp[4] = { 0 }; + uint64_t *src = tmp; + + STATIC_ASSERT(sizeof(tmp) == 32); + memcpy(tmp, new_number, 32); + 8002188: 4603 mov r3, r0 + 800218a: 466c mov r4, sp + 800218c: f100 0520 add.w r5, r0, #32 + 8002190: 6818 ldr r0, [r3, #0] + 8002192: 6859 ldr r1, [r3, #4] + 8002194: 4622 mov r2, r4 + 8002196: c203 stmia r2!, {r0, r1} + 8002198: 3308 adds r3, #8 + 800219a: 42ab cmp r3, r5 + 800219c: 4614 mov r4, r2 + 800219e: d1f7 bne.n 8002190 + + flash_setup0(); + 80021a0: f7ff ff12 bl 8001fc8 + flash_unlock(); + 80021a4: f7ff ff34 bl 8002010 + uint32_t dest = (uint32_t)&rom_secrets->bag_number[0]; + 80021a8: 4d09 ldr r5, [pc, #36] ; (80021d0 ) + + // NOTE: can only write once! No provision for read/check/update. + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80021aa: 4e0a ldr r6, [pc, #40] ; (80021d4 ) + 80021ac: 466c mov r4, sp + if(flash_burn(dest, *src)) { + 80021ae: e8f4 2302 ldrd r2, r3, [r4], #8 + 80021b2: 4628 mov r0, r5 + 80021b4: f00b fa9c bl 800d6f0 <__flash_burn_veneer> + 80021b8: b110 cbz r0, 80021c0 + INCONSISTENT("fail write"); + 80021ba: 4807 ldr r0, [pc, #28] ; (80021d8 ) + 80021bc: f7fe fc44 bl 8000a48 + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80021c0: 3508 adds r5, #8 + 80021c2: 42b5 cmp r5, r6 + 80021c4: d1f3 bne.n 80021ae + } + } + + flash_lock(); +} + 80021c6: b008 add sp, #32 + 80021c8: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + flash_lock(); + 80021cc: f7ff bf18 b.w 8002000 + 80021d0: 0801c050 .word 0x0801c050 + 80021d4: 0801c070 .word 0x0801c070 + 80021d8: 0800d700 .word 0x0800d700 + +080021dc : +// Save bunch of stuff related to SE2. Allow updates to sections that are +// given as ones at this point. +// + void +flash_save_se2_data(const se2_secrets_t *se2) +{ + 80021dc: e92d 41f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, lr} + 80021e0: 4605 mov r5, r0 + uint8_t *dest = (uint8_t *)&rom_secrets->se2; + 80021e2: 4c1a ldr r4, [pc, #104] ; (800224c ) + STATIC_ASSERT(offsetof(rom_secrets_t, se2) % 8 == 0); + + flash_setup0(); + flash_unlock(); + + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 80021e4: f8df 8070 ldr.w r8, [pc, #112] ; 8002258 + flash_setup0(); + 80021e8: f7ff feee bl 8001fc8 + flash_unlock(); + 80021ec: f7ff ff10 bl 8002010 + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 80021f0: 1b2d subs r5, r5, r4 + 80021f2: eb05 0c04 add.w ip, r5, r4 + uint64_t val; + memcpy(&val, src, sizeof(val)); + 80021f6: 5928 ldr r0, [r5, r4] + 80021f8: f8dc 1004 ldr.w r1, [ip, #4] + 80021fc: 466b mov r3, sp + + // don't write if all ones or already written correctly + if(val == ~0) continue; + 80021fe: f1b1 3fff cmp.w r1, #4294967295 ; 0xffffffff + 8002202: bf08 it eq + 8002204: f1b0 3fff cmpeq.w r0, #4294967295 ; 0xffffffff + memcpy(&val, src, sizeof(val)); + 8002208: c303 stmia r3!, {r0, r1} + if(val == ~0) continue; + 800220a: 4607 mov r7, r0 + 800220c: 460e mov r6, r1 + 800220e: d015 beq.n 800223c + if(check_equal(dest, src, 8)) continue; + 8002210: 2208 movs r2, #8 + 8002212: 4661 mov r1, ip + 8002214: 4620 mov r0, r4 + 8002216: f000 fa4c bl 80026b2 + 800221a: b978 cbnz r0, 800223c + + // can't write if not ones already + ASSERT(check_all_ones(dest, 8)); + 800221c: 2108 movs r1, #8 + 800221e: 4620 mov r0, r4 + 8002220: f000 fa2e bl 8002680 + 8002224: b910 cbnz r0, 800222c + 8002226: 480a ldr r0, [pc, #40] ; (8002250 ) + + if(flash_burn((uint32_t)dest, val)) { + INCONSISTENT("fail write"); + 8002228: f7fe fc0e bl 8000a48 + if(flash_burn((uint32_t)dest, val)) { + 800222c: 463a mov r2, r7 + 800222e: 4633 mov r3, r6 + 8002230: 4620 mov r0, r4 + 8002232: f00b fa5d bl 800d6f0 <__flash_burn_veneer> + 8002236: b108 cbz r0, 800223c + INCONSISTENT("fail write"); + 8002238: 4806 ldr r0, [pc, #24] ; (8002254 ) + 800223a: e7f5 b.n 8002228 + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 800223c: 3408 adds r4, #8 + 800223e: 4544 cmp r4, r8 + 8002240: d1d7 bne.n 80021f2 + } + } + + flash_lock(); +} + 8002242: b002 add sp, #8 + 8002244: e8bd 41f0 ldmia.w sp!, {r4, r5, r6, r7, r8, lr} + flash_lock(); + 8002248: f7ff beda b.w 8002000 + 800224c: 0801c0b0 .word 0x0801c0b0 + 8002250: 0800e3e0 .word 0x0800e3e0 + 8002254: 0800d700 .word 0x0800d700 + 8002258: 0801c190 .word 0x0801c190 + +0800225c : +// +// This is really a state-machine, to recover boards that are booted w/ missing AE chip. +// + void +flash_setup(void) +{ + 800225c: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + + // see if we have picked a pairing secret yet. + // NOTE: critical section for glitching (at least in past versions) + // - check_all.. functions have a rng_delay in them already + rng_delay(); + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 8002260: 4d3e ldr r5, [pc, #248] ; (800235c ) +{ + 8002262: b088 sub sp, #32 + flash_setup0(); + 8002264: f7ff feb0 bl 8001fc8 + rng_delay(); + 8002268: f000 fa88 bl 800277c + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 800226c: 2120 movs r1, #32 + 800226e: 4628 mov r0, r5 + 8002270: f000 fa06 bl 8002680 + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 8002274: 2120 movs r1, #32 + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 8002276: 4606 mov r6, r0 + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 8002278: 4628 mov r0, r5 + 800227a: f000 fa0b bl 8002694 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 800227e: 2120 movs r1, #32 + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 8002280: 4607 mov r7, r0 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 8002282: 4837 ldr r0, [pc, #220] ; (8002360 ) + 8002284: f000 f9fc bl 8002680 + bool blank_ae = (~rom_secrets->ae_serial_number[0] == 0); + 8002288: e9d5 8510 ldrd r8, r5, [r5, #64] ; 0x40 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 800228c: 4604 mov r4, r0 + rng_delay(); + 800228e: f000 fa75 bl 800277c + + if(zeroed_ps) { + 8002292: b127 cbz r7, 800229e + // fast brick process leaves us w/ zero pairing secret + oled_show(screen_brick); + 8002294: 4833 ldr r0, [pc, #204] ; (8002364 ) + 8002296: f7fe fdd5 bl 8000e44 + LOCKUP_FOREVER(); + 800229a: bf30 wfi + 800229c: e7fd b.n 800229a + } + + if(blank_ps) { + 800229e: b10e cbz r6, 80022a4 + // get some good entropy, save it. + pick_pairing_secret(); + 80022a0: f7ff feec bl 800207c + + blank_ps = false; + } + + if(blank_xor || blank_ae) { + 80022a4: b92c cbnz r4, 80022b2 + 80022a6: f1b5 3fff cmp.w r5, #4294967295 ; 0xffffffff + 80022aa: bf08 it eq + 80022ac: f1b8 3fff cmpeq.w r8, #4294967295 ; 0xffffffff + 80022b0: d12f bne.n 8002312 + + // setup the SE2 (mostly). handles failures by dying + se2_setup_config(); + 80022b2: f005 faeb bl 800788c + + // configure and lock-down the SE1 + int rv = ae_setup_config(); + 80022b6: f001 f99b bl 80035f0 + 80022ba: 4605 mov r5, r0 + + rng_delay(); + 80022bc: f000 fa5e bl 800277c + if(rv) { + 80022c0: b13d cbz r5, 80022d2 + // Hardware fail speaking to AE chip ... be careful not to brick here. + // Do not continue!! We might fix the board, or add missing pullup, etc. + oled_show(screen_se1_issue); + 80022c2: 4829 ldr r0, [pc, #164] ; (8002368 ) + 80022c4: f7fe fdbe bl 8000e44 + puts("SE1 config fail"); + 80022c8: 4828 ldr r0, [pc, #160] ; (800236c ) + 80022ca: f002 fd6f bl 8004dac + + LOCKUP_FOREVER(); + 80022ce: bf30 wfi + 80022d0: e7fd b.n 80022ce + } + + rng_delay(); + 80022d2: f000 fa53 bl 800277c + if(blank_xor) { + 80022d6: b1a4 cbz r4, 8002302 + flash_unlock(); + 80022d8: f7ff fe9a bl 8002010 + uint64_t *src = (uint64_t *)&rom_secrets->pairing_secret; + 80022dc: 4c1f ldr r4, [pc, #124] ; (800235c ) + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80022de: 4d20 ldr r5, [pc, #128] ; (8002360 ) + uint64_t val = ~(*src); + 80022e0: e9d4 2300 ldrd r2, r3, [r4] + if(flash_burn(dest, val)) { + 80022e4: f104 0020 add.w r0, r4, #32 + 80022e8: 43d2 mvns r2, r2 + 80022ea: 43db mvns r3, r3 + 80022ec: f00b fa00 bl 800d6f0 <__flash_burn_veneer> + 80022f0: b110 cbz r0, 80022f8 + INCONSISTENT("flash xor fail"); + 80022f2: 481f ldr r0, [pc, #124] ; (8002370 ) + 80022f4: f7fe fba8 bl 8000a48 + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80022f8: 3408 adds r4, #8 + 80022fa: 42ac cmp r4, r5 + 80022fc: d1f0 bne.n 80022e0 + flash_lock(); + 80022fe: f7ff fe7f bl 8002000 + // Q: just do it (we warned them) + extern void turn_power_off(void); + turn_power_off(); +#else + // Mk: operator must do it + oled_show(screen_replug); + 8002302: 481c ldr r0, [pc, #112] ; (8002374 ) + 8002304: f7fe fd9e bl 8000e44 + puts("replug required"); + 8002308: 481b ldr r0, [pc, #108] ; (8002378 ) + 800230a: f002 fd4f bl 8004dac + LOCKUP_FOREVER(); + 800230e: bf30 wfi + 8002310: e7fd b.n 800230e + + rng_delay(); + if(!blank_ps && !blank_xor) { + // check the XOR value also written: 2 phase commit + uint8_t tmp[32]; + memcpy(tmp, rom_secrets->pairing_secret, 32); + 8002312: 4d12 ldr r5, [pc, #72] ; (800235c ) + rng_delay(); + 8002314: f000 fa32 bl 800277c + memcpy(tmp, rom_secrets->pairing_secret, 32); + 8002318: cd0f ldmia r5!, {r0, r1, r2, r3} + 800231a: 466c mov r4, sp + 800231c: c40f stmia r4!, {r0, r1, r2, r3} + 800231e: e895 000f ldmia.w r5, {r0, r1, r2, r3} + 8002322: e884 000f stmia.w r4, {r0, r1, r2, r3} + 8002326: 466b mov r3, sp + 8002328: 4a0d ldr r2, [pc, #52] ; (8002360 ) +bool check_equal(const void *aV, const void *bV, int len); + +// XOR-mixin more bytes; acc = acc XOR more for each byte +void static inline xor_mixin(uint8_t *acc, const uint8_t *more, int len) +{ + for(; len; len--, more++, acc++) { + 800232a: 4c14 ldr r4, [pc, #80] ; (800237c ) + 800232c: 4618 mov r0, r3 + *(acc) ^= *(more); + 800232e: 7819 ldrb r1, [r3, #0] + 8002330: f812 5b01 ldrb.w r5, [r2], #1 + 8002334: 4069 eors r1, r5 + for(; len; len--, more++, acc++) { + 8002336: 42a2 cmp r2, r4 + *(acc) ^= *(more); + 8002338: f803 1b01 strb.w r1, [r3], #1 + for(; len; len--, more++, acc++) { + 800233c: d1f7 bne.n 800232e + xor_mixin(tmp, rom_secrets->pairing_secret_xor, 32); + + if(!check_all_ones(tmp, 32)) { + 800233e: 2120 movs r1, #32 + 8002340: f000 f99e bl 8002680 + 8002344: b938 cbnz r0, 8002356 + oled_show(screen_corrupt); + 8002346: 480e ldr r0, [pc, #56] ; (8002380 ) + 8002348: f7fe fd7c bl 8000e44 + puts("corrupt pair sec"); + 800234c: 480d ldr r0, [pc, #52] ; (8002384 ) + 800234e: f002 fd2d bl 8004dac + + // dfu won't save them here, so just die + LOCKUP_FOREVER(); + 8002352: bf30 wfi + 8002354: e7fd b.n 8002352 + // That's fine if we intend to ship units locked already. + + // Do NOT do write every boot, as it might wear-out + // the flash bits in OB. + +} + 8002356: b008 add sp, #32 + 8002358: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + 800235c: 0801c000 .word 0x0801c000 + 8002360: 0801c020 .word 0x0801c020 + 8002364: 0800d80b .word 0x0800d80b + 8002368: 0800df27 .word 0x0800df27 + 800236c: 0800e5da .word 0x0800e5da + 8002370: 0800d700 .word 0x0800d700 + 8002374: 0800dec6 .word 0x0800dec6 + 8002378: 0800e5ea .word 0x0800e5ea + 800237c: 0801c040 .word 0x0801c040 + 8002380: 0800d875 .word 0x0800d875 + 8002384: 0800e5fa .word 0x0800e5fa + +08002388 : +// +// This is a one-way trip. Might need power cycle to (fully?) take effect. +// + void +flash_lockdown_hard(uint8_t rdp_level_code) +{ + 8002388: b510 push {r4, lr} + 800238a: 4604 mov r4, r0 +#if RELEASE + flash_setup0(); + 800238c: f7ff fe1c bl 8001fc8 + + // see FLASH_OB_WRPConfig() + + flash_ob_lock(false); + 8002390: 2000 movs r0, #0 + 8002392: f7ff fe55 bl 8002040 + // lock first 128k-8k against any writes + FLASH->WRP1AR = (num_pages_locked << 16); + 8002396: 4b08 ldr r3, [pc, #32] ; (80023b8 ) + 8002398: f44f 2260 mov.w r2, #917504 ; 0xe0000 + 800239c: 62da str r2, [r3, #44] ; 0x2c + FLASH->WRP1BR = 0xff; // unused. + 800239e: 22ff movs r2, #255 ; 0xff + 80023a0: 631a str r2, [r3, #48] ; 0x30 + FLASH->WRP2AR = 0xff; // unused. + 80023a2: 64da str r2, [r3, #76] ; 0x4c + FLASH->WRP2BR = 0xff; // unused. + 80023a4: 651a str r2, [r3, #80] ; 0x50 + // the RDP level is decreased from Level 1 to Level 0)." + // - D-bus access blocked, even for code running inside the PCROP area! (AN4758) + // So literal values and constant tables and such would need special linking. + + // set protection level + uint32_t was = FLASH->OPTR & ~0xff; + 80023a6: 6a1a ldr r2, [r3, #32] + 80023a8: f022 02ff bic.w r2, r2, #255 ; 0xff + FLASH->OPTR = was | rdp_level_code; // select level X, other values as observed + 80023ac: 4322 orrs r2, r4 + 80023ae: 621a str r2, [r3, #32] +#else + puts2("flash_lockdown_hard("); + puthex2(rdp_level_code); + puts(") skipped"); +#endif +} + 80023b0: e8bd 4010 ldmia.w sp!, {r4, lr} + 80023b4: f7ff bddc b.w 8001f70 + 80023b8: 40022000 .word 0x40022000 + +080023bc : + +// record_highwater_version() +// + int +record_highwater_version(const uint8_t timestamp[8]) +{ + 80023bc: b537 push {r0, r1, r2, r4, r5, lr} + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + + ASSERT(timestamp[0] < 0x40); + ASSERT(timestamp[0] >= 0x10); + 80023be: 7802 ldrb r2, [r0, #0] + 80023c0: 3a10 subs r2, #16 + 80023c2: 2a2f cmp r2, #47 ; 0x2f +{ + 80023c4: 4603 mov r3, r0 + ASSERT(timestamp[0] >= 0x10); + 80023c6: d902 bls.n 80023ce + ASSERT(timestamp[0] < 0x40); + 80023c8: 4810 ldr r0, [pc, #64] ; (800240c ) + 80023ca: f7fe fb3d bl 8000a48 + + uint64_t val = 0; + memcpy(&val, timestamp, 8); + 80023ce: 6800 ldr r0, [r0, #0] + 80023d0: 6859 ldr r1, [r3, #4] + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + 80023d2: 4c0f ldr r4, [pc, #60] ; (8002410 ) + + // just write to first blank slot we can find. + for(int i=0; i) + memcpy(&val, timestamp, 8); + 80023d6: 466a mov r2, sp + 80023d8: c203 stmia r2!, {r0, r1} + if(check_all_ones(otp, 8)) { + 80023da: 2108 movs r1, #8 + 80023dc: 4620 mov r0, r4 + 80023de: f000 f94f bl 8002680 + 80023e2: b168 cbz r0, 8002400 + // write here. + flash_setup0(); + 80023e4: f7ff fdf0 bl 8001fc8 + flash_unlock(); + 80023e8: f7ff fe12 bl 8002010 + flash_burn((uint32_t)otp, val); + 80023ec: e9dd 2300 ldrd r2, r3, [sp] + 80023f0: 4620 mov r0, r4 + 80023f2: f00b f97d bl 800d6f0 <__flash_burn_veneer> + flash_lock(); + 80023f6: f7ff fe03 bl 8002000 + + return 0; + 80023fa: 2000 movs r0, #0 + } + } + + // no space. + return 1; +} + 80023fc: b003 add sp, #12 + 80023fe: bd30 pop {r4, r5, pc} + for(int i=0; i + return 1; + 8002406: 2001 movs r0, #1 + 8002408: e7f8 b.n 80023fc + 800240a: bf00 nop + 800240c: 0800e3e0 .word 0x0800e3e0 + 8002410: 1fff7000 .word 0x1fff7000 + 8002414: 1fff7400 .word 0x1fff7400 + +08002418 : + +// mcu_key_get() +// + const mcu_key_t * +mcu_key_get(bool *valid) +{ + 8002418: b570 push {r4, r5, r6, lr} + // get current "mcu_key" value; first byte will never be 0x0 or 0xff + // - except if no key set yet/recently wiped + // - if none set, returns ptr to first available slot which will be all ones + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + + for(int i=0; i) + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + 800241c: 4c0d ldr r4, [pc, #52] ; (8002454 ) +{ + 800241e: 4606 mov r6, r0 + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + 8002420: 2500 movs r5, #0 + if(ptr->value[0] == 0xff) { + 8002422: 7823 ldrb r3, [r4, #0] + 8002424: 2bff cmp r3, #255 ; 0xff + 8002426: d10b bne.n 8002440 + if(!avail) { + 8002428: 2d00 cmp r5, #0 + 800242a: bf08 it eq + 800242c: 4625 moveq r5, r4 + for(int i=0; i + *valid = true; + return ptr; + } + } + + rng_delay(); + 8002434: f000 f9a2 bl 800277c + *valid = false; + 8002438: 2300 movs r3, #0 + 800243a: 7033 strb r3, [r6, #0] + return avail; + 800243c: 462c mov r4, r5 + 800243e: e005 b.n 800244c + } else if(ptr->value[0] != 0x00) { + 8002440: 2b00 cmp r3, #0 + 8002442: d0f4 beq.n 800242e + rng_delay(); + 8002444: f000 f99a bl 800277c + *valid = true; + 8002448: 2301 movs r3, #1 + 800244a: 7033 strb r3, [r6, #0] +} + 800244c: 4620 mov r0, r4 + 800244e: bd70 pop {r4, r5, r6, pc} + 8002450: 08020000 .word 0x08020000 + 8002454: 0801e000 .word 0x0801e000 + +08002458 : + +// mcu_key_clear() +// + void +mcu_key_clear(const mcu_key_t *cur) +{ + 8002458: b513 push {r0, r1, r4, lr} + if(!cur) { + 800245a: 4604 mov r4, r0 + 800245c: b938 cbnz r0, 800246e + bool valid; + cur = mcu_key_get(&valid); + 800245e: f10d 0007 add.w r0, sp, #7 + 8002462: f7ff ffd9 bl 8002418 + + if(!valid) return; + 8002466: f89d 3007 ldrb.w r3, [sp, #7] + cur = mcu_key_get(&valid); + 800246a: 4604 mov r4, r0 + if(!valid) return; + 800246c: b1fb cbz r3, 80024ae + } + + // no delays here since decision has been made, and don't + // want to give them more time to interrupt us + flash_setup0(); + 800246e: f7ff fdab bl 8001fc8 + flash_unlock(); + 8002472: f7ff fdcd bl 8002010 + uint32_t pos = (uint32_t)cur; + flash_burn(pos, 0); pos += 8; + 8002476: 2200 movs r2, #0 + 8002478: 2300 movs r3, #0 + 800247a: 4620 mov r0, r4 + 800247c: f00b f938 bl 800d6f0 <__flash_burn_veneer> + flash_burn(pos, 0); pos += 8; + 8002480: 2200 movs r2, #0 + 8002482: 2300 movs r3, #0 + 8002484: f104 0008 add.w r0, r4, #8 + 8002488: f00b f932 bl 800d6f0 <__flash_burn_veneer> + flash_burn(pos, 0); pos += 8; + 800248c: 2200 movs r2, #0 + 800248e: 2300 movs r3, #0 + 8002490: f104 0010 add.w r0, r4, #16 + 8002494: f00b f92c bl 800d6f0 <__flash_burn_veneer> + flash_burn(pos, 0); + 8002498: 2200 movs r2, #0 + 800249a: 2300 movs r3, #0 + 800249c: f104 0018 add.w r0, r4, #24 + 80024a0: f00b f926 bl 800d6f0 <__flash_burn_veneer> + flash_lock(); +} + 80024a4: b002 add sp, #8 + 80024a6: e8bd 4010 ldmia.w sp!, {r4, lr} + flash_lock(); + 80024aa: f7ff bda9 b.w 8002000 +} + 80024ae: b002 add sp, #8 + 80024b0: bd10 pop {r4, pc} + ... + +080024b4 : + +// mcu_key_usage() +// + void +mcu_key_usage(int *avail_out, int *consumed_out, int *total_out) +{ + 80024b4: b5f0 push {r4, r5, r6, r7, lr} + const mcu_key_t *ptr = MCU_KEYS; + int avail = 0, used = 0; + 80024b6: 2300 movs r3, #0 + const mcu_key_t *ptr = MCU_KEYS; + 80024b8: 4c09 ldr r4, [pc, #36] ; (80024e0 ) + + for(int i=0; i) + int avail = 0, used = 0; + 80024bc: 461d mov r5, r3 + if(ptr->value[0] == 0xff) { + 80024be: 7826 ldrb r6, [r4, #0] + 80024c0: 2eff cmp r6, #255 ; 0xff + 80024c2: d109 bne.n 80024d8 + avail ++; + 80024c4: 3501 adds r5, #1 + for(int i=0; i + } else if(ptr->value[0] == 0x00) { + used ++; + } + } + + *avail_out = avail; + 80024cc: 6005 str r5, [r0, #0] + *consumed_out = used; + 80024ce: 600b str r3, [r1, #0] + *total_out = NUM_MCU_KEYS; + 80024d0: f44f 7380 mov.w r3, #256 ; 0x100 + 80024d4: 6013 str r3, [r2, #0] +} + 80024d6: bdf0 pop {r4, r5, r6, r7, pc} + } else if(ptr->value[0] == 0x00) { + 80024d8: 2e00 cmp r6, #0 + 80024da: d1f4 bne.n 80024c6 + used ++; + 80024dc: 3301 adds r3, #1 + 80024de: e7f2 b.n 80024c6 + 80024e0: 0801e000 .word 0x0801e000 + 80024e4: 08020000 .word 0x08020000 + +080024e8 : + +// mcu_key_pick() +// + const mcu_key_t * +mcu_key_pick(void) +{ + 80024e8: b5f0 push {r4, r5, r6, r7, lr} + 80024ea: b08b sub sp, #44 ; 0x2c + mcu_key_t n; + + // get some good entropy, and whiten it just in case. + do { + rng_buffer(n.value, 32); + 80024ec: ad02 add r5, sp, #8 + 80024ee: 2120 movs r1, #32 + 80024f0: 4628 mov r0, r5 + 80024f2: f000 f92d bl 8002750 + sha256_single(n.value, 32, n.value); + 80024f6: 462a mov r2, r5 + 80024f8: 2120 movs r1, #32 + 80024fa: 4628 mov r0, r5 + 80024fc: f003 f82e bl 800555c + sha256_single(n.value, 32, n.value); + 8002500: 462a mov r2, r5 + 8002502: 2120 movs r1, #32 + 8002504: 4628 mov r0, r5 + 8002506: f003 f829 bl 800555c + } while(n.value[0] == 0x0 || n.value[0] == 0xff); + 800250a: f89d 3008 ldrb.w r3, [sp, #8] + 800250e: 3b01 subs r3, #1 + 8002510: b2db uxtb r3, r3 + 8002512: 2bfd cmp r3, #253 ; 0xfd + 8002514: d8eb bhi.n 80024ee + + int err = 0; + const mcu_key_t *cur; + + do { + bool valid = false; + 8002516: 2300 movs r3, #0 + cur = mcu_key_get(&valid); + 8002518: 4668 mov r0, sp + bool valid = false; + 800251a: f88d 3000 strb.w r3, [sp] + cur = mcu_key_get(&valid); + 800251e: f7ff ff7b bl 8002418 + + if(!cur) { + 8002522: 4604 mov r4, r0 + 8002524: b938 cbnz r0, 8002536 + // no free slots. we are brick. + puts("mcu full"); + 8002526: 4828 ldr r0, [pc, #160] ; (80025c8 ) + 8002528: f002 fc40 bl 8004dac + oled_show(screen_brick); + 800252c: 4827 ldr r0, [pc, #156] ; (80025cc ) + 800252e: f7fe fc89 bl 8000e44 + + LOCKUP_FOREVER(); + 8002532: bf30 wfi + 8002534: e7fd b.n 8002532 + } + + if(valid) { + 8002536: f89d 3000 ldrb.w r3, [sp] + 800253a: b14b cbz r3, 8002550 + // clear existing key, if it's defined. + ASSERT(cur->value[0] != 0x00); + 800253c: 7803 ldrb r3, [r0, #0] + 800253e: 3b01 subs r3, #1 + 8002540: b2db uxtb r3, r3 + 8002542: 2bfd cmp r3, #253 ; 0xfd + 8002544: d902 bls.n 800254c + 8002546: 4822 ldr r0, [pc, #136] ; (80025d0 ) + 8002548: f7fe fa7e bl 8000a48 + ASSERT(cur->value[0] != 0xff); + + mcu_key_clear(cur); + 800254c: f7ff ff84 bl 8002458 + continue; + } + } while(0); + + // burn it + flash_setup0(); + 8002550: f7ff fd3a bl 8001fc8 + flash_unlock(); + 8002554: f7ff fd5c bl 8002010 + uint32_t pos = (uint32_t)cur; + const uint8_t *fr = n.value; + + for(int i=0; i<32; i+= 8, pos += 8, fr += 8) { + 8002558: 2700 movs r7, #0 + uint64_t v; + memcpy(&v, fr, sizeof(v)); + 800255a: 19ea adds r2, r5, r7 + 800255c: 59e8 ldr r0, [r5, r7] + 800255e: 6851 ldr r1, [r2, #4] + 8002560: 466b mov r3, sp + 8002562: c303 stmia r3!, {r0, r1} + + err = flash_burn(pos, v); + 8002564: 19e0 adds r0, r4, r7 + 8002566: e9dd 2300 ldrd r2, r3, [sp] + 800256a: f00b f8c1 bl 800d6f0 <__flash_burn_veneer> + if(err) break; + 800256e: 4606 mov r6, r0 + 8002570: b910 cbnz r0, 8002578 + for(int i=0; i<32; i+= 8, pos += 8, fr += 8) { + 8002572: 3708 adds r7, #8 + 8002574: 2f20 cmp r7, #32 + 8002576: d1f0 bne.n 800255a + } + flash_lock(); + 8002578: f7ff fd42 bl 8002000 + + // NOTE: Errors not expected, but lets be graceful about them. + + if(err) { + 800257c: b166 cbz r6, 8002598 + // what to do? + puts("burn fail: "); + 800257e: 4815 ldr r0, [pc, #84] ; (80025d4 ) + 8002580: f002 fc14 bl 8004dac + puthex2(err); + 8002584: b2f0 uxtb r0, r6 + 8002586: f002 fbb5 bl 8004cf4 + putchar('\n'); + 800258a: 200a movs r0, #10 + 800258c: f002 fb94 bl 8004cb8 + return NULL; + } + + if(after != cur || !check_equal(after->value, n.value, 32)) { + puts("bad val?"); + return NULL; + 8002590: 2400 movs r4, #0 + } + + return cur; +} + 8002592: 4620 mov r0, r4 + 8002594: b00b add sp, #44 ; 0x2c + 8002596: bdf0 pop {r4, r5, r6, r7, pc} + const mcu_key_t *after = mcu_key_get(&valid); + 8002598: 4668 mov r0, sp + bool valid = false; + 800259a: f88d 6000 strb.w r6, [sp] + const mcu_key_t *after = mcu_key_get(&valid); + 800259e: f7ff ff3b bl 8002418 + if(!valid) { + 80025a2: f89d 2000 ldrb.w r2, [sp] + 80025a6: b91a cbnz r2, 80025b0 + puts("!valid?"); + 80025a8: 480b ldr r0, [pc, #44] ; (80025d8 ) + puts("bad val?"); + 80025aa: f002 fbff bl 8004dac + 80025ae: e7ef b.n 8002590 + if(after != cur || !check_equal(after->value, n.value, 32)) { + 80025b0: 4284 cmp r4, r0 + 80025b2: d001 beq.n 80025b8 + puts("bad val?"); + 80025b4: 4809 ldr r0, [pc, #36] ; (80025dc ) + 80025b6: e7f8 b.n 80025aa + if(after != cur || !check_equal(after->value, n.value, 32)) { + 80025b8: 2220 movs r2, #32 + 80025ba: 4629 mov r1, r5 + 80025bc: f000 f879 bl 80026b2 + 80025c0: 2800 cmp r0, #0 + 80025c2: d1e6 bne.n 8002592 + 80025c4: e7f6 b.n 80025b4 + 80025c6: bf00 nop + 80025c8: 0800e60b .word 0x0800e60b + 80025cc: 0800d80b .word 0x0800d80b + 80025d0: 0800e3e0 .word 0x0800e3e0 + 80025d4: 0800e614 .word 0x0800e614 + 80025d8: 0800e620 .word 0x0800e620 + 80025dc: 0800e628 .word 0x0800e628 + +080025e0 : + +// fast_brick() +// + void +fast_brick(void) +{ + 80025e0: b538 push {r3, r4, r5, lr} +#ifndef RELEASE + puts2("DISABLED fast brick... "); + oled_show(screen_brick); +#else + // do a fast wipe of our key + mcu_key_clear(NULL); + 80025e2: 2000 movs r0, #0 + 80025e4: f7ff ff38 bl 8002458 + + // brick SE1 for future + ae_brick_myself(); + 80025e8: f001 f970 bl 80038cc + + // NOTE: could brick SE1 (somewhat) by dec'ing the counter, which will + // invalidate all PIN hashes + + // no going back from that -- but for privacy, wipe more stuff + oled_show(screen_brick); + 80025ec: 480e ldr r0, [pc, #56] ; (8002628 ) + uint32_t bot = (uint32_t)MCU_KEYS; + flash_page_erase(bot); + + // 2: LFS area first, since holds settings (AES'ed w/ lost key, but yeah) + // 3: the firmware, not a secret anyway + for(uint32_t pos=(FLASH_BASE + 0x200000 - FLASH_ERASE_SIZE); + 80025ee: 4c0f ldr r4, [pc, #60] ; (800262c ) + 80025f0: 4d0f ldr r5, [pc, #60] ; (8002630 ) + oled_show(screen_brick); + 80025f2: f7fe fc27 bl 8000e44 + puts2("fast brick... "); + 80025f6: 480f ldr r0, [pc, #60] ; (8002634 ) + 80025f8: f002 fb4a bl 8004c90 + flash_setup0(); + 80025fc: f7ff fce4 bl 8001fc8 + flash_unlock(); + 8002600: f7ff fd06 bl 8002010 + flash_page_erase(bot); + 8002604: 480a ldr r0, [pc, #40] ; (8002630 ) + 8002606: f00b f877 bl 800d6f8 <__flash_page_erase_veneer> + pos > bot; pos -= FLASH_ERASE_SIZE) { + flash_page_erase(pos); + 800260a: 4620 mov r0, r4 + pos > bot; pos -= FLASH_ERASE_SIZE) { + 800260c: f5a4 5480 sub.w r4, r4, #4096 ; 0x1000 + flash_page_erase(pos); + 8002610: f00b f872 bl 800d6f8 <__flash_page_erase_veneer> + for(uint32_t pos=(FLASH_BASE + 0x200000 - FLASH_ERASE_SIZE); + 8002614: 42ac cmp r4, r5 + 8002616: d1f8 bne.n 800260a + } + flash_lock(); + puts(" done"); + 8002618: 4807 ldr r0, [pc, #28] ; (8002638 ) + flash_lock(); + 800261a: f7ff fcf1 bl 8002000 + puts(" done"); + 800261e: f002 fbc5 bl 8004dac +#endif + + LOCKUP_FOREVER(); + 8002622: bf30 wfi + 8002624: e7fd b.n 8002622 + 8002626: bf00 nop + 8002628: 0800d80b .word 0x0800d80b + 800262c: 081ff000 .word 0x081ff000 + 8002630: 0801e000 .word 0x0801e000 + 8002634: 0800e631 .word 0x0800e631 + 8002638: 0800e640 .word 0x0800e640 + +0800263c : + +// fast_wipe() +// + void +fast_wipe(void) +{ + 800263c: b508 push {r3, lr} + // dump (part of) the main seed key and become a new Coldcard + // - lots of other code can and will detect a missing MCU key as "blank" + // - and the check value on main seed will be garbage now + mcu_key_clear(NULL); + 800263e: 2000 movs r0, #0 + 8002640: f7ff ff0a bl 8002458 + __ASM volatile ("dsb 0xF":::"memory"); + 8002644: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8002648: 4905 ldr r1, [pc, #20] ; (8002660 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800264a: 4b06 ldr r3, [pc, #24] ; (8002664 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 800264c: 68ca ldr r2, [r1, #12] + 800264e: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8002652: 4313 orrs r3, r2 + 8002654: 60cb str r3, [r1, #12] + 8002656: f3bf 8f4f dsb sy + __NOP(); + 800265a: bf00 nop + for(;;) /* wait until reset */ + 800265c: e7fd b.n 800265a + 800265e: bf00 nop + 8002660: e000ed00 .word 0xe000ed00 + 8002664: 05fa0004 .word 0x05fa0004 + +08002668 : +check_all_ones_raw(const void *ptrV, int len) +{ + uint8_t rv = 0xff; + const uint8_t *ptr = (const uint8_t *)ptrV; + + for(; len; len--, ptr++) { + 8002668: 4401 add r1, r0 + uint8_t rv = 0xff; + 800266a: 23ff movs r3, #255 ; 0xff + for(; len; len--, ptr++) { + 800266c: 4288 cmp r0, r1 + 800266e: d103 bne.n 8002678 + rv &= *ptr; + } + + return (rv == 0xff); +} + 8002670: 3bff subs r3, #255 ; 0xff + 8002672: 4258 negs r0, r3 + 8002674: 4158 adcs r0, r3 + 8002676: 4770 bx lr + rv &= *ptr; + 8002678: f810 2b01 ldrb.w r2, [r0], #1 + 800267c: 4013 ands r3, r2 + for(; len; len--, ptr++) { + 800267e: e7f5 b.n 800266c + +08002680 : +// +// Return T if all bytes are 0xFF +// + bool +check_all_ones(const void *ptrV, int len) +{ + 8002680: b507 push {r0, r1, r2, lr} + bool rv = check_all_ones_raw(ptrV, len); + 8002682: f7ff fff1 bl 8002668 + 8002686: 9001 str r0, [sp, #4] + + rng_delay(); + 8002688: f000 f878 bl 800277c + + return rv; +} + 800268c: 9801 ldr r0, [sp, #4] + 800268e: b003 add sp, #12 + 8002690: f85d fb04 ldr.w pc, [sp], #4 + +08002694 : +// +// Return T if all bytes are 0x00 +// + bool +check_all_zeros(const void *ptrV, int len) +{ + 8002694: b510 push {r4, lr} + 8002696: 4401 add r1, r0 + uint8_t rv = 0x0; + 8002698: 2400 movs r4, #0 + const uint8_t *ptr = (const uint8_t *)ptrV; + + for(; len; len--, ptr++) { + 800269a: 4288 cmp r0, r1 + 800269c: d105 bne.n 80026aa + rv |= *ptr; + } + + rng_delay(); + 800269e: f000 f86d bl 800277c + return (rv == 0x00); +} + 80026a2: fab4 f084 clz r0, r4 + 80026a6: 0940 lsrs r0, r0, #5 + 80026a8: bd10 pop {r4, pc} + rv |= *ptr; + 80026aa: f810 3b01 ldrb.w r3, [r0], #1 + 80026ae: 431c orrs r4, r3 + for(; len; len--, ptr++) { + 80026b0: e7f3 b.n 800269a + +080026b2 : + const uint8_t *left = (const uint8_t *)aV; + const uint8_t *right = (const uint8_t *)bV; + uint8_t diff = 0; + int i; + + for (i = 0; i < len; i++) { + 80026b2: 2300 movs r3, #0 +{ + 80026b4: b570 push {r4, r5, r6, lr} + uint8_t diff = 0; + 80026b6: 461c mov r4, r3 + for (i = 0; i < len; i++) { + 80026b8: 4293 cmp r3, r2 + 80026ba: db05 blt.n 80026c8 + diff |= (left[i] ^ right[i]); + } + + rng_delay(); + 80026bc: f000 f85e bl 800277c + return (diff == 0); +} + 80026c0: fab4 f084 clz r0, r4 + 80026c4: 0940 lsrs r0, r0, #5 + 80026c6: bd70 pop {r4, r5, r6, pc} + diff |= (left[i] ^ right[i]); + 80026c8: 5cc5 ldrb r5, [r0, r3] + 80026ca: 5cce ldrb r6, [r1, r3] + 80026cc: 4075 eors r5, r6 + 80026ce: 432c orrs r4, r5 + for (i = 0; i < len; i++) { + 80026d0: 3301 adds r3, #1 + 80026d2: e7f1 b.n 80026b8 + +080026d4 : + } + + // Get the new number + uint32_t rv = RNG->DR; + + if(rv != last_rng_result && rv) { + 80026d4: 4b06 ldr r3, [pc, #24] ; (80026f0 ) + while(!(RNG->SR & RNG_FLAG_DRDY)) { + 80026d6: 4a07 ldr r2, [pc, #28] ; (80026f4 ) + if(rv != last_rng_result && rv) { + 80026d8: 6819 ldr r1, [r3, #0] + while(!(RNG->SR & RNG_FLAG_DRDY)) { + 80026da: 6850 ldr r0, [r2, #4] + 80026dc: 07c0 lsls r0, r0, #31 + 80026de: d5fc bpl.n 80026da + uint32_t rv = RNG->DR; + 80026e0: 6890 ldr r0, [r2, #8] + if(rv != last_rng_result && rv) { + 80026e2: 4281 cmp r1, r0 + 80026e4: d0f9 beq.n 80026da + 80026e6: 2800 cmp r0, #0 + 80026e8: d0f7 beq.n 80026da + last_rng_result = rv; + 80026ea: 6018 str r0, [r3, #0] + + // keep trying if not a new number + } + + // NOT-REACHED +} + 80026ec: 4770 bx lr + 80026ee: bf00 nop + 80026f0: 2009e1b8 .word 0x2009e1b8 + 80026f4: 50060800 .word 0x50060800 + +080026f8 : + if(RNG->CR & RNG_CR_RNGEN) { + 80026f8: 4b12 ldr r3, [pc, #72] ; (8002744 ) + 80026fa: 681a ldr r2, [r3, #0] + 80026fc: 0752 lsls r2, r2, #29 +{ + 80026fe: b513 push {r0, r1, r4, lr} + if(RNG->CR & RNG_CR_RNGEN) { + 8002700: d41d bmi.n 800273e + __HAL_RCC_RNG_CLK_ENABLE(); + 8002702: 4a11 ldr r2, [pc, #68] ; (8002748 ) + 8002704: 6cd1 ldr r1, [r2, #76] ; 0x4c + 8002706: f441 2180 orr.w r1, r1, #262144 ; 0x40000 + 800270a: 64d1 str r1, [r2, #76] ; 0x4c + 800270c: 6cd2 ldr r2, [r2, #76] ; 0x4c + 800270e: f402 2280 and.w r2, r2, #262144 ; 0x40000 + 8002712: 9201 str r2, [sp, #4] + 8002714: 9a01 ldr r2, [sp, #4] + RNG->CR |= RNG_CR_RNGEN; + 8002716: 681a ldr r2, [r3, #0] + 8002718: f042 0204 orr.w r2, r2, #4 + 800271c: 601a str r2, [r3, #0] + uint32_t chk = rng_sample(); + 800271e: f7ff ffd9 bl 80026d4 + 8002722: 4604 mov r4, r0 + uint32_t chk2 = rng_sample(); + 8002724: f7ff ffd6 bl 80026d4 + if(chk == 0 || chk == ~0 + 8002728: 1e63 subs r3, r4, #1 + 800272a: 3303 adds r3, #3 + 800272c: d804 bhi.n 8002738 + || chk2 == 0 || chk2 == ~0 + 800272e: 1e43 subs r3, r0, #1 + 8002730: 3303 adds r3, #3 + 8002732: d801 bhi.n 8002738 + || chk == chk2 + 8002734: 4284 cmp r4, r0 + 8002736: d102 bne.n 800273e + INCONSISTENT("bad rng"); + 8002738: 4804 ldr r0, [pc, #16] ; (800274c ) + 800273a: f7fe f985 bl 8000a48 +} + 800273e: b002 add sp, #8 + 8002740: bd10 pop {r4, pc} + 8002742: bf00 nop + 8002744: 50060800 .word 0x50060800 + 8002748: 40021000 .word 0x40021000 + 800274c: 0800d700 .word 0x0800d700 + +08002750 : + +// rng_buffer() +// + void +rng_buffer(uint8_t *result, int len) +{ + 8002750: b573 push {r0, r1, r4, r5, r6, lr} + 8002752: 460c mov r4, r1 + 8002754: 1845 adds r5, r0, r1 + while(len > 0) { + 8002756: 2c00 cmp r4, #0 + 8002758: eba5 0604 sub.w r6, r5, r4 + 800275c: dc01 bgt.n 8002762 + memcpy(result, &t, MIN(4, len)); + + len -= 4; + result += 4; + } +} + 800275e: b002 add sp, #8 + 8002760: bd70 pop {r4, r5, r6, pc} + uint32_t t = rng_sample(); + 8002762: f7ff ffb7 bl 80026d4 + memcpy(result, &t, MIN(4, len)); + 8002766: 2c04 cmp r4, #4 + 8002768: 4622 mov r2, r4 + uint32_t t = rng_sample(); + 800276a: 9001 str r0, [sp, #4] + memcpy(result, &t, MIN(4, len)); + 800276c: bfa8 it ge + 800276e: 2204 movge r2, #4 + 8002770: a901 add r1, sp, #4 + 8002772: 4630 mov r0, r6 + 8002774: f00a ff56 bl 800d624 + len -= 4; + 8002778: 3c04 subs r4, #4 + result += 4; + 800277a: e7ec b.n 8002756 + +0800277c : +// +// Call anytime. Delays for a random time period to fustrate glitchers. +// + void +rng_delay(void) +{ + 800277c: b508 push {r3, lr} + uint32_t r = rng_sample() % 8; + 800277e: f7ff ffa9 bl 80026d4 + uint32_t cnt = (1< + cnt--; + } +} + 8002792: bd08 pop {r3, pc} + +08002794 <_send_byte>: + static inline void +_send_byte(uint8_t ch) +{ + // reset timeout timer (Systick) + uint32_t ticks = 0; + SysTick->VAL = 0; + 8002794: f04f 22e0 mov.w r2, #3758153728 ; 0xe000e000 +{ + 8002798: b510 push {r4, lr} + SysTick->VAL = 0; + 800279a: 2300 movs r3, #0 + + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 800279c: 4c07 ldr r4, [pc, #28] ; (80027bc <_send_byte+0x28>) + SysTick->VAL = 0; + 800279e: 6193 str r3, [r2, #24] + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 80027a0: 230b movs r3, #11 + 80027a2: 69e1 ldr r1, [r4, #28] + 80027a4: 0609 lsls r1, r1, #24 + 80027a6: d404 bmi.n 80027b2 <_send_byte+0x1e> + // busy-wait until able to send (no fifo?) + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 80027a8: 6911 ldr r1, [r2, #16] + 80027aa: 03c9 lsls r1, r1, #15 + 80027ac: d5f9 bpl.n 80027a2 <_send_byte+0xe> + // failsafe timeout + ticks += 1; + if(ticks > 10) break; + 80027ae: 3b01 subs r3, #1 + 80027b0: d1f7 bne.n 80027a2 <_send_byte+0xe> + } + } + MY_UART->TDR = ch; + 80027b2: 4b02 ldr r3, [pc, #8] ; (80027bc <_send_byte+0x28>) + 80027b4: b280 uxth r0, r0 + 80027b6: 8518 strh r0, [r3, #40] ; 0x28 +} + 80027b8: bd10 pop {r4, pc} + 80027ba: bf00 nop + 80027bc: 40004c00 .word 0x40004c00 + +080027c0 <_send_bits>: + +// _send_bits() +// + static void +_send_bits(uint8_t tx) +{ + 80027c0: b570 push {r4, r5, r6, lr} + 80027c2: 4606 mov r6, r0 + 80027c4: 2508 movs r5, #8 + // serialize and send one byte + uint8_t mask = 0x1; + 80027c6: 2401 movs r4, #1 + + for(int i=0; i<8; i++, mask <<= 1) { + uint8_t h = (tx & mask) ? BIT1 : BIT0; + 80027c8: 4226 tst r6, r4 + + _send_byte(h); + 80027ca: bf14 ite ne + 80027cc: 207f movne r0, #127 ; 0x7f + 80027ce: 207d moveq r0, #125 ; 0x7d + 80027d0: f7ff ffe0 bl 8002794 <_send_byte> + for(int i=0; i<8; i++, mask <<= 1) { + 80027d4: 0064 lsls r4, r4, #1 + 80027d6: 3d01 subs r5, #1 + 80027d8: b2e4 uxtb r4, r4 + 80027da: d1f5 bne.n 80027c8 <_send_bits+0x8> + } +} + 80027dc: bd70 pop {r4, r5, r6, pc} + +080027de <_send_serialized>: + +// _send_serialized() +// + static void +_send_serialized(const uint8_t *buf, int len) +{ + 80027de: b538 push {r3, r4, r5, lr} + 80027e0: 4604 mov r4, r0 + 80027e2: 1845 adds r5, r0, r1 + for(int i=0; i + for(int i=0; i + } +} + 80027f0: bd38 pop {r3, r4, r5, pc} + ... + +080027f4 <_flush_rx>: +// + static inline void +_flush_rx(void) +{ + // reset timeout timer (Systick) + SysTick->VAL = 0; + 80027f4: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 80027f8: 2200 movs r2, #0 + + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 80027fa: 490b ldr r1, [pc, #44] ; (8002828 <_flush_rx+0x34>) + SysTick->VAL = 0; + 80027fc: 619a str r2, [r3, #24] + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 80027fe: 69ca ldr r2, [r1, #28] + 8002800: 0652 lsls r2, r2, #25 + 8002802: d402 bmi.n 800280a <_flush_rx+0x16> + // wait for last bit(byte) to be serialized and sent + + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8002804: 691a ldr r2, [r3, #16] + 8002806: 03d0 lsls r0, r2, #15 + 8002808: d5f9 bpl.n 80027fe <_flush_rx+0xa> + break; + } + } + + // We actually need this delay here! + __NOP(); + 800280a: bf00 nop + __NOP(); + 800280c: bf00 nop + __NOP(); + 800280e: bf00 nop + __NOP(); + 8002810: bf00 nop + __NOP(); + 8002812: bf00 nop + __NOP(); + 8002814: bf00 nop + __NOP(); + 8002816: bf00 nop + __NOP(); + 8002818: bf00 nop + + // clear junk in rx buffer + MY_UART->RQR = USART_RQR_RXFRQ; + 800281a: 4b03 ldr r3, [pc, #12] ; (8002828 <_flush_rx+0x34>) + 800281c: 2208 movs r2, #8 + 800281e: 831a strh r2, [r3, #24] + + // clear overrun error + // clear rx timeout flag + // clear framing error + MY_UART->ICR = USART_ICR_ORECF | USART_ICR_RTOCF | USART_ICR_FECF; + 8002820: f640 020a movw r2, #2058 ; 0x80a + 8002824: 621a str r2, [r3, #32] +} + 8002826: 4770 bx lr + 8002828: 40004c00 .word 0x40004c00 + +0800282c : + uint16_t crc_register = 0; + uint16_t polynom = 0x8005; + uint8_t shift_register; + uint8_t data_bit, crc_bit; + + crc_register = (((uint16_t) crc[0]) & 0x00FF) | (((uint16_t) crc[1]) << 8); + 800282c: 8813 ldrh r3, [r2, #0] +{ + 800282e: b5f0 push {r4, r5, r6, r7, lr} + 8002830: 4408 add r0, r1 + + // Shift CRC to the left by 1. + crc_register <<= 1; + + if ((data_bit ^ crc_bit) != 0) + crc_register ^= polynom; + 8002832: f248 0605 movw r6, #32773 ; 0x8005 + for (counter = 0; counter < length; counter++) { + 8002836: 4281 cmp r1, r0 + 8002838: d103 bne.n 8002842 + } + } + + crc[0] = (uint8_t) (crc_register & 0x00FF); + 800283a: 7013 strb r3, [r2, #0] + crc[1] = (uint8_t) (crc_register >> 8); + 800283c: 0a1b lsrs r3, r3, #8 + 800283e: 7053 strb r3, [r2, #1] +} + 8002840: bdf0 pop {r4, r5, r6, r7, pc} + data_bit = (data[counter] & shift_register) ? 1 : 0; + 8002842: f811 7b01 ldrb.w r7, [r1], #1 + 8002846: 2508 movs r5, #8 + for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) { + 8002848: 2401 movs r4, #1 + data_bit = (data[counter] & shift_register) ? 1 : 0; + 800284a: 4227 tst r7, r4 + crc_bit = crc_register >> 15; + 800284c: ea4f 3cd3 mov.w ip, r3, lsr #15 + if ((data_bit ^ crc_bit) != 0) + 8002850: bf18 it ne + 8002852: f04f 0e01 movne.w lr, #1 + crc_register <<= 1; + 8002856: ea4f 0343 mov.w r3, r3, lsl #1 + if ((data_bit ^ crc_bit) != 0) + 800285a: bf08 it eq + 800285c: f04f 0e00 moveq.w lr, #0 + 8002860: 45e6 cmp lr, ip + crc_register <<= 1; + 8002862: b29b uxth r3, r3 + crc_register ^= polynom; + 8002864: bf18 it ne + 8002866: 4073 eorne r3, r6 + for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) { + 8002868: 0064 lsls r4, r4, #1 + 800286a: 3d01 subs r5, #1 + 800286c: b2e4 uxtb r4, r4 + 800286e: d1ec bne.n 800284a + 8002870: e7e1 b.n 8002836 + +08002872 : + +// ae_check_crc() +// + static bool +ae_check_crc(const uint8_t *data, uint8_t length) +{ + 8002872: b573 push {r0, r1, r4, r5, r6, lr} + uint8_t obs[2] = { 0, 0 }; + + if(data[0] != length) { + 8002874: 7806 ldrb r6, [r0, #0] + uint8_t obs[2] = { 0, 0 }; + 8002876: 2400 movs r4, #0 + if(data[0] != length) { + 8002878: 428e cmp r6, r1 +{ + 800287a: 4605 mov r5, r0 + uint8_t obs[2] = { 0, 0 }; + 800287c: f8ad 4004 strh.w r4, [sp, #4] + if(data[0] != length) { + 8002880: d113 bne.n 80028aa + // length is wrong + STATS(crc_len_error++); + return false; + } + + crc16_chain(length-2, data, obs); + 8002882: 4629 mov r1, r5 + 8002884: 1eb0 subs r0, r6, #2 + + return (obs[0] == data[length-2] && obs[1] == data[length-1]); + 8002886: 4435 add r5, r6 + crc16_chain(length-2, data, obs); + 8002888: aa01 add r2, sp, #4 + 800288a: b2c0 uxtb r0, r0 + 800288c: f7ff ffce bl 800282c + return (obs[0] == data[length-2] && obs[1] == data[length-1]); + 8002890: f89d 2004 ldrb.w r2, [sp, #4] + 8002894: f815 3c02 ldrb.w r3, [r5, #-2] + 8002898: 429a cmp r2, r3 + 800289a: d106 bne.n 80028aa + 800289c: f815 4c01 ldrb.w r4, [r5, #-1] + 80028a0: f89d 0005 ldrb.w r0, [sp, #5] + 80028a4: 1a23 subs r3, r4, r0 + 80028a6: 425c negs r4, r3 + 80028a8: 415c adcs r4, r3 + return false; + 80028aa: 4620 mov r0, r4 +} + 80028ac: b002 add sp, #8 + 80028ae: bd70 pop {r4, r5, r6, pc} + +080028b0 : +{ + 80028b0: b508 push {r3, lr} + _send_byte(0x00); + 80028b2: 2000 movs r0, #0 + 80028b4: f7ff ff6e bl 8002794 <_send_byte> + delay_ms(3); // measured: ~2.9ms + 80028b8: 2003 movs r0, #3 + 80028ba: f001 f81d bl 80038f8 +} + 80028be: e8bd 4008 ldmia.w sp!, {r3, lr} + _flush_rx(); + 80028c2: f7ff bf97 b.w 80027f4 <_flush_rx> + +080028c6 : +{ + 80028c6: b508 push {r3, lr} + ae_wake(); + 80028c8: f7ff fff2 bl 80028b0 +} + 80028cc: e8bd 4008 ldmia.w sp!, {r3, lr} + _send_bits(IOFLAG_IDLE); + 80028d0: 20bb movs r0, #187 ; 0xbb + 80028d2: f7ff bf75 b.w 80027c0 <_send_bits> + ... + +080028d8 : +{ + 80028d8: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + int max_expect = (max_len+1) * 8; + 80028dc: 3101 adds r1, #1 + uint8_t raw[max_expect]; + 80028de: 466b mov r3, sp + 80028e0: eba3 03c1 sub.w r3, r3, r1, lsl #3 +{ + 80028e4: af00 add r7, sp, #0 + 80028e6: 4606 mov r6, r0 + uint8_t raw[max_expect]; + 80028e8: 469d mov sp, r3 + _send_bits(IOFLAG_TX); + 80028ea: 2088 movs r0, #136 ; 0x88 + int max_expect = (max_len+1) * 8; + 80028ec: 00cd lsls r5, r1, #3 + _send_bits(IOFLAG_TX); + 80028ee: f7ff ff67 bl 80027c0 <_send_bits> + _flush_rx(); + 80028f2: f7ff ff7f bl 80027f4 <_flush_rx> + int actual = 0; + 80028f6: 2200 movs r2, #0 + while(!(MY_UART->ISR & UART_FLAG_RXNE) && !(MY_UART->ISR & UART_FLAG_RTOF)) { + 80028f8: 4829 ldr r0, [pc, #164] ; (80029a0 ) + uint8_t raw[max_expect]; + 80028fa: 466c mov r4, sp + for(uint8_t *p = raw; ; actual++) { + 80028fc: 4669 mov r1, sp + SysTick->VAL = 0; + 80028fe: f04f 2ce0 mov.w ip, #3758153728 ; 0xe000e000 + 8002902: 4696 mov lr, r2 + 8002904: f8cc e018 str.w lr, [ip, #24] + while(!(MY_UART->ISR & UART_FLAG_RXNE) && !(MY_UART->ISR & UART_FLAG_RTOF)) { + 8002908: 2305 movs r3, #5 + 800290a: f8d0 801c ldr.w r8, [r0, #28] + 800290e: f018 0f20 tst.w r8, #32 + 8002912: d104 bne.n 800291e + 8002914: f8d0 801c ldr.w r8, [r0, #28] + 8002918: f418 6f00 tst.w r8, #2048 ; 0x800 + 800291c: d008 beq.n 8002930 + if(MY_UART->ISR & UART_FLAG_RXNE) { + 800291e: 69c3 ldr r3, [r0, #28] + 8002920: 069b lsls r3, r3, #26 + 8002922: d52e bpl.n 8002982 + return MY_UART->RDR & 0x7f; + 8002924: 8c83 ldrh r3, [r0, #36] ; 0x24 + if(actual < max_expect) { + 8002926: 42aa cmp r2, r5 + return MY_UART->RDR & 0x7f; + 8002928: b29b uxth r3, r3 + if(actual < max_expect) { + 800292a: db34 blt.n 8002996 + for(uint8_t *p = raw; ; actual++) { + 800292c: 3201 adds r2, #1 + 800292e: e7e9 b.n 8002904 + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8002930: f8dc 8010 ldr.w r8, [ip, #16] + 8002934: f418 3f80 tst.w r8, #65536 ; 0x10000 + 8002938: d0e7 beq.n 800290a + if(ticks >= 5) { + 800293a: 3b01 subs r3, #1 + 800293c: d1e5 bne.n 800290a + actual &= ~7; + 800293e: f022 0107 bic.w r1, r2, #7 + while(from_len > 0) { + 8002942: 3d08 subs r5, #8 + 8002944: 4425 add r5, r4 + 8002946: 4623 mov r3, r4 + 8002948: 4421 add r1, r4 + 800294a: 1ac8 subs r0, r1, r3 + 800294c: 2800 cmp r0, #0 + 800294e: dd14 ble.n 800297a + 8002950: f103 3cff add.w ip, r3, #4294967295 ; 0xffffffff + uint8_t rv = 0, mask = 0x1; + 8002954: 2001 movs r0, #1 + 8002956: 2400 movs r4, #0 + for(int i=0; i<8; i++, mask <<= 1) { + 8002958: f103 0e07 add.w lr, r3, #7 + if(from[i] == BIT1) { + 800295c: f81c 8f01 ldrb.w r8, [ip, #1]! + 8002960: f1b8 0f7f cmp.w r8, #127 ; 0x7f + rv |= mask; + 8002964: bf08 it eq + 8002966: 4304 orreq r4, r0 + for(int i=0; i<8; i++, mask <<= 1) { + 8002968: 0040 lsls r0, r0, #1 + 800296a: 45f4 cmp ip, lr + 800296c: b2c0 uxtb r0, r0 + 800296e: d1f5 bne.n 800295c + from += 8; + 8002970: 3308 adds r3, #8 + if(max_into <= 0) break; + 8002972: 42ab cmp r3, r5 + *(into++) = rv; + 8002974: f806 4b01 strb.w r4, [r6], #1 + if(max_into <= 0) break; + 8002978: d1e7 bne.n 800294a + return actual / 8; + 800297a: 10d0 asrs r0, r2, #3 +} + 800297c: 46bd mov sp, r7 + 800297e: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if(MY_UART->ISR & UART_FLAG_RTOF) { + 8002982: 69c3 ldr r3, [r0, #28] + 8002984: 051b lsls r3, r3, #20 + 8002986: d503 bpl.n 8002990 + MY_UART->ICR = USART_ICR_RTOCF; + 8002988: f44f 6300 mov.w r3, #2048 ; 0x800 + 800298c: 6203 str r3, [r0, #32] + if(ch < 0) { + 800298e: e7d6 b.n 800293e + INCONSISTENT("rxf"); + 8002990: 4804 ldr r0, [pc, #16] ; (80029a4 ) + 8002992: f7fe f859 bl 8000a48 + *(p++) = ch; + 8002996: f003 037f and.w r3, r3, #127 ; 0x7f + 800299a: f801 3b01 strb.w r3, [r1], #1 + 800299e: e7c5 b.n 800292c + 80029a0: 40004c00 .word 0x40004c00 + 80029a4: 0800d700 .word 0x0800d700 + +080029a8 : + if(ae_chip_is_setup == AE_CHIP_IS_SETUP) { + 80029a8: 4b04 ldr r3, [pc, #16] ; (80029bc ) + 80029aa: 681a ldr r2, [r3, #0] + 80029ac: 4b04 ldr r3, [pc, #16] ; (80029c0 ) + 80029ae: 429a cmp r2, r3 + 80029b0: d102 bne.n 80029b8 + _send_bits(IOFLAG_SLEEP); + 80029b2: 20cc movs r0, #204 ; 0xcc + 80029b4: f7ff bf04 b.w 80027c0 <_send_bits> +} + 80029b8: 4770 bx lr + 80029ba: bf00 nop + 80029bc: 2009e1bc .word 0x2009e1bc + 80029c0: 35d25d63 .word 0x35d25d63 + +080029c4 : + __HAL_RCC_UART4_CLK_ENABLE(); + 80029c4: 4b13 ldr r3, [pc, #76] ; (8002a14 ) + 80029c6: 6d9a ldr r2, [r3, #88] ; 0x58 + 80029c8: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 80029cc: 659a str r2, [r3, #88] ; 0x58 + 80029ce: 6d9b ldr r3, [r3, #88] ; 0x58 +{ + 80029d0: b082 sub sp, #8 + __HAL_RCC_UART4_CLK_ENABLE(); + 80029d2: f403 2300 and.w r3, r3, #524288 ; 0x80000 + 80029d6: 9301 str r3, [sp, #4] + 80029d8: 9b01 ldr r3, [sp, #4] + MY_UART->CR1 = 0; + 80029da: 4b0f ldr r3, [pc, #60] ; (8002a18 ) + 80029dc: 2200 movs r2, #0 + 80029de: 601a str r2, [r3, #0] + MY_UART->CR1 = 0x1000002d & ~(0 + 80029e0: 4a0e ldr r2, [pc, #56] ; (8002a1c ) + 80029e2: 601a str r2, [r3, #0] + MY_UART->RTOR = 24; // timeout in bit periods: 3 chars or so + 80029e4: 2218 movs r2, #24 + 80029e6: 615a str r2, [r3, #20] + MY_UART->CR2 = USART_CR2_RTOEN; // rx timeout enable + 80029e8: f44f 0200 mov.w r2, #8388608 ; 0x800000 + 80029ec: 605a str r2, [r3, #4] + MY_UART->CR3 = USART_CR3_HDSEL | USART_CR3_ONEBIT; + 80029ee: f640 0208 movw r2, #2056 ; 0x808 + 80029f2: 609a str r2, [r3, #8] + MY_UART->BRR = 521; // 230400 bps @ 120 Mhz SYSCLK + 80029f4: f240 2209 movw r2, #521 ; 0x209 + 80029f8: 60da str r2, [r3, #12] + MY_UART->ICR = USART_ICR_RTOCF; + 80029fa: f44f 6200 mov.w r2, #2048 ; 0x800 + 80029fe: 621a str r2, [r3, #32] + MY_UART->CR1 |= USART_CR1_UE; + 8002a00: 681a ldr r2, [r3, #0] + 8002a02: f042 0201 orr.w r2, r2, #1 + 8002a06: 601a str r2, [r3, #0] + ae_chip_is_setup = AE_CHIP_IS_SETUP; + 8002a08: 4b05 ldr r3, [pc, #20] ; (8002a20 ) + 8002a0a: 4a06 ldr r2, [pc, #24] ; (8002a24 ) + 8002a0c: 601a str r2, [r3, #0] +} + 8002a0e: b002 add sp, #8 + 8002a10: 4770 bx lr + 8002a12: bf00 nop + 8002a14: 40021000 .word 0x40021000 + 8002a18: 40004c00 .word 0x40004c00 + 8002a1c: 1000002c .word 0x1000002c + 8002a20: 2009e1bc .word 0x2009e1bc + 8002a24: 35d25d63 .word 0x35d25d63 + +08002a28 : + ae_send_idle(); + 8002a28: f7ff bf4d b.w 80028c6 + +08002a2c : +// Read a one-byte status/error code response from chip. It's wrapped as 4 bytes: +// (len=4) (value) (crc16) (crc16) +// + int +ae_read1(void) +{ + 8002a2c: b513 push {r0, r1, r4, lr} + 8002a2e: 2408 movs r4, #8 + uint8_t msg[4]; + + for(int retry=7; retry >= 0; retry--) { + // tell it we want to read a response, read it, and deserialize + int rv = ae_read_response(msg, 4); + 8002a30: 2104 movs r1, #4 + 8002a32: eb0d 0001 add.w r0, sp, r1 + 8002a36: f7ff ff4f bl 80028d8 + + if(rv == 0) { + 8002a3a: 4601 mov r1, r0 + 8002a3c: b938 cbnz r0, 8002a4e + // nothing heard, it's probably still processing + ERR("not rdy"); + STATS(not_ready++); + + delay_ms(5); + 8002a3e: 2005 movs r0, #5 + 8002a40: f000 ff5a bl 80038f8 + for(int retry=7; retry >= 0; retry--) { + 8002a44: 3c01 subs r4, #1 + 8002a46: d1f3 bne.n 8002a30 + try_again: + STATS(l1_retry++); + } + + // fail. + return -1; + 8002a48: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8002a4c: e008 b.n 8002a60 + if(rv != 4) { + 8002a4e: 2804 cmp r0, #4 + 8002a50: d1f8 bne.n 8002a44 + if(!ae_check_crc(msg, 4)) { + 8002a52: a801 add r0, sp, #4 + 8002a54: f7ff ff0d bl 8002872 + 8002a58: 2800 cmp r0, #0 + 8002a5a: d0f3 beq.n 8002a44 + return msg[1]; + 8002a5c: f89d 0005 ldrb.w r0, [sp, #5] +} + 8002a60: b002 add sp, #8 + 8002a62: bd10 pop {r4, pc} + +08002a64 : +// Read and check CRC over N bytes, wrapped in 3-bytes of framing overhead. +// Return -1 for timeout, zero for normal, and one-byte error code otherwise. +// + int +ae_read_n(uint8_t len, uint8_t *body) +{ + 8002a64: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + uint8_t tmp[1+len+2]; + 8002a68: f100 030a add.w r3, r0, #10 + 8002a6c: f403 73fc and.w r3, r3, #504 ; 0x1f8 +{ + 8002a70: af00 add r7, sp, #0 + uint8_t tmp[1+len+2]; + 8002a72: ebad 0d03 sub.w sp, sp, r3 +{ + 8002a76: 460d mov r5, r1 + uint8_t tmp[1+len+2]; + 8002a78: 1cc6 adds r6, r0, #3 + 8002a7a: 46e8 mov r8, sp + 8002a7c: f04f 0908 mov.w r9, #8 + + for(int retry=7; retry >= 0; retry--) { + + int actual = ae_read_response(tmp, len+3); + 8002a80: 4631 mov r1, r6 + 8002a82: 4640 mov r0, r8 + 8002a84: f7ff ff28 bl 80028d8 + if(actual < 4) { + 8002a88: 2803 cmp r0, #3 + int actual = ae_read_response(tmp, len+3); + 8002a8a: 4604 mov r4, r0 + if(actual < 4) { + 8002a8c: dc0b bgt.n 8002aa6 + + if(actual == 0) { + 8002a8e: b910 cbnz r0, 8002a96 + // nothing heard, it's probably still processing + delay_ms(5); + 8002a90: 2005 movs r0, #5 + 8002a92: f000 ff31 bl 80038f8 + + return 0; + + try_again: + STATS(ln_retry++); + ae_wake(); + 8002a96: f7ff ff0b bl 80028b0 + for(int retry=7; retry >= 0; retry--) { + 8002a9a: f1b9 0901 subs.w r9, r9, #1 + 8002a9e: d1ef bne.n 8002a80 + } + + return -1; + 8002aa0: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8002aa4: e007 b.n 8002ab6 + uint8_t resp_len = tmp[0]; + 8002aa6: f898 3000 ldrb.w r3, [r8] + if(resp_len != (len + 3)) { + 8002aaa: 42b3 cmp r3, r6 + 8002aac: d006 beq.n 8002abc + if(resp_len == 4) { + 8002aae: 2b04 cmp r3, #4 + 8002ab0: d1f1 bne.n 8002a96 + return tmp[1]; + 8002ab2: f898 0001 ldrb.w r0, [r8, #1] +} + 8002ab6: 46bd mov sp, r7 + 8002ab8: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + if(!ae_check_crc(tmp, actual)) { + 8002abc: b2c1 uxtb r1, r0 + 8002abe: 4640 mov r0, r8 + 8002ac0: f7ff fed7 bl 8002872 + 8002ac4: 2800 cmp r0, #0 + 8002ac6: d0e6 beq.n 8002a96 + memcpy(body, tmp+1, actual-3); + 8002ac8: 1ee2 subs r2, r4, #3 + 8002aca: f108 0101 add.w r1, r8, #1 + 8002ace: 4628 mov r0, r5 + 8002ad0: f00a fda8 bl 800d624 + return 0; + 8002ad4: 2000 movs r0, #0 + 8002ad6: e7ee b.n 8002ab6 + +08002ad8 : + +// ae_send_n() +// + void +ae_send_n(aeopcode_t opcode, uint8_t p1, uint16_t p2, const uint8_t *data, uint8_t data_len) +{ + 8002ad8: b530 push {r4, r5, lr} + 8002ada: b085 sub sp, #20 + 8002adc: 461d mov r5, r3 + 8002ade: f89d 4020 ldrb.w r4, [sp, #32] + uint8_t framed_len; + uint8_t op; + uint8_t p1; + uint8_t p2_lsb; + uint8_t p2_msb; + } known = { + 8002ae2: f88d 200c strb.w r2, [sp, #12] + 8002ae6: 2377 movs r3, #119 ; 0x77 + 8002ae8: 0a12 lsrs r2, r2, #8 + 8002aea: f88d 3008 strb.w r3, [sp, #8] + .ioflag = IOFLAG_CMD, + .framed_len = (data_len + 7), // 7 = (1 len) + (4 bytes of msg) + (2 crc) + 8002aee: 1de3 adds r3, r4, #7 + } known = { + 8002af0: f88d 3009 strb.w r3, [sp, #9] + 8002af4: f88d 200d strb.w r2, [sp, #13] + 8002af8: f88d 000a strb.w r0, [sp, #10] + 8002afc: f88d 100b strb.w r1, [sp, #11] + STATS(last_op = opcode); + STATS(last_p1 = p1); + STATS(last_p2 = p2); + + // important to wake chip at this point. + ae_wake(); + 8002b00: f7ff fed6 bl 80028b0 + + _send_serialized((const uint8_t *)&known, sizeof(known)); + 8002b04: 2106 movs r1, #6 + 8002b06: a802 add r0, sp, #8 + 8002b08: f7ff fe69 bl 80027de <_send_serialized> + + // CRC will start from frame_len onwards + uint8_t crc[2] = {0, 0}; + 8002b0c: 2300 movs r3, #0 + crc16_chain(sizeof(known)-1, &known.framed_len, crc); + 8002b0e: aa01 add r2, sp, #4 + 8002b10: f10d 0109 add.w r1, sp, #9 + 8002b14: 2005 movs r0, #5 + uint8_t crc[2] = {0, 0}; + 8002b16: f8ad 3004 strh.w r3, [sp, #4] + crc16_chain(sizeof(known)-1, &known.framed_len, crc); + 8002b1a: f7ff fe87 bl 800282c + + // insert a variable-length body area (sometimes) + if(data_len) { + 8002b1e: b144 cbz r4, 8002b32 + _send_serialized(data, data_len); + 8002b20: 4621 mov r1, r4 + 8002b22: 4628 mov r0, r5 + 8002b24: f7ff fe5b bl 80027de <_send_serialized> + + crc16_chain(data_len, data, crc); + 8002b28: aa01 add r2, sp, #4 + 8002b2a: 4629 mov r1, r5 + 8002b2c: 4620 mov r0, r4 + 8002b2e: f7ff fe7d bl 800282c + } + + // send final CRC bytes + _send_serialized(crc, 2); + 8002b32: 2102 movs r1, #2 + 8002b34: a801 add r0, sp, #4 + 8002b36: f7ff fe52 bl 80027de <_send_serialized> +} + 8002b3a: b005 add sp, #20 + 8002b3c: bd30 pop {r4, r5, pc} + +08002b3e : +{ + 8002b3e: b507 push {r0, r1, r2, lr} + ae_send_n(opcode, p1, p2, NULL, 0); + 8002b40: 2300 movs r3, #0 + 8002b42: 9300 str r3, [sp, #0] + 8002b44: f7ff ffc8 bl 8002ad8 +} + 8002b48: b003 add sp, #12 + 8002b4a: f85d fb04 ldr.w pc, [sp], #4 + +08002b4e : +// +// Do Info(p1=2) command, and return result. +// + uint16_t +ae_get_info(void) +{ + 8002b4e: b507 push {r0, r1, r2, lr} + // not doing error checking here + ae_send(OP_Info, 0x2, 0); + 8002b50: 2200 movs r2, #0 + 8002b52: 2102 movs r1, #2 + 8002b54: 2030 movs r0, #48 ; 0x30 + 8002b56: f7ff fff2 bl 8002b3e + + // note: always returns 4 bytes, but most are garbage and unused. + uint8_t tmp[4]; + ae_read_n(4, tmp); + 8002b5a: a901 add r1, sp, #4 + 8002b5c: 2004 movs r0, #4 + 8002b5e: f7ff ff81 bl 8002a64 + + return (tmp[0] << 8) | tmp[1]; + 8002b62: f8bd 0004 ldrh.w r0, [sp, #4] + 8002b66: ba40 rev16 r0, r0 +} + 8002b68: b280 uxth r0, r0 + 8002b6a: b003 add sp, #12 + 8002b6c: f85d fb04 ldr.w pc, [sp], #4 + +08002b70 : +// Load Tempkey with a specific value. Resulting Tempkey cannot be +// used with many commands/keys, but is needed for signing. +// + int +ae_load_nonce(const uint8_t nonce[32]) +{ + 8002b70: b507 push {r0, r1, r2, lr} + // p1=3 + ae_send_n(OP_Nonce, 3, 0, nonce, 32); // 608a ok + 8002b72: 2220 movs r2, #32 +{ + 8002b74: 4603 mov r3, r0 + ae_send_n(OP_Nonce, 3, 0, nonce, 32); // 608a ok + 8002b76: 9200 str r2, [sp, #0] + 8002b78: 2103 movs r1, #3 + 8002b7a: 2200 movs r2, #0 + 8002b7c: 2016 movs r0, #22 + 8002b7e: f7ff ffab bl 8002ad8 + + return ae_read1(); +} + 8002b82: b003 add sp, #12 + 8002b84: f85d eb04 ldr.w lr, [sp], #4 + return ae_read1(); + 8002b88: f7ff bf50 b.w 8002a2c + +08002b8c : +// Load 32bytes of message digest with a specific value. +// Needed for signing. +// + int +ae_load_msgdigest(const uint8_t md[32]) +{ + 8002b8c: b507 push {r0, r1, r2, lr} + ae_send_n(OP_Nonce, (1<<6) | 3, 0, md, 32); + 8002b8e: 2220 movs r2, #32 +{ + 8002b90: 4603 mov r3, r0 + ae_send_n(OP_Nonce, (1<<6) | 3, 0, md, 32); + 8002b92: 9200 str r2, [sp, #0] + 8002b94: 2143 movs r1, #67 ; 0x43 + 8002b96: 2200 movs r2, #0 + 8002b98: 2016 movs r0, #22 + 8002b9a: f7ff ff9d bl 8002ad8 + + return ae_read1(); +} + 8002b9e: b003 add sp, #12 + 8002ba0: f85d eb04 ldr.w lr, [sp], #4 + return ae_read1(); + 8002ba4: f7ff bf42 b.w 8002a2c + +08002ba8 : +// Load Tempkey with a nonce value that we both know, but +// is random and we both know is random! Tricky! +// + int +ae_pick_nonce(const uint8_t num_in[20], uint8_t tempkey[32]) +{ + 8002ba8: b5f0 push {r4, r5, r6, r7, lr} + 8002baa: b09f sub sp, #124 ; 0x7c + // We provide some 20 bytes of randomness to chip + // The chip must provide 32-bytes of random-ness, + // so no choice in args to OP.Nonce here (due to ReqRandom). + ae_send_n(OP_Nonce, 0, 0, num_in, 20); + 8002bac: 2200 movs r2, #0 + 8002bae: 2714 movs r7, #20 + 8002bb0: 4603 mov r3, r0 +{ + 8002bb2: 4605 mov r5, r0 + 8002bb4: 460e mov r6, r1 + ae_send_n(OP_Nonce, 0, 0, num_in, 20); + 8002bb6: 2016 movs r0, #22 + 8002bb8: 4611 mov r1, r2 + 8002bba: 9700 str r7, [sp, #0] + 8002bbc: f7ff ff8c bl 8002ad8 + + // Nonce command returns the RNG result, but not contents of TempKey + uint8_t randout[32]; + int rv = ae_read_n(32, randout); + 8002bc0: a903 add r1, sp, #12 + 8002bc2: 2020 movs r0, #32 + 8002bc4: f7ff ff4e bl 8002a64 + RET_IF_BAD(rv); + 8002bc8: 4604 mov r4, r0 + 8002bca: b9e0 cbnz r0, 8002c06 + // + // return sha256(rndout + num_in + b'\x16\0\0').digest() + // + SHA256_CTX ctx; + + sha256_init(&ctx); + 8002bcc: a80b add r0, sp, #44 ; 0x2c + 8002bce: f002 fc5d bl 800548c + sha256_update(&ctx, randout, 32); + 8002bd2: 2220 movs r2, #32 + 8002bd4: a903 add r1, sp, #12 + 8002bd6: a80b add r0, sp, #44 ; 0x2c + 8002bd8: f002 fc66 bl 80054a8 + sha256_update(&ctx, num_in, 20); + 8002bdc: 463a mov r2, r7 + 8002bde: 4629 mov r1, r5 + 8002be0: a80b add r0, sp, #44 ; 0x2c + 8002be2: f002 fc61 bl 80054a8 + const uint8_t fixed[3] = { 0x16, 0, 0 }; + 8002be6: 4b09 ldr r3, [pc, #36] ; (8002c0c ) + 8002be8: 881a ldrh r2, [r3, #0] + 8002bea: f8ad 2008 strh.w r2, [sp, #8] + 8002bee: 789b ldrb r3, [r3, #2] + 8002bf0: f88d 300a strb.w r3, [sp, #10] + sha256_update(&ctx, fixed, 3); + 8002bf4: a902 add r1, sp, #8 + 8002bf6: a80b add r0, sp, #44 ; 0x2c + 8002bf8: 2203 movs r2, #3 + 8002bfa: f002 fc55 bl 80054a8 + + sha256_final(&ctx, tempkey); + 8002bfe: 4631 mov r1, r6 + 8002c00: a80b add r0, sp, #44 ; 0x2c + 8002c02: f002 fc97 bl 8005534 + + return 0; +} + 8002c06: 4620 mov r0, r4 + 8002c08: b01f add sp, #124 ; 0x7c + 8002c0a: bdf0 pop {r4, r5, r6, r7, pc} + 8002c0c: 0800e674 .word 0x0800e674 + +08002c10 : +// Check that TempKey is holding what we think it does. Uses the MAC +// command over contents of Tempkey and our shared secret. +// + bool +ae_is_correct_tempkey(const uint8_t expected_tempkey[32]) +{ + 8002c10: b570 push {r4, r5, r6, lr} + const uint8_t mode = (1<<6) // include full serial number + | (0<<2) // TempKey.SourceFlag == 0 == 'rand' + | (0<<1) // first 32 bytes are the shared secret + | (1<<0); // second 32 bytes are tempkey + + ae_send(OP_MAC, mode, KEYNUM_pairing); + 8002c12: 2141 movs r1, #65 ; 0x41 +{ + 8002c14: b0a8 sub sp, #160 ; 0xa0 + 8002c16: 4604 mov r4, r0 + ae_send(OP_MAC, mode, KEYNUM_pairing); + 8002c18: 2201 movs r2, #1 + 8002c1a: 2008 movs r0, #8 + 8002c1c: f7ff ff8f bl 8002b3e + + // read chip's answer + uint8_t resp[32]; + int rv = ae_read_n(32, resp); + 8002c20: a905 add r1, sp, #20 + 8002c22: 2020 movs r0, #32 + 8002c24: f7ff ff1e bl 8002a64 + if(rv) return false; + 8002c28: 2800 cmp r0, #0 + 8002c2a: d135 bne.n 8002c98 + ae_send_idle(); + 8002c2c: f7ff fe4b bl 80028c6 + ae_keep_alive(); + + // Duplicate the hash process, and then compare. + SHA256_CTX ctx; + + sha256_init(&ctx); + 8002c30: a815 add r0, sp, #84 ; 0x54 + 8002c32: f002 fc2b bl 800548c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8002c36: 4919 ldr r1, [pc, #100] ; (8002c9c ) + 8002c38: 2220 movs r2, #32 + 8002c3a: a815 add r0, sp, #84 ; 0x54 + 8002c3c: f002 fc34 bl 80054a8 + sha256_update(&ctx, expected_tempkey, 32); + 8002c40: 2220 movs r2, #32 + 8002c42: 4621 mov r1, r4 + 8002c44: a815 add r0, sp, #84 ; 0x54 + 8002c46: f002 fc2f bl 80054a8 + + const uint8_t fixed[16] = { OP_MAC, mode, KEYNUM_pairing, 0x0, + 8002c4a: 4b15 ldr r3, [pc, #84] ; (8002ca0 ) + 8002c4c: aa01 add r2, sp, #4 + 8002c4e: f103 0610 add.w r6, r3, #16 + 8002c52: 4615 mov r5, r2 + 8002c54: 6818 ldr r0, [r3, #0] + 8002c56: 6859 ldr r1, [r3, #4] + 8002c58: 4614 mov r4, r2 + 8002c5a: c403 stmia r4!, {r0, r1} + 8002c5c: 3308 adds r3, #8 + 8002c5e: 42b3 cmp r3, r6 + 8002c60: 4622 mov r2, r4 + 8002c62: d1f7 bne.n 8002c54 + 0,0,0,0, 0,0,0,0, // eight zeros + 0,0,0, // three zeros + 0xEE }; + sha256_update(&ctx, fixed, sizeof(fixed)); + 8002c64: 2210 movs r2, #16 + 8002c66: 4629 mov r1, r5 + 8002c68: a815 add r0, sp, #84 ; 0x54 + 8002c6a: f002 fc1d bl 80054a8 + + sha256_update(&ctx, ((const uint8_t *)rom_secrets->ae_serial_number)+4, 4); + 8002c6e: 490d ldr r1, [pc, #52] ; (8002ca4 ) + 8002c70: 2204 movs r2, #4 + 8002c72: a815 add r0, sp, #84 ; 0x54 + 8002c74: f002 fc18 bl 80054a8 + sha256_update(&ctx, ((const uint8_t *)rom_secrets->ae_serial_number)+0, 4); + 8002c78: 2204 movs r2, #4 + 8002c7a: 490b ldr r1, [pc, #44] ; (8002ca8 ) + 8002c7c: a815 add r0, sp, #84 ; 0x54 + 8002c7e: f002 fc13 bl 80054a8 + // this verifies no problem. + ASSERT(ctx.datalen + (ctx.bitlen/8) == 32+32+1+1+2+8+3+1+4+2+2); // == 88 +#endif + + uint8_t actual[32]; + sha256_final(&ctx, actual); + 8002c82: a90d add r1, sp, #52 ; 0x34 + 8002c84: a815 add r0, sp, #84 ; 0x54 + 8002c86: f002 fc55 bl 8005534 + + return check_equal(actual, resp, 32); + 8002c8a: 2220 movs r2, #32 + 8002c8c: a905 add r1, sp, #20 + 8002c8e: a80d add r0, sp, #52 ; 0x34 + 8002c90: f7ff fd0f bl 80026b2 +} + 8002c94: b028 add sp, #160 ; 0xa0 + 8002c96: bd70 pop {r4, r5, r6, pc} + if(rv) return false; + 8002c98: 2000 movs r0, #0 + 8002c9a: e7fb b.n 8002c94 + 8002c9c: 0801c000 .word 0x0801c000 + 8002ca0: 0800e677 .word 0x0800e677 + 8002ca4: 0801c044 .word 0x0801c044 + 8002ca8: 0801c040 .word 0x0801c040 + +08002cac : +// inside the 508a/608a, like use of a specific key, but not for us to +// authenticate the 508a/608a or its contents/state. +// + int +ae_checkmac(uint8_t keynum, const uint8_t secret[32]) +{ + 8002cac: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8002cb0: b0c2 sub sp, #264 ; 0x108 + + // Since this is part of the hash, we want random bytes + // for our "other data". Also a number for "numin" of nonce + uint8_t od[32], numin[20]; + + rng_buffer(od, sizeof(od)); + 8002cb2: ad0b add r5, sp, #44 ; 0x2c +{ + 8002cb4: 4607 mov r7, r0 + 8002cb6: 460e mov r6, r1 + rng_buffer(od, sizeof(od)); + 8002cb8: 4628 mov r0, r5 + 8002cba: 2120 movs r1, #32 + 8002cbc: f7ff fd48 bl 8002750 + rng_buffer(numin, sizeof(numin)); + 8002cc0: 2114 movs r1, #20 + 8002cc2: a806 add r0, sp, #24 + 8002cc4: f7ff fd44 bl 8002750 + ae_send_idle(); + 8002cc8: f7ff fdfd bl 80028c6 + + // need this one, want to reset watchdog to this point. + ae_keep_alive(); + + // - load tempkey with a known nonce value + uint8_t zeros[8] = {0}; + 8002ccc: 2300 movs r3, #0 + uint8_t tempkey[32]; + rv = ae_pick_nonce(numin, tempkey); + 8002cce: a913 add r1, sp, #76 ; 0x4c + 8002cd0: a806 add r0, sp, #24 + uint8_t zeros[8] = {0}; + 8002cd2: e9cd 3304 strd r3, r3, [sp, #16] + rv = ae_pick_nonce(numin, tempkey); + 8002cd6: f7ff ff67 bl 8002ba8 + RET_IF_BAD(rv); + 8002cda: 4604 mov r4, r0 + 8002cdc: 2800 cmp r0, #0 + 8002cde: d15d bne.n 8002d9c + + // - hash nonce and lots of other bits together + SHA256_CTX ctx; + sha256_init(&ctx); + 8002ce0: a81b add r0, sp, #108 ; 0x6c + 8002ce2: f002 fbd3 bl 800548c + + // shared secret is 32 bytes from flash + sha256_update(&ctx, secret, 32); + 8002ce6: 2220 movs r2, #32 + 8002ce8: 4631 mov r1, r6 + 8002cea: a81b add r0, sp, #108 ; 0x6c + 8002cec: f002 fbdc bl 80054a8 + + sha256_update(&ctx, tempkey, 32); + 8002cf0: 2220 movs r2, #32 + 8002cf2: a913 add r1, sp, #76 ; 0x4c + 8002cf4: a81b add r0, sp, #108 ; 0x6c + 8002cf6: f002 fbd7 bl 80054a8 + sha256_update(&ctx, &od[0], 4); + 8002cfa: 2204 movs r2, #4 + 8002cfc: 4629 mov r1, r5 + 8002cfe: a81b add r0, sp, #108 ; 0x6c + 8002d00: f002 fbd2 bl 80054a8 + + sha256_update(&ctx, zeros, 8); + 8002d04: 2208 movs r2, #8 + 8002d06: a904 add r1, sp, #16 + 8002d08: a81b add r0, sp, #108 ; 0x6c + 8002d0a: f002 fbcd bl 80054a8 + + sha256_update(&ctx, &od[4], 3); + 8002d0e: 2203 movs r2, #3 + 8002d10: a90c add r1, sp, #48 ; 0x30 + 8002d12: a81b add r0, sp, #108 ; 0x6c + 8002d14: f002 fbc8 bl 80054a8 + + uint8_t ee = 0xEE; + 8002d18: 23ee movs r3, #238 ; 0xee + sha256_update(&ctx, &ee, 1); + 8002d1a: 2201 movs r2, #1 + 8002d1c: f10d 010b add.w r1, sp, #11 + 8002d20: a81b add r0, sp, #108 ; 0x6c + uint8_t ee = 0xEE; + 8002d22: f88d 300b strb.w r3, [sp, #11] + sha256_update(&ctx, &ee, 1); + 8002d26: f002 fbbf bl 80054a8 + sha256_update(&ctx, &od[7], 4); + 8002d2a: 2204 movs r2, #4 + 8002d2c: f10d 0133 add.w r1, sp, #51 ; 0x33 + 8002d30: a81b add r0, sp, #108 ; 0x6c + 8002d32: f002 fbb9 bl 80054a8 + + uint8_t snp[2] = { 0x01, 0x23 }; + 8002d36: f242 3301 movw r3, #8961 ; 0x2301 + sha256_update(&ctx, snp, 2); + 8002d3a: 2202 movs r2, #2 + 8002d3c: a903 add r1, sp, #12 + 8002d3e: a81b add r0, sp, #108 ; 0x6c + uint8_t snp[2] = { 0x01, 0x23 }; + 8002d40: f8ad 300c strh.w r3, [sp, #12] + sha256_update(&ctx, snp, 2); + 8002d44: f002 fbb0 bl 80054a8 + sha256_update(&ctx, &od[11], 2); + 8002d48: 2202 movs r2, #2 + 8002d4a: f10d 0137 add.w r1, sp, #55 ; 0x37 + 8002d4e: a81b add r0, sp, #108 ; 0x6c + 8002d50: f002 fbaa bl 80054a8 + uint8_t resp[32]; + uint8_t od[13]; + } req; + + // content doesn't matter, but nice and visible: + memcpy(req.ch3, copyright_msg, 32); + 8002d54: 4b15 ldr r3, [pc, #84] ; (8002dac ) + 8002d56: ac2e add r4, sp, #184 ; 0xb8 + 8002d58: f103 0220 add.w r2, r3, #32 + 8002d5c: 46a0 mov r8, r4 + 8002d5e: 6818 ldr r0, [r3, #0] + 8002d60: 6859 ldr r1, [r3, #4] + 8002d62: 4626 mov r6, r4 + 8002d64: c603 stmia r6!, {r0, r1} + 8002d66: 3308 adds r3, #8 + 8002d68: 4293 cmp r3, r2 + 8002d6a: 4634 mov r4, r6 + 8002d6c: d1f7 bne.n 8002d5e + // this verifies no problem. + int l = (ctx.blocks * 64) + ctx.npartial; + ASSERT(l == 32+32+4+8+3+1+4+2+2); // == 88 +#endif + + sha256_final(&ctx, req.resp); + 8002d6e: a936 add r1, sp, #216 ; 0xd8 + 8002d70: a81b add r0, sp, #108 ; 0x6c + 8002d72: f002 fbdf bl 8005534 + memcpy(req.od, od, 13); + 8002d76: e895 000f ldmia.w r5, {r0, r1, r2, r3} + 8002d7a: ac3e add r4, sp, #248 ; 0xf8 + 8002d7c: c407 stmia r4!, {r0, r1, r2} + 8002d7e: 7023 strb r3, [r4, #0] + + STATIC_ASSERT(sizeof(req) == 32 + 32 + 13); + + // Give our answer to the chip. + ae_send_n(OP_CheckMac, 0x01, keynum, (uint8_t *)&req, sizeof(req)); + 8002d80: 234d movs r3, #77 ; 0x4d + 8002d82: 9300 str r3, [sp, #0] + 8002d84: 463a mov r2, r7 + 8002d86: 4643 mov r3, r8 + 8002d88: 2101 movs r1, #1 + 8002d8a: 2028 movs r0, #40 ; 0x28 + 8002d8c: f7ff fea4 bl 8002ad8 + + rv = ae_read1(); + 8002d90: f7ff fe4c bl 8002a2c + if(rv != 0) { + 8002d94: 4604 mov r4, r0 + 8002d96: b928 cbnz r0, 8002da4 + ae_send_idle(); + 8002d98: f7ff fd95 bl 80028c6 + + // just in case ... always restart watchdog timer. + ae_keep_alive(); + + return 0; +} + 8002d9c: 4620 mov r0, r4 + 8002d9e: b042 add sp, #264 ; 0x108 + 8002da0: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + return -1; + 8002da4: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 8002da8: e7f8 b.n 8002d9c + 8002daa: bf00 nop + 8002dac: 0800e646 .word 0x0800e646 + +08002db0 : + return ae_checkmac(KEYNUM_pairing, rom_secrets->pairing_secret); + 8002db0: 4901 ldr r1, [pc, #4] ; (8002db8 ) + 8002db2: 2001 movs r0, #1 + 8002db4: f7ff bf7a b.w 8002cac + 8002db8: 0801c000 .word 0x0801c000 + +08002dbc : +// Sign a message (already digested) +// + int +ae_sign_authed(uint8_t keynum, const uint8_t msg_hash[32], + uint8_t signature[64], int auth_kn, const uint8_t auth_digest[32]) +{ + 8002dbc: b570 push {r4, r5, r6, lr} + 8002dbe: 460e mov r6, r1 + 8002dc0: 4604 mov r4, r0 + 8002dc2: 4615 mov r5, r2 + // indicate we know the PIN + ae_pair_unlock(); + 8002dc4: f7ff fff4 bl 8002db0 + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + 8002dc8: 9904 ldr r1, [sp, #16] + 8002dca: 2003 movs r0, #3 + 8002dcc: f7ff ff6e bl 8002cac + RET_IF_BAD(rv); + 8002dd0: b990 cbnz r0, 8002df8 + + // send what we need signed + rv = ae_load_msgdigest(msg_hash); + 8002dd2: 4630 mov r0, r6 + 8002dd4: f7ff feda bl 8002b8c + RET_IF_BAD(rv); + 8002dd8: b970 cbnz r0, 8002df8 + + do { + ae_send(OP_Sign, (7<<5), keynum); + 8002dda: b2a4 uxth r4, r4 + 8002ddc: 4622 mov r2, r4 + 8002dde: 21e0 movs r1, #224 ; 0xe0 + 8002de0: 2041 movs r0, #65 ; 0x41 + 8002de2: f7ff feac bl 8002b3e + + delay_ms(60); // min time for processing + 8002de6: 203c movs r0, #60 ; 0x3c + 8002de8: f000 fd86 bl 80038f8 + + rv = ae_read_n(64, signature); + 8002dec: 4629 mov r1, r5 + 8002dee: 2040 movs r0, #64 ; 0x40 + 8002df0: f7ff fe38 bl 8002a64 + } while(rv == AE_ECC_FAULT); + 8002df4: 2805 cmp r0, #5 + 8002df6: d0f1 beq.n 8002ddc + + return rv; +} + 8002df8: bd70 pop {r4, r5, r6, pc} + ... + +08002dfc : + +// ae_gen_ecc_key() +// + int +ae_gen_ecc_key(uint8_t keynum, uint8_t pubkey_out[64]) +{ + 8002dfc: b530 push {r4, r5, lr} + int rv; + uint8_t junk[3] = { 0 }; + 8002dfe: 4b0f ldr r3, [pc, #60] ; (8002e3c ) +{ + 8002e00: b085 sub sp, #20 + uint8_t junk[3] = { 0 }; + 8002e02: f8b3 3013 ldrh.w r3, [r3, #19] + 8002e06: f8ad 300c strh.w r3, [sp, #12] + 8002e0a: 2300 movs r3, #0 +{ + 8002e0c: 460c mov r4, r1 + uint8_t junk[3] = { 0 }; + 8002e0e: f88d 300e strb.w r3, [sp, #14] + + do { + ae_send_n(OP_GenKey, (1<<2), keynum, junk, 3); + 8002e12: 4605 mov r5, r0 + 8002e14: 2303 movs r3, #3 + 8002e16: 462a mov r2, r5 + 8002e18: 2104 movs r1, #4 + 8002e1a: 9300 str r3, [sp, #0] + 8002e1c: 2040 movs r0, #64 ; 0x40 + 8002e1e: ab03 add r3, sp, #12 + 8002e20: f7ff fe5a bl 8002ad8 + + delay_ms(100); // to avoid timeouts + 8002e24: 2064 movs r0, #100 ; 0x64 + 8002e26: f000 fd67 bl 80038f8 + + rv = ae_read_n(64, pubkey_out); + 8002e2a: 4621 mov r1, r4 + 8002e2c: 2040 movs r0, #64 ; 0x40 + 8002e2e: f7ff fe19 bl 8002a64 + } while(rv == AE_ECC_FAULT); + 8002e32: 2805 cmp r0, #5 + 8002e34: d0ee beq.n 8002e14 + + return rv; +} + 8002e36: b005 add sp, #20 + 8002e38: bd30 pop {r4, r5, pc} + 8002e3a: bf00 nop + 8002e3c: 0800e674 .word 0x0800e674 + +08002e40 : +// 508a: Different opcode, OP_HMAC does exactly 32 bytes w/ less steps. +// 608a: Use old SHA256 command, but with new flags. +// + int +ae_hmac32(uint8_t keynum, const uint8_t msg[32], uint8_t digest[32]) +{ + 8002e40: b530 push {r4, r5, lr} + 8002e42: b085 sub sp, #20 + 8002e44: 4615 mov r5, r2 + 8002e46: 9103 str r1, [sp, #12] + // Start SHA w/ HMAC setup + ae_send(OP_SHA, 4, keynum); // 4 = HMAC_Init + 8002e48: 4602 mov r2, r0 + 8002e4a: 2104 movs r1, #4 + 8002e4c: 2047 movs r0, #71 ; 0x47 + 8002e4e: f7ff fe76 bl 8002b3e + + // expect zero, meaning "ready" + int rv = ae_read1(); + 8002e52: f7ff fdeb bl 8002a2c + RET_IF_BAD(rv); + 8002e56: b970 cbnz r0, 8002e76 + + // send the contents to be hashed + ae_send_n(OP_SHA, (3<<6) | 2, 32, msg, 32); // 2 = Finalize, 3=Place output + 8002e58: 2420 movs r4, #32 + 8002e5a: 9b03 ldr r3, [sp, #12] + 8002e5c: 9400 str r4, [sp, #0] + 8002e5e: 4622 mov r2, r4 + 8002e60: 21c2 movs r1, #194 ; 0xc2 + 8002e62: 2047 movs r0, #71 ; 0x47 + 8002e64: f7ff fe38 bl 8002ad8 + + // read result + return ae_read_n(32, digest); + 8002e68: 4629 mov r1, r5 + 8002e6a: 4620 mov r0, r4 +} + 8002e6c: b005 add sp, #20 + 8002e6e: e8bd 4030 ldmia.w sp!, {r4, r5, lr} + return ae_read_n(32, digest); + 8002e72: f7ff bdf7 b.w 8002a64 +} + 8002e76: b005 add sp, #20 + 8002e78: bd30 pop {r4, r5, pc} + +08002e7a : +// +// Return the serial number: it's 9 bytes, altho 3 are fixed. +// + int +ae_get_serial(uint8_t serial[6]) +{ + 8002e7a: b510 push {r4, lr} + ae_send(OP_Read, 0x80, 0x0); + 8002e7c: 2200 movs r2, #0 +{ + 8002e7e: b08c sub sp, #48 ; 0x30 + ae_send(OP_Read, 0x80, 0x0); + 8002e80: 2180 movs r1, #128 ; 0x80 +{ + 8002e82: 4604 mov r4, r0 + ae_send(OP_Read, 0x80, 0x0); + 8002e84: 2002 movs r0, #2 + 8002e86: f7ff fe5a bl 8002b3e + + uint8_t temp[32]; + int rv = ae_read_n(32, temp); + 8002e8a: a904 add r1, sp, #16 + 8002e8c: 2020 movs r0, #32 + 8002e8e: f7ff fde9 bl 8002a64 + RET_IF_BAD(rv); + 8002e92: 4603 mov r3, r0 + 8002e94: b9b8 cbnz r0, 8002ec6 + + // reformat to 9 bytes. + uint8_t ts[9]; + memcpy(ts, &temp[0], 4); + memcpy(&ts[4], &temp[8], 5); + 8002e96: e9dd 0106 ldrd r0, r1, [sp, #24] + 8002e9a: 9a04 ldr r2, [sp, #16] + 8002e9c: f88d 100c strb.w r1, [sp, #12] + + // check the hard-coded values + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 8002ea0: b2d1 uxtb r1, r2 + 8002ea2: 2901 cmp r1, #1 + memcpy(ts, &temp[0], 4); + 8002ea4: 9201 str r2, [sp, #4] + memcpy(&ts[4], &temp[8], 5); + 8002ea6: 9002 str r0, [sp, #8] + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 8002ea8: d110 bne.n 8002ecc + 8002eaa: f3c2 2207 ubfx r2, r2, #8, #8 + 8002eae: 2a23 cmp r2, #35 ; 0x23 + 8002eb0: d10c bne.n 8002ecc + 8002eb2: f89d 200c ldrb.w r2, [sp, #12] + 8002eb6: 2aee cmp r2, #238 ; 0xee + 8002eb8: d10a bne.n 8002ed0 + + // save only the unique bits. + memcpy(serial, ts+2, 6); + 8002eba: f8dd 2006 ldr.w r2, [sp, #6] + 8002ebe: 6022 str r2, [r4, #0] + 8002ec0: f8bd 200a ldrh.w r2, [sp, #10] + 8002ec4: 80a2 strh r2, [r4, #4] + + return 0; +} + 8002ec6: 4618 mov r0, r3 + 8002ec8: b00c add sp, #48 ; 0x30 + 8002eca: bd10 pop {r4, pc} + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 8002ecc: 2301 movs r3, #1 + 8002ece: e7fa b.n 8002ec6 + 8002ed0: 460b mov r3, r1 + 8002ed2: e7f8 b.n 8002ec6 + +08002ed4 : +{ + 8002ed4: b513 push {r0, r1, r4, lr} + ae_wake(); + 8002ed6: f7ff fceb bl 80028b0 + _send_bits(IOFLAG_SLEEP); + 8002eda: 20cc movs r0, #204 ; 0xcc + 8002edc: f7ff fc70 bl 80027c0 <_send_bits> + ae_wake(); + 8002ee0: f7ff fce6 bl 80028b0 + ae_read1(); + 8002ee4: f7ff fda2 bl 8002a2c + uint8_t chk = ae_read1(); + 8002ee8: f7ff fda0 bl 8002a2c + if(chk != AE_AFTER_WAKE) return "wk fl"; + 8002eec: b2c0 uxtb r0, r0 + 8002eee: 2811 cmp r0, #17 + 8002ef0: d10e bne.n 8002f10 + if(ae_get_serial(serial)) return "no ser"; + 8002ef2: 4668 mov r0, sp + 8002ef4: f7ff ffc1 bl 8002e7a + 8002ef8: 4604 mov r4, r0 + 8002efa: b938 cbnz r0, 8002f0c + ae_wake(); + 8002efc: f7ff fcd8 bl 80028b0 + _send_bits(IOFLAG_SLEEP); + 8002f00: 20cc movs r0, #204 ; 0xcc + 8002f02: f7ff fc5d bl 80027c0 <_send_bits> + return NULL; + 8002f06: 4620 mov r0, r4 +} + 8002f08: b002 add sp, #8 + 8002f0a: bd10 pop {r4, pc} + if(ae_get_serial(serial)) return "no ser"; + 8002f0c: 4801 ldr r0, [pc, #4] ; (8002f14 ) + 8002f0e: e7fb b.n 8002f08 + if(chk != AE_AFTER_WAKE) return "wk fl"; + 8002f10: 4801 ldr r0, [pc, #4] ; (8002f18 ) + 8002f12: e7f9 b.n 8002f08 + 8002f14: 0800e667 .word 0x0800e667 + 8002f18: 0800e66e .word 0x0800e66e + +08002f1c : +// +// -- can also lock it. +// + int +ae_write_data_slot(int slot_num, const uint8_t *data, int len, bool lock_it) +{ + 8002f1c: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8002f20: 4699 mov r9, r3 + ASSERT(len >= 32); + 8002f22: f1a2 0320 sub.w r3, r2, #32 +{ + 8002f26: b085 sub sp, #20 + ASSERT(len >= 32); + 8002f28: f5b3 7fc0 cmp.w r3, #384 ; 0x180 +{ + 8002f2c: 4604 mov r4, r0 + 8002f2e: af02 add r7, sp, #8 + 8002f30: 460d mov r5, r1 + 8002f32: 4690 mov r8, r2 + ASSERT(len >= 32); + 8002f34: d902 bls.n 8002f3c + 8002f36: 482d ldr r0, [pc, #180] ; (8002fec ) + 8002f38: f7fd fd86 bl 8000a48 + ASSERT(len <= 416); + + for(int blk=0, xlen=len; xlen>0; blk++, xlen-=32) { + // have to write each "block" of 32-bytes, separately + // zone => data + ae_send_n(OP_Write, 0x80|2, (blk<<8) | (slot_num<<3), data+(blk*32), 32); + 8002f3c: ea4f 0ac0 mov.w sl, r0, lsl #3 + 8002f40: fa0f fa8a sxth.w sl, sl + 8002f44: 2600 movs r6, #0 + 8002f46: f04f 0b20 mov.w fp, #32 + 8002f4a: ebc6 3246 rsb r2, r6, r6, lsl #13 + 8002f4e: ea4a 02c2 orr.w r2, sl, r2, lsl #3 + 8002f52: b292 uxth r2, r2 + 8002f54: 1bab subs r3, r5, r6 + 8002f56: 2182 movs r1, #130 ; 0x82 + 8002f58: 2012 movs r0, #18 + 8002f5a: f8cd b000 str.w fp, [sp] + 8002f5e: f7ff fdbb bl 8002ad8 + + int rv = ae_read1(); + 8002f62: f7ff fd63 bl 8002a2c + RET_IF_BAD(rv); + 8002f66: 2800 cmp r0, #0 + 8002f68: d13c bne.n 8002fe4 + for(int blk=0, xlen=len; xlen>0; blk++, xlen-=32) { + 8002f6a: 3e20 subs r6, #32 + 8002f6c: eb06 0308 add.w r3, r6, r8 + 8002f70: 2b00 cmp r3, #0 + 8002f72: dcea bgt.n 8002f4a + } + + if(lock_it) { + 8002f74: f1b9 0f00 cmp.w r9, #0 + 8002f78: d034 beq.n 8002fe4 + ASSERT(slot_num != 8); // no support for mega slot 8 + 8002f7a: 2c08 cmp r4, #8 + if(lock_it) { + 8002f7c: 466e mov r6, sp + ASSERT(slot_num != 8); // no support for mega slot 8 + 8002f7e: d0da beq.n 8002f36 + ASSERT(len == 32); // probably not a limitation here + 8002f80: f1b8 0f20 cmp.w r8, #32 + 8002f84: d1d7 bne.n 8002f36 + + // Assume 36/72-byte long slot, which will be partially written, and rest + // should be ones. + const int slot_len = (slot_num <= 7) ? 36 : 72; + 8002f86: 2c08 cmp r4, #8 + 8002f88: bfb4 ite lt + 8002f8a: f04f 0824 movlt.w r8, #36 ; 0x24 + 8002f8e: f04f 0848 movge.w r8, #72 ; 0x48 + uint8_t copy[slot_len]; + 8002f92: f108 0307 add.w r3, r8, #7 + 8002f96: f003 03f8 and.w r3, r3, #248 ; 0xf8 + 8002f9a: ebad 0d03 sub.w sp, sp, r3 + 8002f9e: ab02 add r3, sp, #8 + + memset(copy, 0xff, slot_len); + 8002fa0: 4642 mov r2, r8 + 8002fa2: 21ff movs r1, #255 ; 0xff + 8002fa4: 4618 mov r0, r3 + 8002fa6: f00a fb65 bl 800d674 + memcpy(copy, data, len); + 8002faa: f105 0120 add.w r1, r5, #32 + memset(copy, 0xff, slot_len); + 8002fae: 4603 mov r3, r0 + memcpy(copy, data, len); + 8002fb0: 4602 mov r2, r0 + 8002fb2: f855 0b04 ldr.w r0, [r5], #4 + 8002fb6: f842 0b04 str.w r0, [r2], #4 + 8002fba: 428d cmp r5, r1 + 8002fbc: d1f9 bne.n 8002fb2 + + // calc expected CRC + uint8_t crc[2] = {0, 0}; + 8002fbe: 2200 movs r2, #0 + crc16_chain(slot_len, copy, crc); + 8002fc0: 4619 mov r1, r3 + uint8_t crc[2] = {0, 0}; + 8002fc2: 80ba strh r2, [r7, #4] + crc16_chain(slot_len, copy, crc); + 8002fc4: 4640 mov r0, r8 + 8002fc6: 1d3a adds r2, r7, #4 + 8002fc8: f7ff fc30 bl 800282c + + // do the lock + ae_send(OP_Lock, 2 | (slot_num << 2), (crc[1]<<8) | crc[0]); + 8002fcc: 00a1 lsls r1, r4, #2 + 8002fce: f041 0102 orr.w r1, r1, #2 + 8002fd2: 88ba ldrh r2, [r7, #4] + 8002fd4: f001 01fe and.w r1, r1, #254 ; 0xfe + 8002fd8: 2017 movs r0, #23 + 8002fda: f7ff fdb0 bl 8002b3e + + int rv = ae_read1(); + 8002fde: f7ff fd25 bl 8002a2c + RET_IF_BAD(rv); + 8002fe2: 46b5 mov sp, r6 + } + + return 0; +} + 8002fe4: 370c adds r7, #12 + 8002fe6: 46bd mov sp, r7 + 8002fe8: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + 8002fec: 0800e3e0 .word 0x0800e3e0 + +08002ff0 : + +// ae_gendig_slot() +// + int +ae_gendig_slot(int slot_num, const uint8_t slot_contents[32], uint8_t digest[32]) +{ + 8002ff0: b5f0 push {r4, r5, r6, r7, lr} + 8002ff2: b0ab sub sp, #172 ; 0xac + 8002ff4: 4605 mov r5, r0 + 8002ff6: 460f mov r7, r1 + // Construct a digest on the device (and here) that depends on the secret + // contents of a specific slot. + uint8_t num_in[20], tempkey[32]; + + rng_buffer(num_in, sizeof(num_in)); + 8002ff8: a803 add r0, sp, #12 + 8002ffa: 2114 movs r1, #20 +{ + 8002ffc: 4616 mov r6, r2 + rng_buffer(num_in, sizeof(num_in)); + 8002ffe: f7ff fba7 bl 8002750 + int rv = ae_pick_nonce(num_in, tempkey); + 8003002: a90f add r1, sp, #60 ; 0x3c + 8003004: a803 add r0, sp, #12 + 8003006: f7ff fdcf bl 8002ba8 + RET_IF_BAD(rv); + 800300a: 4604 mov r4, r0 + 800300c: 2800 cmp r0, #0 + 800300e: d13d bne.n 800308c + + //using Zone=2="Data" => "KeyID specifies a slot in the Data zone" + ae_send(OP_GenDig, 0x2, slot_num); + 8003010: b2aa uxth r2, r5 + 8003012: 2102 movs r1, #2 + 8003014: 2015 movs r0, #21 + 8003016: f7ff fd92 bl 8002b3e + + rv = ae_read1(); + 800301a: f7ff fd07 bl 8002a2c + RET_IF_BAD(rv); + 800301e: 4604 mov r4, r0 + 8003020: bba0 cbnz r0, 800308c + ae_send_idle(); + 8003022: f7ff fc50 bl 80028c6 + // msg = hkey + b'\x15\x02' + ustruct.pack(" + + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 800302c: 2302 movs r3, #2 + 800302e: f88d 3005 strb.w r3, [sp, #5] + 8003032: 23ee movs r3, #238 ; 0xee + 8003034: f88d 3008 strb.w r3, [sp, #8] + 8003038: 2301 movs r3, #1 + 800303a: 2215 movs r2, #21 + 800303c: f88d 3009 strb.w r3, [sp, #9] + uint8_t zeros[25] = { 0 }; + 8003040: 4621 mov r1, r4 + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 8003042: 2323 movs r3, #35 ; 0x23 + uint8_t zeros[25] = { 0 }; + 8003044: a809 add r0, sp, #36 ; 0x24 + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 8003046: f88d 300a strb.w r3, [sp, #10] + 800304a: f88d 2004 strb.w r2, [sp, #4] + 800304e: f88d 5006 strb.w r5, [sp, #6] + 8003052: f88d 4007 strb.w r4, [sp, #7] + uint8_t zeros[25] = { 0 }; + 8003056: 9408 str r4, [sp, #32] + 8003058: f00a fb0c bl 800d674 + + sha256_update(&ctx, slot_contents, 32); + 800305c: 2220 movs r2, #32 + 800305e: 4639 mov r1, r7 + 8003060: a817 add r0, sp, #92 ; 0x5c + 8003062: f002 fa21 bl 80054a8 + sha256_update(&ctx, args, sizeof(args)); + 8003066: 2207 movs r2, #7 + 8003068: a901 add r1, sp, #4 + 800306a: a817 add r0, sp, #92 ; 0x5c + 800306c: f002 fa1c bl 80054a8 + sha256_update(&ctx, zeros, sizeof(zeros)); + 8003070: 2219 movs r2, #25 + 8003072: a908 add r1, sp, #32 + 8003074: a817 add r0, sp, #92 ; 0x5c + 8003076: f002 fa17 bl 80054a8 + sha256_update(&ctx, tempkey, 32); + 800307a: a90f add r1, sp, #60 ; 0x3c + 800307c: a817 add r0, sp, #92 ; 0x5c + 800307e: 2220 movs r2, #32 + 8003080: f002 fa12 bl 80054a8 + + sha256_final(&ctx, digest); + 8003084: 4631 mov r1, r6 + 8003086: a817 add r0, sp, #92 ; 0x5c + 8003088: f002 fa54 bl 8005534 + + return 0; +} + 800308c: 4620 mov r0, r4 + 800308e: b02b add sp, #172 ; 0xac + 8003090: bdf0 pop {r4, r5, r6, r7, pc} + ... + +08003094 : +{ + 8003094: b507 push {r0, r1, r2, lr} + 8003096: 4602 mov r2, r0 + int rv = ae_gendig_slot(KEYNUM_pairing, rom_secrets->pairing_secret, randout); + 8003098: 9001 str r0, [sp, #4] + 800309a: 490b ldr r1, [pc, #44] ; (80030c8 ) + 800309c: 2001 movs r0, #1 + 800309e: f7ff ffa7 bl 8002ff0 + if(rv || !ae_is_correct_tempkey(randout)) { + 80030a2: 9a01 ldr r2, [sp, #4] + 80030a4: b108 cbz r0, 80030aa + fatal_mitm(); + 80030a6: f7fd fcd9 bl 8000a5c + if(rv || !ae_is_correct_tempkey(randout)) { + 80030aa: 4610 mov r0, r2 + 80030ac: 9201 str r2, [sp, #4] + 80030ae: f7ff fdaf bl 8002c10 + 80030b2: 2800 cmp r0, #0 + 80030b4: d0f7 beq.n 80030a6 + sha256_single(randout, 32, randout); + 80030b6: 9a01 ldr r2, [sp, #4] + 80030b8: 2120 movs r1, #32 + 80030ba: 4610 mov r0, r2 +} + 80030bc: b003 add sp, #12 + 80030be: f85d eb04 ldr.w lr, [sp], #4 + sha256_single(randout, 32, randout); + 80030c2: f002 ba4b b.w 800555c + 80030c6: bf00 nop + 80030c8: 0801c000 .word 0x0801c000 + +080030cc : +{ + 80030cc: b510 push {r4, lr} + 80030ce: b088 sub sp, #32 + int rv = ae_gendig_slot(keynum, secret, digest); + 80030d0: 466a mov r2, sp + 80030d2: f7ff ff8d bl 8002ff0 + RET_IF_BAD(rv); + 80030d6: 4604 mov r4, r0 + 80030d8: b930 cbnz r0, 80030e8 + if(!ae_is_correct_tempkey(digest)) return -2; + 80030da: 4668 mov r0, sp + 80030dc: f7ff fd98 bl 8002c10 + 80030e0: 2800 cmp r0, #0 + 80030e2: bf08 it eq + 80030e4: f06f 0401 mvneq.w r4, #1 +} + 80030e8: 4620 mov r0, r4 + 80030ea: b008 add sp, #32 + 80030ec: bd10 pop {r4, pc} + +080030ee : +// the digest should be, and ask the chip to do the same. Verify we match +// using MAC command (done elsewhere). +// + int +ae_gendig_counter(int counter_num, const uint32_t expected_value, uint8_t digest[32]) +{ + 80030ee: b5f0 push {r4, r5, r6, r7, lr} + 80030f0: b0ad sub sp, #180 ; 0xb4 + 80030f2: 4605 mov r5, r0 + 80030f4: 9101 str r1, [sp, #4] + uint8_t num_in[20], tempkey[32]; + + rng_buffer(num_in, sizeof(num_in)); + 80030f6: a804 add r0, sp, #16 + 80030f8: 2114 movs r1, #20 +{ + 80030fa: 4616 mov r6, r2 + rng_buffer(num_in, sizeof(num_in)); + 80030fc: f7ff fb28 bl 8002750 + int rv = ae_pick_nonce(num_in, tempkey); + 8003100: a909 add r1, sp, #36 ; 0x24 + 8003102: a804 add r0, sp, #16 + 8003104: f7ff fd50 bl 8002ba8 + RET_IF_BAD(rv); + 8003108: 4604 mov r4, r0 + 800310a: 2800 cmp r0, #0 + 800310c: d148 bne.n 80031a0 + + //using Zone=4="Counter" => "KeyID specifies the monotonic counter ID" + ae_send(OP_GenDig, 0x4, counter_num); + 800310e: b2aa uxth r2, r5 + 8003110: 2104 movs r1, #4 + 8003112: 2015 movs r0, #21 + 8003114: f7ff fd13 bl 8002b3e + + rv = ae_read1(); + 8003118: f7ff fc88 bl 8002a2c + RET_IF_BAD(rv); + 800311c: 4604 mov r4, r0 + 800311e: 2800 cmp r0, #0 + 8003120: d13e bne.n 80031a0 + ae_send_idle(); + 8003122: f7ff fbd0 bl 80028c6 + // msg = hkey + b'\x15\x02' + ustruct.pack(" + + uint8_t zeros[32] = { 0 }; + 800312c: 221c movs r2, #28 + 800312e: 4621 mov r1, r4 + 8003130: a812 add r0, sp, #72 ; 0x48 + 8003132: 9411 str r4, [sp, #68] ; 0x44 + 8003134: f00a fa9e bl 800d674 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 8003138: 2315 movs r3, #21 + 800313a: f88d 3008 strb.w r3, [sp, #8] + 800313e: 23ee movs r3, #238 ; 0xee + 8003140: f88d 300c strb.w r3, [sp, #12] + 8003144: 2301 movs r3, #1 + 8003146: 2704 movs r7, #4 + 8003148: f88d 300d strb.w r3, [sp, #13] + + sha256_update(&ctx, zeros, 32); + 800314c: 2220 movs r2, #32 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 800314e: 2323 movs r3, #35 ; 0x23 + sha256_update(&ctx, zeros, 32); + 8003150: a911 add r1, sp, #68 ; 0x44 + 8003152: a819 add r0, sp, #100 ; 0x64 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 8003154: f88d 300e strb.w r3, [sp, #14] + 8003158: f88d 7009 strb.w r7, [sp, #9] + 800315c: f88d 500a strb.w r5, [sp, #10] + 8003160: f88d 400b strb.w r4, [sp, #11] + 8003164: f88d 400f strb.w r4, [sp, #15] + sha256_update(&ctx, zeros, 32); + 8003168: f002 f99e bl 80054a8 + sha256_update(&ctx, args, sizeof(args)); + 800316c: 2208 movs r2, #8 + 800316e: eb0d 0102 add.w r1, sp, r2 + 8003172: a819 add r0, sp, #100 ; 0x64 + 8003174: f002 f998 bl 80054a8 + sha256_update(&ctx, (const uint8_t *)&expected_value, 4); + 8003178: 463a mov r2, r7 + 800317a: eb0d 0107 add.w r1, sp, r7 + 800317e: a819 add r0, sp, #100 ; 0x64 + 8003180: f002 f992 bl 80054a8 + sha256_update(&ctx, zeros, 20); + 8003184: 2214 movs r2, #20 + 8003186: a911 add r1, sp, #68 ; 0x44 + 8003188: a819 add r0, sp, #100 ; 0x64 + 800318a: f002 f98d bl 80054a8 + sha256_update(&ctx, tempkey, 32); + 800318e: a909 add r1, sp, #36 ; 0x24 + 8003190: a819 add r0, sp, #100 ; 0x64 + 8003192: 2220 movs r2, #32 + 8003194: f002 f988 bl 80054a8 + + sha256_final(&ctx, digest); + 8003198: 4631 mov r1, r6 + 800319a: a819 add r0, sp, #100 ; 0x64 + 800319c: f002 f9ca bl 8005534 + + return 0; +} + 80031a0: 4620 mov r0, r4 + 80031a2: b02d add sp, #180 ; 0xb4 + 80031a4: bdf0 pop {r4, r5, r6, r7, pc} + +080031a6 : +{ + 80031a6: b570 push {r4, r5, r6, lr} + ae_send(OP_Counter, 0x0, counter_number); + 80031a8: 460a mov r2, r1 +{ + 80031aa: b088 sub sp, #32 + 80031ac: 4606 mov r6, r0 + 80031ae: 460d mov r5, r1 + ae_send(OP_Counter, 0x0, counter_number); + 80031b0: 2024 movs r0, #36 ; 0x24 + 80031b2: 2100 movs r1, #0 + 80031b4: f7ff fcc3 bl 8002b3e + int rv = ae_read_n(4, (uint8_t *)result); + 80031b8: 4631 mov r1, r6 + 80031ba: 2004 movs r0, #4 + 80031bc: f7ff fc52 bl 8002a64 + RET_IF_BAD(rv); + 80031c0: 4604 mov r4, r0 + 80031c2: b960 cbnz r0, 80031de + rv = ae_gendig_counter(counter_number, *result, digest); + 80031c4: 6831 ldr r1, [r6, #0] + 80031c6: 466a mov r2, sp + 80031c8: 4628 mov r0, r5 + 80031ca: f7ff ff90 bl 80030ee + RET_IF_BAD(rv); + 80031ce: 4604 mov r4, r0 + 80031d0: b928 cbnz r0, 80031de + if(!ae_is_correct_tempkey(digest)) { + 80031d2: 4668 mov r0, sp + 80031d4: f7ff fd1c bl 8002c10 + 80031d8: b908 cbnz r0, 80031de + fatal_mitm(); + 80031da: f7fd fc3f bl 8000a5c +} + 80031de: 4620 mov r0, r4 + 80031e0: b008 add sp, #32 + 80031e2: bd70 pop {r4, r5, r6, pc} + +080031e4 : +{ + 80031e4: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 80031e8: 4606 mov r6, r0 + 80031ea: b089 sub sp, #36 ; 0x24 + 80031ec: 460d mov r5, r1 + 80031ee: 4617 mov r7, r2 + for(int i=0; i + int rv = ae_gendig_counter(counter_number, *result, digest); + 80031fc: 6831 ldr r1, [r6, #0] + 80031fe: 466a mov r2, sp + 8003200: 4628 mov r0, r5 + 8003202: f7ff ff74 bl 80030ee + RET_IF_BAD(rv); + 8003206: 4604 mov r4, r0 + 8003208: b998 cbnz r0, 8003232 + if(!ae_is_correct_tempkey(digest)) { + 800320a: 4668 mov r0, sp + 800320c: f7ff fd00 bl 8002c10 + 8003210: b978 cbnz r0, 8003232 + fatal_mitm(); + 8003212: f7fd fc23 bl 8000a5c + ae_send(OP_Counter, 0x1, counter_number); + 8003216: 464a mov r2, r9 + 8003218: 2101 movs r1, #1 + 800321a: 2024 movs r0, #36 ; 0x24 + 800321c: f7ff fc8f bl 8002b3e + int rv = ae_read_n(4, (uint8_t *)result); + 8003220: 4631 mov r1, r6 + 8003222: 2004 movs r0, #4 + 8003224: f7ff fc1e bl 8002a64 + RET_IF_BAD(rv); + 8003228: 4604 mov r4, r0 + 800322a: b910 cbnz r0, 8003232 + for(int i=0; i +} + 8003232: 4620 mov r0, r4 + 8003234: b009 add sp, #36 ; 0x24 + 8003236: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + +0800323a : +// ae_encrypted_read32() +// + int +ae_encrypted_read32(int data_slot, int blk, + int read_kn, const uint8_t read_key[32], uint8_t data[32]) +{ + 800323a: b5f0 push {r4, r5, r6, r7, lr} + 800323c: b08b sub sp, #44 ; 0x2c + 800323e: 4617 mov r7, r2 + 8003240: 460e mov r6, r1 + 8003242: 9d10 ldr r5, [sp, #64] ; 0x40 + 8003244: 9301 str r3, [sp, #4] + 8003246: 4604 mov r4, r0 + uint8_t digest[32]; + + ae_pair_unlock(); + 8003248: f7ff fdb2 bl 8002db0 + + int rv = ae_gendig_slot(read_kn, read_key, digest); + 800324c: 9901 ldr r1, [sp, #4] + 800324e: aa02 add r2, sp, #8 + 8003250: 4638 mov r0, r7 + 8003252: f7ff fecd bl 8002ff0 + RET_IF_BAD(rv); + 8003256: b9c0 cbnz r0, 800328a + + // read nth 32-byte "block" + ae_send(OP_Read, 0x82, (blk << 8) | (data_slot<<3)); + 8003258: 00e4 lsls r4, r4, #3 + 800325a: ea44 2206 orr.w r2, r4, r6, lsl #8 + 800325e: 2182 movs r1, #130 ; 0x82 + 8003260: 2002 movs r0, #2 + 8003262: b292 uxth r2, r2 + 8003264: f7ff fc6b bl 8002b3e + + rv = ae_read_n(32, data); + 8003268: 4629 mov r1, r5 + 800326a: 2020 movs r0, #32 + 800326c: f7ff fbfa bl 8002a64 + RET_IF_BAD(rv); + 8003270: b958 cbnz r0, 800328a + 8003272: 1e6a subs r2, r5, #1 + 8003274: ab02 add r3, sp, #8 + 8003276: 351f adds r5, #31 + *(acc) ^= *(more); + 8003278: f812 1f01 ldrb.w r1, [r2, #1]! + 800327c: f813 4b01 ldrb.w r4, [r3], #1 + for(; len; len--, more++, acc++) { + 8003280: 4295 cmp r5, r2 + *(acc) ^= *(more); + 8003282: ea81 0104 eor.w r1, r1, r4 + 8003286: 7011 strb r1, [r2, #0] + for(; len; len--, more++, acc++) { + 8003288: d1f6 bne.n 8003278 + + xor_mixin(data, digest, 32); + + return 0; +} + 800328a: b00b add sp, #44 ; 0x2c + 800328c: bdf0 pop {r4, r5, r6, r7, pc} + ... + +08003290 : + +// ae_encrypted_read() +// + int +ae_encrypted_read(int data_slot, int read_kn, const uint8_t read_key[32], uint8_t *data, int len) +{ + 8003290: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8003294: b08b sub sp, #44 ; 0x2c + 8003296: 4607 mov r7, r0 + 8003298: 9d12 ldr r5, [sp, #72] ; 0x48 + // not clear if chip supports 4-byte encrypted reads + ASSERT((len == 32) || (len == 72)); + 800329a: 2d20 cmp r5, #32 +{ + 800329c: 4688 mov r8, r1 + 800329e: 4691 mov r9, r2 + 80032a0: 461e mov r6, r3 + ASSERT((len == 32) || (len == 72)); + 80032a2: d004 beq.n 80032ae + 80032a4: 2d48 cmp r5, #72 ; 0x48 + 80032a6: d002 beq.n 80032ae + 80032a8: 4815 ldr r0, [pc, #84] ; (8003300 ) + 80032aa: f7fd fbcd bl 8000a48 + + int rv = ae_encrypted_read32(data_slot, 0, read_kn, read_key, data); + 80032ae: 9600 str r6, [sp, #0] + 80032b0: 464b mov r3, r9 + 80032b2: 4642 mov r2, r8 + 80032b4: 2100 movs r1, #0 + 80032b6: 4638 mov r0, r7 + 80032b8: f7ff ffbf bl 800323a + RET_IF_BAD(rv); + 80032bc: 4604 mov r4, r0 + 80032be: b9d0 cbnz r0, 80032f6 + + if(len == 32) return 0; + 80032c0: 2d20 cmp r5, #32 + 80032c2: d018 beq.n 80032f6 + + rv = ae_encrypted_read32(data_slot, 1, read_kn, read_key, data+32); + 80032c4: f106 0320 add.w r3, r6, #32 + 80032c8: 9300 str r3, [sp, #0] + 80032ca: 4642 mov r2, r8 + 80032cc: 464b mov r3, r9 + 80032ce: 2101 movs r1, #1 + 80032d0: 4638 mov r0, r7 + 80032d2: f7ff ffb2 bl 800323a + RET_IF_BAD(rv); + 80032d6: 4604 mov r4, r0 + 80032d8: b968 cbnz r0, 80032f6 + + uint8_t tmp[32]; + rv = ae_encrypted_read32(data_slot, 2, read_kn, read_key, tmp); + 80032da: ad02 add r5, sp, #8 + 80032dc: 9500 str r5, [sp, #0] + 80032de: 464b mov r3, r9 + 80032e0: 4642 mov r2, r8 + 80032e2: 2102 movs r1, #2 + 80032e4: 4638 mov r0, r7 + 80032e6: f7ff ffa8 bl 800323a + RET_IF_BAD(rv); + 80032ea: 4604 mov r4, r0 + 80032ec: b918 cbnz r0, 80032f6 + + memcpy(data+64, tmp, 72-64); + 80032ee: 462a mov r2, r5 + 80032f0: ca03 ldmia r2!, {r0, r1} + 80032f2: 6430 str r0, [r6, #64] ; 0x40 + 80032f4: 6471 str r1, [r6, #68] ; 0x44 + + return 0; +} + 80032f6: 4620 mov r0, r4 + 80032f8: b00b add sp, #44 ; 0x2c + 80032fa: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 80032fe: bf00 nop + 8003300: 0800e3e0 .word 0x0800e3e0 + +08003304 : +// ae_encrypted_write() +// + int +ae_encrypted_write32(int data_slot, int blk, int write_kn, + const uint8_t write_key[32], const uint8_t data[32]) +{ + 8003304: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8003308: b0b8 sub sp, #224 ; 0xe0 + 800330a: 4617 mov r7, r2 + 800330c: 460d mov r5, r1 + 800330e: 9e3e ldr r6, [sp, #248] ; 0xf8 + 8003310: 9303 str r3, [sp, #12] + 8003312: 4604 mov r4, r0 + uint8_t digest[32]; + + ae_pair_unlock(); + 8003314: f7ff fd4c bl 8002db0 + + // generate a hash over shared secret and rng + int rv = ae_gendig_slot(write_kn, write_key, digest); + 8003318: 9903 ldr r1, [sp, #12] + 800331a: aa0d add r2, sp, #52 ; 0x34 + 800331c: 4638 mov r0, r7 + 800331e: f7ff fe67 bl 8002ff0 + RET_IF_BAD(rv); + 8003322: 2800 cmp r0, #0 + 8003324: d151 bne.n 80033ca + 8003326: 1e72 subs r2, r6, #1 + 8003328: af0d add r7, sp, #52 ; 0x34 + 800332a: a915 add r1, sp, #84 ; 0x54 + 800332c: f106 0c1f add.w ip, r6, #31 + + // encrypt the data to be written, and append an authenticating MAC + uint8_t body[32 + 32]; + + for(int i=0; i<32; i++) { + body[i] = data[i] ^ digest[i]; + 8003330: f812 ef01 ldrb.w lr, [r2, #1]! + 8003334: f817 0b01 ldrb.w r0, [r7], #1 + for(int i=0; i<32; i++) { + 8003338: 4562 cmp r2, ip + body[i] = data[i] ^ digest[i]; + 800333a: ea80 000e eor.w r0, r0, lr + 800333e: f801 0b01 strb.w r0, [r1], #1 + for(int i=0; i<32; i++) { + 8003342: d1f5 bne.n 8003330 + // + (b'\0'*25) + // + new_value) + // assert len(msg) == 32+1+1+2+1+2+25+32 + // + SHA256_CTX ctx; + sha256_init(&ctx); + 8003344: a825 add r0, sp, #148 ; 0x94 + 8003346: f002 f8a1 bl 800548c + + uint8_t p1 = 0x80|2; // 32 bytes into a data slot + uint8_t p2_lsb = (data_slot << 3); + uint8_t p2_msb = blk; + + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 800334a: 22ee movs r2, #238 ; 0xee + 800334c: f88d 2014 strb.w r2, [sp, #20] + 8003350: 2201 movs r2, #1 + 8003352: f88d 2015 strb.w r2, [sp, #21] + uint8_t p2_lsb = (data_slot << 3); + 8003356: 00e4 lsls r4, r4, #3 + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 8003358: 2223 movs r2, #35 ; 0x23 + uint8_t zeros[25] = { 0 }; + 800335a: 2100 movs r1, #0 + uint8_t p2_lsb = (data_slot << 3); + 800335c: b2e4 uxtb r4, r4 + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 800335e: 2712 movs r7, #18 + 8003360: f04f 0882 mov.w r8, #130 ; 0x82 + 8003364: f88d 2016 strb.w r2, [sp, #22] + uint8_t zeros[25] = { 0 }; + 8003368: a807 add r0, sp, #28 + 800336a: 2215 movs r2, #21 + 800336c: 9106 str r1, [sp, #24] + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 800336e: f88d 7010 strb.w r7, [sp, #16] + 8003372: f88d 8011 strb.w r8, [sp, #17] + 8003376: f88d 4012 strb.w r4, [sp, #18] + uint8_t p2_msb = blk; + 800337a: f88d 5013 strb.w r5, [sp, #19] + uint8_t zeros[25] = { 0 }; + 800337e: f00a f979 bl 800d674 + + sha256_update(&ctx, digest, 32); + 8003382: 2220 movs r2, #32 + 8003384: a90d add r1, sp, #52 ; 0x34 + 8003386: a825 add r0, sp, #148 ; 0x94 + 8003388: f002 f88e bl 80054a8 + sha256_update(&ctx, args, sizeof(args)); + 800338c: 2207 movs r2, #7 + 800338e: a904 add r1, sp, #16 + 8003390: a825 add r0, sp, #148 ; 0x94 + 8003392: f002 f889 bl 80054a8 + sha256_update(&ctx, zeros, sizeof(zeros)); + 8003396: 2219 movs r2, #25 + 8003398: a906 add r1, sp, #24 + 800339a: a825 add r0, sp, #148 ; 0x94 + 800339c: f002 f884 bl 80054a8 + sha256_update(&ctx, data, 32); + 80033a0: 2220 movs r2, #32 + 80033a2: 4631 mov r1, r6 + 80033a4: a825 add r0, sp, #148 ; 0x94 + 80033a6: f002 f87f bl 80054a8 + + sha256_final(&ctx, &body[32]); + 80033aa: a91d add r1, sp, #116 ; 0x74 + 80033ac: a825 add r0, sp, #148 ; 0x94 + 80033ae: f002 f8c1 bl 8005534 + + ae_send_n(OP_Write, p1, (p2_msb << 8) | p2_lsb, body, sizeof(body)); + 80033b2: 2140 movs r1, #64 ; 0x40 + 80033b4: ea44 2205 orr.w r2, r4, r5, lsl #8 + 80033b8: b292 uxth r2, r2 + 80033ba: 9100 str r1, [sp, #0] + 80033bc: ab15 add r3, sp, #84 ; 0x54 + 80033be: 4641 mov r1, r8 + 80033c0: 4638 mov r0, r7 + 80033c2: f7ff fb89 bl 8002ad8 + + return ae_read1(); + 80033c6: f7ff fb31 bl 8002a2c +} + 80033ca: b038 add sp, #224 ; 0xe0 + 80033cc: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +080033d0 : +// ae_encrypted_write() +// + int +ae_encrypted_write(int data_slot, int write_kn, const uint8_t write_key[32], + const uint8_t *data, int len) +{ + 80033d0: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80033d4: b08a sub sp, #40 ; 0x28 + ASSERT(data_slot >= 0); + ASSERT(data_slot <= 15); + 80033d6: 280f cmp r0, #15 +{ + 80033d8: 9d12 ldr r5, [sp, #72] ; 0x48 + 80033da: 4606 mov r6, r0 + 80033dc: 460f mov r7, r1 + 80033de: 4690 mov r8, r2 + 80033e0: 4699 mov r9, r3 + ASSERT(data_slot <= 15); + 80033e2: d902 bls.n 80033ea + ASSERT(data_slot >= 0); + 80033e4: 4814 ldr r0, [pc, #80] ; (8003438 ) + 80033e6: f7fd fb2f bl 8000a48 + + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 80033ea: 2400 movs r4, #0 + int here = MIN(32, len); + + // be nice and don't read past end of input buffer + uint8_t tmp[32] = { 0 }; + 80033ec: 46a2 mov sl, r4 + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 80033ee: 2d00 cmp r5, #0 + 80033f0: dd1d ble.n 800342e + uint8_t tmp[32] = { 0 }; + 80033f2: 221c movs r2, #28 + 80033f4: 2100 movs r1, #0 + 80033f6: a803 add r0, sp, #12 + 80033f8: f8cd a008 str.w sl, [sp, #8] + 80033fc: f00a f93a bl 800d674 + memcpy(tmp, data+(32*blk), here); + 8003400: ab02 add r3, sp, #8 + 8003402: 2d20 cmp r5, #32 + 8003404: 462a mov r2, r5 + 8003406: eb09 1144 add.w r1, r9, r4, lsl #5 + 800340a: bfa8 it ge + 800340c: 2220 movge r2, #32 + 800340e: 4618 mov r0, r3 + 8003410: f00a f908 bl 800d624 + + int rv = ae_encrypted_write32(data_slot, blk, write_kn, write_key, tmp); + 8003414: 4643 mov r3, r8 + 8003416: 9000 str r0, [sp, #0] + 8003418: 463a mov r2, r7 + 800341a: 4621 mov r1, r4 + 800341c: 4630 mov r0, r6 + 800341e: f7ff ff71 bl 8003304 + RET_IF_BAD(rv); + 8003422: b928 cbnz r0, 8003430 + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 8003424: 3401 adds r4, #1 + 8003426: 2c03 cmp r4, #3 + 8003428: f1a5 0520 sub.w r5, r5, #32 + 800342c: d1df bne.n 80033ee + } + + return 0; + 800342e: 2000 movs r0, #0 +} + 8003430: b00a add sp, #40 ; 0x28 + 8003432: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 8003436: bf00 nop + 8003438: 0800e3e0 .word 0x0800e3e0 + +0800343c : + +// ae_read_data_slot() +// + int +ae_read_data_slot(int slot_num, uint8_t *data, int len) +{ + 800343c: b570 push {r4, r5, r6, lr} + ASSERT((len == 4) || (len == 32) || (len == 72)); + 800343e: 2a04 cmp r2, #4 +{ + 8003440: b088 sub sp, #32 + 8003442: 460d mov r5, r1 + 8003444: 4616 mov r6, r2 + ASSERT((len == 4) || (len == 32) || (len == 72)); + 8003446: d006 beq.n 8003456 + 8003448: 2a20 cmp r2, #32 + 800344a: d038 beq.n 80034be + 800344c: 2a48 cmp r2, #72 ; 0x48 + 800344e: d036 beq.n 80034be + 8003450: 481c ldr r0, [pc, #112] ; (80034c4 ) + 8003452: f7fd faf9 bl 8000a48 + + // zone => data + // only reading first block of 32 bytes. ignore the rest + ae_send(OP_Read, (len == 4 ? 0x00 : 0x80) | 2, (slot_num<<3)); + 8003456: 2102 movs r1, #2 + 8003458: 00c4 lsls r4, r0, #3 + 800345a: b2a2 uxth r2, r4 + 800345c: 2002 movs r0, #2 + 800345e: f7ff fb6e bl 8002b3e + + int rv = ae_read_n((len == 4) ? 4 : 32, data); + 8003462: 2e04 cmp r6, #4 + 8003464: 4629 mov r1, r5 + 8003466: bf0c ite eq + 8003468: 2004 moveq r0, #4 + 800346a: 2020 movne r0, #32 + 800346c: f7ff fafa bl 8002a64 + RET_IF_BAD(rv); + 8003470: 4603 mov r3, r0 + 8003472: bb08 cbnz r0, 80034b8 + + if(len == 72) { + 8003474: 2e48 cmp r6, #72 ; 0x48 + 8003476: d11f bne.n 80034b8 + // read second block + ae_send(OP_Read, 0x82, (1<<8) | (slot_num<<3)); + 8003478: b224 sxth r4, r4 + 800347a: f444 7280 orr.w r2, r4, #256 ; 0x100 + 800347e: b292 uxth r2, r2 + 8003480: 2182 movs r1, #130 ; 0x82 + 8003482: 2002 movs r0, #2 + 8003484: f7ff fb5b bl 8002b3e + + int rv = ae_read_n(32, data+32); + 8003488: f105 0120 add.w r1, r5, #32 + 800348c: 2020 movs r0, #32 + 800348e: f7ff fae9 bl 8002a64 + RET_IF_BAD(rv); + 8003492: 4603 mov r3, r0 + 8003494: b980 cbnz r0, 80034b8 + + // read third block, but only using part of it + uint8_t tmp[32]; + ae_send(OP_Read, 0x82, (2<<8) | (slot_num<<3)); + 8003496: f444 7400 orr.w r4, r4, #512 ; 0x200 + 800349a: b2a2 uxth r2, r4 + 800349c: 2182 movs r1, #130 ; 0x82 + 800349e: 2002 movs r0, #2 + 80034a0: f7ff fb4d bl 8002b3e + + rv = ae_read_n(32, tmp); + 80034a4: 4669 mov r1, sp + 80034a6: 2020 movs r0, #32 + 80034a8: f7ff fadc bl 8002a64 + RET_IF_BAD(rv); + 80034ac: 4603 mov r3, r0 + 80034ae: b918 cbnz r0, 80034b8 + + memcpy(data+64, tmp, 72-64); + 80034b0: 466a mov r2, sp + 80034b2: ca03 ldmia r2!, {r0, r1} + 80034b4: 6428 str r0, [r5, #64] ; 0x40 + 80034b6: 6469 str r1, [r5, #68] ; 0x44 + } + + return 0; +} + 80034b8: 4618 mov r0, r3 + 80034ba: b008 add sp, #32 + 80034bc: bd70 pop {r4, r5, r6, pc} + ae_send(OP_Read, (len == 4 ? 0x00 : 0x80) | 2, (slot_num<<3)); + 80034be: 2182 movs r1, #130 ; 0x82 + 80034c0: e7ca b.n 8003458 + 80034c2: bf00 nop + 80034c4: 0800e3e0 .word 0x0800e3e0 + +080034c8 : + +// ae_set_gpio() +// + int +ae_set_gpio(int state) +{ + 80034c8: b513 push {r0, r1, r4, lr} + // 1=turn on green, 0=red light (if not yet configured to be secure) + ae_send(OP_Info, 3, 2 | (!!state)); + 80034ca: 1e04 subs r4, r0, #0 + 80034cc: bf14 ite ne + 80034ce: 2203 movne r2, #3 + 80034d0: 2202 moveq r2, #2 + 80034d2: 2103 movs r1, #3 + 80034d4: 2030 movs r0, #48 ; 0x30 + 80034d6: f7ff fb32 bl 8002b3e + + // "Always return the current state in the first byte followed by three bytes of 0x00" + // - simple 1/0, in LSB. + uint8_t resp[4]; + + int rv = ae_read_n(4, resp); + 80034da: a901 add r1, sp, #4 + 80034dc: 2004 movs r0, #4 + 80034de: f7ff fac1 bl 8002a64 + RET_IF_BAD(rv); + 80034e2: b928 cbnz r0, 80034f0 + + return (resp[0] != state) ? -1 : 0; + 80034e4: f89d 0004 ldrb.w r0, [sp, #4] + 80034e8: 1b00 subs r0, r0, r4 + 80034ea: bf18 it ne + 80034ec: f04f 30ff movne.w r0, #4294967295 ; 0xffffffff +} + 80034f0: b002 add sp, #8 + 80034f2: bd10 pop {r4, pc} + +080034f4 : +// +// Set the GPIO using secure hash generated somehow already. +// + int +ae_set_gpio_secure(uint8_t digest[32]) +{ + 80034f4: b538 push {r3, r4, r5, lr} + 80034f6: 4605 mov r5, r0 + ae_pair_unlock(); + 80034f8: f7ff fc5a bl 8002db0 + ae_checkmac(KEYNUM_firmware, digest); + 80034fc: 4629 mov r1, r5 + 80034fe: 200e movs r0, #14 + 8003500: f7ff fbd4 bl 8002cac + + int rv = ae_set_gpio(1); + 8003504: 2001 movs r0, #1 + 8003506: f7ff ffdf bl 80034c8 + + if(rv == 0) { + 800350a: 4604 mov r4, r0 + 800350c: b940 cbnz r0, 8003520 + // trust that readback, and so do a verify that the chip has + // the digest we think it does. If MitM wanted to turn off the output, + // they can do that anytime regardless. We just don't want them to be + // able to fake it being set, and therefore bypass the + // "unsigned firmware" delay and warning. + ae_pair_unlock(); + 800350e: f7ff fc4f bl 8002db0 + + if(ae_checkmac_hard(KEYNUM_firmware, digest) != 0) { + 8003512: 4629 mov r1, r5 + 8003514: 200e movs r0, #14 + 8003516: f7ff fdd9 bl 80030cc + 800351a: b108 cbz r0, 8003520 + fatal_mitm(); + 800351c: f7fd fa9e bl 8000a5c + } + } + + return rv; +} + 8003520: 4620 mov r0, r4 + 8003522: bd38 pop {r3, r4, r5, pc} + +08003524 : +// +// IMPORTANT: do not trust this result, could be MitM'ed. +// + uint8_t +ae_get_gpio(void) +{ + 8003524: b507 push {r0, r1, r2, lr} + // not doing error checking here + ae_send(OP_Info, 0x3, 0); + 8003526: 2200 movs r2, #0 + 8003528: 2103 movs r1, #3 + 800352a: 2030 movs r0, #48 ; 0x30 + 800352c: f7ff fb07 bl 8002b3e + + // note: always returns 4 bytes, but most are garbage and unused. + uint8_t tmp[4]; + ae_read_n(4, tmp); + 8003530: a901 add r1, sp, #4 + 8003532: 2004 movs r0, #4 + 8003534: f7ff fa96 bl 8002a64 + + return tmp[0]; +} + 8003538: f89d 0004 ldrb.w r0, [sp, #4] + 800353c: b003 add sp, #12 + 800353e: f85d fb04 ldr.w pc, [sp], #4 + +08003542 : +// +// Read a 4-byte area from config area, or -1 if fail. +// + int +ae_read_config_word(int offset, uint8_t *dest) +{ + 8003542: b510 push {r4, lr} + offset &= 0x7f; + + // read 32 bits (aligned) + ae_send(OP_Read, 0x00, offset/4); + 8003544: f3c0 0284 ubfx r2, r0, #2, #5 +{ + 8003548: 460c mov r4, r1 + ae_send(OP_Read, 0x00, offset/4); + 800354a: 2002 movs r0, #2 + 800354c: 2100 movs r1, #0 + 800354e: f7ff faf6 bl 8002b3e + + int rv = ae_read_n(4, dest); + 8003552: 4621 mov r1, r4 + 8003554: 2004 movs r0, #4 + 8003556: f7ff fa85 bl 8002a64 + if(rv) return -1; + 800355a: 3800 subs r0, #0 + 800355c: bf18 it ne + 800355e: 2001 movne r0, #1 + + return 0; +} + 8003560: 4240 negs r0, r0 + 8003562: bd10 pop {r4, pc} + +08003564 : +{ + 8003564: b513 push {r0, r1, r4, lr} + 8003566: 4604 mov r4, r0 + ae_read_config_word(offset, tmp); + 8003568: a901 add r1, sp, #4 + 800356a: f7ff ffea bl 8003542 + return tmp[offset % 4]; + 800356e: 4263 negs r3, r4 + 8003570: f003 0303 and.w r3, r3, #3 + 8003574: f004 0403 and.w r4, r4, #3 + 8003578: bf58 it pl + 800357a: 425c negpl r4, r3 + 800357c: f104 0308 add.w r3, r4, #8 + 8003580: eb0d 0403 add.w r4, sp, r3 +} + 8003584: f814 0c04 ldrb.w r0, [r4, #-4] + 8003588: b002 add sp, #8 + 800358a: bd10 pop {r4, pc} + +0800358c : + +// ae_destroy_key() +// + int +ae_destroy_key(int keynum) +{ + 800358c: b510 push {r4, lr} + 800358e: b090 sub sp, #64 ; 0x40 + uint8_t numin[20]; + + // Load tempkey with a known (random) nonce value + rng_buffer(numin, sizeof(numin)); + 8003590: 2114 movs r1, #20 +{ + 8003592: 4604 mov r4, r0 + rng_buffer(numin, sizeof(numin)); + 8003594: a803 add r0, sp, #12 + 8003596: f7ff f8db bl 8002750 + ae_send_n(OP_Nonce, 0, 0, numin, 20); + 800359a: 2314 movs r3, #20 + 800359c: 2200 movs r2, #0 + 800359e: 9300 str r3, [sp, #0] + 80035a0: 4611 mov r1, r2 + 80035a2: 2016 movs r0, #22 + 80035a4: ab03 add r3, sp, #12 + 80035a6: f7ff fa97 bl 8002ad8 + + // Nonce command returns the RNG result, not contents of TempKey, + // but since we are destroying, no need to calculate what it is. + uint8_t randout[32]; + int rv = ae_read_n(32, randout); + 80035aa: a908 add r1, sp, #32 + 80035ac: 2020 movs r0, #32 + 80035ae: f7ff fa59 bl 8002a64 + RET_IF_BAD(rv); + 80035b2: b930 cbnz r0, 80035c2 + + // do a "DeriveKey" operation, based on that! + ae_send(OP_DeriveKey, 0x00, keynum); + 80035b4: 4601 mov r1, r0 + 80035b6: b2a2 uxth r2, r4 + 80035b8: 201c movs r0, #28 + 80035ba: f7ff fac0 bl 8002b3e + + return ae_read1(); + 80035be: f7ff fa35 bl 8002a2c +} + 80035c2: b010 add sp, #64 ; 0x40 + 80035c4: bd10 pop {r4, pc} + +080035c6 : + +// ae_config_read() +// + int +ae_config_read(uint8_t config[128]) +{ + 80035c6: b538 push {r3, r4, r5, lr} + 80035c8: 4605 mov r5, r0 + for(int blk=0; blk<4; blk++) { + 80035ca: 2400 movs r4, #0 + // read 32 bytes (aligned) from config "zone" + ae_send(OP_Read, 0x80, blk<<3); + 80035cc: 00e2 lsls r2, r4, #3 + 80035ce: 2180 movs r1, #128 ; 0x80 + 80035d0: 2002 movs r0, #2 + 80035d2: b292 uxth r2, r2 + 80035d4: f7ff fab3 bl 8002b3e + + int rv = ae_read_n(32, &config[32*blk]); + 80035d8: eb05 1144 add.w r1, r5, r4, lsl #5 + 80035dc: 2020 movs r0, #32 + 80035de: f7ff fa41 bl 8002a64 + if(rv) return EIO; + 80035e2: b918 cbnz r0, 80035ec + for(int blk=0; blk<4; blk++) { + 80035e4: 3401 adds r4, #1 + 80035e6: 2c04 cmp r4, #4 + 80035e8: d1f0 bne.n 80035cc + } + + return 0; +} + 80035ea: bd38 pop {r3, r4, r5, pc} + if(rv) return EIO; + 80035ec: 2005 movs r0, #5 + 80035ee: e7fc b.n 80035ea + +080035f0 : +// us to write the (existing) pairing secret into, they would see the pairing +// secret in cleartext. They could then restore original chip and access freely. +// + int +ae_setup_config(void) +{ + 80035f0: b5f0 push {r4, r5, r6, r7, lr} + 80035f2: 2405 movs r4, #5 + 80035f4: f5ad 7d41 sub.w sp, sp, #772 ; 0x304 + // Need to wake up AE, since many things happen before this point. + for(int retry=0; retry<5; retry++) { + if(!ae_probe()) break; + 80035f8: f7ff fc6c bl 8002ed4 + 80035fc: b108 cbz r0, 8003602 + for(int retry=0; retry<5; retry++) { + 80035fe: 3c01 subs r4, #1 + 8003600: d1fa bne.n 80035f8 + // Is data zone is locked? + // Allow rest of function to happen if it's not. + +#if 1 + // 0x55 = unlocked; 0x00 = locked + bool data_locked = (ae_read_config_byte(86) != 0x55); + 8003602: 2056 movs r0, #86 ; 0x56 + 8003604: f7ff ffae bl 8003564 + if(data_locked) return 0; // basically success + 8003608: 2855 cmp r0, #85 ; 0x55 + 800360a: f040 80df bne.w 80037cc + + // To lock, we need a CRC over whole thing, but we + // only set a few values... plus the serial number is + // in there, so start with some readout. + uint8_t config[128]; + int rv = ae_config_read(config); + 800360e: a838 add r0, sp, #224 ; 0xe0 + 8003610: f7ff ffd9 bl 80035c6 + if(rv) return rv; + 8003614: 4604 mov r4, r0 + 8003616: 2800 cmp r0, #0 + 8003618: f040 80d9 bne.w 80037ce + uint8_t config[128]; + while(ae_config_read(config)) ; +#endif + + // verify some fixed values + ASSERT(config[0] == 0x01); + 800361c: f89d 30e0 ldrb.w r3, [sp, #224] ; 0xe0 + 8003620: 2b01 cmp r3, #1 + 8003622: d002 beq.n 800362a + 8003624: 486f ldr r0, [pc, #444] ; (80037e4 ) + + ae_keep_alive(); + + // lock config zone + if(ae_lock_config_zone(config)) { + INCONSISTENT("conf lock"); + 8003626: f7fd fa0f bl 8000a48 + ASSERT(config[1] == 0x23); + 800362a: f89d 30e1 ldrb.w r3, [sp, #225] ; 0xe1 + 800362e: 2b23 cmp r3, #35 ; 0x23 + 8003630: d1f8 bne.n 8003624 + ASSERT(config[12] == 0xee); + 8003632: f89d 30ec ldrb.w r3, [sp, #236] ; 0xec + 8003636: 2bee cmp r3, #238 ; 0xee + 8003638: d1f4 bne.n 8003624 + int8_t partno = ((config[6]>>4)&0xf); + 800363a: f89d 30e6 ldrb.w r3, [sp, #230] ; 0xe6 + ASSERT(partno == 6); + 800363e: 091b lsrs r3, r3, #4 + 8003640: 2b06 cmp r3, #6 + 8003642: d1ef bne.n 8003624 + memcpy(serial, &config[0], 4); + 8003644: 9b38 ldr r3, [sp, #224] ; 0xe0 + 8003646: 9303 str r3, [sp, #12] + memcpy(&serial[4], &config[8], 5); + 8003648: ab3a add r3, sp, #232 ; 0xe8 + 800364a: e893 0003 ldmia.w r3, {r0, r1} + 800364e: 9004 str r0, [sp, #16] + 8003650: f88d 1014 strb.w r1, [sp, #20] + if(check_all_ones(rom_secrets->ae_serial_number, 9)) { + 8003654: 4864 ldr r0, [pc, #400] ; (80037e8 ) + 8003656: 2109 movs r1, #9 + 8003658: f7ff f812 bl 8002680 + 800365c: b110 cbz r0, 8003664 + flash_save_ae_serial(serial); + 800365e: a803 add r0, sp, #12 + 8003660: f7fe fd66 bl 8002130 + if(!check_equal(rom_secrets->ae_serial_number, serial, 9)) { + 8003664: 4860 ldr r0, [pc, #384] ; (80037e8 ) + 8003666: 2209 movs r2, #9 + 8003668: a903 add r1, sp, #12 + 800366a: f7ff f822 bl 80026b2 + 800366e: 2800 cmp r0, #0 + 8003670: f000 80b6 beq.w 80037e0 + if(config[87] == 0x55) { + 8003674: f89d 3137 ldrb.w r3, [sp, #311] ; 0x137 + 8003678: 2b55 cmp r3, #85 ; 0x55 + 800367a: d12b bne.n 80036d4 + memcpy(&config[16], config_1, sizeof(config_1)); + 800367c: 495b ldr r1, [pc, #364] ; (80037ec ) + 800367e: 2244 movs r2, #68 ; 0x44 + 8003680: a83c add r0, sp, #240 ; 0xf0 + 8003682: f009 ffcf bl 800d624 + memcpy(&config[90], config_2, sizeof(config_2)); + 8003686: 4b5a ldr r3, [pc, #360] ; (80037f0 ) + 8003688: f50d 729d add.w r2, sp, #314 ; 0x13a + 800368c: f103 0124 add.w r1, r3, #36 ; 0x24 + 8003690: f853 0b04 ldr.w r0, [r3], #4 + 8003694: f842 0b04 str.w r0, [r2], #4 + 8003698: 428b cmp r3, r1 + 800369a: d1f9 bne.n 8003690 + 800369c: 881b ldrh r3, [r3, #0] + 800369e: 8013 strh r3, [r2, #0] + for(int n=16; n<128; n+= 4) { + 80036a0: 2510 movs r5, #16 + ae_send_n(OP_Write, 0, n/4, &config[n], 4); + 80036a2: 2604 movs r6, #4 + if(n == 84) continue; // that word not writable + 80036a4: 2d54 cmp r5, #84 ; 0x54 + 80036a6: d130 bne.n 800370a + for(int n=16; n<128; n+= 4) { + 80036a8: 3504 adds r5, #4 + 80036aa: 2d80 cmp r5, #128 ; 0x80 + 80036ac: d1fa bne.n 80036a4 + ae_send_idle(); + 80036ae: f7ff f90a bl 80028c6 + uint8_t crc[2] = {0, 0}; + 80036b2: 2600 movs r6, #0 + crc16_chain(128, config, crc); + 80036b4: aa58 add r2, sp, #352 ; 0x160 + 80036b6: a938 add r1, sp, #224 ; 0xe0 + 80036b8: 4628 mov r0, r5 + uint8_t crc[2] = {0, 0}; + 80036ba: f8ad 6160 strh.w r6, [sp, #352] ; 0x160 + crc16_chain(128, config, crc); + 80036be: f7ff f8b5 bl 800282c + ae_send(OP_Lock, 0x0, (crc[1]<<8) | crc[0]); + 80036c2: f8bd 2160 ldrh.w r2, [sp, #352] ; 0x160 + 80036c6: 4631 mov r1, r6 + 80036c8: 2017 movs r0, #23 + 80036ca: f7ff fa38 bl 8002b3e + return ae_read1(); + 80036ce: f7ff f9ad bl 8002a2c + if(ae_lock_config_zone(config)) { + 80036d2: bb38 cbnz r0, 8003724 + // Load data zone with some known values. + // The datazone still unlocked, so no encryption needed (nor possible). + + // will use zeros for all PIN codes, and customer-defined-secret starting values + uint8_t zeros[72]; + memset(zeros, 0, sizeof(zeros)); + 80036d4: 2248 movs r2, #72 ; 0x48 + 80036d6: 2100 movs r1, #0 + 80036d8: a826 add r0, sp, #152 ; 0x98 + 80036da: f009 ffcb bl 800d674 + se2_save_auth_pubkey(pubkey); + break; + } + + case 0: + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 80036de: 4e45 ldr r6, [pc, #276] ; (80037f4 ) + 80036e0: f8bd 5138 ldrh.w r5, [sp, #312] ; 0x138 + if(ae_write_data_slot(kn, rom_secrets->pairing_secret, 32, false)) { + 80036e4: 4f44 ldr r7, [pc, #272] ; (80037f8 ) + ae_send_idle(); + 80036e6: f7ff f8ee bl 80028c6 + if(!(unlocked & (1< + switch(kn) { + 80036f2: 2c0e cmp r4, #14 + 80036f4: d85c bhi.n 80037b0 + 80036f6: e8df f004 tbb [pc, r4] + 80036fa: 176e .short 0x176e + 80036fc: 29202920 .word 0x29202920 + 8003700: 2d304c3e .word 0x2d304c3e + 8003704: 2d2d2d2d .word 0x2d2d2d2d + 8003708: 29 .byte 0x29 + 8003709: 00 .byte 0x00 + ae_send_n(OP_Write, 0, n/4, &config[n], 4); + 800370a: ab38 add r3, sp, #224 ; 0xe0 + 800370c: 442b add r3, r5 + 800370e: f3c5 028f ubfx r2, r5, #2, #16 + 8003712: 2100 movs r1, #0 + 8003714: 2012 movs r0, #18 + 8003716: 9600 str r6, [sp, #0] + 8003718: f7ff f9de bl 8002ad8 + int rv = ae_read1(); + 800371c: f7ff f986 bl 8002a2c + if(rv) return rv; + 8003720: 2800 cmp r0, #0 + 8003722: d0c1 beq.n 80036a8 + INCONSISTENT("conf lock"); + 8003724: 4835 ldr r0, [pc, #212] ; (80037fc ) + 8003726: e77e b.n 8003626 + if(ae_write_data_slot(kn, rom_secrets->pairing_secret, 32, false)) { + 8003728: 2300 movs r3, #0 + 800372a: 2220 movs r2, #32 + 800372c: 4639 mov r1, r7 + 800372e: 2001 movs r0, #1 + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 8003730: f7ff fbf4 bl 8002f1c + 8003734: 2800 cmp r0, #0 + 8003736: d03b beq.n 80037b0 + 8003738: e7f4 b.n 8003724 + rng_buffer(tmp, sizeof(tmp)); + 800373a: 2120 movs r1, #32 + 800373c: a806 add r0, sp, #24 + 800373e: f7ff f807 bl 8002750 + if(ae_write_data_slot(kn, tmp, 32, true)) { + 8003742: 2301 movs r3, #1 + 8003744: 2220 movs r2, #32 + 8003746: a906 add r1, sp, #24 + if(ae_write_data_slot(kn, zeros, 32, false)) { + 8003748: 4620 mov r0, r4 + 800374a: e7f1 b.n 8003730 + 800374c: 2300 movs r3, #0 + 800374e: 2220 movs r2, #32 + 8003750: a926 add r1, sp, #152 ; 0x98 + 8003752: e7f9 b.n 8003748 + if(ae_write_data_slot(kn, zeros, 72, false)) { + 8003754: 2300 movs r3, #0 + 8003756: 2248 movs r2, #72 ; 0x48 + 8003758: e7fa b.n 8003750 + uint8_t long_zeros[416] = {0}; + 800375a: 2300 movs r3, #0 + 800375c: 4619 mov r1, r3 + 800375e: f44f 72ce mov.w r2, #412 ; 0x19c + 8003762: a859 add r0, sp, #356 ; 0x164 + 8003764: 9358 str r3, [sp, #352] ; 0x160 + 8003766: f009 ff85 bl 800d674 + if(ae_write_data_slot(kn, long_zeros, 416, false)) { + 800376a: 2300 movs r3, #0 + 800376c: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 8003770: a958 add r1, sp, #352 ; 0x160 + 8003772: 2008 movs r0, #8 + 8003774: e7dc b.n 8003730 + uint32_t buf[32/4] = { 1024, 1024 }; + 8003776: 2218 movs r2, #24 + 8003778: 2100 movs r1, #0 + 800377a: a810 add r0, sp, #64 ; 0x40 + 800377c: f009 ff7a bl 800d674 + 8003780: f44f 6380 mov.w r3, #1024 ; 0x400 + 8003784: e9cd 330e strd r3, r3, [sp, #56] ; 0x38 + if(ae_write_data_slot(KEYNUM_match_count, (const uint8_t *)buf,sizeof(buf),false)) { + 8003788: 2220 movs r2, #32 + 800378a: 2300 movs r3, #0 + 800378c: a90e add r1, sp, #56 ; 0x38 + 800378e: 2006 movs r0, #6 + 8003790: e7ce b.n 8003730 + if(ae_checkmac_hard(KEYNUM_main_pin, zeros) != 0) { + 8003792: a926 add r1, sp, #152 ; 0x98 + 8003794: 2003 movs r0, #3 + 8003796: f7ff fc99 bl 80030cc + 800379a: 2800 cmp r0, #0 + 800379c: d1c2 bne.n 8003724 + if(ae_gen_ecc_key(KEYNUM_joiner_key, pubkey)) { + 800379e: a916 add r1, sp, #88 ; 0x58 + 80037a0: 2007 movs r0, #7 + 80037a2: f7ff fb2b bl 8002dfc + 80037a6: 2800 cmp r0, #0 + 80037a8: d1bc bne.n 8003724 + se2_save_auth_pubkey(pubkey); + 80037aa: a816 add r0, sp, #88 ; 0x58 + 80037ac: f004 f932 bl 8007a14 + for(int kn=0; kn<16; kn++) { + 80037b0: 3401 adds r4, #1 + 80037b2: 2c10 cmp r4, #16 + 80037b4: d197 bne.n 80036e6 + ae_send_idle(); + 80037b6: f7ff f886 bl 80028c6 + ae_send(OP_Lock, 0x81, 0x0000); + 80037ba: 2200 movs r2, #0 + 80037bc: 2181 movs r1, #129 ; 0x81 + 80037be: 2017 movs r0, #23 + 80037c0: f7ff f9bd bl 8002b3e + return ae_read1(); + 80037c4: f7ff f932 bl 8002a2c + } + } + + // lock the data zone and effectively enter normal operation. + ae_keep_alive(); + if(ae_lock_data_zone()) { + 80037c8: 2800 cmp r0, #0 + 80037ca: d1ab bne.n 8003724 + if(data_locked) return 0; // basically success + 80037cc: 2400 movs r4, #0 + INCONSISTENT("data lock"); + } + + return 0; +} + 80037ce: 4620 mov r0, r4 + 80037d0: f50d 7d41 add.w sp, sp, #772 ; 0x304 + 80037d4: bdf0 pop {r4, r5, r6, r7, pc} + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 80037d6: 2301 movs r3, #1 + 80037d8: 2220 movs r2, #32 + 80037da: 4631 mov r1, r6 + 80037dc: 2000 movs r0, #0 + 80037de: e7a7 b.n 8003730 + return EPERM; + 80037e0: 2401 movs r4, #1 + 80037e2: e7f4 b.n 80037ce + 80037e4: 0800e3e0 .word 0x0800e3e0 + 80037e8: 0801c040 .word 0x0801c040 + 80037ec: 0800e68a .word 0x0800e68a + 80037f0: 0800e6ce .word 0x0800e6ce + 80037f4: 0800e646 .word 0x0800e646 + 80037f8: 0801c000 .word 0x0801c000 + 80037fc: 0800d700 .word 0x0800d700 + +08003800 : +// - but our time to do each iteration is limited by software SHA256 in ae_pair_unlock +// + int +ae_stretch_iter(const uint8_t start[32], uint8_t end[32], int iterations) +{ + ASSERT(start != end); // we can't work inplace + 8003800: 4288 cmp r0, r1 +{ + 8003802: b570 push {r4, r5, r6, lr} + 8003804: 460c mov r4, r1 + 8003806: 4615 mov r5, r2 + ASSERT(start != end); // we can't work inplace + 8003808: d102 bne.n 8003810 + 800380a: 4810 ldr r0, [pc, #64] ; (800384c ) + 800380c: f7fd f91c bl 8000a48 + memcpy(end, start, 32); + 8003810: 460b mov r3, r1 + 8003812: f100 0220 add.w r2, r0, #32 + 8003816: f850 1b04 ldr.w r1, [r0], #4 + 800381a: f843 1b04 str.w r1, [r3], #4 + 800381e: 4290 cmp r0, r2 + 8003820: d1f9 bne.n 8003816 + + for(int i=0; i + + int rv = ae_hmac32(KEYNUM_pin_stretch, end, end); + RET_IF_BAD(rv); + } + + return 0; + 8003828: 2000 movs r0, #0 +} + 800382a: bd70 pop {r4, r5, r6, pc} + if(ae_pair_unlock()) return -2; + 800382c: f7ff fac0 bl 8002db0 + 8003830: b940 cbnz r0, 8003844 + int rv = ae_hmac32(KEYNUM_pin_stretch, end, end); + 8003832: 4622 mov r2, r4 + 8003834: 4621 mov r1, r4 + 8003836: 2002 movs r0, #2 + 8003838: f7ff fb02 bl 8002e40 + RET_IF_BAD(rv); + 800383c: 2800 cmp r0, #0 + 800383e: d1f4 bne.n 800382a + for(int i=0; i + if(ae_pair_unlock()) return -2; + 8003844: f06f 0001 mvn.w r0, #1 + 8003848: e7ef b.n 800382a + 800384a: bf00 nop + 800384c: 0800e3e0 .word 0x0800e3e0 + +08003850 : +// Apply HMAC using secret in chip as a HMAC key, then encrypt +// the result a little because read in clear over bus. +// + int +ae_mixin_key(uint8_t keynum, const uint8_t start[32], uint8_t end[32]) +{ + 8003850: b570 push {r4, r5, r6, lr} + 8003852: b096 sub sp, #88 ; 0x58 + ASSERT(start != end); // we can't work inplace + 8003854: 4291 cmp r1, r2 +{ + 8003856: 460e mov r6, r1 + 8003858: 4614 mov r4, r2 + 800385a: f88d 0007 strb.w r0, [sp, #7] + ASSERT(start != end); // we can't work inplace + 800385e: d102 bne.n 8003866 + 8003860: 4818 ldr r0, [pc, #96] ; (80038c4 ) + 8003862: f7fd f8f1 bl 8000a48 + + if(ae_pair_unlock()) return -1; + 8003866: f7ff faa3 bl 8002db0 + 800386a: bb40 cbnz r0, 80038be + + ASSERT(keynum != 0); + 800386c: f89d 0007 ldrb.w r0, [sp, #7] + 8003870: 2800 cmp r0, #0 + 8003872: d0f5 beq.n 8003860 + int rv = ae_hmac32(keynum, start, end); + 8003874: 4622 mov r2, r4 + 8003876: 4631 mov r1, r6 + 8003878: f7ff fae2 bl 8002e40 + RET_IF_BAD(rv); + 800387c: 4605 mov r5, r0 + 800387e: b9d8 cbnz r0, 80038b8 + // use the value provided in cleartext[sic--it's not] write back shortly (to test it). + // Solution: one more SHA256, and to be safe, mixin lots of values! + + SHA256_CTX ctx; + + sha256_init(&ctx); + 8003880: a803 add r0, sp, #12 + 8003882: f001 fe03 bl 800548c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003886: 4910 ldr r1, [pc, #64] ; (80038c8 ) + 8003888: 2220 movs r2, #32 + 800388a: a803 add r0, sp, #12 + 800388c: f001 fe0c bl 80054a8 + sha256_update(&ctx, start, 32); + 8003890: 2220 movs r2, #32 + 8003892: 4631 mov r1, r6 + 8003894: a803 add r0, sp, #12 + 8003896: f001 fe07 bl 80054a8 + sha256_update(&ctx, &keynum, 1); + 800389a: 2201 movs r2, #1 + 800389c: f10d 0107 add.w r1, sp, #7 + 80038a0: a803 add r0, sp, #12 + 80038a2: f001 fe01 bl 80054a8 + sha256_update(&ctx, end, 32); + 80038a6: 4621 mov r1, r4 + 80038a8: a803 add r0, sp, #12 + 80038aa: 2220 movs r2, #32 + 80038ac: f001 fdfc bl 80054a8 + sha256_final(&ctx, end); + 80038b0: 4621 mov r1, r4 + 80038b2: a803 add r0, sp, #12 + 80038b4: f001 fe3e bl 8005534 + + return 0; +} + 80038b8: 4628 mov r0, r5 + 80038ba: b016 add sp, #88 ; 0x58 + 80038bc: bd70 pop {r4, r5, r6, pc} + if(ae_pair_unlock()) return -1; + 80038be: f04f 35ff mov.w r5, #4294967295 ; 0xffffffff + 80038c2: e7f9 b.n 80038b8 + 80038c4: 0800e3e0 .word 0x0800e3e0 + 80038c8: 0801c000 .word 0x0801c000 + +080038cc : +// Immediately destroy the pairing secret so that we become +// a useless brick. Ignore errors but retry. +// + void +ae_brick_myself(void) +{ + 80038cc: b510 push {r4, lr} + for(int retry=0; retry<10; retry++) { + 80038ce: 2400 movs r4, #0 + ae_reset_chip(); + 80038d0: f7ff f86a bl 80029a8 + + if(retry) rng_delay(); + 80038d4: b10c cbz r4, 80038da + 80038d6: f7fe ff51 bl 800277c + + ae_pair_unlock(); + 80038da: f7ff fa69 bl 8002db0 + + // Concern: MitM could block this by trashing our write + // - but they have to do it without causing CRC or other comm error + // - ten times + int rv = ae_destroy_key(KEYNUM_pairing); + 80038de: 2001 movs r0, #1 + 80038e0: f7ff fe54 bl 800358c + if(rv == 0) break; + 80038e4: b120 cbz r0, 80038f0 + for(int retry=0; retry<10; retry++) { + 80038e6: 3401 adds r4, #1 + + rng_delay(); + 80038e8: f7fe ff48 bl 800277c + for(int retry=0; retry<10; retry++) { + 80038ec: 2c0a cmp r4, #10 + 80038ee: d1ef bne.n 80038d0 + } + + ae_reset_chip(); +} + 80038f0: e8bd 4010 ldmia.w sp!, {r4, lr} + ae_reset_chip(); + 80038f4: f7ff b858 b.w 80029a8 + +080038f8 : +// + void +delay_ms(int ms) +{ + // Clear the COUNTFLAG and reset value to zero + SysTick->VAL = 0; + 80038f8: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 80038fc: 2200 movs r2, #0 + 80038fe: 619a str r2, [r3, #24] + //SysTick->CTRL; + + // Wait for ticks to happen + while(ms > 0) { + 8003900: 2800 cmp r0, #0 + 8003902: dc00 bgt.n 8003906 + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + ms--; + } + } +} + 8003904: 4770 bx lr + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8003906: 691a ldr r2, [r3, #16] + 8003908: 03d2 lsls r2, r2, #15 + ms--; + 800390a: bf48 it mi + 800390c: f100 30ff addmi.w r0, r0, #4294967295 ; 0xffffffff + 8003910: e7f6 b.n 8003900 + +08003912 : +// Replace HAL version which needs interrupts +// + void +HAL_Delay(uint32_t Delay) +{ + delay_ms(Delay); + 8003912: f7ff bff1 b.w 80038f8 + ... + +08003918 : + // NOTES: + // - try not to limit PCB changes for future revs; leave unused unchanged. + // - oled_setup() uses pins on PA4 thru PA8 + + // enable clock to GPIO's ... we will be using them all at some point + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003918: 4b39 ldr r3, [pc, #228] ; (8003a00 ) +{ + 800391a: b570 push {r4, r5, r6, lr} + __HAL_RCC_GPIOA_CLK_ENABLE(); + 800391c: 6cda ldr r2, [r3, #76] ; 0x4c + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); + + { // Onewire bus pins used for ATECC608 comms + GPIO_InitTypeDef setup = { + 800391e: 4c39 ldr r4, [pc, #228] ; (8003a04 ) + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003920: f042 0201 orr.w r2, r2, #1 + 8003924: 64da str r2, [r3, #76] ; 0x4c + 8003926: 6cda ldr r2, [r3, #76] ; 0x4c +{ + 8003928: b08a sub sp, #40 ; 0x28 + __HAL_RCC_GPIOA_CLK_ENABLE(); + 800392a: f002 0201 and.w r2, r2, #1 + 800392e: 9200 str r2, [sp, #0] + 8003930: 9a00 ldr r2, [sp, #0] + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8003932: 6cda ldr r2, [r3, #76] ; 0x4c + 8003934: f042 0202 orr.w r2, r2, #2 + 8003938: 64da str r2, [r3, #76] ; 0x4c + 800393a: 6cda ldr r2, [r3, #76] ; 0x4c + 800393c: f002 0202 and.w r2, r2, #2 + 8003940: 9201 str r2, [sp, #4] + 8003942: 9a01 ldr r2, [sp, #4] + __HAL_RCC_GPIOC_CLK_ENABLE(); + 8003944: 6cda ldr r2, [r3, #76] ; 0x4c + 8003946: f042 0204 orr.w r2, r2, #4 + 800394a: 64da str r2, [r3, #76] ; 0x4c + 800394c: 6cda ldr r2, [r3, #76] ; 0x4c + 800394e: f002 0204 and.w r2, r2, #4 + 8003952: 9202 str r2, [sp, #8] + 8003954: 9a02 ldr r2, [sp, #8] + __HAL_RCC_GPIOD_CLK_ENABLE(); + 8003956: 6cda ldr r2, [r3, #76] ; 0x4c + 8003958: f042 0208 orr.w r2, r2, #8 + 800395c: 64da str r2, [r3, #76] ; 0x4c + 800395e: 6cda ldr r2, [r3, #76] ; 0x4c + 8003960: f002 0208 and.w r2, r2, #8 + 8003964: 9203 str r2, [sp, #12] + 8003966: 9a03 ldr r2, [sp, #12] + __HAL_RCC_GPIOE_CLK_ENABLE(); + 8003968: 6cda ldr r2, [r3, #76] ; 0x4c + 800396a: f042 0210 orr.w r2, r2, #16 + 800396e: 64da str r2, [r3, #76] ; 0x4c + 8003970: 6cdb ldr r3, [r3, #76] ; 0x4c + 8003972: f003 0310 and.w r3, r3, #16 + 8003976: 9304 str r3, [sp, #16] + 8003978: 9b04 ldr r3, [sp, #16] + GPIO_InitTypeDef setup = { + 800397a: cc0f ldmia r4!, {r0, r1, r2, r3} + 800397c: ad05 add r5, sp, #20 + 800397e: c50f stmia r5!, {r0, r1, r2, r3} + 8003980: 6823 ldr r3, [r4, #0] + 8003982: 602b str r3, [r5, #0] + .Mode = GPIO_MODE_AF_OD, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = GPIO_AF8_UART4, + }; + HAL_GPIO_Init(ONEWIRE_PORT, &setup); + 8003984: a905 add r1, sp, #20 + 8003986: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 800398a: f7fd fb41 bl 8001010 + } + + // Bugfix: re-init of console port pins seems to wreck + // the mpy uart code, so avoid after first time. + if(USART1->BRR == 0) { + 800398e: 4b1e ldr r3, [pc, #120] ; (8003a08 ) + 8003990: 68de ldr r6, [r3, #12] + 8003992: b9ae cbnz r6, 80039c0 + // debug console: USART1 = PA9=Tx & PA10=Rx + GPIO_InitTypeDef setup = { + 8003994: 3404 adds r4, #4 + 8003996: cc0f ldmia r4!, {r0, r1, r2, r3} + 8003998: ad05 add r5, sp, #20 + 800399a: c50f stmia r5!, {r0, r1, r2, r3} + 800399c: 6823 ldr r3, [r4, #0] + 800399e: 602b str r3, [r5, #0] + .Mode = GPIO_MODE_AF_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = GPIO_AF7_USART1, + }; + HAL_GPIO_Init(GPIOA, &setup); + 80039a0: a905 add r1, sp, #20 + 80039a2: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 80039a6: f7fd fb33 bl 8001010 + + setup.Pin = GPIO_PIN_10; + 80039aa: f44f 6380 mov.w r3, #1024 ; 0x400 + setup.Mode = GPIO_MODE_INPUT; + 80039ae: e9cd 3605 strd r3, r6, [sp, #20] + setup.Pull = GPIO_PULLUP; + HAL_GPIO_Init(GPIOA, &setup); + 80039b2: a905 add r1, sp, #20 + setup.Pull = GPIO_PULLUP; + 80039b4: 2301 movs r3, #1 + HAL_GPIO_Init(GPIOA, &setup); + 80039b6: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + setup.Pull = GPIO_PULLUP; + 80039ba: 9307 str r3, [sp, #28] + HAL_GPIO_Init(GPIOA, &setup); + 80039bc: f7fd fb28 bl 8001010 + } + + // SD active LED: PC7 + // USB active LED: PC6 + { GPIO_InitTypeDef setup = { + 80039c0: 2400 movs r4, #0 + 80039c2: 26c0 movs r6, #192 ; 0xc0 + 80039c4: 2501 movs r5, #1 + .Pin = GPIO_PIN_7 | GPIO_PIN_6, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOC, &setup); + 80039c6: a905 add r1, sp, #20 + 80039c8: 4810 ldr r0, [pc, #64] ; (8003a0c ) + { GPIO_InitTypeDef setup = { + 80039ca: 9409 str r4, [sp, #36] ; 0x24 + 80039cc: e9cd 4407 strd r4, r4, [sp, #28] + 80039d0: e9cd 6505 strd r6, r5, [sp, #20] + HAL_GPIO_Init(GPIOC, &setup); + 80039d4: f7fd fb1c bl 8001010 + + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7|GPIO_PIN_6, 0); // turn LEDs off + 80039d8: 4622 mov r2, r4 + 80039da: 4631 mov r1, r6 + 80039dc: 480b ldr r0, [pc, #44] ; (8003a0c ) + 80039de: f7fd fc91 bl 8001304 + } + + // SD card detect switch: PC13 + { GPIO_InitTypeDef setup = { + 80039e2: 2210 movs r2, #16 + 80039e4: 4621 mov r1, r4 + 80039e6: a806 add r0, sp, #24 + 80039e8: f009 fe44 bl 800d674 + 80039ec: f44f 5300 mov.w r3, #8192 ; 0x2000 + .Pin = GPIO_PIN_13, + .Mode = GPIO_MODE_INPUT, + .Pull = GPIO_PULLUP, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOC, &setup); + 80039f0: 4806 ldr r0, [pc, #24] ; (8003a0c ) + { GPIO_InitTypeDef setup = { + 80039f2: 9305 str r3, [sp, #20] + HAL_GPIO_Init(GPIOC, &setup); + 80039f4: a905 add r1, sp, #20 + { GPIO_InitTypeDef setup = { + 80039f6: 9507 str r5, [sp, #28] + HAL_GPIO_Init(GPIOC, &setup); + 80039f8: f7fd fb0a bl 8001010 + + // elsewhere... + //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1); + //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 0); +#endif +} + 80039fc: b00a add sp, #40 ; 0x28 + 80039fe: bd70 pop {r4, r5, r6, pc} + 8003a00: 40021000 .word 0x40021000 + 8003a04: 0800e6f4 .word 0x0800e6f4 + 8003a08: 40013800 .word 0x40013800 + 8003a0c: 48000800 .word 0x48000800 + +08003a10 : + +// reboot_nonce() +// + static inline void +reboot_nonce(SHA256_CTX *ctx) +{ + 8003a10: b537 push {r0, r1, r2, r4, r5, lr} + uint32_t a = CRC->INIT; + 8003a12: 4d09 ldr r5, [pc, #36] ; (8003a38 ) + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003a14: 2204 movs r2, #4 + uint32_t a = CRC->INIT; + 8003a16: 692b ldr r3, [r5, #16] + 8003a18: 9301 str r3, [sp, #4] + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003a1a: eb0d 0102 add.w r1, sp, r2 +{ + 8003a1e: 4604 mov r4, r0 + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003a20: f001 fd42 bl 80054a8 + + a = CRC->POL; + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003a24: 2204 movs r2, #4 + a = CRC->POL; + 8003a26: 696b ldr r3, [r5, #20] + 8003a28: 9301 str r3, [sp, #4] + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003a2a: eb0d 0102 add.w r1, sp, r2 + 8003a2e: 4620 mov r0, r4 + 8003a30: f001 fd3a bl 80054a8 +} + 8003a34: b003 add sp, #12 + 8003a36: bd30 pop {r4, r5, pc} + 8003a38: 40023000 .word 0x40023000 + +08003a3c : +// +// Hash up a string of digits into 32-bytes of goodness. +// + static void +pin_hash(const char *pin, int pin_len, uint8_t result[32], uint32_t purpose) +{ + 8003a3c: b570 push {r4, r5, r6, lr} + 8003a3e: b096 sub sp, #88 ; 0x58 + ASSERT(pin_len <= MAX_PIN_LEN); + 8003a40: 2920 cmp r1, #32 +{ + 8003a42: 4606 mov r6, r0 + 8003a44: 460d mov r5, r1 + 8003a46: 4614 mov r4, r2 + 8003a48: 9301 str r3, [sp, #4] + ASSERT(pin_len <= MAX_PIN_LEN); + 8003a4a: dd02 ble.n 8003a52 + 8003a4c: 4817 ldr r0, [pc, #92] ; (8003aac ) + 8003a4e: f7fc fffb bl 8000a48 + + if(pin_len == 0) { + 8003a52: b929 cbnz r1, 8003a60 + // zero-length PIN is considered the "blank" one: all zero + memset(result, 0, 32); + 8003a54: 2220 movs r2, #32 + 8003a56: 4620 mov r0, r4 + 8003a58: f009 fe0c bl 800d674 + // and run that thru SE2 as well + se2_pin_hash(result, purpose); + + // and a second-sha256 on that, just in case. + sha256_single(result, 32, result); +} + 8003a5c: b016 add sp, #88 ; 0x58 + 8003a5e: bd70 pop {r4, r5, r6, pc} + sha256_init(&ctx); + 8003a60: a803 add r0, sp, #12 + 8003a62: f001 fd13 bl 800548c + sha256_update(&ctx, rom_secrets->hash_cache_secret, 32); + 8003a66: a803 add r0, sp, #12 + 8003a68: 4911 ldr r1, [pc, #68] ; (8003ab0 ) + 8003a6a: 2220 movs r2, #32 + 8003a6c: f001 fd1c bl 80054a8 + sha256_update(&ctx, (uint8_t *)&purpose, 4); + 8003a70: 2204 movs r2, #4 + 8003a72: eb0d 0102 add.w r1, sp, r2 + 8003a76: a803 add r0, sp, #12 + 8003a78: f001 fd16 bl 80054a8 + sha256_update(&ctx, (uint8_t *)pin, pin_len); + 8003a7c: 462a mov r2, r5 + 8003a7e: 4631 mov r1, r6 + 8003a80: a803 add r0, sp, #12 + 8003a82: f001 fd11 bl 80054a8 + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003a86: 2220 movs r2, #32 + 8003a88: a803 add r0, sp, #12 + 8003a8a: 490a ldr r1, [pc, #40] ; (8003ab4 ) + 8003a8c: f001 fd0c bl 80054a8 + sha256_final(&ctx, result); + 8003a90: 4621 mov r1, r4 + 8003a92: a803 add r0, sp, #12 + 8003a94: f001 fd4e bl 8005534 + se2_pin_hash(result, purpose); + 8003a98: 9901 ldr r1, [sp, #4] + 8003a9a: 4620 mov r0, r4 + 8003a9c: f004 fc32 bl 8008304 + sha256_single(result, 32, result); + 8003aa0: 4622 mov r2, r4 + 8003aa2: 2120 movs r1, #32 + 8003aa4: 4620 mov r0, r4 + 8003aa6: f001 fd59 bl 800555c + 8003aaa: e7d7 b.n 8003a5c + 8003aac: 0800e3e0 .word 0x0800e3e0 + 8003ab0: 0801c070 .word 0x0801c070 + 8003ab4: 0801c000 .word 0x0801c000 + +08003ab8 <_hmac_attempt>: +// +// Maybe should be proper HMAC from fips std? Can be changed later. +// + static void +_hmac_attempt(const pinAttempt_t *args, uint8_t result[32]) +{ + 8003ab8: b530 push {r4, r5, lr} + 8003aba: b095 sub sp, #84 ; 0x54 + 8003abc: 4604 mov r4, r0 + SHA256_CTX ctx; + + sha256_init(&ctx); + 8003abe: a801 add r0, sp, #4 +{ + 8003ac0: 460d mov r5, r1 + sha256_init(&ctx); + 8003ac2: f001 fce3 bl 800548c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003ac6: 4911 ldr r1, [pc, #68] ; (8003b0c <_hmac_attempt+0x54>) + 8003ac8: 2220 movs r2, #32 + 8003aca: a801 add r0, sp, #4 + 8003acc: f001 fcec bl 80054a8 + reboot_nonce(&ctx); + 8003ad0: a801 add r0, sp, #4 + 8003ad2: f7ff ff9d bl 8003a10 + sha256_update(&ctx, (uint8_t *)args, offsetof(pinAttempt_t, hmac)); + 8003ad6: 2244 movs r2, #68 ; 0x44 + 8003ad8: 4621 mov r1, r4 + 8003ada: a801 add r0, sp, #4 + 8003adc: f001 fce4 bl 80054a8 + + if(args->magic_value == PA_MAGIC_V2) { + 8003ae0: 6822 ldr r2, [r4, #0] + 8003ae2: 4b0b ldr r3, [pc, #44] ; (8003b10 <_hmac_attempt+0x58>) + 8003ae4: 429a cmp r2, r3 + 8003ae6: d105 bne.n 8003af4 <_hmac_attempt+0x3c> + sha256_update(&ctx, (uint8_t *)args->cached_main_pin, + 8003ae8: 2220 movs r2, #32 + 8003aea: f104 01f8 add.w r1, r4, #248 ; 0xf8 + 8003aee: a801 add r0, sp, #4 + 8003af0: f001 fcda bl 80054a8 + msizeof(pinAttempt_t, cached_main_pin)); + } + + sha256_final(&ctx, result); + 8003af4: 4629 mov r1, r5 + 8003af6: a801 add r0, sp, #4 + 8003af8: f001 fd1c bl 8005534 + + // and a second-sha256 on that, just in case. + sha256_single(result, 32, result); + 8003afc: 462a mov r2, r5 + 8003afe: 2120 movs r1, #32 + 8003b00: 4628 mov r0, r5 + 8003b02: f001 fd2b bl 800555c +} + 8003b06: b015 add sp, #84 ; 0x54 + 8003b08: bd30 pop {r4, r5, pc} + 8003b0a: bf00 nop + 8003b0c: 0801c000 .word 0x0801c000 + 8003b10: 2eaf6312 .word 0x2eaf6312 + +08003b14 <_validate_attempt>: + +// _validate_attempt() +// + static int +_validate_attempt(const pinAttempt_t *args, bool first_time) +{ + 8003b14: b510 push {r4, lr} + 8003b16: 4604 mov r4, r0 + 8003b18: b088 sub sp, #32 + if(first_time) { + 8003b1a: b969 cbnz r1, 8003b38 <_validate_attempt+0x24> + // no hmac needed for setup call + } else { + // if hmac is defined, better be right. + uint8_t actual[32]; + + _hmac_attempt(args, actual); + 8003b1c: 4669 mov r1, sp + 8003b1e: f7ff ffcb bl 8003ab8 <_hmac_attempt> + + if(!check_equal(actual, args->hmac, 32)) { + 8003b22: 2220 movs r2, #32 + 8003b24: f104 0144 add.w r1, r4, #68 ; 0x44 + 8003b28: 4668 mov r0, sp + 8003b2a: f7fe fdc2 bl 80026b2 + 8003b2e: b918 cbnz r0, 8003b38 <_validate_attempt+0x24> + // hmac is wrong? + return EPIN_HMAC_FAIL; + 8003b30: f06f 0063 mvn.w r0, #99 ; 0x63 + if((args->change_flags & CHANGE__MASK) != args->change_flags) return EPIN_RANGE_ERR; + + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + + return 0; +} + 8003b34: b008 add sp, #32 + 8003b36: bd10 pop {r4, pc} + if(args->magic_value == PA_MAGIC_V2) { + 8003b38: 6822 ldr r2, [r4, #0] + 8003b3a: 4b10 ldr r3, [pc, #64] ; (8003b7c <_validate_attempt+0x68>) + 8003b3c: 429a cmp r2, r3 + 8003b3e: d117 bne.n 8003b70 <_validate_attempt+0x5c> + if(args->pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003b40: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8003b42: 2b20 cmp r3, #32 + 8003b44: dc17 bgt.n 8003b76 <_validate_attempt+0x62> + if(args->old_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003b46: f8d4 3088 ldr.w r3, [r4, #136] ; 0x88 + 8003b4a: 2b20 cmp r3, #32 + 8003b4c: dc13 bgt.n 8003b76 <_validate_attempt+0x62> + if(args->new_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003b4e: f8d4 30ac ldr.w r3, [r4, #172] ; 0xac + 8003b52: 2b20 cmp r3, #32 + 8003b54: dc0f bgt.n 8003b76 <_validate_attempt+0x62> + if((args->change_flags & CHANGE__MASK) != args->change_flags) return EPIN_RANGE_ERR; + 8003b56: 6e63 ldr r3, [r4, #100] ; 0x64 + 8003b58: f640 727f movw r2, #3967 ; 0xf7f + 8003b5c: 4393 bics r3, r2 + 8003b5e: d10a bne.n 8003b76 <_validate_attempt+0x62> + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + 8003b60: 6863 ldr r3, [r4, #4] + return 0; + 8003b62: f033 0301 bics.w r3, r3, #1 + 8003b66: bf14 ite ne + 8003b68: f06f 0066 mvnne.w r0, #102 ; 0x66 + 8003b6c: 2000 moveq r0, #0 + 8003b6e: e7e1 b.n 8003b34 <_validate_attempt+0x20> + return EPIN_BAD_MAGIC; + 8003b70: f06f 0065 mvn.w r0, #101 ; 0x65 + 8003b74: e7de b.n 8003b34 <_validate_attempt+0x20> + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + 8003b76: f06f 0066 mvn.w r0, #102 ; 0x66 + 8003b7a: e7db b.n 8003b34 <_validate_attempt+0x20> + 8003b7c: 2eaf6312 .word 0x2eaf6312 + +08003b80 : + +// warmup_ae() +// + static int +warmup_ae(void) +{ + 8003b80: b510 push {r4, lr} + ae_setup(); + 8003b82: f7fe ff1f bl 80029c4 + 8003b86: 2405 movs r4, #5 + + for(int retry=0; retry<5; retry++) { + if(!ae_probe()) break; + 8003b88: f7ff f9a4 bl 8002ed4 + 8003b8c: b108 cbz r0, 8003b92 + for(int retry=0; retry<5; retry++) { + 8003b8e: 3c01 subs r4, #1 + 8003b90: d1fa bne.n 8003b88 + } + + if(ae_pair_unlock()) return -1; + 8003b92: f7ff f90d bl 8002db0 + 8003b96: 4604 mov r4, r0 + 8003b98: b918 cbnz r0, 8003ba2 + + // reset watchdog timer + ae_keep_alive(); + 8003b9a: f7fe ff45 bl 8002a28 + + return 0; +} + 8003b9e: 4620 mov r0, r4 + 8003ba0: bd10 pop {r4, pc} + if(ae_pair_unlock()) return -1; + 8003ba2: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 8003ba6: e7fa b.n 8003b9e + +08003ba8 <_read_slot_as_counter>: +{ + 8003ba8: b530 push {r4, r5, lr} + 8003baa: b091 sub sp, #68 ; 0x44 + uint32_t padded[32/4] = { 0 }; + 8003bac: 2220 movs r2, #32 +{ + 8003bae: 4604 mov r4, r0 + 8003bb0: 460d mov r5, r1 + uint32_t padded[32/4] = { 0 }; + 8003bb2: 4668 mov r0, sp + 8003bb4: 2100 movs r1, #0 + 8003bb6: f009 fd5d bl 800d674 + ae_pair_unlock(); + 8003bba: f7ff f8f9 bl 8002db0 + if(ae_read_data_slot(slot, (uint8_t *)padded, 32)) return -1; + 8003bbe: 2220 movs r2, #32 + 8003bc0: 4669 mov r1, sp + 8003bc2: 4620 mov r0, r4 + 8003bc4: f7ff fc3a bl 800343c + 8003bc8: b120 cbz r0, 8003bd4 <_read_slot_as_counter+0x2c> + 8003bca: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff +} + 8003bce: 4620 mov r0, r4 + 8003bd0: b011 add sp, #68 ; 0x44 + 8003bd2: bd30 pop {r4, r5, pc} + ae_pair_unlock(); + 8003bd4: f7ff f8ec bl 8002db0 + if(ae_gendig_slot(slot, (const uint8_t *)padded, tempkey)) return -1; + 8003bd8: 4620 mov r0, r4 + 8003bda: aa08 add r2, sp, #32 + 8003bdc: 4669 mov r1, sp + 8003bde: f7ff fa07 bl 8002ff0 + 8003be2: 4604 mov r4, r0 + 8003be4: 2800 cmp r0, #0 + 8003be6: d1f0 bne.n 8003bca <_read_slot_as_counter+0x22> + if(!ae_is_correct_tempkey(tempkey)) fatal_mitm(); + 8003be8: a808 add r0, sp, #32 + 8003bea: f7ff f811 bl 8002c10 + 8003bee: b908 cbnz r0, 8003bf4 <_read_slot_as_counter+0x4c> + 8003bf0: f7fc ff34 bl 8000a5c + *dest = padded[0]; + 8003bf4: 9b00 ldr r3, [sp, #0] + 8003bf6: 602b str r3, [r5, #0] + return 0; + 8003bf8: e7e9 b.n 8003bce <_read_slot_as_counter+0x26> + +08003bfa : +{ + 8003bfa: b530 push {r4, r5, lr} + 8003bfc: b095 sub sp, #84 ; 0x54 + 8003bfe: 4605 mov r5, r0 + ae_pair_unlock(); + 8003c00: f7ff f8d6 bl 8002db0 + uint32_t padded[32/4] = { 0 }; + 8003c04: 2220 movs r2, #32 + 8003c06: 2100 movs r1, #0 + 8003c08: a804 add r0, sp, #16 + 8003c0a: f009 fd33 bl 800d674 + if(ae_read_data_slot(slot, (uint8_t *)padded, 32)) return -1; + 8003c0e: 2220 movs r2, #32 + 8003c10: a904 add r1, sp, #16 + 8003c12: 2005 movs r0, #5 + 8003c14: f7ff fc12 bl 800343c + 8003c18: b118 cbz r0, 8003c22 + 8003c1a: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff +} + 8003c1e: b015 add sp, #84 ; 0x54 + 8003c20: bd30 pop {r4, r5, pc} + ae_pair_unlock(); + 8003c22: f7ff f8c5 bl 8002db0 + if(ae_gendig_slot(slot, (const uint8_t *)padded, tempkey)) return -1; + 8003c26: aa0c add r2, sp, #48 ; 0x30 + 8003c28: a904 add r1, sp, #16 + 8003c2a: 2005 movs r0, #5 + 8003c2c: f7ff f9e0 bl 8002ff0 + 8003c30: 4604 mov r4, r0 + 8003c32: 2800 cmp r0, #0 + 8003c34: d1f1 bne.n 8003c1a + if(!ae_is_correct_tempkey(tempkey)) fatal_mitm(); + 8003c36: a80c add r0, sp, #48 ; 0x30 + 8003c38: f7fe ffea bl 8002c10 + 8003c3c: b908 cbnz r0, 8003c42 + 8003c3e: f7fc ff0d bl 8000a5c + if(_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -1; + 8003c42: a901 add r1, sp, #4 + 8003c44: 2005 movs r0, #5 + uint32_t lastgood=0, match_count=0, counter=0; + 8003c46: e9cd 4401 strd r4, r4, [sp, #4] + 8003c4a: 9403 str r4, [sp, #12] + if(_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -1; + 8003c4c: f7ff ffac bl 8003ba8 <_read_slot_as_counter> + 8003c50: 2800 cmp r0, #0 + 8003c52: d1e2 bne.n 8003c1a + if(_read_slot_as_counter(KEYNUM_match_count, &match_count)) return -1; + 8003c54: a902 add r1, sp, #8 + 8003c56: 2006 movs r0, #6 + 8003c58: f7ff ffa6 bl 8003ba8 <_read_slot_as_counter> + 8003c5c: 4601 mov r1, r0 + 8003c5e: 2800 cmp r0, #0 + 8003c60: d1db bne.n 8003c1a + if(ae_get_counter(&counter, 0)) return -1; + 8003c62: a803 add r0, sp, #12 + 8003c64: f7ff fa9f bl 80031a6 + 8003c68: 2800 cmp r0, #0 + 8003c6a: d1d6 bne.n 8003c1a + if(lastgood > counter) { + 8003c6c: 9a01 ldr r2, [sp, #4] + 8003c6e: 9903 ldr r1, [sp, #12] + match_count &= ~31; + 8003c70: 9b02 ldr r3, [sp, #8] + if(lastgood > counter) { + 8003c72: 428a cmp r2, r1 + match_count &= ~31; + 8003c74: f023 031f bic.w r3, r3, #31 + args->num_fails = counter - lastgood; + 8003c78: bf94 ite ls + 8003c7a: 1a8a subls r2, r1, r2 + args->num_fails = 99; + 8003c7c: 2263 movhi r2, #99 ; 0x63 + if(counter < match_count) { + 8003c7e: 4299 cmp r1, r3 + args->attempts_left = match_count - counter; + 8003c80: bf34 ite cc + 8003c82: 1a5b subcc r3, r3, r1 + args->attempts_left = 0; + 8003c84: 2300 movcs r3, #0 + 8003c86: 636a str r2, [r5, #52] ; 0x34 + 8003c88: 63ab str r3, [r5, #56] ; 0x38 + 8003c8a: e7c8 b.n 8003c1e + +08003c8c : + +// updates_for_good_login() +// + static int +updates_for_good_login(uint8_t digest[32]) +{ + 8003c8c: b5f0 push {r4, r5, r6, r7, lr} + 8003c8e: b08d sub sp, #52 ; 0x34 + // User got the main PIN right: update the attempt counters, + // to document this (lastgood) and also bump the match counter if needed + + uint32_t count; + int rv = ae_get_counter(&count, 0); + 8003c90: 2100 movs r1, #0 +{ + 8003c92: 4606 mov r6, r0 + int rv = ae_get_counter(&count, 0); + 8003c94: a802 add r0, sp, #8 + 8003c96: f7ff fa86 bl 80031a6 + if(rv) goto fail; + 8003c9a: 4601 mov r1, r0 + 8003c9c: 2800 cmp r0, #0 + 8003c9e: d13b bne.n 8003d18 + + // Challenge: Have to update both the counter, and the target match value because + // no other way to have exact value. + + uint32_t mc = (count + MAX_TARGET_ATTEMPTS + 32) & ~31; + 8003ca0: 9b02 ldr r3, [sp, #8] + 8003ca2: f103 042d add.w r4, r3, #45 ; 0x2d + 8003ca6: f024 041f bic.w r4, r4, #31 + ASSERT(mc >= count); + 8003caa: 42a3 cmp r3, r4 + 8003cac: d902 bls.n 8003cb4 + 8003cae: 481d ldr r0, [pc, #116] ; (8003d24 ) + 8003cb0: f7fc feca bl 8000a48 + + int bump = (mc - MAX_TARGET_ATTEMPTS) - count; + 8003cb4: 1ae3 subs r3, r4, r3 + 8003cb6: f1a3 050d sub.w r5, r3, #13 + ASSERT(bump >= 1); + 8003cba: 3b0e subs r3, #14 + 8003cbc: 2b1f cmp r3, #31 + 8003cbe: d8f6 bhi.n 8003cae + // Would rather update the counter first, so that a hostile interruption can't increase + // attempts (altho the attacker knows the pin at that point?!) .. but chip won't + // let the counter go past the match value, so that has to be first. + + // set the new "match count" + { uint32_t tmp[32/4] = {mc, mc} ; + 8003cc0: 2218 movs r2, #24 + 8003cc2: eb0d 0002 add.w r0, sp, r2 + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003cc6: 2720 movs r7, #32 + { uint32_t tmp[32/4] = {mc, mc} ; + 8003cc8: f009 fcd4 bl 800d674 + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003ccc: 2103 movs r1, #3 + 8003cce: 9700 str r7, [sp, #0] + 8003cd0: ab04 add r3, sp, #16 + 8003cd2: 4632 mov r2, r6 + 8003cd4: 2006 movs r0, #6 + { uint32_t tmp[32/4] = {mc, mc} ; + 8003cd6: e9cd 4404 strd r4, r4, [sp, #16] + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003cda: f7ff fb79 bl 80033d0 + if(rv) goto fail; + 8003cde: 4601 mov r1, r0 + 8003ce0: b9d0 cbnz r0, 8003d18 + } + + // incr the counter a bunch to get to that-13 + uint32_t new_count = 0; + 8003ce2: 9003 str r0, [sp, #12] + rv = ae_add_counter(&new_count, 0, bump); + 8003ce4: 462a mov r2, r5 + 8003ce6: a803 add r0, sp, #12 + 8003ce8: f7ff fa7c bl 80031e4 + if(rv) goto fail; + 8003cec: 4601 mov r1, r0 + 8003cee: b998 cbnz r0, 8003d18 + + ASSERT(new_count == count + bump); + 8003cf0: 9b02 ldr r3, [sp, #8] + 8003cf2: 441d add r5, r3 + 8003cf4: 9b03 ldr r3, [sp, #12] + 8003cf6: 429d cmp r5, r3 + 8003cf8: d1d9 bne.n 8003cae + ASSERT(mc > new_count); + 8003cfa: 42a5 cmp r5, r4 + 8003cfc: d2d7 bcs.n 8003cae + + // Update the "last good" counter + { uint32_t tmp[32/4] = {new_count, 0 }; + 8003cfe: 221c movs r2, #28 + 8003d00: a805 add r0, sp, #20 + 8003d02: f009 fcb7 bl 800d674 + rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003d06: 9700 str r7, [sp, #0] + 8003d08: ab04 add r3, sp, #16 + 8003d0a: 4632 mov r2, r6 + 8003d0c: 2103 movs r1, #3 + 8003d0e: 2005 movs r0, #5 + { uint32_t tmp[32/4] = {new_count, 0 }; + 8003d10: 9504 str r5, [sp, #16] + rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003d12: f7ff fb5d bl 80033d0 + if(rv) goto fail; + 8003d16: b118 cbz r0, 8003d20 + // just be reducing attempts. + + return 0; + +fail: + ae_reset_chip(); + 8003d18: f7fe fe46 bl 80029a8 + return EPIN_AE_FAIL; + 8003d1c: f06f 0069 mvn.w r0, #105 ; 0x69 +} + 8003d20: b00d add sp, #52 ; 0x34 + 8003d22: bdf0 pop {r4, r5, r6, r7, pc} + 8003d24: 0800e3e0 .word 0x0800e3e0 + +08003d28 : +{ + 8003d28: b5f0 push {r4, r5, r6, r7, lr} + 8003d2a: 4615 mov r5, r2 + 8003d2c: b089 sub sp, #36 ; 0x24 + if(pin_len == 0) { + 8003d2e: 460c mov r4, r1 + 8003d30: b931 cbnz r1, 8003d40 + memset(result, 0, 32); + 8003d32: 2220 movs r2, #32 + 8003d34: 4628 mov r0, r5 + 8003d36: f009 fc9d bl 800d674 +} + 8003d3a: 4620 mov r0, r4 + 8003d3c: b009 add sp, #36 ; 0x24 + 8003d3e: bdf0 pop {r4, r5, r6, r7, pc} + pin_hash(pin, pin_len, tmp, PIN_PURPOSE_NORMAL); + 8003d40: 4b0f ldr r3, [pc, #60] ; (8003d80 ) + 8003d42: 466a mov r2, sp + 8003d44: f7ff fe7a bl 8003a3c + int rv = ae_stretch_iter(tmp, result, KDF_ITER_PIN); + 8003d48: 2208 movs r2, #8 + 8003d4a: 4629 mov r1, r5 + 8003d4c: 4668 mov r0, sp + 8003d4e: f7ff fd57 bl 8003800 + if(rv) return EPIN_AE_FAIL; + 8003d52: 4604 mov r4, r0 + 8003d54: b988 cbnz r0, 8003d7a + memcpy(tmp, result, 32); + 8003d56: 462b mov r3, r5 + 8003d58: 466e mov r6, sp + 8003d5a: f105 0720 add.w r7, r5, #32 + 8003d5e: 6818 ldr r0, [r3, #0] + 8003d60: 6859 ldr r1, [r3, #4] + 8003d62: 4632 mov r2, r6 + 8003d64: c203 stmia r2!, {r0, r1} + 8003d66: 3308 adds r3, #8 + 8003d68: 42bb cmp r3, r7 + 8003d6a: 4616 mov r6, r2 + 8003d6c: d1f7 bne.n 8003d5e + ae_mixin_key(KEYNUM_pin_attempt, tmp, result); + 8003d6e: 462a mov r2, r5 + 8003d70: 4669 mov r1, sp + 8003d72: 2004 movs r0, #4 + 8003d74: f7ff fd6c bl 8003850 + return 0; + 8003d78: e7df b.n 8003d3a + if(rv) return EPIN_AE_FAIL; + 8003d7a: f06f 0469 mvn.w r4, #105 ; 0x69 + 8003d7e: e7dc b.n 8003d3a + 8003d80: 334d1858 .word 0x334d1858 + +08003d84 : +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8003d84: b5f0 push {r4, r5, r6, r7, lr} + args->delay_achieved = slot->tc_arg; + 8003d86: 88cb ldrh r3, [r1, #6] + 8003d88: 62c3 str r3, [r0, #44] ; 0x2c +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8003d8a: f5ad 7d0d sub.w sp, sp, #564 ; 0x234 + memcpy(key, &args->private_state, sizeof(args->private_state)); + 8003d8e: 6c03 ldr r3, [r0, #64] ; 0x40 + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8003d90: 4d0f ldr r5, [pc, #60] ; (8003dd0 ) + memcpy(key, &args->private_state, sizeof(args->private_state)); + 8003d92: 9303 str r3, [sp, #12] +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8003d94: 4606 mov r6, r0 + 8003d96: 460f mov r7, r1 + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8003d98: cd0f ldmia r5!, {r0, r1, r2, r3} + 8003d9a: ac04 add r4, sp, #16 + 8003d9c: c40f stmia r4!, {r0, r1, r2, r3} + 8003d9e: e895 0007 ldmia.w r5, {r0, r1, r2} + 8003da2: e884 0007 stmia.w r4, {r0, r1, r2} + aes_init(&ctx); + 8003da6: a80b add r0, sp, #44 ; 0x2c + 8003da8: f004 fb5a bl 8008460 + aes_add(&ctx, (uint8_t *)slot, 32); + 8003dac: 4639 mov r1, r7 + 8003dae: a80b add r0, sp, #44 ; 0x2c + 8003db0: 2220 movs r2, #32 + 8003db2: f004 fb5b bl 800846c + aes_done(&ctx, args->cached_main_pin, 32, key, NULL); + 8003db6: 2300 movs r3, #0 + 8003db8: 9300 str r3, [sp, #0] + 8003dba: 2220 movs r2, #32 + 8003dbc: ab03 add r3, sp, #12 + 8003dbe: f106 01f8 add.w r1, r6, #248 ; 0xf8 + 8003dc2: a80b add r0, sp, #44 ; 0x2c + 8003dc4: f004 fb68 bl 8008498 +} + 8003dc8: f50d 7d0d add.w sp, sp, #564 ; 0x234 + 8003dcc: bdf0 pop {r4, r5, r6, r7, pc} + 8003dce: bf00 nop + 8003dd0: 0801c074 .word 0x0801c074 + +08003dd4 : + __HAL_RCC_CRC_CLK_ENABLE(); + 8003dd4: 4b09 ldr r3, [pc, #36] ; (8003dfc ) + 8003dd6: 6c9a ldr r2, [r3, #72] ; 0x48 +{ + 8003dd8: b513 push {r0, r1, r4, lr} + __HAL_RCC_CRC_CLK_ENABLE(); + 8003dda: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8003dde: 649a str r2, [r3, #72] ; 0x48 + 8003de0: 6c9b ldr r3, [r3, #72] ; 0x48 + CRC->INIT = rng_sample(); + 8003de2: 4c07 ldr r4, [pc, #28] ; (8003e00 ) + __HAL_RCC_CRC_CLK_ENABLE(); + 8003de4: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 8003de8: 9301 str r3, [sp, #4] + 8003dea: 9b01 ldr r3, [sp, #4] + CRC->INIT = rng_sample(); + 8003dec: f7fe fc72 bl 80026d4 + 8003df0: 6120 str r0, [r4, #16] + CRC->POL = rng_sample(); + 8003df2: f7fe fc6f bl 80026d4 + 8003df6: 6160 str r0, [r4, #20] +} + 8003df8: b002 add sp, #8 + 8003dfa: bd10 pop {r4, pc} + 8003dfc: 40021000 .word 0x40021000 + 8003e00: 40023000 .word 0x40023000 + +08003e04 : +{ + 8003e04: b510 push {r4, lr} + 8003e06: b094 sub sp, #80 ; 0x50 + 8003e08: 4604 mov r4, r0 + sha256_init(&ctx); + 8003e0a: a801 add r0, sp, #4 + 8003e0c: f001 fb3e bl 800548c + reboot_nonce(&ctx); + 8003e10: a801 add r0, sp, #4 + 8003e12: f7ff fdfd bl 8003a10 + sha256_update(&ctx, rom_secrets->hash_cache_secret, 32); + 8003e16: 2220 movs r2, #32 + 8003e18: a801 add r0, sp, #4 + 8003e1a: 4904 ldr r1, [pc, #16] ; (8003e2c ) + 8003e1c: f001 fb44 bl 80054a8 + sha256_final(&ctx, key); + 8003e20: 4621 mov r1, r4 + 8003e22: a801 add r0, sp, #4 + 8003e24: f001 fb86 bl 8005534 +} + 8003e28: b014 add sp, #80 ; 0x50 + 8003e2a: bd10 pop {r4, pc} + 8003e2c: 0801c070 .word 0x0801c070 + +08003e30 : +{ + 8003e30: b530 push {r4, r5, lr} + 8003e32: 460d mov r5, r1 + 8003e34: b089 sub sp, #36 ; 0x24 + 8003e36: 4604 mov r4, r0 + if(!check_all_zeros(digest, 32)) { + 8003e38: 2120 movs r1, #32 + 8003e3a: 4628 mov r0, r5 + 8003e3c: f7fe fc2a bl 8002694 + 8003e40: b9a0 cbnz r0, 8003e6c + pin_cache_get_key(value); + 8003e42: 4668 mov r0, sp + 8003e44: f7ff ffde bl 8003e04 + 8003e48: 466b mov r3, sp + 8003e4a: f105 0120 add.w r1, r5, #32 + *(acc) ^= *(more); + 8003e4e: 781a ldrb r2, [r3, #0] + 8003e50: f815 0b01 ldrb.w r0, [r5], #1 + 8003e54: 4042 eors r2, r0 + for(; len; len--, more++, acc++) { + 8003e56: 428d cmp r5, r1 + *(acc) ^= *(more); + 8003e58: f803 2b01 strb.w r2, [r3], #1 + for(; len; len--, more++, acc++) { + 8003e5c: d1f7 bne.n 8003e4e + ASSERT(args->magic_value == PA_MAGIC_V2); + 8003e5e: 6822 ldr r2, [r4, #0] + 8003e60: 4b0d ldr r3, [pc, #52] ; (8003e98 ) + 8003e62: 429a cmp r2, r3 + 8003e64: d008 beq.n 8003e78 + 8003e66: 480d ldr r0, [pc, #52] ; (8003e9c ) + 8003e68: f7fc fdee bl 8000a48 + memset(value, 0, 32); + 8003e6c: 2220 movs r2, #32 + 8003e6e: 2100 movs r1, #0 + 8003e70: 4668 mov r0, sp + 8003e72: f009 fbff bl 800d674 + 8003e76: e7f2 b.n 8003e5e + memcpy(args->cached_main_pin, value, 32); + 8003e78: 466b mov r3, sp + 8003e7a: f104 02f8 add.w r2, r4, #248 ; 0xf8 + 8003e7e: ad08 add r5, sp, #32 + 8003e80: 461c mov r4, r3 + 8003e82: cc03 ldmia r4!, {r0, r1} + 8003e84: 42ac cmp r4, r5 + 8003e86: 6010 str r0, [r2, #0] + 8003e88: 6051 str r1, [r2, #4] + 8003e8a: 4623 mov r3, r4 + 8003e8c: f102 0208 add.w r2, r2, #8 + 8003e90: d1f6 bne.n 8003e80 +} + 8003e92: b009 add sp, #36 ; 0x24 + 8003e94: bd30 pop {r4, r5, pc} + 8003e96: bf00 nop + 8003e98: 2eaf6312 .word 0x2eaf6312 + 8003e9c: 0800e3e0 .word 0x0800e3e0 + +08003ea0 : +{ + 8003ea0: b510 push {r4, lr} + ASSERT(args->magic_value == PA_MAGIC_V2); + 8003ea2: 6802 ldr r2, [r0, #0] + 8003ea4: 4b14 ldr r3, [pc, #80] ; (8003ef8 ) + 8003ea6: 429a cmp r2, r3 +{ + 8003ea8: b088 sub sp, #32 + 8003eaa: 460c mov r4, r1 + ASSERT(args->magic_value == PA_MAGIC_V2); + 8003eac: d002 beq.n 8003eb4 + 8003eae: 4813 ldr r0, [pc, #76] ; (8003efc ) + 8003eb0: f7fc fdca bl 8000a48 + memcpy(digest, args->cached_main_pin, 32); + 8003eb4: f100 03f8 add.w r3, r0, #248 ; 0xf8 + 8003eb8: 460a mov r2, r1 + 8003eba: f500 708c add.w r0, r0, #280 ; 0x118 + 8003ebe: f853 1b04 ldr.w r1, [r3], #4 + 8003ec2: f842 1b04 str.w r1, [r2], #4 + 8003ec6: 4283 cmp r3, r0 + 8003ec8: d1f9 bne.n 8003ebe + if(!check_all_zeros(digest, 32)) { + 8003eca: 2120 movs r1, #32 + 8003ecc: 4620 mov r0, r4 + 8003ece: f7fe fbe1 bl 8002694 + 8003ed2: b970 cbnz r0, 8003ef2 + pin_cache_get_key(key); + 8003ed4: 4668 mov r0, sp + 8003ed6: f7ff ff95 bl 8003e04 + 8003eda: 1e62 subs r2, r4, #1 + 8003edc: 466b mov r3, sp + 8003ede: 341f adds r4, #31 + *(acc) ^= *(more); + 8003ee0: f812 1f01 ldrb.w r1, [r2, #1]! + 8003ee4: f813 0b01 ldrb.w r0, [r3], #1 + for(; len; len--, more++, acc++) { + 8003ee8: 42a2 cmp r2, r4 + *(acc) ^= *(more); + 8003eea: ea81 0100 eor.w r1, r1, r0 + 8003eee: 7011 strb r1, [r2, #0] + for(; len; len--, more++, acc++) { + 8003ef0: d1f6 bne.n 8003ee0 +} + 8003ef2: b008 add sp, #32 + 8003ef4: bd10 pop {r4, pc} + 8003ef6: bf00 nop + 8003ef8: 2eaf6312 .word 0x2eaf6312 + 8003efc: 0800e3e0 .word 0x0800e3e0 + +08003f00 : +{ + 8003f00: b530 push {r4, r5, lr} + 8003f02: b091 sub sp, #68 ; 0x44 + pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_WORDS); + 8003f04: 4b0b ldr r3, [pc, #44] ; (8003f34 ) +{ + 8003f06: 4615 mov r5, r2 + pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_WORDS); + 8003f08: 466a mov r2, sp + 8003f0a: f7ff fd97 bl 8003a3c + ae_setup(); + 8003f0e: f7fe fd59 bl 80029c4 + int rv = ae_stretch_iter(tmp, digest, KDF_ITER_WORDS); + 8003f12: 2206 movs r2, #6 + 8003f14: a908 add r1, sp, #32 + 8003f16: 4668 mov r0, sp + 8003f18: f7ff fc72 bl 8003800 + 8003f1c: 4604 mov r4, r0 + ae_reset_chip(); + 8003f1e: f7fe fd43 bl 80029a8 + if(rv) return -1; + 8003f22: b924 cbnz r4, 8003f2e + memcpy(result, digest, 4); + 8003f24: 9b08 ldr r3, [sp, #32] + 8003f26: 602b str r3, [r5, #0] +} + 8003f28: 4620 mov r0, r4 + 8003f2a: b011 add sp, #68 ; 0x44 + 8003f2c: bd30 pop {r4, r5, pc} + if(rv) return -1; + 8003f2e: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 8003f32: e7f9 b.n 8003f28 + 8003f34: 2e6d6773 .word 0x2e6d6773 + +08003f38 : +} + 8003f38: 2000 movs r0, #0 + 8003f3a: 4770 bx lr + +08003f3c : +{ + 8003f3c: b5f0 push {r4, r5, r6, r7, lr} + int rv = _validate_attempt(args, true); + 8003f3e: 2101 movs r1, #1 +{ + 8003f40: b091 sub sp, #68 ; 0x44 + 8003f42: 4605 mov r5, r0 + int rv = _validate_attempt(args, true); + 8003f44: f7ff fde6 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 8003f48: 4604 mov r4, r0 + 8003f4a: bb28 cbnz r0, 8003f98 + if(args->is_secondary) { + 8003f4c: 686b ldr r3, [r5, #4] + 8003f4e: 2b00 cmp r3, #0 + 8003f50: d158 bne.n 8004004 + int pin_len = args->pin_len; + 8003f52: 6aaf ldr r7, [r5, #40] ; 0x28 + memcpy(pin_copy, args->pin, pin_len); + 8003f54: f105 0608 add.w r6, r5, #8 + 8003f58: 463a mov r2, r7 + 8003f5a: 4631 mov r1, r6 + 8003f5c: 4668 mov r0, sp + 8003f5e: f009 fb61 bl 800d624 + memset(args, 0, PIN_ATTEMPT_SIZE_V2); + 8003f62: f44f 728c mov.w r2, #280 ; 0x118 + 8003f66: 4621 mov r1, r4 + 8003f68: 4628 mov r0, r5 + 8003f6a: f009 fb83 bl 800d674 + args->magic_value = PA_MAGIC_V2; + 8003f6e: 4b28 ldr r3, [pc, #160] ; (8004010 ) + 8003f70: 602b str r3, [r5, #0] + memcpy(args->pin, pin_copy, pin_len); + 8003f72: 463a mov r2, r7 + 8003f74: 4669 mov r1, sp + args->pin_len = pin_len; + 8003f76: 62af str r7, [r5, #40] ; 0x28 + memcpy(args->pin, pin_copy, pin_len); + 8003f78: 4630 mov r0, r6 + 8003f7a: f009 fb53 bl 800d624 + if(warmup_ae()) { + 8003f7e: f7ff fdff bl 8003b80 + 8003f82: 2800 cmp r0, #0 + 8003f84: d141 bne.n 800400a + if(get_last_success(args)) { + 8003f86: 4628 mov r0, r5 + 8003f88: f7ff fe37 bl 8003bfa + 8003f8c: 4604 mov r4, r0 + 8003f8e: b130 cbz r0, 8003f9e + ae_reset_chip(); + 8003f90: f7fe fd0a bl 80029a8 + return EPIN_AE_FAIL; + 8003f94: f06f 0469 mvn.w r4, #105 ; 0x69 +} + 8003f98: 4620 mov r0, r4 + 8003f9a: b011 add sp, #68 ; 0x44 + 8003f9c: bdf0 pop {r4, r5, r6, r7, pc} + uint8_t blank[32] = {0}; + 8003f9e: 4601 mov r1, r0 + 8003fa0: 221c movs r2, #28 + args->delay_achieved = 0; + 8003fa2: e9c5 000b strd r0, r0, [r5, #44] ; 0x2c + uint8_t blank[32] = {0}; + 8003fa6: 9008 str r0, [sp, #32] + 8003fa8: a809 add r0, sp, #36 ; 0x24 + 8003faa: f009 fb63 bl 800d674 + ae_reset_chip(); + 8003fae: f7fe fcfb bl 80029a8 + ae_pair_unlock(); + 8003fb2: f7fe fefd bl 8002db0 + int is_blank = (ae_checkmac_hard(keynum, blank) == 0); + 8003fb6: a908 add r1, sp, #32 + 8003fb8: 2003 movs r0, #3 + 8003fba: f7ff f887 bl 80030cc + 8003fbe: 4606 mov r6, r0 + ae_reset_chip(); + 8003fc0: f7fe fcf2 bl 80029a8 + if(pin_is_blank(KEYNUM_main_pin)) { + 8003fc4: b9c6 cbnz r6, 8003ff8 + args->state_flags |= PA_SUCCESSFUL | PA_IS_BLANK; + 8003fc6: 6beb ldr r3, [r5, #60] ; 0x3c + const uint8_t zeros[32] = {0}; + 8003fc8: 9408 str r4, [sp, #32] + args->state_flags |= PA_SUCCESSFUL | PA_IS_BLANK; + 8003fca: f043 0303 orr.w r3, r3, #3 + 8003fce: 63eb str r3, [r5, #60] ; 0x3c + const uint8_t zeros[32] = {0}; + 8003fd0: 221c movs r2, #28 + 8003fd2: 4621 mov r1, r4 + 8003fd4: a809 add r0, sp, #36 ; 0x24 + 8003fd6: f009 fb4d bl 800d674 + pin_cache_save(args, zeros); + 8003fda: a908 add r1, sp, #32 + 8003fdc: 4628 mov r0, r5 + 8003fde: f7ff ff27 bl 8003e30 + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8003fe2: f7fe fb77 bl 80026d4 + 8003fe6: 4b0b ldr r3, [pc, #44] ; (8004014 ) + 8003fe8: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8003fec: f020 0001 bic.w r0, r0, #1 + args->delay_achieved = 0; + 8003ff0: e9c5 440b strd r4, r4, [r5, #44] ; 0x2c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8003ff4: 4058 eors r0, r3 + 8003ff6: 6428 str r0, [r5, #64] ; 0x40 + _hmac_attempt(args, args->hmac); + 8003ff8: f105 0144 add.w r1, r5, #68 ; 0x44 + 8003ffc: 4628 mov r0, r5 + 8003ffe: f7ff fd5b bl 8003ab8 <_hmac_attempt> +} + 8004002: e7c9 b.n 8003f98 + return EPIN_PRIMARY_ONLY; + 8004004: f06f 0471 mvn.w r4, #113 ; 0x71 + 8004008: e7c6 b.n 8003f98 + return EPIN_I_AM_BRICK; + 800400a: f06f 0468 mvn.w r4, #104 ; 0x68 + 800400e: e7c3 b.n 8003f98 + 8004010: 2eaf6312 .word 0x2eaf6312 + 8004014: 0801c000 .word 0x0801c000 + +08004018 : +} + 8004018: 2000 movs r0, #0 + 800401a: 4770 bx lr + +0800401c : +// +// Do the PIN check, and return a value. Or fail. +// + int +pin_login_attempt(pinAttempt_t *args) +{ + 800401c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + bool deltamode = false; + char tmp_pin[32]; + + int rv = _validate_attempt(args, false); + 8004020: 2100 movs r1, #0 +{ + 8004022: b0c7 sub sp, #284 ; 0x11c + 8004024: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 8004026: f7ff fd75 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 800402a: 4605 mov r5, r0 + 800402c: 2800 cmp r0, #0 + 800402e: d179 bne.n 8004124 + + if(args->state_flags & PA_SUCCESSFUL) { + 8004030: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004032: 07d9 lsls r1, r3, #31 + 8004034: f100 80c5 bmi.w 80041c2 + } + + // Mk4: Check SE2 first to see if this is a "trick" pin. + // - this call may have side-effects, like wiping keys, bricking, etc. + trick_slot_t slot; + bool is_trick = se2_test_trick_pin(args->pin, args->pin_len, &slot, false); + 8004038: f104 0808 add.w r8, r4, #8 + 800403c: 4603 mov r3, r0 + 800403e: 6aa1 ldr r1, [r4, #40] ; 0x28 + 8004040: aa26 add r2, sp, #152 ; 0x98 + 8004042: 4640 mov r0, r8 + 8004044: f003 fee8 bl 8007e18 + + if(is_trick) { + 8004048: 4606 mov r6, r0 + 800404a: 2800 cmp r0, #0 + 800404c: d04b beq.n 80040e6 + // Mark as success + args->state_flags = PA_SUCCESSFUL; + args->num_fails = 0; + args->attempts_left = MAX_TARGET_ATTEMPTS; + + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 800404e: f9bd 209c ldrsh.w r2, [sp, #156] ; 0x9c + args->num_fails = 0; + 8004052: 6365 str r5, [r4, #52] ; 0x34 + args->state_flags = PA_SUCCESSFUL; + 8004054: 2301 movs r3, #1 + 8004056: 63e3 str r3, [r4, #60] ; 0x3c + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 8004058: 2a00 cmp r2, #0 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 800405a: f04f 030d mov.w r3, #13 + 800405e: 63a3 str r3, [r4, #56] ; 0x38 + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 8004060: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + 8004064: da4f bge.n 8004106 + 8004066: f413 5fc0 tst.w r3, #6144 ; 0x1800 + 800406a: bf0c ite eq + 800406c: 2701 moveq r7, #1 + 800406e: 2700 movne r7, #0 + if(check_all_zeros(slot.xdata, 32) || wipe) { + 8004070: 2120 movs r1, #32 + 8004072: a828 add r0, sp, #160 ; 0xa0 + 8004074: f7fe fb0e bl 8002694 + 8004078: b900 cbnz r0, 800407c + 800407a: b11f cbz r7, 8004084 + args->state_flags |= PA_ZERO_SECRET; + 800407c: 6be3 ldr r3, [r4, #60] ; 0x3c + 800407e: f043 0310 orr.w r3, r3, #16 + 8004082: 63e3 str r3, [r4, #60] ; 0x3c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8004084: f7fe fb26 bl 80026d4 + 8004088: 4b51 ldr r3, [pc, #324] ; (80041d0 ) + 800408a: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 800408e: f040 0001 orr.w r0, r0, #1 + 8004092: 4058 eors r0, r3 + args->delay_required = (slot->tc_flags & ~TC_HIDDEN_MASK); + 8004094: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8004098: 6420 str r0, [r4, #64] ; 0x40 + args->delay_required = (slot->tc_flags & ~TC_HIDDEN_MASK); + 800409a: f423 4278 bic.w r2, r3, #63488 ; 0xf800 + 800409e: 6322 str r2, [r4, #48] ; 0x30 + if(slot->tc_flags & TC_DELTA_MODE) { + 80040a0: 055a lsls r2, r3, #21 + 80040a2: d532 bpl.n 800410a + args->delay_achieved = 0; + 80040a4: 2300 movs r3, #0 + 80040a6: 62e3 str r3, [r4, #44] ; 0x2c + memcpy(tmp_pin, pin, pin_len); + 80040a8: 6aa7 ldr r7, [r4, #40] ; 0x28 + // Thug gave wrong PIN, but we are going to let them + // past (by calculating correct PIN, up to 4 digits different), + // and the mpy firmware can do tricky stuff to protect funds + // even though the private key is known at that point. + deltamode = true; + apply_pin_delta(args->pin, args->pin_len, slot.tc_arg, tmp_pin); + 80040aa: f8bd 909e ldrh.w r9, [sp, #158] ; 0x9e + memcpy(tmp_pin, pin, pin_len); + 80040ae: ab04 add r3, sp, #16 + 80040b0: 463a mov r2, r7 + 80040b2: 4641 mov r1, r8 + 80040b4: 4618 mov r0, r3 + 80040b6: f009 fab5 bl 800d624 + tmp_pin[pin_len] = 0; + 80040ba: 2200 movs r2, #0 + 80040bc: 55c2 strb r2, [r0, r7] + char *p = &tmp_pin[pin_len-1]; + 80040be: 1e7a subs r2, r7, #1 + 80040c0: 4402 add r2, r0 + 80040c2: 2104 movs r1, #4 + if(*p == '-') p--; + 80040c4: 7813 ldrb r3, [r2, #0] + 80040c6: 2b2d cmp r3, #45 ; 0x2d + 80040c8: f009 030f and.w r3, r9, #15 + 80040cc: bf08 it eq + 80040ce: f102 32ff addeq.w r2, r2, #4294967295 ; 0xffffffff + if((here >= 0) && (here <= 9)) { + 80040d2: 2b09 cmp r3, #9 + *p = '0' + here; + 80040d4: bf9c itt ls + 80040d6: 3330 addls r3, #48 ; 0x30 + 80040d8: 7013 strbls r3, [r2, #0] + for(int i=0; i<4; i++, p--) { + 80040da: 3901 subs r1, #1 + replacement >>= 4; + 80040dc: ea4f 1919 mov.w r9, r9, lsr #4 + for(int i=0; i<4; i++, p--) { + 80040e0: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 80040e4: d1ee bne.n 80040c4 + return 0; + } + +real_login: + // unlock the AE chip + if(warmup_ae()) return EPIN_I_AM_BRICK; + 80040e6: f7ff fd4b bl 8003b80 + 80040ea: 2800 cmp r0, #0 + 80040ec: d16c bne.n 80041c8 + + // hash up the pin now, assuming we'll use it on main PIN + uint8_t digest[32]; + rv = pin_hash_attempt(deltamode ? tmp_pin : args->pin, args->pin_len, digest); + 80040ee: b10e cbz r6, 80040f4 + 80040f0: f10d 0810 add.w r8, sp, #16 + 80040f4: 6aa1 ldr r1, [r4, #40] ; 0x28 + 80040f6: aa0c add r2, sp, #48 ; 0x30 + 80040f8: 4640 mov r0, r8 + 80040fa: f7ff fe15 bl 8003d28 + if(rv) return EPIN_AE_FAIL; + 80040fe: b1a8 cbz r0, 800412c + + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + if(rv) { + ae_reset_chip(); + + return EPIN_AE_FAIL; + 8004100: f06f 0569 mvn.w r5, #105 ; 0x69 + 8004104: e00e b.n 8004124 + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 8004106: 462f mov r7, r5 + 8004108: e7b2 b.n 8004070 + 800410a: a926 add r1, sp, #152 ; 0x98 + 800410c: 4620 mov r0, r4 + 800410e: f7ff fe39 bl 8003d84 + if(slot.tc_flags & TC_DELTA_MODE) { + 8004112: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + 8004116: 055b lsls r3, r3, #21 + 8004118: d4c6 bmi.n 80040a8 + _hmac_attempt(args, args->hmac); + 800411a: f104 0144 add.w r1, r4, #68 ; 0x44 + 800411e: 4620 mov r0, r4 + 8004120: f7ff fcca bl 8003ab8 <_hmac_attempt> + } + + _sign_attempt(args); + + return 0; +} + 8004124: 4628 mov r0, r5 + 8004126: b047 add sp, #284 ; 0x11c + 8004128: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + ae_reset_chip(); + 800412c: f7fe fc3c bl 80029a8 + ae_pair_unlock(); + 8004130: f7fe fe3e bl 8002db0 + return (ae_checkmac_hard(KEYNUM_main_pin, digest) == 0); + 8004134: a90c add r1, sp, #48 ; 0x30 + 8004136: 2003 movs r0, #3 + 8004138: f7fe ffc8 bl 80030cc + if(!is_main_pin(digest)) { + 800413c: b130 cbz r0, 800414c + se2_handle_bad_pin(args->num_fails + 1); + 800413e: 6b60 ldr r0, [r4, #52] ; 0x34 + 8004140: 3001 adds r0, #1 + 8004142: f003 ff45 bl 8007fd0 + return EPIN_AUTH_FAIL; + 8004146: f06f 056f mvn.w r5, #111 ; 0x6f + 800414a: e7eb b.n 8004124 + rv = updates_for_good_login(digest); + 800414c: a80c add r0, sp, #48 ; 0x30 + 800414e: f7ff fd9d bl 8003c8c + if(rv) return EPIN_AE_FAIL; + 8004152: 4607 mov r7, r0 + 8004154: 2800 cmp r0, #0 + 8004156: d1d3 bne.n 8004100 + pin_cache_save(args, digest); + 8004158: a90c add r1, sp, #48 ; 0x30 + 800415a: 4620 mov r0, r4 + 800415c: f7ff fe68 bl 8003e30 + args->state_flags = PA_SUCCESSFUL; + 8004160: 2301 movs r3, #1 + 8004162: 63e3 str r3, [r4, #60] ; 0x3c + args->num_fails = 0; + 8004164: 6367 str r7, [r4, #52] ; 0x34 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 8004166: 230d movs r3, #13 + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + 8004168: 2748 movs r7, #72 ; 0x48 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 800416a: 63a3 str r3, [r4, #56] ; 0x38 + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + 800416c: 9700 str r7, [sp, #0] + 800416e: ab14 add r3, sp, #80 ; 0x50 + 8004170: aa0c add r2, sp, #48 ; 0x30 + 8004172: 2103 movs r1, #3 + 8004174: 2009 movs r0, #9 + 8004176: f7ff f88b bl 8003290 + if(rv) { + 800417a: b110 cbz r0, 8004182 + ae_reset_chip(); + 800417c: f7fe fc14 bl 80029a8 + 8004180: e7be b.n 8004100 + ae_reset_chip(); + 8004182: f7fe fc11 bl 80029a8 + mcu_key_get(&mcu_key_valid); + 8004186: f10d 000f add.w r0, sp, #15 + 800418a: f7fe f945 bl 8002418 + if(check_all_zeros(ts, AE_SECRET_LEN) || !mcu_key_valid) { + 800418e: 4639 mov r1, r7 + 8004190: a814 add r0, sp, #80 ; 0x50 + 8004192: f7fe fa7f bl 8002694 + 8004196: b910 cbnz r0, 800419e + 8004198: f89d 300f ldrb.w r3, [sp, #15] + 800419c: b91b cbnz r3, 80041a6 + args->state_flags |= PA_ZERO_SECRET; + 800419e: 6be3 ldr r3, [r4, #60] ; 0x3c + 80041a0: f043 0310 orr.w r3, r3, #16 + 80041a4: 63e3 str r3, [r4, #60] ; 0x3c + if(!deltamode) { + 80041a6: 2e00 cmp r6, #0 + 80041a8: d1b7 bne.n 800411a + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 80041aa: f7fe fa93 bl 80026d4 + 80041ae: 4b08 ldr r3, [pc, #32] ; (80041d0 ) + 80041b0: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80041b4: f020 0001 bic.w r0, r0, #1 + 80041b8: 4058 eors r0, r3 + args->delay_achieved = 0; + 80041ba: e9c4 660b strd r6, r6, [r4, #44] ; 0x2c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 80041be: 6420 str r0, [r4, #64] ; 0x40 + return; + 80041c0: e7ab b.n 800411a + return EPIN_WRONG_SUCCESS; + 80041c2: f06f 056c mvn.w r5, #108 ; 0x6c + 80041c6: e7ad b.n 8004124 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 80041c8: f06f 0568 mvn.w r5, #104 ; 0x68 + 80041cc: e7aa b.n 8004124 + 80041ce: bf00 nop + 80041d0: 0801c000 .word 0x0801c000 + +080041d4 : +// +// Verify we know the main PIN, but don't do anything with it. +// + int +pin_check_logged_in(const pinAttempt_t *args, bool *is_trick) +{ + 80041d4: b570 push {r4, r5, r6, lr} + 80041d6: 460e mov r6, r1 + 80041d8: b088 sub sp, #32 + int rv = _validate_attempt(args, false); + 80041da: 2100 movs r1, #0 +{ + 80041dc: 4605 mov r5, r0 + int rv = _validate_attempt(args, false); + 80041de: f7ff fc99 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 80041e2: 4604 mov r4, r0 + 80041e4: b980 cbnz r0, 8004208 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80041e6: 6beb ldr r3, [r5, #60] ; 0x3c + 80041e8: 07da lsls r2, r3, #31 + 80041ea: d520 bpl.n 800422e + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 80041ec: 4b11 ldr r3, [pc, #68] ; (8004234 ) + 80041ee: 6c2a ldr r2, [r5, #64] ; 0x40 + 80041f0: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80041f4: 4053 eors r3, r2 + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(get_is_trick(args, NULL)) { + 80041f6: 07db lsls r3, r3, #31 + 80041f8: d509 bpl.n 800420e + // they used a trick pin to get this far. Amuse them more. + *is_trick = true; + 80041fa: 2301 movs r3, #1 + 80041fc: 7033 strb r3, [r6, #0] + + // should calibrate this, but smart money will just look at the bus + delay_ms(10); + 80041fe: 200a movs r0, #10 + 8004200: f7ff fb7a bl 80038f8 + rng_delay(); + 8004204: f7fe faba bl 800277c + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + if(rv) return EPIN_AUTH_FAIL; + } + + return 0; +} + 8004208: 4620 mov r0, r4 + 800420a: b008 add sp, #32 + 800420c: bd70 pop {r4, r5, r6, pc} + pin_cache_restore(args, auth_digest); + 800420e: 4669 mov r1, sp + *is_trick = false; + 8004210: 7030 strb r0, [r6, #0] + pin_cache_restore(args, auth_digest); + 8004212: 4628 mov r0, r5 + 8004214: f7ff fe44 bl 8003ea0 + ae_pair_unlock(); + 8004218: f7fe fdca bl 8002db0 + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + 800421c: 4669 mov r1, sp + 800421e: 2003 movs r0, #3 + 8004220: f7fe fd44 bl 8002cac + if(rv) return EPIN_AUTH_FAIL; + 8004224: 1e04 subs r4, r0, #0 + 8004226: bf18 it ne + 8004228: f06f 046f mvnne.w r4, #111 ; 0x6f + 800422c: e7ec b.n 8004208 + return EPIN_WRONG_SUCCESS; + 800422e: f06f 046c mvn.w r4, #108 ; 0x6c + 8004232: e7e9 b.n 8004208 + 8004234: 0801c000 .word 0x0801c000 + +08004238 : +// +// Change the PIN and/or the secret. (Must also know the previous value, or it must be blank) +// + int +pin_change(pinAttempt_t *args) +{ + 8004238: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 800423c: 2100 movs r1, #0 +{ + 800423e: b0a4 sub sp, #144 ; 0x90 + 8004240: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 8004242: f7ff fc67 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 8004246: 4605 mov r5, r0 + 8004248: 2800 cmp r0, #0 + 800424a: f040 8094 bne.w 8004376 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 800424e: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004250: 07d9 lsls r1, r3, #31 + 8004252: f140 809c bpl.w 800438e + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(args->state_flags & PA_IS_BLANK) { + 8004256: 079a lsls r2, r3, #30 + 8004258: d502 bpl.n 8004260 + // if blank, must provide blank value + if(args->pin_len) return EPIN_RANGE_ERR; + 800425a: 6aa3 ldr r3, [r4, #40] ; 0x28 + 800425c: 2b00 cmp r3, #0 + 800425e: d158 bne.n 8004312 + } + + // Look at change flags. + const uint32_t cf = args->change_flags; + + ASSERT(!args->is_secondary); + 8004260: 6863 ldr r3, [r4, #4] + const uint32_t cf = args->change_flags; + 8004262: f8d4 9064 ldr.w r9, [r4, #100] ; 0x64 + ASSERT(!args->is_secondary); + 8004266: b113 cbz r3, 800426e + 8004268: 484c ldr r0, [pc, #304] ; (800439c ) + 800426a: f7fc fbed bl 8000a48 + if(cf & CHANGE_SECONDARY_WALLET_PIN) { + // obsolete secondary support, can't support. + return EPIN_BAD_REQUEST; + } + if(cf & (CHANGE_DURESS_PIN | CHANGE_DURESS_SECRET | CHANGE_BRICKME_PIN)) { + 800426e: f019 0f36 tst.w r9, #54 ; 0x36 + 8004272: d10b bne.n 800428c + // we need some new API for trick PIN lookup/changes. + return EPIN_BAD_REQUEST; + } + if(!(cf & (CHANGE_WALLET_PIN | CHANGE_SECRET))) { + 8004274: f019 0f09 tst.w r9, #9 + 8004278: d04b beq.n 8004312 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 800427a: 4b49 ldr r3, [pc, #292] ; (80043a0 ) + 800427c: 6c22 ldr r2, [r4, #64] ; 0x40 + 800427e: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8004282: 4053 eors r3, r2 + // If they authorized w/ a trick PIN, new policy is to wipe ourselves if + // they try to change PIN code or the secret. + // - it's hard to fake them out here, and they may be onto us. + // - this protects the seed, but does end the game somewhat + // - all trick PINs will still be in effect, and looks like random reset + if(get_is_trick(args, NULL)) { + 8004284: 07db lsls r3, r3, #31 + 8004286: d504 bpl.n 8004292 + // User is a thug.. kill secret and reboot w/o any notice + fast_wipe(); + 8004288: f7fe f9d8 bl 800263c + return EPIN_BAD_REQUEST; + 800428c: f06f 0567 mvn.w r5, #103 ; 0x67 + 8004290: e071 b.n 8004376 + // NOT-REACHED + return EPIN_BAD_REQUEST; + } + + // unlock the AE chip + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004292: f7ff fc75 bl 8003b80 + 8004296: 4605 mov r5, r0 + 8004298: 2800 cmp r0, #0 + 800429a: d17b bne.n 8004394 + // If they tricked us to get to this point, doesn't matter as + // below SE1 validates it all again. + + // Restore cached version of PIN digest: fast + uint8_t required_digest[32]; + pin_cache_restore(args, required_digest); + 800429c: f10d 0808 add.w r8, sp, #8 + 80042a0: 4641 mov r1, r8 + 80042a2: 4620 mov r0, r4 + 80042a4: f7ff fdfc bl 8003ea0 + + // Calculate new PIN hashed value: will be slow to do + if(cf & CHANGE_WALLET_PIN) { + 80042a8: f019 0f01 tst.w r9, #1 + 80042ac: d021 beq.n 80042f2 + uint8_t new_digest[32]; + rv = pin_hash_attempt(args->new_pin, args->new_pin_len, new_digest); + 80042ae: f8d4 10ac ldr.w r1, [r4, #172] ; 0xac + 80042b2: aa12 add r2, sp, #72 ; 0x48 + 80042b4: f104 008c add.w r0, r4, #140 ; 0x8c + 80042b8: f7ff fd36 bl 8003d28 + if(rv) goto ae_fail; + 80042bc: 2800 cmp r0, #0 + 80042be: d161 bne.n 8004384 + + if(ae_encrypted_write(KEYNUM_main_pin, KEYNUM_main_pin, required_digest, new_digest, 32)) { + 80042c0: 2320 movs r3, #32 + 80042c2: 2103 movs r1, #3 + 80042c4: 9300 str r3, [sp, #0] + 80042c6: 4642 mov r2, r8 + 80042c8: ab12 add r3, sp, #72 ; 0x48 + 80042ca: 4608 mov r0, r1 + 80042cc: f7ff f880 bl 80033d0 + 80042d0: 2800 cmp r0, #0 + 80042d2: d157 bne.n 8004384 + goto ae_fail; + } + + memcpy(required_digest, new_digest, 32); + 80042d4: af12 add r7, sp, #72 ; 0x48 + 80042d6: cf0f ldmia r7!, {r0, r1, r2, r3} + 80042d8: 4646 mov r6, r8 + 80042da: c60f stmia r6!, {r0, r1, r2, r3} + 80042dc: e897 000f ldmia.w r7, {r0, r1, r2, r3} + 80042e0: e886 000f stmia.w r6, {r0, r1, r2, r3} + + // main pin is changing; reset counter to zero (good login) and our cache + pin_cache_save(args, new_digest); + 80042e4: 4620 mov r0, r4 + 80042e6: a912 add r1, sp, #72 ; 0x48 + 80042e8: f7ff fda2 bl 8003e30 + + updates_for_good_login(new_digest); + 80042ec: a812 add r0, sp, #72 ; 0x48 + 80042ee: f7ff fccd bl 8003c8c + } + + // Recording new secret. + // Note the required_digest might have just changed above. + if(cf & CHANGE_SECRET) { + 80042f2: f019 0f08 tst.w r9, #8 + 80042f6: d037 beq.n 8004368 + int which = (args->change_flags >> 8) & 0xf; + 80042f8: 6e63 ldr r3, [r4, #100] ; 0x64 + 80042fa: 121b asrs r3, r3, #8 + switch(which) { + 80042fc: f013 020c ands.w r2, r3, #12 + 8004300: d107 bne.n 8004312 + 8004302: 4928 ldr r1, [pc, #160] ; (80043a4 ) + int which = (args->change_flags >> 8) & 0xf; + 8004304: f003 030f and.w r3, r3, #15 + 8004308: f911 a003 ldrsb.w sl, [r1, r3] + uint8_t tmp[AE_SECRET_LEN]; + uint8_t check[32]; + + // what slot (key number) are updating? (probably: KEYNUM_secret) + int target_slot = keynum_for_secret(args); + if(target_slot < 0) return EPIN_RANGE_ERR; + 800430c: f1ba 0f00 cmp.w sl, #0 + 8004310: da02 bge.n 8004318 + if(args->pin_len) return EPIN_RANGE_ERR; + 8004312: f06f 0566 mvn.w r5, #102 ; 0x66 + 8004316: e02e b.n 8004376 + + se2_encrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, required_digest); + 8004318: f104 07b0 add.w r7, r4, #176 ; 0xb0 + 800431c: ae0a add r6, sp, #40 ; 0x28 + 800431e: ab12 add r3, sp, #72 ; 0x48 + 8004320: 2148 movs r1, #72 ; 0x48 + + // write into two slots + if(ae_encrypted_write(target_slot, KEYNUM_main_pin, + 8004322: f04f 0948 mov.w r9, #72 ; 0x48 + se2_encrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, required_digest); + 8004326: f8cd 8004 str.w r8, [sp, #4] + 800432a: 9600 str r6, [sp, #0] + 800432c: 4638 mov r0, r7 + 800432e: f003 ff33 bl 8008198 + if(ae_encrypted_write(target_slot, KEYNUM_main_pin, + 8004332: 2103 movs r1, #3 + 8004334: f8cd 9000 str.w r9, [sp] + 8004338: eb0d 0309 add.w r3, sp, r9 + 800433c: 4642 mov r2, r8 + 800433e: 4650 mov r0, sl + 8004340: f7ff f846 bl 80033d0 + 8004344: 4601 mov r1, r0 + 8004346: b9e8 cbnz r0, 8004384 + required_digest, tmp, AE_SECRET_LEN)){ + goto ae_fail; + } + if(ae_encrypted_write32(KEYNUM_check_secret, 0, KEYNUM_main_pin, required_digest, check)){ + 8004348: 9600 str r6, [sp, #0] + 800434a: 4643 mov r3, r8 + 800434c: 2203 movs r2, #3 + 800434e: 200a movs r0, #10 + 8004350: f7fe ffd8 bl 8003304 + 8004354: b9b0 cbnz r0, 8004384 + goto ae_fail; + } + + // update the zero-secret flag to be correct. + if(cf & CHANGE_SECRET) { + if(check_all_zeros(args->secret, AE_SECRET_LEN)) { + 8004356: 4649 mov r1, r9 + 8004358: 4638 mov r0, r7 + 800435a: f7fe f99b bl 8002694 + 800435e: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004360: b168 cbz r0, 800437e + args->state_flags |= PA_ZERO_SECRET; + 8004362: f043 0310 orr.w r3, r3, #16 + 8004366: 63e3 str r3, [r4, #60] ; 0x3c + args->state_flags &= ~PA_ZERO_SECRET; + } + } + } + + ae_reset_chip(); + 8004368: f7fe fb1e bl 80029a8 + _hmac_attempt(args, args->hmac); + 800436c: f104 0144 add.w r1, r4, #68 ; 0x44 + 8004370: 4620 mov r0, r4 + 8004372: f7ff fba1 bl 8003ab8 <_hmac_attempt> + +ae_fail: + ae_reset_chip(); + + return EPIN_AE_FAIL; +} + 8004376: 4628 mov r0, r5 + 8004378: b024 add sp, #144 ; 0x90 + 800437a: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + args->state_flags &= ~PA_ZERO_SECRET; + 800437e: f023 0310 bic.w r3, r3, #16 + 8004382: e7f0 b.n 8004366 + ae_reset_chip(); + 8004384: f7fe fb10 bl 80029a8 + return EPIN_AE_FAIL; + 8004388: f06f 0569 mvn.w r5, #105 ; 0x69 + 800438c: e7f3 b.n 8004376 + return EPIN_WRONG_SUCCESS; + 800438e: f06f 056c mvn.w r5, #108 ; 0x6c + 8004392: e7f0 b.n 8004376 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004394: f06f 0568 mvn.w r5, #104 ; 0x68 + 8004398: e7ed b.n 8004376 + 800439a: bf00 nop + 800439c: 0800e3e0 .word 0x0800e3e0 + 80043a0: 0801c000 .word 0x0801c000 + 80043a4: 0800e71c .word 0x0800e71c + +080043a8 : +// To encourage not keeping the secret in memory, a way to fetch it after you've already +// proven you know the PIN. +// + int +pin_fetch_secret(pinAttempt_t *args) +{ + 80043a8: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 80043ac: 2100 movs r1, #0 +{ + 80043ae: f5ad 7d38 sub.w sp, sp, #736 ; 0x2e0 + 80043b2: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 80043b4: f7ff fbae bl 8003b14 <_validate_attempt> + if(rv) return rv; + 80043b8: 4605 mov r5, r0 + 80043ba: 2800 cmp r0, #0 + 80043bc: d144 bne.n 8004448 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80043be: 6be3 ldr r3, [r4, #60] ; 0x3c + 80043c0: 07db lsls r3, r3, #31 + 80043c2: f140 80e3 bpl.w 800458c + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + if(args->change_flags & CHANGE_DURESS_SECRET) { + 80043c6: 6e65 ldr r5, [r4, #100] ; 0x64 + 80043c8: f015 0510 ands.w r5, r5, #16 + 80043cc: f040 80e1 bne.w 8004592 + + // fetch the already-hashed pin + // - no real need to re-prove PIN knowledge. + // - if they tricked us, doesn't matter as below the SE validates it all again + uint8_t digest[32]; + pin_cache_restore(args, digest); + 80043d0: f10d 081c add.w r8, sp, #28 + 80043d4: 4641 mov r1, r8 + 80043d6: 4620 mov r0, r4 + 80043d8: f7ff fd62 bl 8003ea0 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 80043dc: 4b70 ldr r3, [pc, #448] ; (80045a0 ) + 80043de: 6c26 ldr r6, [r4, #64] ; 0x40 + 80043e0: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80043e4: 4073 eors r3, r6 + if(!slot || !is_trick) return is_trick; + 80043e6: 07df lsls r7, r3, #31 + 80043e8: d577 bpl.n 80044da + memset(slot, 0, sizeof(trick_slot_t)); + 80043ea: 2280 movs r2, #128 ; 0x80 + 80043ec: 4629 mov r1, r5 + 80043ee: a817 add r0, sp, #92 ; 0x5c + 80043f0: f009 f940 bl 800d674 + if(args->delay_required & TC_DELTA_MODE) { + 80043f4: 6b23 ldr r3, [r4, #48] ; 0x30 + 80043f6: 0558 lsls r0, r3, #21 + 80043f8: d52b bpl.n 8004452 + slot->tc_flags = args->delay_required; + 80043fa: f8ad 3060 strh.w r3, [sp, #96] ; 0x60 + slot->slot_num = -1; // unknown + 80043fe: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004402: 9317 str r3, [sp, #92] ; 0x5c + + // determine if we should proceed under duress + trick_slot_t slot; + bool is_trick = get_is_trick(args, &slot); + + if(is_trick && !(slot.tc_flags & TC_DELTA_MODE)) { + 8004404: f8bd 6060 ldrh.w r6, [sp, #96] ; 0x60 + 8004408: f416 6180 ands.w r1, r6, #1024 ; 0x400 + 800440c: d165 bne.n 80044da + // emulate a 24-word wallet, or xprv based wallet + // see stash.py for encoding details + memset(args->secret, 0, AE_SECRET_LEN); + 800440e: 2248 movs r2, #72 ; 0x48 + 8004410: f104 00b0 add.w r0, r4, #176 ; 0xb0 + 8004414: f009 f92e bl 800d674 + + if(slot.tc_flags & TC_WORD_WALLET) { + 8004418: 04f1 lsls r1, r6, #19 + 800441a: d54c bpl.n 80044b6 + if(check_all_zeros(&slot.xdata[16], 16)) { + 800441c: ae1d add r6, sp, #116 ; 0x74 + 800441e: 2110 movs r1, #16 + 8004420: 4630 mov r0, r6 + 8004422: f7fe f937 bl 8002694 + // 2nd half is zeros, must be 12-word wallet + args->secret[0] = 0x80; // 12 word phrase + memcpy(&args->secret[1], slot.xdata, 16); + 8004426: f104 03b1 add.w r3, r4, #177 ; 0xb1 + if(check_all_zeros(&slot.xdata[16], 16)) { + 800442a: 2800 cmp r0, #0 + 800442c: d034 beq.n 8004498 + args->secret[0] = 0x80; // 12 word phrase + 800442e: 2280 movs r2, #128 ; 0x80 + 8004430: f884 20b0 strb.w r2, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 16); + 8004434: ac19 add r4, sp, #100 ; 0x64 + 8004436: 4622 mov r2, r4 + 8004438: ca03 ldmia r2!, {r0, r1} + 800443a: 42b2 cmp r2, r6 + 800443c: 6018 str r0, [r3, #0] + 800443e: 6059 str r1, [r3, #4] + 8004440: 4614 mov r4, r2 + 8004442: f103 0308 add.w r3, r3, #8 + 8004446: d1f6 bne.n 8004436 + ae_reset_chip(); + + if(rv) return EPIN_AE_FAIL; + + return 0; +} + 8004448: 4628 mov r0, r5 + 800444a: f50d 7d38 add.w sp, sp, #736 ; 0x2e0 + 800444e: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8004452: 4f54 ldr r7, [pc, #336] ; (80045a4 ) + memcpy(key, &args->private_state, sizeof(args->private_state)); + 8004454: 960f str r6, [sp, #60] ; 0x3c + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8004456: cf0f ldmia r7!, {r0, r1, r2, r3} + 8004458: ae10 add r6, sp, #64 ; 0x40 + 800445a: c60f stmia r6!, {r0, r1, r2, r3} + 800445c: e897 0007 ldmia.w r7, {r0, r1, r2} + 8004460: e886 0007 stmia.w r6, {r0, r1, r2} + aes_init(&ctx); + 8004464: a837 add r0, sp, #220 ; 0xdc + 8004466: f003 fffb bl 8008460 + aes_add(&ctx, args->cached_main_pin, 32); + 800446a: 2220 movs r2, #32 + 800446c: f104 01f8 add.w r1, r4, #248 ; 0xf8 + 8004470: a837 add r0, sp, #220 ; 0xdc + 8004472: f003 fffb bl 800846c + aes_done(&ctx, (uint8_t *)slot, 32, key, NULL); + 8004476: a917 add r1, sp, #92 ; 0x5c + 8004478: 9500 str r5, [sp, #0] + 800447a: ab0f add r3, sp, #60 ; 0x3c + 800447c: 2220 movs r2, #32 + 800447e: a837 add r0, sp, #220 ; 0xdc + 8004480: f004 f80a bl 8008498 + if(slot->tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)) { + 8004484: f8bd 1060 ldrh.w r1, [sp, #96] ; 0x60 + 8004488: f411 5fc0 tst.w r1, #6144 ; 0x1800 + 800448c: d0ba beq.n 8004404 + se2_read_trick_data(slot->slot_num, slot->tc_flags, slot->xdata); + 800448e: 9817 ldr r0, [sp, #92] ; 0x5c + 8004490: aa19 add r2, sp, #100 ; 0x64 + 8004492: f003 fc87 bl 8007da4 + if(is_trick && !(slot.tc_flags & TC_DELTA_MODE)) { + 8004496: e7b5 b.n 8004404 + args->secret[0] = 0x82; // 24 word phrase + 8004498: 2282 movs r2, #130 ; 0x82 + 800449a: f884 20b0 strb.w r2, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 32); + 800449e: ae21 add r6, sp, #132 ; 0x84 + 80044a0: aa19 add r2, sp, #100 ; 0x64 + 80044a2: 4614 mov r4, r2 + 80044a4: cc03 ldmia r4!, {r0, r1} + 80044a6: 42b4 cmp r4, r6 + 80044a8: 6018 str r0, [r3, #0] + 80044aa: 6059 str r1, [r3, #4] + 80044ac: 4622 mov r2, r4 + 80044ae: f103 0308 add.w r3, r3, #8 + 80044b2: d1f6 bne.n 80044a2 + 80044b4: e7c8 b.n 8004448 + } else if(slot.tc_flags & TC_XPRV_WALLET) { + 80044b6: 0532 lsls r2, r6, #20 + 80044b8: d5c6 bpl.n 8004448 + args->secret[0] = 0x01; // XPRV mode + 80044ba: 2301 movs r3, #1 + 80044bc: f884 30b0 strb.w r3, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 64); + 80044c0: aa19 add r2, sp, #100 ; 0x64 + 80044c2: 34b1 adds r4, #177 ; 0xb1 + 80044c4: ae29 add r6, sp, #164 ; 0xa4 + 80044c6: 4613 mov r3, r2 + 80044c8: cb03 ldmia r3!, {r0, r1} + 80044ca: 42b3 cmp r3, r6 + 80044cc: 6020 str r0, [r4, #0] + 80044ce: 6061 str r1, [r4, #4] + 80044d0: 461a mov r2, r3 + 80044d2: f104 0408 add.w r4, r4, #8 + 80044d6: d1f6 bne.n 80044c6 + 80044d8: e7b6 b.n 8004448 + int which = (args->change_flags >> 8) & 0xf; + 80044da: 6e63 ldr r3, [r4, #100] ; 0x64 + 80044dc: 121b asrs r3, r3, #8 + switch(which) { + 80044de: f013 0f0c tst.w r3, #12 + 80044e2: d159 bne.n 8004598 + 80044e4: 4a30 ldr r2, [pc, #192] ; (80045a8 ) + int which = (args->change_flags >> 8) & 0xf; + 80044e6: f003 030f and.w r3, r3, #15 + 80044ea: f912 9003 ldrsb.w r9, [r2, r3] + if(kn < 0) return EPIN_RANGE_ERR; + 80044ee: f1b9 0f00 cmp.w r9, #0 + 80044f2: db51 blt.n 8004598 + 80044f4: 2703 movs r7, #3 + rv = ae_encrypted_read(kn, KEYNUM_main_pin, digest, tmp, AE_SECRET_LEN); + 80044f6: f04f 0a48 mov.w sl, #72 ; 0x48 + 80044fa: 2103 movs r1, #3 + 80044fc: f8cd a000 str.w sl, [sp] + 8004500: ab37 add r3, sp, #220 ; 0xdc + 8004502: 4642 mov r2, r8 + 8004504: 4648 mov r0, r9 + 8004506: f7fe fec3 bl 8003290 + if(rv) continue; + 800450a: 4601 mov r1, r0 + 800450c: b130 cbz r0, 800451c + for(int retry=0; retry<3; retry++) { + 800450e: 3f01 subs r7, #1 + 8004510: d1f3 bne.n 80044fa + ae_reset_chip(); + 8004512: f7fe fa49 bl 80029a8 + if(rv) return EPIN_AE_FAIL; + 8004516: f06f 0569 mvn.w r5, #105 ; 0x69 + 800451a: e795 b.n 8004448 + rv = ae_encrypted_read32(KEYNUM_check_secret, 0, KEYNUM_main_pin, digest, check); + 800451c: ae0f add r6, sp, #60 ; 0x3c + 800451e: 9600 str r6, [sp, #0] + 8004520: 4643 mov r3, r8 + 8004522: 2203 movs r2, #3 + 8004524: 200a movs r0, #10 + 8004526: f7fe fe88 bl 800323a + if(rv) continue; + 800452a: 4605 mov r5, r0 + 800452c: 2800 cmp r0, #0 + 800452e: d1ee bne.n 800450e + se2_decrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, digest, &is_valid); + 8004530: f10d 071b add.w r7, sp, #27 + 8004534: f104 00b0 add.w r0, r4, #176 ; 0xb0 + 8004538: ab37 add r3, sp, #220 ; 0xdc + 800453a: e9cd 8701 strd r8, r7, [sp, #4] + 800453e: 9600 str r6, [sp, #0] + 8004540: 462a mov r2, r5 + 8004542: 2148 movs r1, #72 ; 0x48 + 8004544: 9005 str r0, [sp, #20] + 8004546: f003 fe7d bl 8008244 + if(!is_valid) { + 800454a: f89d 301b ldrb.w r3, [sp, #27] + 800454e: 9805 ldr r0, [sp, #20] + 8004550: b993 cbnz r3, 8004578 + memset(args->secret, 0, AE_SECRET_LEN); + 8004552: 2248 movs r2, #72 ; 0x48 + 8004554: 4629 mov r1, r5 + 8004556: f009 f88d bl 800d674 + if(!(args->state_flags & PA_ZERO_SECRET)) { + 800455a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800455c: 06db lsls r3, r3, #27 + 800455e: d408 bmi.n 8004572 + args->state_flags |= PA_ZERO_SECRET; + 8004560: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004562: f043 0310 orr.w r3, r3, #16 + 8004566: 63e3 str r3, [r4, #60] ; 0x3c + _hmac_attempt(args, args->hmac); + 8004568: f104 0144 add.w r1, r4, #68 ; 0x44 + 800456c: 4620 mov r0, r4 + 800456e: f7ff faa3 bl 8003ab8 <_hmac_attempt> + ae_reset_chip(); + 8004572: f7fe fa19 bl 80029a8 + if(rv) return EPIN_AE_FAIL; + 8004576: e767 b.n 8004448 + if(!args->secret[0] && check_all_zeros(args->secret, AE_SECRET_LEN)) { + 8004578: f894 30b0 ldrb.w r3, [r4, #176] ; 0xb0 + 800457c: 2b00 cmp r3, #0 + 800457e: d1f8 bne.n 8004572 + 8004580: 2148 movs r1, #72 ; 0x48 + 8004582: f7fe f887 bl 8002694 + 8004586: 2800 cmp r0, #0 + 8004588: d0f3 beq.n 8004572 + 800458a: e7e9 b.n 8004560 + return EPIN_WRONG_SUCCESS; + 800458c: f06f 056c mvn.w r5, #108 ; 0x6c + 8004590: e75a b.n 8004448 + return EPIN_BAD_REQUEST; + 8004592: f06f 0567 mvn.w r5, #103 ; 0x67 + 8004596: e757 b.n 8004448 + if(kn < 0) return EPIN_RANGE_ERR; + 8004598: f06f 0566 mvn.w r5, #102 ; 0x66 + 800459c: e754 b.n 8004448 + 800459e: bf00 nop + 80045a0: 0801c000 .word 0x0801c000 + 80045a4: 0801c074 .word 0x0801c074 + 80045a8: 0800e71c .word 0x0800e71c + +080045ac : +// - new API so whole thing provided in one shot? encryption issues: provide +// "dest" and all 416 bytes end up there (read case only). +// + int +pin_long_secret(pinAttempt_t *args, uint8_t *dest) +{ + 80045ac: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 80045b0: 460f mov r7, r1 + 80045b2: b099 sub sp, #100 ; 0x64 + // Validate args and signature + int rv = _validate_attempt(args, false); + 80045b4: 2100 movs r1, #0 +{ + 80045b6: 4606 mov r6, r0 + int rv = _validate_attempt(args, false); + 80045b8: f7ff faac bl 8003b14 <_validate_attempt> + if(rv) return rv; + 80045bc: 4604 mov r4, r0 + 80045be: b9b8 cbnz r0, 80045f0 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80045c0: 6bf3 ldr r3, [r6, #60] ; 0x3c + 80045c2: 07da lsls r2, r3, #31 + 80045c4: f140 80a5 bpl.w 8004712 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 80045c8: 4b55 ldr r3, [pc, #340] ; (8004720 ) + 80045ca: 6c32 ldr r2, [r6, #64] ; 0x40 + 80045cc: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80045d0: 4053 eors r3, r2 + } + + // determine if we should proceed under duress/in some trick way + bool is_trick = get_is_trick(args, NULL); + + if(is_trick) { + 80045d2: 07db lsls r3, r3, #31 + 80045d4: d510 bpl.n 80045f8 + // Not supported in trick mode. Pretend it's all zeros. Accept all writes. + memset(args->secret, 0, 32); + 80045d6: 4601 mov r1, r0 + 80045d8: 2220 movs r2, #32 + 80045da: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 80045de: f009 f849 bl 800d674 + if(dest) memset(dest, 0, AE_LONG_SECRET_LEN); + 80045e2: b12f cbz r7, 80045f0 + 80045e4: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 80045e8: 4621 mov r1, r4 + 80045ea: 4638 mov r0, r7 + 80045ec: f009 f842 bl 800d674 + +se2_fail: + ae_reset_chip(); + + return EPIN_SE2_FAIL; +} + 80045f0: 4620 mov r0, r4 + 80045f2: b019 add sp, #100 ; 0x64 + 80045f4: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + int blk = (args->change_flags >> 8) & 0xf; + 80045f8: 6e73 ldr r3, [r6, #100] ; 0x64 + 80045fa: f3c3 2803 ubfx r8, r3, #8, #4 + if(blk > 13) return EPIN_RANGE_ERR; + 80045fe: f1b8 0f0d cmp.w r8, #13 + 8004602: f300 8089 bgt.w 8004718 + pin_cache_restore(args, digest); + 8004606: a908 add r1, sp, #32 + 8004608: 4630 mov r0, r6 + 800460a: f7ff fc49 bl 8003ea0 + if(!(args->change_flags & CHANGE_SECRET)) { + 800460e: 6e71 ldr r1, [r6, #100] ; 0x64 + 8004610: f011 0908 ands.w r9, r1, #8 + 8004614: d156 bne.n 80046c4 + if(!dest) { + 8004616: bb27 cbnz r7, 8004662 + rv = ae_encrypted_read32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, tmp); + 8004618: af10 add r7, sp, #64 ; 0x40 + 800461a: 9700 str r7, [sp, #0] + 800461c: ab08 add r3, sp, #32 + 800461e: 2203 movs r2, #3 + 8004620: 4641 mov r1, r8 + 8004622: 2008 movs r0, #8 + 8004624: f7fe fe09 bl 800323a + if(rv) goto fail; + 8004628: 4605 mov r5, r0 + 800462a: 2800 cmp r0, #0 + 800462c: d16a bne.n 8004704 + se2_decrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest, &is_valid); + 800462e: f10d 031f add.w r3, sp, #31 + 8004632: 9302 str r3, [sp, #8] + 8004634: ab08 add r3, sp, #32 + 8004636: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 800463a: e9cd 4300 strd r4, r3, [sp] + 800463e: ea4f 1248 mov.w r2, r8, lsl #5 + 8004642: 463b mov r3, r7 + 8004644: 2120 movs r1, #32 + 8004646: 9005 str r0, [sp, #20] + 8004648: f003 fdfc bl 8008244 + if(!is_valid) { + 800464c: f89d 301f ldrb.w r3, [sp, #31] + 8004650: 9805 ldr r0, [sp, #20] + 8004652: b91b cbnz r3, 800465c + memset(args->secret, 0, 32); + 8004654: 2220 movs r2, #32 + 8004656: 4621 mov r1, r4 + memset(dest, 0, AE_LONG_SECRET_LEN); + 8004658: f009 f80c bl 800d674 + ae_reset_chip(); + 800465c: f7fe f9a4 bl 80029a8 + if(rv) return EPIN_AE_FAIL; + 8004660: e7c6 b.n 80045f0 + 8004662: 463e mov r6, r7 + rv = ae_encrypted_read32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, p); + 8004664: 9600 str r6, [sp, #0] + 8004666: ab08 add r3, sp, #32 + 8004668: 2203 movs r2, #3 + 800466a: 4649 mov r1, r9 + 800466c: 2008 movs r0, #8 + 800466e: f7fe fde4 bl 800323a + if(rv) goto fail; + 8004672: 4605 mov r5, r0 + 8004674: 2800 cmp r0, #0 + 8004676: d145 bne.n 8004704 + for(blk=0; blk<13; blk++, p += 32) { + 8004678: f109 0901 add.w r9, r9, #1 + 800467c: f1b9 0f0d cmp.w r9, #13 + 8004680: f106 0620 add.w r6, r6, #32 + 8004684: d1ee bne.n 8004664 + ASSERT(p == dest+AE_LONG_SECRET_LEN); + 8004686: f507 73d0 add.w r3, r7, #416 ; 0x1a0 + 800468a: 429e cmp r6, r3 + 800468c: d002 beq.n 8004694 + 800468e: 4825 ldr r0, [pc, #148] ; (8004724 ) + 8004690: f7fc f9da bl 8000a48 + se2_decrypt_secret(dest, AE_LONG_SECRET_LEN, 0, dest, NULL, digest, &is_valid); + 8004694: ab10 add r3, sp, #64 ; 0x40 + 8004696: 9302 str r3, [sp, #8] + 8004698: ab08 add r3, sp, #32 + 800469a: e9cd 0300 strd r0, r3, [sp] + 800469e: 4602 mov r2, r0 + 80046a0: 463b mov r3, r7 + 80046a2: f44f 71d0 mov.w r1, #416 ; 0x1a0 + 80046a6: 4638 mov r0, r7 + 80046a8: f003 fdcc bl 8008244 + if(!is_valid) { + 80046ac: f89d 4040 ldrb.w r4, [sp, #64] ; 0x40 + 80046b0: b924 cbnz r4, 80046bc + memset(dest, 0, AE_LONG_SECRET_LEN); + 80046b2: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 80046b6: 4621 mov r1, r4 + 80046b8: 4638 mov r0, r7 + 80046ba: e7cd b.n 8004658 + ae_reset_chip(); + 80046bc: f7fe f974 bl 80029a8 + return 0; + 80046c0: 462c mov r4, r5 + 80046c2: e795 b.n 80045f0 + uint8_t tmp[32] = {0}; + 80046c4: 221c movs r2, #28 + 80046c6: 4621 mov r1, r4 + 80046c8: a811 add r0, sp, #68 ; 0x44 + 80046ca: 9410 str r4, [sp, #64] ; 0x40 + if(se2_encrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest)) { + 80046cc: ad10 add r5, sp, #64 ; 0x40 + uint8_t tmp[32] = {0}; + 80046ce: f008 ffd1 bl 800d674 + if(se2_encrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest)) { + 80046d2: ab08 add r3, sp, #32 + 80046d4: e9cd 4300 strd r4, r3, [sp] + 80046d8: ea4f 1248 mov.w r2, r8, lsl #5 + 80046dc: 462b mov r3, r5 + 80046de: 2120 movs r1, #32 + 80046e0: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 80046e4: f003 fd58 bl 8008198 + 80046e8: b120 cbz r0, 80046f4 + ae_reset_chip(); + 80046ea: f7fe f95d bl 80029a8 + return EPIN_SE2_FAIL; + 80046ee: f06f 0472 mvn.w r4, #114 ; 0x72 + 80046f2: e77d b.n 80045f0 + rv = ae_encrypted_write32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, tmp); + 80046f4: 9500 str r5, [sp, #0] + 80046f6: ab08 add r3, sp, #32 + 80046f8: 2203 movs r2, #3 + 80046fa: 4641 mov r1, r8 + 80046fc: 2008 movs r0, #8 + 80046fe: f7fe fe01 bl 8003304 + 8004702: 4605 mov r5, r0 + ae_reset_chip(); + 8004704: f7fe f950 bl 80029a8 + if(rv) return EPIN_AE_FAIL; + 8004708: 2d00 cmp r5, #0 + 800470a: bf18 it ne + 800470c: f06f 0469 mvnne.w r4, #105 ; 0x69 + 8004710: e76e b.n 80045f0 + return EPIN_WRONG_SUCCESS; + 8004712: f06f 046c mvn.w r4, #108 ; 0x6c + 8004716: e76b b.n 80045f0 + if(blk > 13) return EPIN_RANGE_ERR; + 8004718: f06f 0466 mvn.w r4, #102 ; 0x66 + 800471c: e768 b.n 80045f0 + 800471e: bf00 nop + 8004720: 0801c000 .word 0x0801c000 + 8004724: 0800e3e0 .word 0x0800e3e0 + +08004728 : +// +// Record current flash checksum and make green light go on. +// + int +pin_firmware_greenlight(pinAttempt_t *args) +{ + 8004728: b530 push {r4, r5, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 800472a: 2100 movs r1, #0 +{ + 800472c: b09b sub sp, #108 ; 0x6c + 800472e: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 8004730: f7ff f9f0 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 8004734: bb20 cbnz r0, 8004780 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 8004736: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004738: 07da lsls r2, r3, #31 + 800473a: d529 bpl.n 8004790 + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(args->is_secondary) { + 800473c: 6865 ldr r5, [r4, #4] + 800473e: bb55 cbnz r5, 8004796 + return EPIN_PRIMARY_ONLY; + } + + // load existing PIN's hash + uint8_t digest[32]; + pin_cache_restore(args, digest); + 8004740: a902 add r1, sp, #8 + 8004742: 4620 mov r0, r4 + 8004744: f7ff fbac bl 8003ea0 + + // step 1: calc the value to use + uint8_t fw_check[32], world_check[32]; + checksum_flash(fw_check, world_check, 0); + 8004748: 462a mov r2, r5 + 800474a: a912 add r1, sp, #72 ; 0x48 + 800474c: a80a add r0, sp, #40 ; 0x28 + 800474e: f7fd f973 bl 8001a38 + + // step 2: write it out to chip. + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004752: f7ff fa15 bl 8003b80 + 8004756: bb08 cbnz r0, 800479c + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004758: 4b12 ldr r3, [pc, #72] ; (80047a4 ) + 800475a: 6c22 ldr r2, [r4, #64] ; 0x40 + 800475c: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8004760: 4053 eors r3, r2 + + // under duress, we can't fake this, but we go through the motions anyway + if(!get_is_trick(args, NULL)) { + 8004762: 07db lsls r3, r3, #31 + 8004764: d40e bmi.n 8004784 + rv = ae_encrypted_write(KEYNUM_firmware, KEYNUM_main_pin, digest, world_check, 32); + 8004766: 2320 movs r3, #32 + 8004768: 9300 str r3, [sp, #0] + 800476a: aa02 add r2, sp, #8 + 800476c: ab12 add r3, sp, #72 ; 0x48 + 800476e: 2103 movs r1, #3 + 8004770: 200e movs r0, #14 + 8004772: f7fe fe2d bl 80033d0 + + if(rv) { + 8004776: b128 cbz r0, 8004784 + ae_reset_chip(); + 8004778: f7fe f916 bl 80029a8 + + return EPIN_AE_FAIL; + 800477c: f06f 0069 mvn.w r0, #105 ; 0x69 + + return EPIN_AE_FAIL; + } + + return 0; +} + 8004780: b01b add sp, #108 ; 0x6c + 8004782: bd30 pop {r4, r5, pc} + rv = ae_set_gpio_secure(world_check); + 8004784: a812 add r0, sp, #72 ; 0x48 + 8004786: f7fe feb5 bl 80034f4 + if(rv) { + 800478a: 2800 cmp r0, #0 + 800478c: d0f8 beq.n 8004780 + 800478e: e7f3 b.n 8004778 + return EPIN_WRONG_SUCCESS; + 8004790: f06f 006c mvn.w r0, #108 ; 0x6c + 8004794: e7f4 b.n 8004780 + return EPIN_PRIMARY_ONLY; + 8004796: f06f 0071 mvn.w r0, #113 ; 0x71 + 800479a: e7f1 b.n 8004780 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 800479c: f06f 0068 mvn.w r0, #104 ; 0x68 + 80047a0: e7ee b.n 8004780 + 80047a2: bf00 nop + 80047a4: 0801c000 .word 0x0801c000 + +080047a8 : +// Update the system firmware via file in PSRAM. Arrange for +// light to stay green through out process. +// + int +pin_firmware_upgrade(pinAttempt_t *args) +{ + 80047a8: b570 push {r4, r5, r6, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 80047aa: 2100 movs r1, #0 +{ + 80047ac: b092 sub sp, #72 ; 0x48 + 80047ae: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 80047b0: f7ff f9b0 bl 8003b14 <_validate_attempt> + if(rv) return rv; + 80047b4: 2800 cmp r0, #0 + 80047b6: d14e bne.n 8004856 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80047b8: 6be3 ldr r3, [r4, #60] ; 0x3c + 80047ba: 07da lsls r2, r3, #31 + 80047bc: d54d bpl.n 800485a + // must come here with a successful PIN login + return EPIN_WRONG_SUCCESS; + } + + if(args->change_flags != CHANGE_FIRMWARE) { + 80047be: 6e63 ldr r3, [r4, #100] ; 0x64 + 80047c0: 2b40 cmp r3, #64 ; 0x40 + 80047c2: d11c bne.n 80047fe + } + + // expecting start/length relative to psram start + uint32_t *about = (uint32_t *)args->secret; + uint32_t start = about[0]; + uint32_t len = about[1]; + 80047c4: e9d4 562c ldrd r5, r6, [r4, #176] ; 0xb0 + + if(len < 32768) return EPIN_RANGE_ERR; + 80047c8: f5a6 4300 sub.w r3, r6, #32768 ; 0x8000 + 80047cc: f5b3 1ffc cmp.w r3, #2064384 ; 0x1f8000 + 80047d0: d846 bhi.n 8004860 + if(len > 2<<20) return EPIN_RANGE_ERR; + if(start+len > PSRAM_SIZE) return EPIN_RANGE_ERR; + 80047d2: 19ab adds r3, r5, r6 + 80047d4: f5b3 0f00 cmp.w r3, #8388608 ; 0x800000 + 80047d8: d842 bhi.n 8004860 + + const uint8_t *data = (const uint8_t *)PSRAM_BASE+start; + 80047da: f105 4510 add.w r5, r5, #2415919104 ; 0x90000000 + + // verify a firmware image that's in RAM, and calc its digest + // - also applies watermark policy, etc + uint8_t world_check[32]; + bool ok = verify_firmware_in_ram(data, len, world_check); + 80047de: aa02 add r2, sp, #8 + 80047e0: 4631 mov r1, r6 + 80047e2: 4628 mov r0, r5 + 80047e4: f7fd fa36 bl 8001c54 + if(!ok) { + 80047e8: 2800 cmp r0, #0 + 80047ea: d03c beq.n 8004866 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 80047ec: 4b21 ldr r3, [pc, #132] ; (8004874 ) + 80047ee: 6c22 ldr r2, [r4, #64] ; 0x40 + 80047f0: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80047f4: 4053 eors r3, r2 + return EPIN_AUTH_FAIL; + } + + // under duress, we can't fake this, so kill ourselves. + if(get_is_trick(args, NULL)) { + 80047f6: 07db lsls r3, r3, #31 + 80047f8: d504 bpl.n 8004804 + // User is a thug.. kill secret and reboot w/o any notice + fast_wipe(); + 80047fa: f7fd ff1f bl 800263c + return EPIN_BAD_REQUEST; + 80047fe: f06f 0067 mvn.w r0, #103 ; 0x67 + 8004802: e028 b.n 8004856 + return EPIN_BAD_REQUEST; + } + + // load existing PIN's hash + uint8_t digest[32]; + pin_cache_restore(args, digest); + 8004804: a90a add r1, sp, #40 ; 0x28 + 8004806: 4620 mov r0, r4 + 8004808: f7ff fb4a bl 8003ea0 + + // step 1: calc the value to use, see above + if(warmup_ae()) return EPIN_I_AM_BRICK; + 800480c: f7ff f9b8 bl 8003b80 + 8004810: bb60 cbnz r0, 800486c + + // step 2: write it out to chip. + rv = ae_encrypted_write(KEYNUM_firmware, KEYNUM_main_pin, digest, world_check, 32); + 8004812: 2320 movs r3, #32 + 8004814: 9300 str r3, [sp, #0] + 8004816: aa0a add r2, sp, #40 ; 0x28 + 8004818: ab02 add r3, sp, #8 + 800481a: 2103 movs r1, #3 + 800481c: 200e movs r0, #14 + 800481e: f7fe fdd7 bl 80033d0 + if(rv) goto fail; + 8004822: b9a0 cbnz r0, 800484e + + // this turns on green light + rv = ae_set_gpio_secure(world_check); + 8004824: a802 add r0, sp, #8 + 8004826: f7fe fe65 bl 80034f4 + if(rv) goto fail; + 800482a: b980 cbnz r0, 800484e + + // -- point of no return -- + + // burn it, shows progress + psram_do_upgrade(data, len); + 800482c: 4631 mov r1, r6 + 800482e: 4628 mov r0, r5 + 8004830: f000 fbf4 bl 800501c + 8004834: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8004838: 490f ldr r1, [pc, #60] ; (8004878 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800483a: 4b10 ldr r3, [pc, #64] ; (800487c ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 800483c: 68ca ldr r2, [r1, #12] + 800483e: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8004842: 4313 orrs r3, r2 + 8004844: 60cb str r3, [r1, #12] + 8004846: f3bf 8f4f dsb sy + __NOP(); + 800484a: bf00 nop + for(;;) /* wait until reset */ + 800484c: e7fd b.n 800484a + NVIC_SystemReset(); + + return 0; + +fail: + ae_reset_chip(); + 800484e: f7fe f8ab bl 80029a8 + + return EPIN_AE_FAIL; + 8004852: f06f 0069 mvn.w r0, #105 ; 0x69 +} + 8004856: b012 add sp, #72 ; 0x48 + 8004858: bd70 pop {r4, r5, r6, pc} + return EPIN_WRONG_SUCCESS; + 800485a: f06f 006c mvn.w r0, #108 ; 0x6c + 800485e: e7fa b.n 8004856 + if(len < 32768) return EPIN_RANGE_ERR; + 8004860: f06f 0066 mvn.w r0, #102 ; 0x66 + 8004864: e7f7 b.n 8004856 + return EPIN_AUTH_FAIL; + 8004866: f06f 006f mvn.w r0, #111 ; 0x6f + 800486a: e7f4 b.n 8004856 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 800486c: f06f 0068 mvn.w r0, #104 ; 0x68 + 8004870: e7f1 b.n 8004856 + 8004872: bf00 nop + 8004874: 0801c000 .word 0x0801c000 + 8004878: e000ed00 .word 0xe000ed00 + 800487c: 05fa0004 .word 0x05fa0004 + +08004880 : + +// strcat_hex() +// + void +strcat_hex(char *msg, const void *d, int len) +{ + 8004880: b570 push {r4, r5, r6, lr} + 8004882: 4616 mov r6, r2 + 8004884: 4604 mov r4, r0 + 8004886: 460d mov r5, r1 + char *p = msg+strlen(msg); + 8004888: f008 ff27 bl 800d6da + const uint8_t *h = (const uint8_t *)d; + + for(; len; len--, h++) { + *(p++) = hexmap[(*h>>4) & 0xf]; + 800488c: 4a0b ldr r2, [pc, #44] ; (80048bc ) + char *p = msg+strlen(msg); + 800488e: 4420 add r0, r4 + for(; len; len--, h++) { + 8004890: 1e69 subs r1, r5, #1 + 8004892: eb00 0646 add.w r6, r0, r6, lsl #1 + 8004896: 42b0 cmp r0, r6 + 8004898: d102 bne.n 80048a0 + *(p++) = hexmap[(*h>>0) & 0xf]; + } + + *(p++) = 0; + 800489a: 2300 movs r3, #0 + 800489c: 7003 strb r3, [r0, #0] +} + 800489e: bd70 pop {r4, r5, r6, pc} + *(p++) = hexmap[(*h>>4) & 0xf]; + 80048a0: f811 3f01 ldrb.w r3, [r1, #1]! + 80048a4: 091b lsrs r3, r3, #4 + 80048a6: 5cd3 ldrb r3, [r2, r3] + 80048a8: f800 3b02 strb.w r3, [r0], #2 + *(p++) = hexmap[(*h>>0) & 0xf]; + 80048ac: 780b ldrb r3, [r1, #0] + 80048ae: f003 030f and.w r3, r3, #15 + 80048b2: 5cd3 ldrb r3, [r2, r3] + 80048b4: f800 3c01 strb.w r3, [r0, #-1] + for(; len; len--, h++) { + 80048b8: e7ed b.n 8004896 + 80048ba: bf00 nop + 80048bc: 0800e752 .word 0x0800e752 + +080048c0 : + * parameters in the USART_InitTypeDef and initialize the associated handle. + * @param husart USART handle. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_USART_Init(USART_HandleTypeDef *husart) +{ + 80048c0: b5f8 push {r3, r4, r5, r6, r7, lr} + /* Check the USART handle allocation */ + if (husart == NULL) + 80048c2: 4604 mov r4, r0 + 80048c4: b910 cbnz r0, 80048cc + { + return HAL_ERROR; + 80048c6: 2501 movs r5, #1 + /* Enable the Peripheral */ + __HAL_USART_ENABLE(husart); + + /* TEACK and/or REACK to check before moving husart->State to Ready */ + return (USART_CheckIdleState(husart)); +} + 80048c8: 4628 mov r0, r5 + 80048ca: bdf8 pop {r3, r4, r5, r6, r7, pc} + if (husart->State == HAL_USART_STATE_RESET) + 80048cc: f890 3059 ldrb.w r3, [r0, #89] ; 0x59 + 80048d0: f003 02ff and.w r2, r3, #255 ; 0xff + 80048d4: b90b cbnz r3, 80048da + husart->Lock = HAL_UNLOCKED; + 80048d6: f880 2058 strb.w r2, [r0, #88] ; 0x58 + __HAL_USART_DISABLE(husart); + 80048da: 6823 ldr r3, [r4, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 80048dc: 6921 ldr r1, [r4, #16] + husart->State = HAL_USART_STATE_BUSY; + 80048de: 2502 movs r5, #2 + 80048e0: f884 5059 strb.w r5, [r4, #89] ; 0x59 + __HAL_USART_DISABLE(husart); + 80048e4: 681a ldr r2, [r3, #0] + 80048e6: f022 0201 bic.w r2, r2, #1 + 80048ea: 601a str r2, [r3, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 80048ec: 68a2 ldr r2, [r4, #8] + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 80048ee: 6818 ldr r0, [r3, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 80048f0: 430a orrs r2, r1 + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 80048f2: 49a9 ldr r1, [pc, #676] ; (8004b98 ) + 80048f4: 4001 ands r1, r0 + 80048f6: 430a orrs r2, r1 + 80048f8: 6961 ldr r1, [r4, #20] + MODIFY_REG(husart->Instance->CR2, USART_CR2_FIELDS, tmpreg); + 80048fa: 69a0 ldr r0, [r4, #24] + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 80048fc: 430a orrs r2, r1 + 80048fe: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 8004902: 601a str r2, [r3, #0] + MODIFY_REG(husart->Instance->CR2, USART_CR2_FIELDS, tmpreg); + 8004904: 6859 ldr r1, [r3, #4] + 8004906: 6a22 ldr r2, [r4, #32] + 8004908: f421 517c bic.w r1, r1, #16128 ; 0x3f00 + 800490c: f021 0109 bic.w r1, r1, #9 + 8004910: 4302 orrs r2, r0 + 8004912: 430a orrs r2, r1 + 8004914: 69e1 ldr r1, [r4, #28] + 8004916: 430a orrs r2, r1 + 8004918: 68e1 ldr r1, [r4, #12] + 800491a: 430a orrs r2, r1 + 800491c: f442 6200 orr.w r2, r2, #2048 ; 0x800 + 8004920: 605a str r2, [r3, #4] + MODIFY_REG(husart->Instance->PRESC, USART_PRESC_PRESCALER, husart->Init.ClockPrescaler); + 8004922: 6ad9 ldr r1, [r3, #44] ; 0x2c + 8004924: 6a62 ldr r2, [r4, #36] ; 0x24 + 8004926: f021 010f bic.w r1, r1, #15 + 800492a: 4311 orrs r1, r2 + 800492c: 62d9 str r1, [r3, #44] ; 0x2c + USART_GETCLOCKSOURCE(husart, clocksource); + 800492e: 499b ldr r1, [pc, #620] ; (8004b9c ) + 8004930: 428b cmp r3, r1 + 8004932: d10e bne.n 8004952 + 8004934: 4b9a ldr r3, [pc, #616] ; (8004ba0 ) + 8004936: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 800493a: f003 0303 and.w r3, r3, #3 + 800493e: 42ab cmp r3, r5 + 8004940: f000 80cd beq.w 8004ade + 8004944: 2b03 cmp r3, #3 + 8004946: d01a beq.n 800497e + 8004948: 2b01 cmp r3, #1 + 800494a: d153 bne.n 80049f4 + pclk = HAL_RCC_GetSysClockFreq(); + 800494c: f003 ff3e bl 80087cc + 8004950: e052 b.n 80049f8 + USART_GETCLOCKSOURCE(husart, clocksource); + 8004952: 4994 ldr r1, [pc, #592] ; (8004ba4 ) + 8004954: 428b cmp r3, r1 + 8004956: d13c bne.n 80049d2 + 8004958: 4b91 ldr r3, [pc, #580] ; (8004ba0 ) + 800495a: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 800495e: f003 030c and.w r3, r3, #12 + 8004962: 2b08 cmp r3, #8 + 8004964: f000 80bb beq.w 8004ade + 8004968: d807 bhi.n 800497a + 800496a: 2b00 cmp r3, #0 + 800496c: f000 80b4 beq.w 8004ad8 + 8004970: 2b04 cmp r3, #4 + 8004972: d0eb beq.n 800494c + uint32_t usartdiv = 0x00000000; + 8004974: 2300 movs r3, #0 + ret = HAL_ERROR; + 8004976: 2501 movs r5, #1 + 8004978: e06e b.n 8004a58 + USART_GETCLOCKSOURCE(husart, clocksource); + 800497a: 2b0c cmp r3, #12 + 800497c: d1fa bne.n 8004974 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 800497e: 2a00 cmp r2, #0 + 8004980: f000 80fb beq.w 8004b7a + 8004984: 2a01 cmp r2, #1 + 8004986: f000 80fa beq.w 8004b7e + 800498a: 2a02 cmp r2, #2 + 800498c: f000 80f9 beq.w 8004b82 + 8004990: 2a03 cmp r2, #3 + 8004992: f000 80f8 beq.w 8004b86 + 8004996: 2a04 cmp r2, #4 + 8004998: f000 80f7 beq.w 8004b8a + 800499c: 2a05 cmp r2, #5 + 800499e: f000 80f6 beq.w 8004b8e + 80049a2: 2a06 cmp r2, #6 + 80049a4: f000 80f5 beq.w 8004b92 + 80049a8: 2a07 cmp r2, #7 + 80049aa: f000 8101 beq.w 8004bb0 + 80049ae: 2a08 cmp r2, #8 + 80049b0: f000 8100 beq.w 8004bb4 + 80049b4: 2a09 cmp r2, #9 + 80049b6: f000 80ff beq.w 8004bb8 + 80049ba: 2a0a cmp r2, #10 + 80049bc: f000 80fe beq.w 8004bbc + 80049c0: 2a0b cmp r2, #11 + 80049c2: bf14 ite ne + 80049c4: 2201 movne r2, #1 + 80049c6: f44f 7280 moveq.w r2, #256 ; 0x100 + 80049ca: 6861 ldr r1, [r4, #4] + 80049cc: f44f 4300 mov.w r3, #32768 ; 0x8000 + 80049d0: e0a1 b.n 8004b16 + USART_GETCLOCKSOURCE(husart, clocksource); + 80049d2: 4975 ldr r1, [pc, #468] ; (8004ba8 ) + 80049d4: 428b cmp r3, r1 + 80049d6: d1cd bne.n 8004974 + 80049d8: 4b71 ldr r3, [pc, #452] ; (8004ba0 ) + 80049da: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 80049de: f003 0330 and.w r3, r3, #48 ; 0x30 + 80049e2: 2b20 cmp r3, #32 + 80049e4: d07b beq.n 8004ade + 80049e6: d803 bhi.n 80049f0 + 80049e8: 2b00 cmp r3, #0 + 80049ea: d075 beq.n 8004ad8 + 80049ec: 2b10 cmp r3, #16 + 80049ee: e7c0 b.n 8004972 + 80049f0: 2b30 cmp r3, #48 ; 0x30 + 80049f2: e7c3 b.n 800497c + pclk = HAL_RCC_GetPCLK2Freq(); + 80049f4: f004 faf8 bl 8008fe8 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 80049f8: 6a62 ldr r2, [r4, #36] ; 0x24 + 80049fa: 2a00 cmp r2, #0 + 80049fc: f000 80a7 beq.w 8004b4e + 8004a00: 2a01 cmp r2, #1 + 8004a02: f000 80a6 beq.w 8004b52 + 8004a06: 2a02 cmp r2, #2 + 8004a08: f000 80a5 beq.w 8004b56 + 8004a0c: 2a03 cmp r2, #3 + 8004a0e: f000 80a4 beq.w 8004b5a + 8004a12: 2a04 cmp r2, #4 + 8004a14: f000 80a3 beq.w 8004b5e + 8004a18: 2a05 cmp r2, #5 + 8004a1a: f000 80a2 beq.w 8004b62 + 8004a1e: 2a06 cmp r2, #6 + 8004a20: f000 80a1 beq.w 8004b66 + 8004a24: 2a07 cmp r2, #7 + 8004a26: f000 80a0 beq.w 8004b6a + 8004a2a: 2a08 cmp r2, #8 + 8004a2c: f000 809f beq.w 8004b6e + 8004a30: 2a09 cmp r2, #9 + 8004a32: f000 809e beq.w 8004b72 + 8004a36: 2a0a cmp r2, #10 + 8004a38: f000 809d beq.w 8004b76 + 8004a3c: 2a0b cmp r2, #11 + 8004a3e: bf14 ite ne + 8004a40: 2201 movne r2, #1 + 8004a42: f44f 7280 moveq.w r2, #256 ; 0x100 + 8004a46: 6861 ldr r1, [r4, #4] + 8004a48: fbb0 f0f2 udiv r0, r0, r2 + 8004a4c: 084b lsrs r3, r1, #1 + 8004a4e: eb03 0340 add.w r3, r3, r0, lsl #1 + HAL_StatusTypeDef ret = HAL_OK; + 8004a52: 2500 movs r5, #0 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004a54: fbb3 f3f1 udiv r3, r3, r1 + if ((usartdiv >= USART_BRR_MIN) && (usartdiv <= USART_BRR_MAX)) + 8004a58: f1a3 0110 sub.w r1, r3, #16 + 8004a5c: f64f 72ef movw r2, #65519 ; 0xffef + 8004a60: 4291 cmp r1, r2 + brrtemp = (uint16_t)(usartdiv & 0xFFF0U); + 8004a62: bf9f itttt ls + 8004a64: f023 020f bicls.w r2, r3, #15 + 8004a68: b292 uxthls r2, r2 + brrtemp |= (uint16_t)((usartdiv & (uint16_t)0x000FU) >> 1U); + 8004a6a: f3c3 0342 ubfxls r3, r3, #1, #3 + husart->Instance->BRR = brrtemp; + 8004a6e: 6821 ldrls r1, [r4, #0] + 8004a70: bf9a itte ls + 8004a72: 4313 orrls r3, r2 + 8004a74: 60cb strls r3, [r1, #12] + ret = HAL_ERROR; + 8004a76: 2501 movhi r5, #1 + husart->NbTxDataToProcess = 1U; + 8004a78: 2301 movs r3, #1 + husart->RxISR = NULL; + 8004a7a: 2200 movs r2, #0 + if (USART_SetConfig(husart) == HAL_ERROR) + 8004a7c: 429d cmp r5, r3 + husart->TxISR = NULL; + 8004a7e: e9c4 2212 strd r2, r2, [r4, #72] ; 0x48 + husart->NbTxDataToProcess = 1U; + 8004a82: 87a3 strh r3, [r4, #60] ; 0x3c + husart->NbRxDataToProcess = 1U; + 8004a84: 8763 strh r3, [r4, #58] ; 0x3a + if (USART_SetConfig(husart) == HAL_ERROR) + 8004a86: f43f af1e beq.w 80048c6 + husart->Instance->CR2 &= ~USART_CR2_LINEN; + 8004a8a: 6823 ldr r3, [r4, #0] + 8004a8c: 6859 ldr r1, [r3, #4] + 8004a8e: f421 4180 bic.w r1, r1, #16384 ; 0x4000 + 8004a92: 6059 str r1, [r3, #4] + husart->Instance->CR3 &= ~(USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN); + 8004a94: 6899 ldr r1, [r3, #8] + 8004a96: f021 012a bic.w r1, r1, #42 ; 0x2a + 8004a9a: 6099 str r1, [r3, #8] + __HAL_USART_ENABLE(husart); + 8004a9c: 6819 ldr r1, [r3, #0] + 8004a9e: f041 0101 orr.w r1, r1, #1 + 8004aa2: 6019 str r1, [r3, #0] + husart->ErrorCode = HAL_USART_ERROR_NONE; + 8004aa4: 65e2 str r2, [r4, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8004aa6: f002 fb21 bl 80070ec + if ((husart->Instance->CR1 & USART_CR1_TE) == USART_CR1_TE) + 8004aaa: 6823 ldr r3, [r4, #0] + 8004aac: 681b ldr r3, [r3, #0] + 8004aae: 071a lsls r2, r3, #28 + tickstart = HAL_GetTick(); + 8004ab0: 4607 mov r7, r0 + if ((husart->Instance->CR1 & USART_CR1_TE) == USART_CR1_TE) + 8004ab2: f100 8085 bmi.w 8004bc0 + if ((husart->Instance->CR1 & USART_CR1_RE) == USART_CR1_RE) + 8004ab6: 6823 ldr r3, [r4, #0] + 8004ab8: 681b ldr r3, [r3, #0] + 8004aba: 075b lsls r3, r3, #29 + 8004abc: d505 bpl.n 8004aca + while ((__HAL_USART_GET_FLAG(husart, Flag) ? SET : RESET) == Status) + 8004abe: 6823 ldr r3, [r4, #0] + 8004ac0: 69de ldr r6, [r3, #28] + 8004ac2: f416 0680 ands.w r6, r6, #4194304 ; 0x400000 + 8004ac6: f000 808e beq.w 8004be6 + husart->State = HAL_USART_STATE_READY; + 8004aca: 2301 movs r3, #1 + 8004acc: f884 3059 strb.w r3, [r4, #89] ; 0x59 + __HAL_UNLOCK(husart); + 8004ad0: 2300 movs r3, #0 + 8004ad2: f884 3058 strb.w r3, [r4, #88] ; 0x58 + return HAL_OK; + 8004ad6: e6f7 b.n 80048c8 + pclk = HAL_RCC_GetPCLK1Freq(); + 8004ad8: f004 fa74 bl 8008fc4 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004adc: e78c b.n 80049f8 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(HSI_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004ade: b302 cbz r2, 8004b22 + 8004ae0: 2a01 cmp r2, #1 + 8004ae2: d020 beq.n 8004b26 + 8004ae4: 2a02 cmp r2, #2 + 8004ae6: d020 beq.n 8004b2a + 8004ae8: 2a03 cmp r2, #3 + 8004aea: d020 beq.n 8004b2e + 8004aec: 2a04 cmp r2, #4 + 8004aee: d020 beq.n 8004b32 + 8004af0: 2a05 cmp r2, #5 + 8004af2: d020 beq.n 8004b36 + 8004af4: 2a06 cmp r2, #6 + 8004af6: d020 beq.n 8004b3a + 8004af8: 2a07 cmp r2, #7 + 8004afa: d020 beq.n 8004b3e + 8004afc: 2a08 cmp r2, #8 + 8004afe: d020 beq.n 8004b42 + 8004b00: 2a09 cmp r2, #9 + 8004b02: d020 beq.n 8004b46 + 8004b04: 2a0a cmp r2, #10 + 8004b06: d020 beq.n 8004b4a + 8004b08: 2a0b cmp r2, #11 + 8004b0a: bf14 ite ne + 8004b0c: 2201 movne r2, #1 + 8004b0e: f44f 7280 moveq.w r2, #256 ; 0x100 + 8004b12: 6861 ldr r1, [r4, #4] + 8004b14: 4b25 ldr r3, [pc, #148] ; (8004bac ) + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004b16: fbb3 f2f2 udiv r2, r3, r2 + 8004b1a: 084b lsrs r3, r1, #1 + 8004b1c: eb03 0342 add.w r3, r3, r2, lsl #1 + 8004b20: e797 b.n 8004a52 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(HSI_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004b22: 2201 movs r2, #1 + 8004b24: e7f5 b.n 8004b12 + 8004b26: 2202 movs r2, #2 + 8004b28: e7f3 b.n 8004b12 + 8004b2a: 2204 movs r2, #4 + 8004b2c: e7f1 b.n 8004b12 + 8004b2e: 2206 movs r2, #6 + 8004b30: e7ef b.n 8004b12 + 8004b32: 2208 movs r2, #8 + 8004b34: e7ed b.n 8004b12 + 8004b36: 220a movs r2, #10 + 8004b38: e7eb b.n 8004b12 + 8004b3a: 220c movs r2, #12 + 8004b3c: e7e9 b.n 8004b12 + 8004b3e: 2210 movs r2, #16 + 8004b40: e7e7 b.n 8004b12 + 8004b42: 2220 movs r2, #32 + 8004b44: e7e5 b.n 8004b12 + 8004b46: 2240 movs r2, #64 ; 0x40 + 8004b48: e7e3 b.n 8004b12 + 8004b4a: 2280 movs r2, #128 ; 0x80 + 8004b4c: e7e1 b.n 8004b12 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004b4e: 2201 movs r2, #1 + 8004b50: e779 b.n 8004a46 + 8004b52: 2202 movs r2, #2 + 8004b54: e777 b.n 8004a46 + 8004b56: 2204 movs r2, #4 + 8004b58: e775 b.n 8004a46 + 8004b5a: 2206 movs r2, #6 + 8004b5c: e773 b.n 8004a46 + 8004b5e: 2208 movs r2, #8 + 8004b60: e771 b.n 8004a46 + 8004b62: 220a movs r2, #10 + 8004b64: e76f b.n 8004a46 + 8004b66: 220c movs r2, #12 + 8004b68: e76d b.n 8004a46 + 8004b6a: 2210 movs r2, #16 + 8004b6c: e76b b.n 8004a46 + 8004b6e: 2220 movs r2, #32 + 8004b70: e769 b.n 8004a46 + 8004b72: 2240 movs r2, #64 ; 0x40 + 8004b74: e767 b.n 8004a46 + 8004b76: 2280 movs r2, #128 ; 0x80 + 8004b78: e765 b.n 8004a46 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004b7a: 2201 movs r2, #1 + 8004b7c: e725 b.n 80049ca + 8004b7e: 2202 movs r2, #2 + 8004b80: e723 b.n 80049ca + 8004b82: 2204 movs r2, #4 + 8004b84: e721 b.n 80049ca + 8004b86: 2206 movs r2, #6 + 8004b88: e71f b.n 80049ca + 8004b8a: 2208 movs r2, #8 + 8004b8c: e71d b.n 80049ca + 8004b8e: 220a movs r2, #10 + 8004b90: e71b b.n 80049ca + 8004b92: 220c movs r2, #12 + 8004b94: e719 b.n 80049ca + 8004b96: bf00 nop + 8004b98: cfff69f3 .word 0xcfff69f3 + 8004b9c: 40013800 .word 0x40013800 + 8004ba0: 40021000 .word 0x40021000 + 8004ba4: 40004400 .word 0x40004400 + 8004ba8: 40004800 .word 0x40004800 + 8004bac: 00f42400 .word 0x00f42400 + 8004bb0: 2210 movs r2, #16 + 8004bb2: e70a b.n 80049ca + 8004bb4: 2220 movs r2, #32 + 8004bb6: e708 b.n 80049ca + 8004bb8: 2240 movs r2, #64 ; 0x40 + 8004bba: e706 b.n 80049ca + 8004bbc: 2280 movs r2, #128 ; 0x80 + 8004bbe: e704 b.n 80049ca + while ((__HAL_USART_GET_FLAG(husart, Flag) ? SET : RESET) == Status) + 8004bc0: 6823 ldr r3, [r4, #0] + 8004bc2: 69de ldr r6, [r3, #28] + 8004bc4: f416 1600 ands.w r6, r6, #2097152 ; 0x200000 + 8004bc8: f47f af75 bne.w 8004ab6 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 8004bcc: f002 fa8e bl 80070ec + 8004bd0: 1bc0 subs r0, r0, r7 + 8004bd2: f5b0 7f7a cmp.w r0, #1000 ; 0x3e8 + 8004bd6: d9f3 bls.n 8004bc0 + husart->State = HAL_USART_STATE_READY; + 8004bd8: 2301 movs r3, #1 + 8004bda: f884 3059 strb.w r3, [r4, #89] ; 0x59 + __HAL_UNLOCK(husart); + 8004bde: f884 6058 strb.w r6, [r4, #88] ; 0x58 + return HAL_TIMEOUT; + 8004be2: 2503 movs r5, #3 + 8004be4: e670 b.n 80048c8 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 8004be6: f002 fa81 bl 80070ec + 8004bea: 1bc0 subs r0, r0, r7 + 8004bec: f5b0 7f7a cmp.w r0, #1000 ; 0x3e8 + 8004bf0: f67f af65 bls.w 8004abe + 8004bf4: e7f0 b.n 8004bd8 + 8004bf6: bf00 nop + +08004bf8 : + __HAL_RCC_USART1_CONFIG(RCC_USART1CLKSOURCE_SYSCLK); + 8004bf8: 4b14 ldr r3, [pc, #80] ; (8004c4c ) + 8004bfa: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8004bfe: f022 0203 bic.w r2, r2, #3 + 8004c02: f042 0201 orr.w r2, r2, #1 +{ + 8004c06: b513 push {r0, r1, r4, lr} + __HAL_RCC_USART1_CONFIG(RCC_USART1CLKSOURCE_SYSCLK); + 8004c08: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + __HAL_RCC_USART1_CLK_ENABLE(); + 8004c0c: 6e1a ldr r2, [r3, #96] ; 0x60 + memset(&con, 0, sizeof(con)); + 8004c0e: 4c10 ldr r4, [pc, #64] ; (8004c50 ) + __HAL_RCC_USART1_CLK_ENABLE(); + 8004c10: f442 4280 orr.w r2, r2, #16384 ; 0x4000 + 8004c14: 661a str r2, [r3, #96] ; 0x60 + 8004c16: 6e1b ldr r3, [r3, #96] ; 0x60 + 8004c18: f403 4380 and.w r3, r3, #16384 ; 0x4000 + 8004c1c: 9301 str r3, [sp, #4] + memset(&con, 0, sizeof(con)); + 8004c1e: 2258 movs r2, #88 ; 0x58 + 8004c20: 2100 movs r1, #0 + 8004c22: f104 0008 add.w r0, r4, #8 + __HAL_RCC_USART1_CLK_ENABLE(); + 8004c26: 9b01 ldr r3, [sp, #4] + memset(&con, 0, sizeof(con)); + 8004c28: f008 fd24 bl 800d674 + con.Init.BaudRate = 115200; + 8004c2c: 4a09 ldr r2, [pc, #36] ; (8004c54 ) + 8004c2e: f44f 33e1 mov.w r3, #115200 ; 0x1c200 + 8004c32: e9c4 2300 strd r2, r3, [r4] + HAL_StatusTypeDef rv = HAL_USART_Init(&con); + 8004c36: 4620 mov r0, r4 + con.Init.Mode = USART_MODE_TX_RX; + 8004c38: 230c movs r3, #12 + 8004c3a: 6163 str r3, [r4, #20] + HAL_StatusTypeDef rv = HAL_USART_Init(&con); + 8004c3c: f7ff fe40 bl 80048c0 + ASSERT(rv == HAL_OK); + 8004c40: b110 cbz r0, 8004c48 + 8004c42: 4805 ldr r0, [pc, #20] ; (8004c58 ) + 8004c44: f7fb ff00 bl 8000a48 +} + 8004c48: b002 add sp, #8 + 8004c4a: bd10 pop {r4, pc} + 8004c4c: 40021000 .word 0x40021000 + 8004c50: 2009e1c0 .word 0x2009e1c0 + 8004c54: 40013800 .word 0x40013800 + 8004c58: 0800e3e0 .word 0x0800e3e0 + +08004c5c : + * @param Timeout Timeout duration. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_USART_Transmit(USART_HandleTypeDef *husart, uint8_t *pTxData, uint16_t Size, uint32_t Timeout) +{ + while(Size > 0U) { + 8004c5c: 4b0b ldr r3, [pc, #44] ; (8004c8c ) + 8004c5e: 440a add r2, r1 + 8004c60: 4291 cmp r1, r2 + 8004c62: d10b bne.n 8004c7c + MY_UART->TDR = *pTxData; + pTxData++; + Size --; + } + + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 8004c64: 69da ldr r2, [r3, #28] + 8004c66: 0652 lsls r2, r2, #25 + 8004c68: d5fc bpl.n 8004c64 + // wait for final byte to be sent + } + + // Clear Transmission Complete Flag + MY_UART->ICR = USART_CLEAR_TCF; + 8004c6a: 2240 movs r2, #64 ; 0x40 + 8004c6c: 621a str r2, [r3, #32] + + // Clear overrun flag and discard the received data + MY_UART->ICR = USART_CLEAR_OREF; + 8004c6e: 2208 movs r2, #8 + 8004c70: 621a str r2, [r3, #32] + MY_UART->RQR = USART_RXDATA_FLUSH_REQUEST; + 8004c72: 831a strh r2, [r3, #24] + MY_UART->RQR = USART_TXDATA_FLUSH_REQUEST; + 8004c74: 2210 movs r2, #16 + 8004c76: 831a strh r2, [r3, #24] + + return HAL_OK; +} + 8004c78: 2000 movs r0, #0 + 8004c7a: 4770 bx lr + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 8004c7c: 69d8 ldr r0, [r3, #28] + 8004c7e: 0600 lsls r0, r0, #24 + 8004c80: d5fc bpl.n 8004c7c + MY_UART->TDR = *pTxData; + 8004c82: f811 0b01 ldrb.w r0, [r1], #1 + 8004c86: 8518 strh r0, [r3, #40] ; 0x28 + Size --; + 8004c88: e7ea b.n 8004c60 + 8004c8a: bf00 nop + 8004c8c: 40013800 .word 0x40013800 + +08004c90 : +{ + 8004c90: b510 push {r4, lr} + 8004c92: 4604 mov r4, r0 + rng_delay(); + 8004c94: f7fd fd72 bl 800277c + HAL_USART_Transmit(&con, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY); + 8004c98: 4620 mov r0, r4 + 8004c9a: f008 fd1e bl 800d6da + 8004c9e: 4621 mov r1, r4 + 8004ca0: b282 uxth r2, r0 + 8004ca2: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004ca6: 4803 ldr r0, [pc, #12] ; (8004cb4 ) + 8004ca8: f7ff ffd8 bl 8004c5c +} + 8004cac: e8bd 4010 ldmia.w sp!, {r4, lr} + rng_delay(); + 8004cb0: f7fd bd64 b.w 800277c + 8004cb4: 2009e1c0 .word 0x2009e1c0 + +08004cb8 : +{ + 8004cb8: b513 push {r0, r1, r4, lr} + 8004cba: 4604 mov r4, r0 + uint8_t cb = c; + 8004cbc: f88d 0007 strb.w r0, [sp, #7] + rng_delay(); + 8004cc0: f7fd fd5c bl 800277c + if(cb != '\n') { + 8004cc4: f89d 3007 ldrb.w r3, [sp, #7] + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004cc8: 4808 ldr r0, [pc, #32] ; (8004cec ) + if(cb != '\n') { + 8004cca: 2b0a cmp r3, #10 + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004ccc: bf08 it eq + 8004cce: 4908 ldreq r1, [pc, #32] ; (8004cf0 ) + HAL_USART_Transmit(&con, &cb, 1, HAL_MAX_DELAY); + 8004cd0: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004cd4: bf1a itte ne + 8004cd6: 2201 movne r2, #1 + 8004cd8: f10d 0107 addne.w r1, sp, #7 + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004cdc: 2202 moveq r2, #2 + 8004cde: f7ff ffbd bl 8004c5c + rng_delay(); + 8004ce2: f7fd fd4b bl 800277c +} + 8004ce6: 4620 mov r0, r4 + 8004ce8: b002 add sp, #8 + 8004cea: bd10 pop {r4, pc} + 8004cec: 2009e1c0 .word 0x2009e1c0 + 8004cf0: 0800e74f .word 0x0800e74f + +08004cf4 : +{ + 8004cf4: b538 push {r3, r4, r5, lr} + putchar(hexmap[(b>>4) & 0xf]); + 8004cf6: 4d06 ldr r5, [pc, #24] ; (8004d10 ) + 8004cf8: 0903 lsrs r3, r0, #4 +{ + 8004cfa: 4604 mov r4, r0 + putchar(hexmap[(b>>0) & 0xf]); + 8004cfc: f004 040f and.w r4, r4, #15 + putchar(hexmap[(b>>4) & 0xf]); + 8004d00: 5ce8 ldrb r0, [r5, r3] + 8004d02: f7ff ffd9 bl 8004cb8 + putchar(hexmap[(b>>0) & 0xf]); + 8004d06: 5d28 ldrb r0, [r5, r4] +} + 8004d08: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + putchar(hexmap[(b>>0) & 0xf]); + 8004d0c: f7ff bfd4 b.w 8004cb8 + 8004d10: 0800e752 .word 0x0800e752 + +08004d14 : +{ + 8004d14: b538 push {r3, r4, r5, lr} + putchar(hexmap[(w>>12) & 0xf]); + 8004d16: 4d0b ldr r5, [pc, #44] ; (8004d44 ) + 8004d18: 0b03 lsrs r3, r0, #12 +{ + 8004d1a: 4604 mov r4, r0 + putchar(hexmap[(w>>12) & 0xf]); + 8004d1c: 5ce8 ldrb r0, [r5, r3] + 8004d1e: f7ff ffcb bl 8004cb8 + putchar(hexmap[(w>>8) & 0xf]); + 8004d22: f3c4 2303 ubfx r3, r4, #8, #4 + 8004d26: 5ce8 ldrb r0, [r5, r3] + 8004d28: f7ff ffc6 bl 8004cb8 + putchar(hexmap[(w>>4) & 0xf]); + 8004d2c: f3c4 1303 ubfx r3, r4, #4, #4 + putchar(hexmap[(w>>0) & 0xf]); + 8004d30: f004 040f and.w r4, r4, #15 + putchar(hexmap[(w>>4) & 0xf]); + 8004d34: 5ce8 ldrb r0, [r5, r3] + 8004d36: f7ff ffbf bl 8004cb8 + putchar(hexmap[(w>>0) & 0xf]); + 8004d3a: 5d28 ldrb r0, [r5, r4] +} + 8004d3c: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + putchar(hexmap[(w>>0) & 0xf]); + 8004d40: f7ff bfba b.w 8004cb8 + 8004d44: 0800e752 .word 0x0800e752 + +08004d48 : +{ + 8004d48: b510 push {r4, lr} + 8004d4a: 4604 mov r4, r0 + puthex4(w >> 16); + 8004d4c: 0c00 lsrs r0, r0, #16 + 8004d4e: f7ff ffe1 bl 8004d14 + puthex4(w & 0xffff); + 8004d52: b2a0 uxth r0, r4 +} + 8004d54: e8bd 4010 ldmia.w sp!, {r4, lr} + puthex4(w & 0xffff); + 8004d58: f7ff bfdc b.w 8004d14 + +08004d5c : +{ + 8004d5c: b5f8 push {r3, r4, r5, r6, r7, lr} + 8004d5e: 4605 mov r5, r0 + 8004d60: 2604 movs r6, #4 + for(int m=1000; m; m /= 10) { + 8004d62: f44f 747a mov.w r4, #1000 ; 0x3e8 + char n = '0' + ((w / m) % 10); + 8004d66: 270a movs r7, #10 + if(w >= m) { + 8004d68: 42a5 cmp r5, r4 + 8004d6a: db09 blt.n 8004d80 + char n = '0' + ((w / m) % 10); + 8004d6c: fb95 f3f4 sdiv r3, r5, r4 + 8004d70: fb93 f0f7 sdiv r0, r3, r7 + 8004d74: fb07 3310 mls r3, r7, r0, r3 + 8004d78: 3330 adds r3, #48 ; 0x30 + putchar(n); + 8004d7a: b2d8 uxtb r0, r3 + 8004d7c: f7ff ff9c bl 8004cb8 + for(int m=1000; m; m /= 10) { + 8004d80: fb94 f4f7 sdiv r4, r4, r7 + 8004d84: 3e01 subs r6, #1 + 8004d86: d1ef bne.n 8004d68 +} + 8004d88: bdf8 pop {r3, r4, r5, r6, r7, pc} + +08004d8a : +{ + 8004d8a: b570 push {r4, r5, r6, lr} + 8004d8c: 4606 mov r6, r0 + 8004d8e: 460d mov r5, r1 + for(int i=0; i +} + 8004d96: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + putchar('\n'); + 8004d9a: 200a movs r0, #10 + 8004d9c: f7ff bf8c b.w 8004cb8 + puthex2(data[i]); + 8004da0: 5d30 ldrb r0, [r6, r4] + 8004da2: f7ff ffa7 bl 8004cf4 + for(int i=0; i + ... + +08004dac : +{ + 8004dac: b513 push {r0, r1, r4, lr} + 8004dae: 9001 str r0, [sp, #4] + int ln = strlen(msg); + 8004db0: f008 fc93 bl 800d6da + 8004db4: 4604 mov r4, r0 + rng_delay(); + 8004db6: f7fd fce1 bl 800277c + if(ln) HAL_USART_Transmit(&con, (uint8_t *)msg, ln, HAL_MAX_DELAY); + 8004dba: 9901 ldr r1, [sp, #4] + 8004dbc: b12c cbz r4, 8004dca + 8004dbe: 4809 ldr r0, [pc, #36] ; (8004de4 ) + 8004dc0: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004dc4: b2a2 uxth r2, r4 + 8004dc6: f7ff ff49 bl 8004c5c + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004dca: 4907 ldr r1, [pc, #28] ; (8004de8 ) + 8004dcc: 4805 ldr r0, [pc, #20] ; (8004de4 ) + 8004dce: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004dd2: 2202 movs r2, #2 + 8004dd4: f7ff ff42 bl 8004c5c + rng_delay(); + 8004dd8: f7fd fcd0 bl 800277c +} + 8004ddc: 2001 movs r0, #1 + 8004dde: b002 add sp, #8 + 8004de0: bd10 pop {r4, pc} + 8004de2: bf00 nop + 8004de4: 2009e1c0 .word 0x2009e1c0 + 8004de8: 0800e74f .word 0x0800e74f + +08004dec : + +// psram_send_byte() +// + void +psram_send_byte(OSPI_HandleTypeDef *qh, uint8_t cmd_byte, bool is_quad) +{ + 8004dec: b570 push {r4, r5, r6, lr} + 8004dee: b094 sub sp, #80 ; 0x50 + 8004df0: 4604 mov r4, r0 + 8004df2: 460e mov r6, r1 + 8004df4: 4615 mov r5, r2 + // Send single-byte commands to the PSRAM chip. Quad mode or normal SPI. + + OSPI_RegularCmdTypeDef cmd = { + 8004df6: 2100 movs r1, #0 + 8004df8: 2250 movs r2, #80 ; 0x50 + 8004dfa: 4668 mov r0, sp + 8004dfc: f008 fc3a bl 800d674 + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .Instruction = cmd_byte, // Exit Quad Mode + .InstructionMode = is_quad ? HAL_OSPI_INSTRUCTION_4_LINES : HAL_OSPI_INSTRUCTION_1_LINE, + 8004e00: 2d00 cmp r5, #0 + 8004e02: bf14 ite ne + 8004e04: 2303 movne r3, #3 + 8004e06: 2301 moveq r3, #1 + .DataMode = HAL_OSPI_DATA_NONE, + .NbData = 0, // how much to read in bytes + }; + + // Start and finish a "Indirection functional mode" request + HAL_OSPI_Command(qh, &cmd, HAL_MAX_DELAY); + 8004e08: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 8004e0c: 4669 mov r1, sp + 8004e0e: 4620 mov r0, r4 + OSPI_RegularCmdTypeDef cmd = { + 8004e10: 9602 str r6, [sp, #8] + 8004e12: 9303 str r3, [sp, #12] + HAL_OSPI_Command(qh, &cmd, HAL_MAX_DELAY); + 8004e14: f006 f884 bl 800af20 +} + 8004e18: b014 add sp, #80 ; 0x50 + 8004e1a: bd70 pop {r4, r5, r6, pc} + +08004e1c : + +// psram_setup() +// + void +psram_setup(void) +{ + 8004e1c: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8004e20: b0c6 sub sp, #280 ; 0x118 + // Using OSPI1 block + OSPI_HandleTypeDef qh = { 0 }; + 8004e22: 2250 movs r2, #80 ; 0x50 + 8004e24: 2100 movs r1, #0 + 8004e26: a80a add r0, sp, #40 ; 0x28 + 8004e28: f008 fc24 bl 800d674 + + // enable clocks + __HAL_RCC_OSPI1_CLK_ENABLE(); + 8004e2c: 4b6a ldr r3, [pc, #424] ; (8004fd8 ) + // reset module + __HAL_RCC_OSPI1_FORCE_RESET(); + __HAL_RCC_OSPI1_RELEASE_RESET(); + + // configure pins: Port E PE10-PE15 + GPIO_InitTypeDef setup = { + 8004e2e: 4c6b ldr r4, [pc, #428] ; (8004fdc ) + __HAL_RCC_OSPI1_CLK_ENABLE(); + 8004e30: 6d1a ldr r2, [r3, #80] ; 0x50 + 8004e32: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8004e36: 651a str r2, [r3, #80] ; 0x50 + 8004e38: 6d1a ldr r2, [r3, #80] ; 0x50 + 8004e3a: f402 7280 and.w r2, r2, #256 ; 0x100 + 8004e3e: 9201 str r2, [sp, #4] + 8004e40: 9a01 ldr r2, [sp, #4] + __HAL_RCC_GPIOE_CLK_ENABLE(); + 8004e42: 6cda ldr r2, [r3, #76] ; 0x4c + 8004e44: f042 0210 orr.w r2, r2, #16 + 8004e48: 64da str r2, [r3, #76] ; 0x4c + 8004e4a: 6cda ldr r2, [r3, #76] ; 0x4c + 8004e4c: f002 0210 and.w r2, r2, #16 + 8004e50: 9202 str r2, [sp, #8] + 8004e52: 9a02 ldr r2, [sp, #8] + __HAL_RCC_OSPI1_FORCE_RESET(); + 8004e54: 6b1a ldr r2, [r3, #48] ; 0x30 + 8004e56: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8004e5a: 631a str r2, [r3, #48] ; 0x30 + __HAL_RCC_OSPI1_RELEASE_RESET(); + 8004e5c: 6b1a ldr r2, [r3, #48] ; 0x30 + 8004e5e: f422 7280 bic.w r2, r2, #256 ; 0x100 + 8004e62: 631a str r2, [r3, #48] ; 0x30 + GPIO_InitTypeDef setup = { + 8004e64: cc0f ldmia r4!, {r0, r1, r2, r3} + 8004e66: ad05 add r5, sp, #20 + 8004e68: c50f stmia r5!, {r0, r1, r2, r3} + 8004e6a: 6823 ldr r3, [r4, #0] + .Mode = GPIO_MODE_AF_PP, // not sure + .Pull = GPIO_NOPULL, // not sure + .Speed = GPIO_SPEED_FREQ_VERY_HIGH, + .Alternate = GPIO_AF10_OCTOSPIM_P1, + }; + HAL_GPIO_Init(GPIOE, &setup); + 8004e6c: 485c ldr r0, [pc, #368] ; (8004fe0 ) + GPIO_InitTypeDef setup = { + 8004e6e: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOE, &setup); + 8004e70: a905 add r1, sp, #20 + 8004e72: f7fc f8cd bl 8001010 + + + // Config operational values + qh.Instance = OCTOSPI1; + qh.Init.FifoThreshold = 1; // ?? unused + 8004e76: 4b5b ldr r3, [pc, #364] ; (8004fe4 ) + 8004e78: 2701 movs r7, #1 + qh.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; + qh.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // want standard mode (but octo only?) + qh.Init.DeviceSize = 24; // assume max size, actual is 8Mbyte + 8004e7a: 2218 movs r2, #24 + qh.Init.FifoThreshold = 1; // ?? unused + 8004e7c: e9cd 370a strd r3, r7, [sp, #40] ; 0x28 + qh.Init.ChipSelectHighTime = 1; // 1, maxed out, seems to work + 8004e80: e9cd 270e strd r2, r7, [sp, #56] ; 0x38 + qh.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; + 8004e84: 2300 movs r3, #0 + qh.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; // maybe? + 8004e86: f04f 5280 mov.w r2, #268435456 ; 0x10000000 + qh.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; // required! + qh.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; // low clock between ops (required, see errata) +#if HCLK_FREQUENCY == 80000000 + qh.Init.ClockPrescaler = 1; // prescaler (1=>80Mhz, 2=>40Mhz, etc) +#elif HCLK_FREQUENCY == 120000000 + qh.Init.ClockPrescaler = 2; // prescaler (1=>120Mhz, 2=>60Mhz, etc) + 8004e8a: f04f 0802 mov.w r8, #2 +#else +# error "testing needed" +#endif + qh.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; // dont need it? + 8004e8e: f04f 0908 mov.w r9, #8 + // - (during reads) 3 => 400ns 4 => 660ns 5+ => 1us + // - LATER: Errata 2.8.1 => says shall not use + qh.Init.ChipSelectBoundary = 0; + + // module init + HAL_StatusTypeDef rv = HAL_OSPI_Init(&qh); + 8004e92: a80a add r0, sp, #40 ; 0x28 + qh.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // want standard mode (but octo only?) + 8004e94: e9cd 330c strd r3, r3, [sp, #48] ; 0x30 + qh.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; // low clock between ops (required, see errata) + 8004e98: e9cd 3310 strd r3, r3, [sp, #64] ; 0x40 + qh.Init.ChipSelectBoundary = 0; + 8004e9c: e9cd 3915 strd r3, r9, [sp, #84] ; 0x54 + qh.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; // maybe? + 8004ea0: 9214 str r2, [sp, #80] ; 0x50 + qh.Init.ClockPrescaler = 2; // prescaler (1=>120Mhz, 2=>60Mhz, etc) + 8004ea2: f8cd 8048 str.w r8, [sp, #72] ; 0x48 + HAL_StatusTypeDef rv = HAL_OSPI_Init(&qh); + 8004ea6: f005 ffd1 bl 800ae4c + ASSERT(rv == HAL_OK); + 8004eaa: 4606 mov r6, r0 + 8004eac: b110 cbz r0, 8004eb4 + 8004eae: 484e ldr r0, [pc, #312] ; (8004fe8 ) + 8004eb0: f7fb fdca bl 8000a48 + + // do some SPI commands first + + // Exit Quad mode, to get to a known state, after first power-up + psram_send_byte(&qh, 0xf5, true); + 8004eb4: 463a mov r2, r7 + 8004eb6: 21f5 movs r1, #245 ; 0xf5 + 8004eb8: a80a add r0, sp, #40 ; 0x28 + 8004eba: f7ff ff97 bl 8004dec + + // Chip Reset sequence + psram_send_byte(&qh, 0x66, false); // reset enable + 8004ebe: 4632 mov r2, r6 + 8004ec0: 2166 movs r1, #102 ; 0x66 + 8004ec2: a80a add r0, sp, #40 ; 0x28 + 8004ec4: f7ff ff92 bl 8004dec + + // Read Electronic ID + // - length not clear from datasheet, but repeats after 8 bytes + uint8_t psram_chip_eid[8]; + + { OSPI_RegularCmdTypeDef cmd = { + 8004ec8: ad32 add r5, sp, #200 ; 0xc8 + psram_send_byte(&qh, 0x99, false); // reset + 8004eca: 4632 mov r2, r6 + 8004ecc: 2199 movs r1, #153 ; 0x99 + 8004ece: a80a add r0, sp, #40 ; 0x28 + 8004ed0: f7ff ff8c bl 8004dec + { OSPI_RegularCmdTypeDef cmd = { + 8004ed4: 2250 movs r2, #80 ; 0x50 + 8004ed6: 4631 mov r1, r6 + 8004ed8: 4628 mov r0, r5 + 8004eda: f008 fbcb bl 800d674 + 8004ede: 239f movs r3, #159 ; 0x9f + 8004ee0: e9cd 3734 strd r3, r7, [sp, #208] ; 0xd0 + 8004ee4: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 8004ee8: f44f 7380 mov.w r3, #256 ; 0x100 + 8004eec: e9cd 3a39 strd r3, sl, [sp, #228] ; 0xe4 + .DataMode = HAL_OSPI_DATA_1_LINE, + .NbData = sizeof(psram_chip_eid), // how much to read in bytes + }; + + // Start a "Indirection functional mode" request + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004ef0: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + { OSPI_RegularCmdTypeDef cmd = { + 8004ef4: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004ef8: 4629 mov r1, r5 + 8004efa: a80a add r0, sp, #40 ; 0x28 + { OSPI_RegularCmdTypeDef cmd = { + 8004efc: e9cd 3940 strd r3, r9, [sp, #256] ; 0x100 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004f00: f006 f80e bl 800af20 + if(rv != HAL_OK) goto fail; + 8004f04: 2800 cmp r0, #0 + 8004f06: d15d bne.n 8004fc4 + + rv = HAL_OSPI_Receive(&qh, psram_chip_eid, HAL_MAX_DELAY); + 8004f08: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 8004f0c: a903 add r1, sp, #12 + 8004f0e: a80a add r0, sp, #40 ; 0x28 + 8004f10: f006 f938 bl 800b184 + if(rv != HAL_OK) goto fail; + 8004f14: 4606 mov r6, r0 + 8004f16: 2800 cmp r0, #0 + 8004f18: d154 bne.n 8004fc4 + } + + //puts2("PSRAM EID: "); + //hex_dump(psram_chip_eid, sizeof(psram_chip_eid)); + ASSERT(psram_chip_eid[0] == 0x0d); + 8004f1a: f89d 300c ldrb.w r3, [sp, #12] + 8004f1e: 2b0d cmp r3, #13 + 8004f20: d1c5 bne.n 8004eae + ASSERT(psram_chip_eid[1] == 0x5d); + 8004f22: f89d 300d ldrb.w r3, [sp, #13] + 8004f26: 2b5d cmp r3, #93 ; 0x5d + 8004f28: d1c1 bne.n 8004eae + // .. other bits seem pretty similar between devices, they don't claim they are UUID + + // Put into Quad mode + psram_send_byte(&qh, 0x35, false); // 0x35 = Enter Quad Mode + 8004f2a: 4602 mov r2, r0 + 8004f2c: 2135 movs r1, #53 ; 0x35 + 8004f2e: a80a add r0, sp, #40 ; 0x28 + 8004f30: f7ff ff5c bl 8004dec + + // Configure read/write cycles for mem-mapped mode + { OSPI_RegularCmdTypeDef cmd = { + 8004f34: 4631 mov r1, r6 + 8004f36: 224c movs r2, #76 ; 0x4c + 8004f38: a81f add r0, sp, #124 ; 0x7c + 8004f3a: f008 fb9b bl 800d674 + 8004f3e: f04f 0903 mov.w r9, #3 + 8004f42: f8cd 8078 str.w r8, [sp, #120] ; 0x78 + 8004f46: f8cd 8080 str.w r8, [sp, #128] ; 0x80 + .DataMode = HAL_OSPI_DATA_4_LINES, + .NbData = 0, // don't care / TBD? + }; + + // Config for write + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004f4a: a91e add r1, sp, #120 ; 0x78 + { OSPI_RegularCmdTypeDef cmd = { + 8004f4c: f44f 7840 mov.w r8, #768 ; 0x300 + 8004f50: f04f 7640 mov.w r6, #50331648 ; 0x3000000 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004f54: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 8004f58: a80a add r0, sp, #40 ; 0x28 + { OSPI_RegularCmdTypeDef cmd = { + 8004f5a: e9cd 8a25 strd r8, sl, [sp, #148] ; 0x94 + 8004f5e: f8cd 9084 str.w r9, [sp, #132] ; 0x84 + 8004f62: 962c str r6, [sp, #176] ; 0xb0 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8004f64: f005 ffdc bl 800af20 + if(rv != HAL_OK) goto fail; + 8004f68: 4601 mov r1, r0 + 8004f6a: bb58 cbnz r0, 8004fc4 + + // .. for read + OSPI_RegularCmdTypeDef cmd2 = { + 8004f6c: 224c movs r2, #76 ; 0x4c + 8004f6e: a833 add r0, sp, #204 ; 0xcc + 8004f70: f008 fb80 bl 800d674 + 8004f74: 23eb movs r3, #235 ; 0xeb + 8004f76: e9cd 3934 strd r3, r9, [sp, #208] ; 0xd0 + .DataMode = HAL_OSPI_DATA_4_LINES, + .NbData = 0, // don't care / TBD? + }; + + // Config for read + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 8004f7a: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + OSPI_RegularCmdTypeDef cmd2 = { + 8004f7e: 2306 movs r3, #6 + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 8004f80: 4629 mov r1, r5 + 8004f82: a80a add r0, sp, #40 ; 0x28 + OSPI_RegularCmdTypeDef cmd2 = { + 8004f84: e9cd 8a39 strd r8, sl, [sp, #228] ; 0xe4 + 8004f88: 9732 str r7, [sp, #200] ; 0xc8 + 8004f8a: 9640 str r6, [sp, #256] ; 0x100 + 8004f8c: 9343 str r3, [sp, #268] ; 0x10c + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 8004f8e: f005 ffc7 bl 800af20 + if(rv != HAL_OK) goto fail; + 8004f92: b9b8 cbnz r0, 8004fc4 + } + + // config for memmap + { OSPI_MemoryMappedTypeDef mmap = { + 8004f94: e9d4 0101 ldrd r0, r1, [r4, #4] + 8004f98: e885 0003 stmia.w r5, {r0, r1} + // Need this so that CS lines returns to inactive sometimes. + .TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_ENABLE, + .TimeOutPeriod = 16, // no idea, max value 0xffff + }; + + rv = HAL_OSPI_MemoryMapped(&qh, &mmap); + 8004f9c: 4629 mov r1, r5 + 8004f9e: a80a add r0, sp, #40 ; 0x28 + 8004fa0: f006 f9d6 bl 800b350 + if(rv != HAL_OK) goto fail; + 8004fa4: b970 cbnz r0, 8004fc4 +#else + // Only a quick operational check only here. Non-destructive. + { __IO uint32_t *ptr = (uint32_t *)(PSRAM_BASE+PSRAM_SIZE-4); + uint32_t tmp; + + tmp = *ptr; + 8004fa6: 4b11 ldr r3, [pc, #68] ; (8004fec ) + *ptr = 0x55aa1234; + 8004fa8: 4a11 ldr r2, [pc, #68] ; (8004ff0 ) + tmp = *ptr; + 8004faa: f8d3 1ffc ldr.w r1, [r3, #4092] ; 0xffc + *ptr = 0x55aa1234; + 8004fae: f8c3 2ffc str.w r2, [r3, #4092] ; 0xffc + if(*ptr != 0x55aa1234) goto fail; + 8004fb2: f8d3 0ffc ldr.w r0, [r3, #4092] ; 0xffc + 8004fb6: 4290 cmp r0, r2 + 8004fb8: d104 bne.n 8004fc4 + *ptr = tmp; + 8004fba: f8c3 1ffc str.w r1, [r3, #4092] ; 0xffc + + oled_setup(); + oled_show(screen_fatal); + + LOCKUP_FOREVER(); +} + 8004fbe: b046 add sp, #280 ; 0x118 + 8004fc0: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + puts("PSRAM fail"); + 8004fc4: 480b ldr r0, [pc, #44] ; (8004ff4 ) + 8004fc6: f7ff fef1 bl 8004dac + oled_setup(); + 8004fca: f7fb feab bl 8000d24 + oled_show(screen_fatal); + 8004fce: 480a ldr r0, [pc, #40] ; (8004ff8 ) + 8004fd0: f7fb ff38 bl 8000e44 + LOCKUP_FOREVER(); + 8004fd4: bf30 wfi + 8004fd6: e7fd b.n 8004fd4 + 8004fd8: 40021000 .word 0x40021000 + 8004fdc: 0800e790 .word 0x0800e790 + 8004fe0: 48001000 .word 0x48001000 + 8004fe4: a0001000 .word 0xa0001000 + 8004fe8: 0800e3e0 .word 0x0800e3e0 + 8004fec: 907ff000 .word 0x907ff000 + 8004ff0: 55aa1234 .word 0x55aa1234 + 8004ff4: 0800e762 .word 0x0800e762 + 8004ff8: 0800db52 .word 0x0800db52 + +08004ffc : + +// psram_wipe() +// + void +psram_wipe(void) +{ + 8004ffc: b508 push {r3, lr} + if(OCTOSPI1->CR == 0) return; // PSRAM not enabled (yet?) + 8004ffe: 4b06 ldr r3, [pc, #24] ; (8005018 ) + 8005000: 681b ldr r3, [r3, #0] + 8005002: b143 cbz r3, 8005016 + + // Fast! But real; maybe 150ms + //puts2("PSRAM Wipe: "); + memset4((uint32_t *)PSRAM_BASE, rng_sample(), PSRAM_SIZE); + 8005004: f7fd fb66 bl 80026d4 + 8005008: f04f 4310 mov.w r3, #2415919104 ; 0x90000000 + *dest = value; + 800500c: f843 0b04 str.w r0, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8005010: f113 4fdf cmn.w r3, #1870659584 ; 0x6f800000 + 8005014: d1fa bne.n 800500c + //puts("done"); +} + 8005016: bd08 pop {r3, pc} + 8005018: a0001000 .word 0xa0001000 + +0800501c : +// NOTE: Incoming start address is typically not aligned. +// + void +psram_do_upgrade(const uint8_t *start, uint32_t size) +{ + ASSERT(size >= FW_MIN_LENGTH); + 800501c: f5b1 2f80 cmp.w r1, #262144 ; 0x40000 +{ + 8005020: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + 8005024: 4606 mov r6, r0 + 8005026: 460d mov r5, r1 + ASSERT(size >= FW_MIN_LENGTH); + 8005028: d202 bcs.n 8005030 + 800502a: 481e ldr r0, [pc, #120] ; (80050a4 ) + 800502c: f7fb fd0c bl 8000a48 + + // In case of reset/crash, we can recover, so save + // what we need for that -- yes, we will re-verify signatures + volatile recovery_header_t *h = RECHDR_POS; + h->start = start; + 8005030: 4b1d ldr r3, [pc, #116] ; (80050a8 ) + h->size = size; + h->magic1 = RECHDR_MAGIC1; + 8005032: 4a1e ldr r2, [pc, #120] ; (80050ac ) + h->start = start; + 8005034: 6058 str r0, [r3, #4] + h->size = size; + 8005036: 6099 str r1, [r3, #8] + h->magic1 = RECHDR_MAGIC1; + 8005038: 601a str r2, [r3, #0] + h->magic2 = RECHDR_MAGIC2; + 800503a: 4a1d ldr r2, [pc, #116] ; (80050b0 ) + 800503c: 60da str r2, [r3, #12] + + flash_setup0(); + 800503e: f7fc ffc3 bl 8001fc8 + flash_unlock(); + 8005042: f7fc ffe5 bl 8002010 + for(uint32_t pos=0; pos < size; pos += 8) { + uint32_t dest = FIRMWARE_START+pos; + + if(dest % (4*FLASH_ERASE_SIZE) == 0) { + // show some progress + oled_show_progress(screen_upgrading, pos*100/size); + 8005046: f8df 906c ldr.w r9, [pc, #108] ; 80050b4 + for(uint32_t pos=0; pos < size; pos += 8) { + 800504a: 2400 movs r4, #0 + oled_show_progress(screen_upgrading, pos*100/size); + 800504c: f04f 0864 mov.w r8, #100 ; 0x64 + uint32_t dest = FIRMWARE_START+pos; + 8005050: f104 6700 add.w r7, r4, #134217728 ; 0x8000000 + if(dest % (4*FLASH_ERASE_SIZE) == 0) { + 8005054: f3c4 030d ubfx r3, r4, #0, #14 + 8005058: f507 3700 add.w r7, r7, #131072 ; 0x20000 + 800505c: b933 cbnz r3, 800506c + oled_show_progress(screen_upgrading, pos*100/size); + 800505e: fb08 f104 mul.w r1, r8, r4 + 8005062: 4648 mov r0, r9 + 8005064: fbb1 f1f5 udiv r1, r1, r5 + 8005068: f7fb ff2e bl 8000ec8 + } + + if(dest % FLASH_ERASE_SIZE == 0) { + 800506c: f3c7 030b ubfx r3, r7, #0, #12 + 8005070: b923 cbnz r3, 800507c + // page erase as we go + rv = flash_page_erase(dest); + 8005072: 4638 mov r0, r7 + 8005074: f008 fb40 bl 800d6f8 <__flash_page_erase_veneer> + puts2("erase rv="); + puthex2(rv); + putchar('\n'); + } +#endif + ASSERT(rv == 0); + 8005078: 2800 cmp r0, #0 + 800507a: d1d6 bne.n 800502a + } + + memcpy(&tmp, start+pos, 8); + 800507c: 1932 adds r2, r6, r4 + 800507e: 5930 ldr r0, [r6, r4] + 8005080: 6851 ldr r1, [r2, #4] + 8005082: 466b mov r3, sp + 8005084: c303 stmia r3!, {r0, r1} + rv = flash_burn(dest, tmp); + 8005086: 4638 mov r0, r7 + 8005088: e9dd 2300 ldrd r2, r3, [sp] + 800508c: f008 fb30 bl 800d6f0 <__flash_burn_veneer> + puts2(" addr="); + puthex8(dest); + putchar('\n'); + } +#endif + ASSERT(rv == 0); + 8005090: 2800 cmp r0, #0 + 8005092: d1ca bne.n 800502a + for(uint32_t pos=0; pos < size; pos += 8) { + 8005094: 3408 adds r4, #8 + 8005096: 42a5 cmp r5, r4 + 8005098: d8da bhi.n 8005050 + } + + flash_lock(); +} + 800509a: b003 add sp, #12 + 800509c: e8bd 43f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, lr} + flash_lock(); + 80050a0: f7fc bfae b.w 8002000 + 80050a4: 0800e3e0 .word 0x0800e3e0 + 80050a8: 907ff800 .word 0x907ff800 + 80050ac: dbcc8350 .word 0xdbcc8350 + 80050b0: bafcfba3 .word 0xbafcfba3 + 80050b4: 0800e18b .word 0x0800e18b + +080050b8 : +{ + 80050b8: b510 push {r4, lr} + if( (h->magic1 != RECHDR_MAGIC1) + 80050ba: 4c1f ldr r4, [pc, #124] ; (8005138 ) + 80050bc: 4b1f ldr r3, [pc, #124] ; (800513c ) + 80050be: 6822 ldr r2, [r4, #0] + 80050c0: 429a cmp r2, r3 +{ + 80050c2: b088 sub sp, #32 + if( (h->magic1 != RECHDR_MAGIC1) + 80050c4: d113 bne.n 80050ee + || (h->magic2 != RECHDR_MAGIC2) + 80050c6: 68e2 ldr r2, [r4, #12] + 80050c8: 4b1d ldr r3, [pc, #116] ; (8005140 ) + 80050ca: 429a cmp r2, r3 + 80050cc: d10f bne.n 80050ee + || ((uint32_t)h->start < PSRAM_BASE) + 80050ce: 6863 ldr r3, [r4, #4] + 80050d0: f1b3 4f10 cmp.w r3, #2415919104 ; 0x90000000 + 80050d4: d30b bcc.n 80050ee + || ((uint32_t)h->start >= PSRAM_BASE+(PSRAM_SIZE/2)) + 80050d6: 6862 ldr r2, [r4, #4] + 80050d8: 4b1a ldr r3, [pc, #104] ; (8005144 ) + 80050da: 429a cmp r2, r3 + 80050dc: d807 bhi.n 80050ee + || (h->size > FW_MAX_LENGTH_MK4) + 80050de: 68a3 ldr r3, [r4, #8] + 80050e0: f5b3 1ff0 cmp.w r3, #1966080 ; 0x1e0000 + 80050e4: d803 bhi.n 80050ee + || (h->size < FW_MIN_LENGTH) + 80050e6: 68a3 ldr r3, [r4, #8] + 80050e8: f5b3 2f80 cmp.w r3, #262144 ; 0x40000 + 80050ec: d205 bcs.n 80050fa + puts("PSR: nada"); + 80050ee: 4816 ldr r0, [pc, #88] ; (8005148 ) + puts("PSR: version"); + 80050f0: f7ff fe5c bl 8004dac +} + 80050f4: 2000 movs r0, #0 + 80050f6: b008 add sp, #32 + 80050f8: bd10 pop {r4, pc} + bool ok = verify_firmware_in_ram(h->start, h->size, world_check); + 80050fa: 6860 ldr r0, [r4, #4] + 80050fc: 68a1 ldr r1, [r4, #8] + 80050fe: 466a mov r2, sp + 8005100: f7fc fda8 bl 8001c54 + if(!ok) { + 8005104: b908 cbnz r0, 800510a + puts("PSR: !check"); + 8005106: 4811 ldr r0, [pc, #68] ; (800514c ) + 8005108: e7f2 b.n 80050f0 + if(!verify_world_checksum(world_check)) { + 800510a: 4668 mov r0, sp + 800510c: f7fc fdf6 bl 8001cfc + 8005110: b908 cbnz r0, 8005116 + puts("PSR: version"); + 8005112: 480f ldr r0, [pc, #60] ; (8005150 ) + 8005114: e7ec b.n 80050f0 + psram_do_upgrade(h->start, h->size); + 8005116: 6860 ldr r0, [r4, #4] + 8005118: 68a1 ldr r1, [r4, #8] + 800511a: f7ff ff7f bl 800501c + 800511e: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8005122: 490c ldr r1, [pc, #48] ; (8005154 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8005124: 4b0c ldr r3, [pc, #48] ; (8005158 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8005126: 68ca ldr r2, [r1, #12] + 8005128: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800512c: 4313 orrs r3, r2 + 800512e: 60cb str r3, [r1, #12] + 8005130: f3bf 8f4f dsb sy + __NOP(); + 8005134: bf00 nop + for(;;) /* wait until reset */ + 8005136: e7fd b.n 8005134 + 8005138: 907ff800 .word 0x907ff800 + 800513c: dbcc8350 .word 0xdbcc8350 + 8005140: bafcfba3 .word 0xbafcfba3 + 8005144: 903fffff .word 0x903fffff + 8005148: 0800e76d .word 0x0800e76d + 800514c: 0800e777 .word 0x0800e777 + 8005150: 0800e783 .word 0x0800e783 + 8005154: e000ed00 .word 0xe000ed00 + 8005158: 05fa0004 .word 0x05fa0004 + +0800515c : + +// sdcard_light() +// + void inline +sdcard_light(bool on) +{ + 800515c: 4602 mov r2, r0 + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, !!on); // turn LED off + 800515e: 2180 movs r1, #128 ; 0x80 + 8005160: 4801 ldr r0, [pc, #4] ; (8005168 ) + 8005162: f7fc b8cf b.w 8001304 + 8005166: bf00 nop + 8005168: 48000800 .word 0x48000800 + +0800516c : + +// sdcard_is_inserted() +// + bool +sdcard_is_inserted(void) +{ + 800516c: b508 push {r3, lr} +#ifdef FOR_Q1_ONLY + return !HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_3); // PD3 - inserted when low (Q) +#else + return !!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13); // PC13 - inserted when high (Mk4) + 800516e: f44f 5100 mov.w r1, #8192 ; 0x2000 + 8005172: 4803 ldr r0, [pc, #12] ; (8005180 ) + 8005174: f7fc f8c0 bl 80012f8 +#endif +} + 8005178: 3800 subs r0, #0 + 800517a: bf18 it ne + 800517c: 2001 movne r0, #1 + 800517e: bd08 pop {r3, pc} + 8005180: 48000800 .word 0x48000800 + +08005184 : + +// sdcard_try_file() +// + void +sdcard_try_file(uint32_t blk_pos) +{ + 8005184: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8005188: 4606 mov r6, r0 + 800518a: f5ad 7d0a sub.w sp, sp, #552 ; 0x228 + oled_show(screen_verify); + 800518e: 4832 ldr r0, [pc, #200] ; (8005258 ) + uint8_t *ps = (uint8_t *)PSRAM_BASE; + //uint8_t buf[512*8]; // half of all our SRAM 0x00002000 + uint8_t buf[512]; // slower, but works. + + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + int rv = HAL_SD_ReadBlocks(&hsd, buf, blk_pos+(off/512), sizeof(buf)/512, 60000); + 8005190: f8df 80e4 ldr.w r8, [pc, #228] ; 8005278 + oled_show(screen_verify); + 8005194: f7fb fe56 bl 8000e44 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 8005198: 2500 movs r5, #0 + int rv = HAL_SD_ReadBlocks(&hsd, buf, blk_pos+(off/512), sizeof(buf)/512, 60000); + 800519a: f64e 2760 movw r7, #60000 ; 0xea60 + 800519e: 9700 str r7, [sp, #0] + 80051a0: 2301 movs r3, #1 + 80051a2: eb06 2255 add.w r2, r6, r5, lsr #9 + 80051a6: a90a add r1, sp, #40 ; 0x28 + 80051a8: 4640 mov r0, r8 + 80051aa: f006 fe4d bl 800be48 + if(rv != HAL_OK) { + 80051ae: 4604 mov r4, r0 + 80051b0: b130 cbz r0, 80051c0 + puts("long read fail"); + 80051b2: 482a ldr r0, [pc, #168] ; (800525c ) + + // Check we have the **right** firmware, based on the world check sum + // but don't set the light at this point. + // - this includes check over bootrom (ourselves) + if(!verify_world_checksum(world_check)) { + puts("wrong world"); + 80051b4: f7ff fdfa bl 8004dac + // Do the upgrade, using PSRAM data. + psram_do_upgrade(start, len); + + // done + NVIC_SystemReset(); +} + 80051b8: f50d 7d0a add.w sp, sp, #552 ; 0x228 + 80051bc: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + memcpy(ps + off, buf, sizeof(buf)); + 80051c0: f105 4010 add.w r0, r5, #2415919104 ; 0x90000000 + 80051c4: f44f 7200 mov.w r2, #512 ; 0x200 + 80051c8: a90a add r1, sp, #40 ; 0x28 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 80051ca: f505 7500 add.w r5, r5, #512 ; 0x200 + memcpy(ps + off, buf, sizeof(buf)); + 80051ce: f008 fa29 bl 800d624 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 80051d2: f5b5 1ff0 cmp.w r5, #1966080 ; 0x1e0000 + 80051d6: d1e2 bne.n 800519e + for(int idx=0; idxtargets; idx++) { + 80051d8: f04f 4310 mov.w r3, #2415919104 ; 0x90000000 + if(elem->addr == FIRMWARE_START) { + 80051dc: 4d20 ldr r5, [pc, #128] ; (8005260 ) + for(int idx=0; idxtargets; idx++) { + 80051de: 7a99 ldrb r1, [r3, #10] + 80051e0: 4620 mov r0, r4 + ptr += sizeof(DFUFile_t); + 80051e2: 330b adds r3, #11 + for(int idx=0; idxtargets; idx++) { + 80051e4: 4288 cmp r0, r1 + 80051e6: db01 blt.n 80051ec + puts("DFU parse fail"); + 80051e8: 481e ldr r0, [pc, #120] ; (8005264 ) + 80051ea: e7e3 b.n 80051b4 + for(int ei=0; eielements; ei++) { + 80051ec: f8d3 610e ldr.w r6, [r3, #270] ; 0x10e + 80051f0: 2200 movs r2, #0 + ptr += sizeof(DFUTarget_t); + 80051f2: f503 7389 add.w r3, r3, #274 ; 0x112 + for(int ei=0; eielements; ei++) { + 80051f6: 42b2 cmp r2, r6 + 80051f8: d101 bne.n 80051fe + for(int idx=0; idxtargets; idx++) { + 80051fa: 3001 adds r0, #1 + 80051fc: e7f2 b.n 80051e4 + ptr += sizeof(DFUElement_t); + 80051fe: 461c mov r4, r3 + if(elem->addr == FIRMWARE_START) { + 8005200: f854 7b08 ldr.w r7, [r4], #8 + 8005204: 42af cmp r7, r5 + 8005206: d110 bne.n 800522a + *target_size = elem->size; + 8005208: 685d ldr r5, [r3, #4] + bool ok = verify_firmware_in_ram(start, len, world_check); + 800520a: aa02 add r2, sp, #8 + 800520c: 4629 mov r1, r5 + 800520e: 4620 mov r0, r4 + 8005210: f7fc fd20 bl 8001c54 + if(!ok) return; + 8005214: 2800 cmp r0, #0 + 8005216: d0cf beq.n 80051b8 + puts("good firmware"); + 8005218: 4813 ldr r0, [pc, #76] ; (8005268 ) + 800521a: f7ff fdc7 bl 8004dac + if(!verify_world_checksum(world_check)) { + 800521e: a802 add r0, sp, #8 + 8005220: f7fc fd6c bl 8001cfc + 8005224: b920 cbnz r0, 8005230 + puts("wrong world"); + 8005226: 4811 ldr r0, [pc, #68] ; (800526c ) + 8005228: e7c4 b.n 80051b4 + for(int ei=0; eielements; ei++) { + 800522a: 3201 adds r2, #1 + ptr += sizeof(DFUElement_t); + 800522c: 4623 mov r3, r4 + 800522e: e7e2 b.n 80051f6 + sdcard_light(false); + 8005230: 2000 movs r0, #0 + 8005232: f7ff ff93 bl 800515c + psram_do_upgrade(start, len); + 8005236: 4629 mov r1, r5 + 8005238: 4620 mov r0, r4 + 800523a: f7ff feef bl 800501c + 800523e: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8005242: 490b ldr r1, [pc, #44] ; (8005270 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8005244: 4b0b ldr r3, [pc, #44] ; (8005274 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8005246: 68ca ldr r2, [r1, #12] + 8005248: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800524c: 4313 orrs r3, r2 + 800524e: 60cb str r3, [r1, #12] + 8005250: f3bf 8f4f dsb sy + __NOP(); + 8005254: bf00 nop + for(;;) /* wait until reset */ + 8005256: e7fd b.n 8005254 + 8005258: 0800e242 .word 0x0800e242 + 800525c: 0800e7ac .word 0x0800e7ac + 8005260: 08020000 .word 0x08020000 + 8005264: 0800e7bb .word 0x0800e7bb + 8005268: 0800e7ca .word 0x0800e7ca + 800526c: 0800e7d8 .word 0x0800e7d8 + 8005270: e000ed00 .word 0xe000ed00 + 8005274: 05fa0004 .word 0x05fa0004 + 8005278: 2009e220 .word 0x2009e220 + +0800527c : + +// sdcard_search() +// + void +sdcard_search(void) +{ + 800527c: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + oled_show(screen_search); + 8005280: 4854 ldr r0, [pc, #336] ; (80053d4 ) +{ + 8005282: f5ad 7d04 sub.w sp, sp, #528 ; 0x210 + oled_show(screen_search); + 8005286: f7fb fddd bl 8000e44 + + if(!sdcard_is_inserted()) return; + 800528a: f7ff ff6f bl 800516c + 800528e: 2800 cmp r0, #0 + 8005290: d07a beq.n 8005388 + __HAL_RCC_SDMMC1_CLK_ENABLE(); + 8005292: 4f51 ldr r7, [pc, #324] ; (80053d8 ) + + uint32_t num_blocks; + + // open card (power it) and get details, do setup + puts2("sdcard_search: "); + 8005294: 4851 ldr r0, [pc, #324] ; (80053dc ) + { GPIO_InitTypeDef setup = { + 8005296: 4c52 ldr r4, [pc, #328] ; (80053e0 ) + puts2("sdcard_search: "); + 8005298: f7ff fcfa bl 8004c90 + __HAL_RCC_SDMMC1_CLK_ENABLE(); + 800529c: 6cfb ldr r3, [r7, #76] ; 0x4c + 800529e: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 80052a2: 64fb str r3, [r7, #76] ; 0x4c + 80052a4: 6cfb ldr r3, [r7, #76] ; 0x4c + 80052a6: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 80052aa: 9303 str r3, [sp, #12] + 80052ac: 9b03 ldr r3, [sp, #12] + { GPIO_InitTypeDef setup = { + 80052ae: cc0f ldmia r4!, {r0, r1, r2, r3} + 80052b0: ad04 add r5, sp, #16 + 80052b2: c50f stmia r5!, {r0, r1, r2, r3} + 80052b4: f854 3b04 ldr.w r3, [r4], #4 + 80052b8: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOC, &setup); + 80052ba: 484a ldr r0, [pc, #296] ; (80053e4 ) + 80052bc: a904 add r1, sp, #16 + 80052be: f7fb fea7 bl 8001010 + { GPIO_InitTypeDef setup = { + 80052c2: cc0f ldmia r4!, {r0, r1, r2, r3} + 80052c4: ae04 add r6, sp, #16 + 80052c6: c60f stmia r6!, {r0, r1, r2, r3} + 80052c8: 6823 ldr r3, [r4, #0] + 80052ca: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOD, &setup); + 80052cc: a904 add r1, sp, #16 + 80052ce: 4846 ldr r0, [pc, #280] ; (80053e8 ) + memset(&hsd, 0, sizeof(SD_HandleTypeDef)); + 80052d0: 4d46 ldr r5, [pc, #280] ; (80053ec ) + HAL_GPIO_Init(GPIOD, &setup); + 80052d2: f7fb fe9d bl 8001010 + __HAL_RCC_SDMMC1_FORCE_RESET(); + 80052d6: 6afb ldr r3, [r7, #44] ; 0x2c + 80052d8: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 80052dc: 62fb str r3, [r7, #44] ; 0x2c + __HAL_RCC_SDMMC1_RELEASE_RESET(); + 80052de: 6afb ldr r3, [r7, #44] ; 0x2c + 80052e0: f423 0380 bic.w r3, r3, #4194304 ; 0x400000 + 80052e4: 62fb str r3, [r7, #44] ; 0x2c + sdcard_setup(); + delay_ms(100); + 80052e6: 2064 movs r0, #100 ; 0x64 + 80052e8: f7fe fb06 bl 80038f8 + memset(&hsd, 0, sizeof(SD_HandleTypeDef)); + 80052ec: 2280 movs r2, #128 ; 0x80 + 80052ee: 2100 movs r1, #0 + 80052f0: 4628 mov r0, r5 + 80052f2: f008 f9bf bl 800d674 + puts2("sdcard_probe: "); + 80052f6: 483e ldr r0, [pc, #248] ; (80053f0 ) + 80052f8: f7ff fcca bl 8004c90 + hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + 80052fc: 4a3d ldr r2, [pc, #244] ; (80053f4 ) + 80052fe: 2300 movs r3, #0 + 8005300: e9c5 2300 strd r2, r3, [r5] + hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE; + 8005304: f44f 5280 mov.w r2, #4096 ; 0x1000 + hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; + 8005308: e9c5 2302 strd r2, r3, [r5, #8] + hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + 800530c: 612b str r3, [r5, #16] + int rv = HAL_SD_Init(&hsd); + 800530e: 4628 mov r0, r5 + hsd.Init.ClockDiv = SDMMC_TRANSFER_CLK_DIV; + 8005310: 2303 movs r3, #3 + 8005312: 616b str r3, [r5, #20] + int rv = HAL_SD_Init(&hsd); + 8005314: f007 fb12 bl 800c93c + if(rv != HAL_OK) { + 8005318: 4604 mov r4, r0 + 800531a: b130 cbz r0, 800532a + puts("init fail"); + 800531c: 4836 ldr r0, [pc, #216] ; (80053f8 ) + oled_show_progress(screen_search, pos*100 / num_blocks); + sdcard_light(true); + } + } + +} + 800531e: f50d 7d04 add.w sp, sp, #528 ; 0x210 + 8005322: e8bd 41f0 ldmia.w sp!, {r4, r5, r6, r7, r8, lr} + puts("bsize?"); + 8005326: f7ff bd41 b.w 8004dac + sdcard_light(true); + 800532a: 2001 movs r0, #1 + 800532c: f7ff ff16 bl 800515c + rv = HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO); + 8005330: 4621 mov r1, r4 + 8005332: 4628 mov r0, r5 + 8005334: f007 fbda bl 800caec + if(rv != HAL_OK) { + 8005338: b108 cbz r0, 800533e + puts("speed"); + 800533a: 4830 ldr r0, [pc, #192] ; (80053fc ) + 800533c: e7ef b.n 800531e + rv = HAL_SD_ConfigWideBusOperation(&hsd, SDMMC_BUS_WIDE_4B); + 800533e: f44f 4180 mov.w r1, #16384 ; 0x4000 + 8005342: 4628 mov r0, r5 + 8005344: f007 fa24 bl 800c790 + if(rv != HAL_OK) { + 8005348: 4604 mov r4, r0 + 800534a: b108 cbz r0, 8005350 + puts("wide"); + 800534c: 482c ldr r0, [pc, #176] ; (8005400 ) + 800534e: e7e6 b.n 800531e + if(hsd.SdCard.BlockSize != 512) { + 8005350: 6d2b ldr r3, [r5, #80] ; 0x50 + 8005352: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 8005356: d001 beq.n 800535c + puts("bsize?"); + 8005358: 482a ldr r0, [pc, #168] ; (8005404 ) + 800535a: e7e0 b.n 800531e + puts("ok"); + 800535c: 482a ldr r0, [pc, #168] ; (8005408 ) + *num_blocks = hsd.SdCard.BlockNbr; + 800535e: 6cee ldr r6, [r5, #76] ; 0x4c + if(memcmp(blk, "DfuSe", 5) == 0) { + 8005360: 4f2a ldr r7, [pc, #168] ; (800540c ) + oled_show_progress(screen_search, pos*100 / num_blocks); + 8005362: f8df 8070 ldr.w r8, [pc, #112] ; 80053d4 + puts("ok"); + 8005366: f7ff fd21 bl 8004dac + for(int pos=0; pos + int rv = HAL_SD_ReadBlocks(&hsd, blk, pos, 1, 60000); + 800536e: f64e 2360 movw r3, #60000 ; 0xea60 + 8005372: 9300 str r3, [sp, #0] + 8005374: 4622 mov r2, r4 + 8005376: 2301 movs r3, #1 + 8005378: a904 add r1, sp, #16 + 800537a: 4628 mov r0, r5 + 800537c: f006 fd64 bl 800be48 + if(rv != HAL_OK) { + 8005380: b130 cbz r0, 8005390 + puts("fail read"); + 8005382: 4823 ldr r0, [pc, #140] ; (8005410 ) + 8005384: f7ff fd12 bl 8004dac +} + 8005388: f50d 7d04 add.w sp, sp, #528 ; 0x210 + 800538c: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if(memcmp(blk, "DfuSe", 5) == 0) { + 8005390: 2205 movs r2, #5 + 8005392: 4639 mov r1, r7 + 8005394: a804 add r0, sp, #16 + 8005396: f008 f935 bl 800d604 + 800539a: b9b0 cbnz r0, 80053ca + puts2("found @ "); + 800539c: 481d ldr r0, [pc, #116] ; (8005414 ) + 800539e: f7ff fc77 bl 8004c90 + puthex8(pos); + 80053a2: 4620 mov r0, r4 + 80053a4: f7ff fcd0 bl 8004d48 + putchar('\n'); + 80053a8: 200a movs r0, #10 + 80053aa: f7ff fc85 bl 8004cb8 + sdcard_try_file(pos); + 80053ae: 4620 mov r0, r4 + 80053b0: f7ff fee8 bl 8005184 + oled_show_progress(screen_search, pos*100 / num_blocks); + 80053b4: 2164 movs r1, #100 ; 0x64 + 80053b6: 4640 mov r0, r8 + 80053b8: 4361 muls r1, r4 + 80053ba: fbb1 f1f6 udiv r1, r1, r6 + 80053be: f7fb fd83 bl 8000ec8 + sdcard_light(true); + 80053c2: 2001 movs r0, #1 + 80053c4: f7ff feca bl 800515c + 80053c8: e001 b.n 80053ce + if(pos % 128 == 0) { + 80053ca: 0663 lsls r3, r4, #25 + 80053cc: d0f2 beq.n 80053b4 + for(int pos=0; pos + 80053d2: bf00 nop + 80053d4: 0800e079 .word 0x0800e079 + 80053d8: 40021000 .word 0x40021000 + 80053dc: 0800e7e4 .word 0x0800e7e4 + 80053e0: 0800e84c .word 0x0800e84c + 80053e4: 48000800 .word 0x48000800 + 80053e8: 48000c00 .word 0x48000c00 + 80053ec: 2009e220 .word 0x2009e220 + 80053f0: 0800e7f4 .word 0x0800e7f4 + 80053f4: 50062400 .word 0x50062400 + 80053f8: 0800e803 .word 0x0800e803 + 80053fc: 0800e80d .word 0x0800e80d + 8005400: 0800e813 .word 0x0800e813 + 8005404: 0800e818 .word 0x0800e818 + 8005408: 0800e81f .word 0x0800e81f + 800540c: 0800e82c .word 0x0800e82c + 8005410: 0800e822 .word 0x0800e822 + 8005414: 0800e832 .word 0x0800e832 + +08005418 : + +// sdcard_recovery() +// + void +sdcard_recovery(void) +{ + 8005418: b508 push {r3, lr} + // Use SDCard to recover. Must be precise version they tried to + // install before, and will be slow AF. + + puts("Recovery mode."); + 800541a: 480b ldr r0, [pc, #44] ; (8005448 ) + while(1) { + // .. need them to insert a card + + sdcard_light(false); + while(!sdcard_is_inserted()) { + oled_show(screen_recovery); + 800541c: 4c0b ldr r4, [pc, #44] ; (800544c ) + puts("Recovery mode."); + 800541e: f7ff fcc5 bl 8004dac + sdcard_light(false); + 8005422: 2000 movs r0, #0 + 8005424: f7ff fe9a bl 800515c + while(!sdcard_is_inserted()) { + 8005428: f7ff fea0 bl 800516c + 800542c: b128 cbz r0, 800543a + delay_ms(200); + } + + // look for binary, will reset system if successful + sdcard_light(true); + 800542e: 2001 movs r0, #1 + 8005430: f7ff fe94 bl 800515c + sdcard_search(); + 8005434: f7ff ff22 bl 800527c + sdcard_light(false); + 8005438: e7f3 b.n 8005422 + oled_show(screen_recovery); + 800543a: 4620 mov r0, r4 + 800543c: f7fb fd02 bl 8000e44 + delay_ms(200); + 8005440: 20c8 movs r0, #200 ; 0xc8 + 8005442: f7fe fa59 bl 80038f8 + 8005446: e7ef b.n 8005428 + 8005448: 0800e83b .word 0x0800e83b + 800544c: 0800dc78 .word 0x0800dc78 + +08005450 : +#include + +// so we don't need stm32l4xx_hal_hash_ex.c +HAL_StatusTypeDef HAL_HASHEx_SHA256_Accmlt(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size) +{ + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA256); + 8005450: 4b01 ldr r3, [pc, #4] ; (8005458 ) + 8005452: f005 ba41 b.w 800a8d8 + 8005456: bf00 nop + 8005458: 00040080 .word 0x00040080 + +0800545c : +} + +HAL_StatusTypeDef HAL_HASHEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 800545c: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256); + 800545e: 4c04 ldr r4, [pc, #16] ; (8005470 ) + 8005460: 9401 str r4, [sp, #4] + 8005462: 9c04 ldr r4, [sp, #16] + 8005464: 9400 str r4, [sp, #0] + 8005466: f005 f993 bl 800a790 +} + 800546a: b002 add sp, #8 + 800546c: bd10 pop {r4, pc} + 800546e: bf00 nop + 8005470: 00040080 .word 0x00040080 + +08005474 : + +HAL_StatusTypeDef HAL_HMACEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 8005474: b513 push {r0, r1, r4, lr} + return HMAC_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256); + 8005476: 4c04 ldr r4, [pc, #16] ; (8005488 ) + 8005478: 9401 str r4, [sp, #4] + 800547a: 9c04 ldr r4, [sp, #16] + 800547c: 9400 str r4, [sp, #0] + 800547e: f005 fbc9 bl 800ac14 +} + 8005482: b002 add sp, #8 + 8005484: bd10 pop {r4, pc} + 8005486: bf00 nop + 8005488: 00040080 .word 0x00040080 + +0800548c : + +void sha256_init(SHA256_CTX *ctx) +{ + 800548c: b510 push {r4, lr} + memset(ctx, 0, sizeof(SHA256_CTX)); + 800548e: 2248 movs r2, #72 ; 0x48 +{ + 8005490: 4604 mov r4, r0 + memset(ctx, 0, sizeof(SHA256_CTX)); + 8005492: 2100 movs r1, #0 + 8005494: 3004 adds r0, #4 + 8005496: f008 f8ed bl 800d674 + +#if 1 + ctx->num_pending = 0; + ctx->hh.Init.DataType = HASH_DATATYPE_8B; + 800549a: 2320 movs r3, #32 + 800549c: 6023 str r3, [r4, #0] + HAL_HASH_Init(&ctx->hh); + 800549e: 4620 mov r0, r4 + __HAL_HASH_RESET_MDMAT(); + + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, + HASH_ALGOSELECTION_SHA256 | HASH_CR_INIT); +#endif +} + 80054a0: e8bd 4010 ldmia.w sp!, {r4, lr} + HAL_HASH_Init(&ctx->hh); + 80054a4: f005 b802 b.w 800a4ac + +080054a8 : + +void sha256_update(SHA256_CTX *ctx, const uint8_t data[], uint32_t len) +{ + 80054a8: b5f8 push {r3, r4, r5, r6, r7, lr} + HAL_StatusTypeDef rv; + + // clear out any pending bytes + if(ctx->num_pending + len >= 4) { + 80054aa: f890 3048 ldrb.w r3, [r0, #72] ; 0x48 + 80054ae: 4413 add r3, r2 + 80054b0: 2b03 cmp r3, #3 +{ + 80054b2: 4605 mov r5, r0 + 80054b4: 460e mov r6, r1 + 80054b6: 4614 mov r4, r2 + if(ctx->num_pending + len >= 4) { + 80054b8: d818 bhi.n 80054ec + } + } + + // write full blocks + uint32_t blocks = len / 4; + if(blocks) { + 80054ba: 2c03 cmp r4, #3 + 80054bc: d926 bls.n 800550c +#if 1 + rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, (uint8_t *)data, blocks*4); + 80054be: f024 0703 bic.w r7, r4, #3 + 80054c2: 463a mov r2, r7 + 80054c4: 4631 mov r1, r6 + 80054c6: 4628 mov r0, r5 + 80054c8: f7ff ffc2 bl 8005450 + ASSERT(rv == HAL_OK); + 80054cc: b9c8 cbnz r0, 8005502 + uint32_t tmp; + memcpy(&tmp, data, 4); + HASH->DIN = tmp; + } +#endif + len -= blocks*4; + 80054ce: f004 0403 and.w r4, r4, #3 + data += blocks*4; + 80054d2: 443e add r6, r7 + 80054d4: e01a b.n 800550c + ctx->pending[ctx->num_pending++] = *data; + 80054d6: 1c5a adds r2, r3, #1 + 80054d8: b2d2 uxtb r2, r2 + 80054da: f885 2048 strb.w r2, [r5, #72] ; 0x48 + 80054de: 442b add r3, r5 + 80054e0: f816 1b01 ldrb.w r1, [r6], #1 + 80054e4: f883 1044 strb.w r1, [r3, #68] ; 0x44 + if(!len) break; + 80054e8: 3c01 subs r4, #1 + 80054ea: d00d beq.n 8005508 + while(ctx->num_pending != 4) { + 80054ec: f895 3048 ldrb.w r3, [r5, #72] ; 0x48 + 80054f0: 2b04 cmp r3, #4 + 80054f2: d1f0 bne.n 80054d6 + rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, ctx->pending, 4); + 80054f4: 2204 movs r2, #4 + 80054f6: f105 0144 add.w r1, r5, #68 ; 0x44 + 80054fa: 4628 mov r0, r5 + 80054fc: f7ff ffa8 bl 8005450 + ASSERT(rv == HAL_OK); + 8005500: b140 cbz r0, 8005514 + 8005502: 480b ldr r0, [pc, #44] ; (8005530 ) + 8005504: f7fb faa0 bl 8000a48 + if(ctx->num_pending == 4) { + 8005508: 2a04 cmp r2, #4 + 800550a: d0f3 beq.n 80054f4 + 800550c: 4434 add r4, r6 + } + + // save runt for later + ASSERT(len <= 3); + while(len) { + 800550e: 42b4 cmp r4, r6 + 8005510: d103 bne.n 800551a + ctx->pending[ctx->num_pending++] = *data; + data++; + len--; + } +} + 8005512: bdf8 pop {r3, r4, r5, r6, r7, pc} + ctx->num_pending = 0; + 8005514: f885 0048 strb.w r0, [r5, #72] ; 0x48 + 8005518: e7cf b.n 80054ba + ctx->pending[ctx->num_pending++] = *data; + 800551a: f895 3048 ldrb.w r3, [r5, #72] ; 0x48 + 800551e: 1c5a adds r2, r3, #1 + 8005520: f885 2048 strb.w r2, [r5, #72] ; 0x48 + 8005524: 442b add r3, r5 + 8005526: f816 2b01 ldrb.w r2, [r6], #1 + 800552a: f883 2044 strb.w r2, [r3, #68] ; 0x44 + len--; + 800552e: e7ee b.n 800550e + 8005530: 0800e3e0 .word 0x0800e3e0 + +08005534 : + +void sha256_final(SHA256_CTX *ctx, uint8_t digest[32]) +{ + 8005534: b513 push {r0, r1, r4, lr} + // Do final 0-3 bytes, pad and return digest. +#if 1 + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&ctx->hh, + 8005536: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800553a: 9200 str r2, [sp, #0] +{ + 800553c: 460b mov r3, r1 + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&ctx->hh, + 800553e: f890 2048 ldrb.w r2, [r0, #72] ; 0x48 + 8005542: f100 0144 add.w r1, r0, #68 ; 0x44 + 8005546: f7ff ff89 bl 800545c + ctx->pending, ctx->num_pending, digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 800554a: b110 cbz r0, 8005552 + 800554c: 4802 ldr r0, [pc, #8] ; (8005558 ) + 800554e: f7fb fa7b bl 8000a48 + tmp = __REV(HASH_DIGEST->HR[6]); + memcpy(out, &tmp, 4); out += 4; + tmp = __REV(HASH_DIGEST->HR[7]); + memcpy(out, &tmp, 4); +#endif +} + 8005552: b002 add sp, #8 + 8005554: bd10 pop {r4, pc} + 8005556: bf00 nop + 8005558: 0800e3e0 .word 0x0800e3e0 + +0800555c : +// +// single-shot version (best) +// + void +sha256_single(const uint8_t data[], uint32_t len, uint8_t digest[32]) +{ + 800555c: b530 push {r4, r5, lr} + 800555e: b097 sub sp, #92 ; 0x5c + 8005560: 4604 mov r4, r0 + 8005562: 460d mov r5, r1 + 8005564: 9203 str r2, [sp, #12] + HASH_HandleTypeDef hh = {0}; + 8005566: 2100 movs r1, #0 + 8005568: 2240 movs r2, #64 ; 0x40 + 800556a: a806 add r0, sp, #24 + 800556c: f008 f882 bl 800d674 + + hh.Init.DataType = HASH_DATATYPE_8B; + 8005570: 2220 movs r2, #32 + + HAL_HASH_Init(&hh); + 8005572: a805 add r0, sp, #20 + hh.Init.DataType = HASH_DATATYPE_8B; + 8005574: 9205 str r2, [sp, #20] + HAL_HASH_Init(&hh); + 8005576: f004 ff99 bl 800a4ac + + // It's called "Start" but it handles the runt packet, so really can only + // be used once at end of message, or for whole message. + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&hh, (uint8_t *)data, len, + 800557a: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800557e: 9200 str r2, [sp, #0] + 8005580: 9b03 ldr r3, [sp, #12] + 8005582: 462a mov r2, r5 + 8005584: 4621 mov r1, r4 + 8005586: a805 add r0, sp, #20 + 8005588: f7ff ff68 bl 800545c + digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 800558c: b110 cbz r0, 8005594 + 800558e: 4802 ldr r0, [pc, #8] ; (8005598 ) + 8005590: f7fb fa5a bl 8000a48 +} + 8005594: b017 add sp, #92 ; 0x5c + 8005596: bd30 pop {r4, r5, pc} + 8005598: 0800e3e0 .word 0x0800e3e0 + +0800559c : +// hmac_sha256_init() +// + void +hmac_sha256_init(HMAC_CTX *ctx) +{ + memset(ctx, 0, sizeof(HMAC_CTX)); + 800559c: f44f 7282 mov.w r2, #260 ; 0x104 + 80055a0: 2100 movs r1, #0 + 80055a2: f008 b867 b.w 800d674 + ... + +080055a8 : + +// hmac_sha256_update() +// + void +hmac_sha256_update(HMAC_CTX *ctx, const uint8_t data[], uint32_t len) +{ + 80055a8: b538 push {r3, r4, r5, lr} + 80055aa: 4604 mov r4, r0 + // simple append + ASSERT(ctx->num_pending + len < sizeof(ctx->pending)); + 80055ac: f8d0 0100 ldr.w r0, [r0, #256] ; 0x100 + 80055b0: 1883 adds r3, r0, r2 + 80055b2: 2bff cmp r3, #255 ; 0xff +{ + 80055b4: 4615 mov r5, r2 + ASSERT(ctx->num_pending + len < sizeof(ctx->pending)); + 80055b6: d902 bls.n 80055be + 80055b8: 4805 ldr r0, [pc, #20] ; (80055d0 ) + 80055ba: f7fb fa45 bl 8000a48 + + memcpy(ctx->pending+ctx->num_pending, data, len); + 80055be: 4420 add r0, r4 + 80055c0: f008 f830 bl 800d624 + + ctx->num_pending += len; + 80055c4: f8d4 2100 ldr.w r2, [r4, #256] ; 0x100 + 80055c8: 442a add r2, r5 + 80055ca: f8c4 2100 str.w r2, [r4, #256] ; 0x100 +} + 80055ce: bd38 pop {r3, r4, r5, pc} + 80055d0: 0800e3e0 .word 0x0800e3e0 + +080055d4 : + +// hmac_sha256_final() +// + void +hmac_sha256_final(HMAC_CTX *ctx, const uint8_t key[32], uint8_t digest[32]) +{ + 80055d4: b530 push {r4, r5, lr} + 80055d6: b097 sub sp, #92 ; 0x5c + 80055d8: 4604 mov r4, r0 + 80055da: 460d mov r5, r1 + 80055dc: 9203 str r2, [sp, #12] + HASH_HandleTypeDef hh = {0}; + 80055de: 2100 movs r1, #0 + 80055e0: 2238 movs r2, #56 ; 0x38 + 80055e2: a808 add r0, sp, #32 + 80055e4: f008 f846 bl 800d674 + + hh.Init.DataType = HASH_DATATYPE_8B; + 80055e8: 2220 movs r2, #32 + hh.Init.pKey = (uint8_t *)key; // const viol due to API dumbness + hh.Init.KeySize = 32; + + HAL_HASH_Init(&hh); + 80055ea: a805 add r0, sp, #20 + hh.Init.KeySize = 32; + 80055ec: e9cd 2506 strd r2, r5, [sp, #24] + hh.Init.DataType = HASH_DATATYPE_8B; + 80055f0: 9205 str r2, [sp, #20] + HAL_HASH_Init(&hh); + 80055f2: f004 ff5b bl 800a4ac + + HAL_StatusTypeDef rv = HAL_HMACEx_SHA256_Start(&hh, + 80055f6: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 80055fa: 9200 str r2, [sp, #0] + 80055fc: 9b03 ldr r3, [sp, #12] + 80055fe: f8d4 2100 ldr.w r2, [r4, #256] ; 0x100 + 8005602: 4621 mov r1, r4 + 8005604: a805 add r0, sp, #20 + 8005606: f7ff ff35 bl 8005474 + ctx->pending, ctx->num_pending, digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 800560a: b110 cbz r0, 8005612 + 800560c: 4802 ldr r0, [pc, #8] ; (8005618 ) + 800560e: f7fb fa1b bl 8000a48 +} + 8005612: b017 add sp, #92 ; 0x5c + 8005614: bd30 pop {r4, r5, pc} + 8005616: bf00 nop + 8005618: 0800e3e0 .word 0x0800e3e0 + +0800561c : + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + 800561c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + ); + +#else /* Thumb-1 */ + uint32_t r4, r5, r6, r7; + + __asm__ volatile ( + 8005620: 3b01 subs r3, #1 + 8005622: 009b lsls r3, r3, #2 + 8005624: 4698 mov r8, r3 + 8005626: 005b lsls r3, r3, #1 + 8005628: 4699 mov r9, r3 + 800562a: 2300 movs r3, #0 + 800562c: 2400 movs r4, #0 + 800562e: 2500 movs r5, #0 + 8005630: 2600 movs r6, #0 + 8005632: b401 push {r0} + 8005634: 2700 movs r7, #0 + 8005636: e002 b.n 800563e + 8005638: 0037 movs r7, r6 + 800563a: 4640 mov r0, r8 + 800563c: 1a3f subs r7, r7, r0 + 800563e: b478 push {r3, r4, r5, r6} + 8005640: 1bf0 subs r0, r6, r7 + 8005642: 5814 ldr r4, [r2, r0] + 8005644: 59c8 ldr r0, [r1, r7] + 8005646: 0c03 lsrs r3, r0, #16 + 8005648: b280 uxth r0, r0 + 800564a: 0c25 lsrs r5, r4, #16 + 800564c: b2a4 uxth r4, r4 + 800564e: 001e movs r6, r3 + 8005650: 436e muls r6, r5 + 8005652: 4363 muls r3, r4 + 8005654: 4345 muls r5, r0 + 8005656: 4360 muls r0, r4 + 8005658: 2400 movs r4, #0 + 800565a: 195b adds r3, r3, r5 + 800565c: 4164 adcs r4, r4 + 800565e: 0424 lsls r4, r4, #16 + 8005660: 1936 adds r6, r6, r4 + 8005662: 041c lsls r4, r3, #16 + 8005664: 0c1b lsrs r3, r3, #16 + 8005666: 1900 adds r0, r0, r4 + 8005668: 415e adcs r6, r3 + 800566a: bc38 pop {r3, r4, r5} + 800566c: 181b adds r3, r3, r0 + 800566e: 4174 adcs r4, r6 + 8005670: 2000 movs r0, #0 + 8005672: 4145 adcs r5, r0 + 8005674: bc40 pop {r6} + 8005676: 3704 adds r7, #4 + 8005678: 4547 cmp r7, r8 + 800567a: dc01 bgt.n 8005680 + 800567c: 42b7 cmp r7, r6 + 800567e: ddde ble.n 800563e + 8005680: 9800 ldr r0, [sp, #0] + 8005682: 5183 str r3, [r0, r6] + 8005684: 4623 mov r3, r4 + 8005686: 462c mov r4, r5 + 8005688: 2500 movs r5, #0 + 800568a: 3604 adds r6, #4 + 800568c: 4546 cmp r6, r8 + 800568e: ddd1 ble.n 8005634 + 8005690: 454e cmp r6, r9 + 8005692: ddd1 ble.n 8005638 + 8005694: 5183 str r3, [r0, r6] + 8005696: bc01 pop {r0} + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) + : "r8", "r9", "cc", "memory" + ); +#endif +} + 8005698: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + +0800569c : + +#if !asm_clear +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + 800569c: ea21 71e1 bic.w r1, r1, r1, asr #31 + 80056a0: 008a lsls r2, r1, #2 + 80056a2: 2100 movs r1, #0 + 80056a4: f007 bfe6 b.w 800d674 + +080056a8 : +} +#endif /* !asm_clear */ + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { + 80056a8: b510 push {r4, lr} + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + 80056aa: 2300 movs r3, #0 + uECC_word_t bits = 0; + 80056ac: 461a mov r2, r3 + for (i = 0; i < num_words; ++i) { + 80056ae: b25c sxtb r4, r3 + 80056b0: 42a1 cmp r1, r4 + 80056b2: dc03 bgt.n 80056bc + bits |= vli[i]; + } + return (bits == 0); +} + 80056b4: fab2 f082 clz r0, r2 + 80056b8: 0940 lsrs r0, r0, #5 + 80056ba: bd10 pop {r4, pc} + bits |= vli[i]; + 80056bc: f850 4023 ldr.w r4, [r0, r3, lsl #2] + 80056c0: 3301 adds r3, #1 + 80056c2: 4322 orrs r2, r4 + for (i = 0; i < num_words; ++i) { + 80056c4: e7f3 b.n 80056ae + +080056c6 : + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 80056c6: 114a asrs r2, r1, #5 + 80056c8: 2301 movs r3, #1 + 80056ca: f850 0022 ldr.w r0, [r0, r2, lsl #2] + 80056ce: f001 011f and.w r1, r1, #31 + 80056d2: fa03 f101 lsl.w r1, r3, r1 +} + 80056d6: 4008 ands r0, r1 + 80056d8: 4770 bx lr + +080056da : +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { + wordcount_t i; + /* Search from the end until we find a non-zero digit. + We do it in reverse because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + 80056da: 3901 subs r1, #1 + + return (i + 1); +} + +/* Counts the number of bits required to represent vli. */ +uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { + 80056dc: b510 push {r4, lr} + 80056de: b249 sxtb r1, r1 + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + 80056e0: 1d04 adds r4, r0, #4 + 80056e2: 060a lsls r2, r1, #24 + 80056e4: b2cb uxtb r3, r1 + 80056e6: d404 bmi.n 80056f2 + 80056e8: 3901 subs r1, #1 + 80056ea: f854 2021 ldr.w r2, [r4, r1, lsl #2] + 80056ee: 2a00 cmp r2, #0 + 80056f0: d0f7 beq.n 80056e2 + return (i + 1); + 80056f2: 3301 adds r3, #1 + 80056f4: b25b sxtb r3, r3 + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + 80056f6: b173 cbz r3, 8005716 + return 0; + } + + digit = vli[num_digits - 1]; + 80056f8: f103 4280 add.w r2, r3, #1073741824 ; 0x40000000 + 80056fc: 3a01 subs r2, #1 + 80056fe: f850 2022 ldr.w r2, [r0, r2, lsl #2] + for (i = 0; digit; ++i) { + 8005702: 2000 movs r0, #0 + 8005704: b922 cbnz r2, 8005710 + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); + 8005706: 3b01 subs r3, #1 + 8005708: eb00 1343 add.w r3, r0, r3, lsl #5 + 800570c: b218 sxth r0, r3 +} + 800570e: bd10 pop {r4, pc} + digit >>= 1; + 8005710: 0852 lsrs r2, r2, #1 + for (i = 0; digit; ++i) { + 8005712: 3001 adds r0, #1 + 8005714: e7f6 b.n 8005704 + return 0; + 8005716: 4618 mov r0, r3 + 8005718: e7f9 b.n 800570e + +0800571a : + +/* Sets dest = src. */ +#if !asm_set +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + 800571a: b510 push {r4, lr} + wordcount_t i; + for (i = 0; i < num_words; ++i) { + 800571c: 2300 movs r3, #0 + 800571e: b25c sxtb r4, r3 + 8005720: 42a2 cmp r2, r4 + 8005722: dc00 bgt.n 8005726 + dest[i] = src[i]; + } +} + 8005724: bd10 pop {r4, pc} + dest[i] = src[i]; + 8005726: f851 4023 ldr.w r4, [r1, r3, lsl #2] + 800572a: f840 4023 str.w r4, [r0, r3, lsl #2] + for (i = 0; i < num_words; ++i) { + 800572e: 3301 adds r3, #1 + 8005730: e7f5 b.n 800571e + +08005732 : +#endif /* !asm_set */ + +/* Returns sign of left - right. */ +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + 8005732: b510 push {r4, lr} + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + 8005734: 3a01 subs r2, #1 + 8005736: b252 sxtb r2, r2 + 8005738: 0613 lsls r3, r2, #24 + 800573a: d501 bpl.n 8005740 + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; + 800573c: 2000 movs r0, #0 +} + 800573e: bd10 pop {r4, pc} + if (left[i] > right[i]) { + 8005740: f850 4022 ldr.w r4, [r0, r2, lsl #2] + 8005744: f851 3022 ldr.w r3, [r1, r2, lsl #2] + 8005748: 429c cmp r4, r3 + 800574a: d805 bhi.n 8005758 + } else if (left[i] < right[i]) { + 800574c: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 8005750: d2f2 bcs.n 8005738 + return -1; + 8005752: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8005756: e7f2 b.n 800573e + return 1; + 8005758: 2001 movs r0, #1 + 800575a: e7f0 b.n 800573e + +0800575c : +#if !asm_rshift1 +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + 800575c: eb00 0181 add.w r1, r0, r1, lsl #2 + uECC_word_t carry = 0; + 8005760: 2300 movs r3, #0 + while (vli-- > end) { + 8005762: 4288 cmp r0, r1 + 8005764: d300 bcc.n 8005768 + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} + 8005766: 4770 bx lr + uECC_word_t temp = *vli; + 8005768: f851 2d04 ldr.w r2, [r1, #-4]! + *vli = (temp >> 1) | carry; + 800576c: ea43 0352 orr.w r3, r3, r2, lsr #1 + 8005770: 600b str r3, [r1, #0] + carry = temp << (uECC_WORD_BITS - 1); + 8005772: 07d3 lsls r3, r2, #31 + 8005774: e7f5 b.n 8005762 + +08005776 : +/* Computes result = (left * right) % mod. */ +uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + 8005776: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800577a: b0b5 sub sp, #212 ; 0xd4 + 800577c: 461f mov r7, r3 + 800577e: f99d 50f8 ldrsb.w r5, [sp, #248] ; 0xf8 + 8005782: 4680 mov r8, r0 + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, num_words); + 8005784: 462b mov r3, r5 + 8005786: a804 add r0, sp, #16 + 8005788: f7ff ff48 bl 800561c + uECC_word_t *v[2] = {tmp, product}; + 800578c: ab24 add r3, sp, #144 ; 0x90 + 800578e: e9cd 3002 strd r3, r0, [sp, #8] + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); + 8005792: 4629 mov r1, r5 + 8005794: 4638 mov r0, r7 + 8005796: f7ff ffa0 bl 80056da + 800579a: ebc0 1085 rsb r0, r0, r5, lsl #6 + 800579e: b204 sxth r4, r0 + wordcount_t word_shift = shift / uECC_WORD_BITS; + 80057a0: 2c00 cmp r4, #0 + 80057a2: 4626 mov r6, r4 + 80057a4: bfb8 it lt + 80057a6: f104 061f addlt.w r6, r4, #31 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 80057aa: 4263 negs r3, r4 + wordcount_t word_shift = shift / uECC_WORD_BITS; + 80057ac: f346 1647 sbfx r6, r6, #5, #8 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 80057b0: f003 031f and.w r3, r3, #31 + 80057b4: f004 091f and.w r9, r4, #31 + uECC_vli_clear(mod_multiple, word_shift); + 80057b8: 4631 mov r1, r6 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 80057ba: bf58 it pl + 80057bc: f1c3 0900 rsbpl r9, r3, #0 + uECC_vli_clear(mod_multiple, word_shift); + 80057c0: a814 add r0, sp, #80 ; 0x50 + 80057c2: f7ff ff6b bl 800569c + if (bit_shift > 0) { + 80057c6: f1b9 0f00 cmp.w r9, #0 + 80057ca: b236 sxth r6, r6 + 80057cc: dd2b ble.n 8005826 + 80057ce: ab14 add r3, sp, #80 ; 0x50 + uECC_word_t carry = 0; + 80057d0: 2200 movs r2, #0 + 80057d2: eb03 0686 add.w r6, r3, r6, lsl #2 + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + 80057d6: f1c9 0c20 rsb ip, r9, #32 + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 80057da: 4613 mov r3, r2 + 80057dc: 42ab cmp r3, r5 + 80057de: d317 bcc.n 8005810 + for (i = 0; i < num_words * 2; ++i) { + 80057e0: 006b lsls r3, r5, #1 + 80057e2: 9301 str r3, [sp, #4] + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 80057e4: ab14 add r3, sp, #80 ; 0x50 + 80057e6: eb03 0985 add.w r9, r3, r5, lsl #2 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 80057ea: 1e6f subs r7, r5, #1 + 80057ec: ab34 add r3, sp, #208 ; 0xd0 + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 80057ee: 2601 movs r6, #1 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 80057f0: eb03 0787 add.w r7, r3, r7, lsl #2 + for (index = 1; shift >= 0; --shift) { + 80057f4: 2c00 cmp r4, #0 + 80057f6: da54 bge.n 80058a2 + uECC_vli_set(result, v[index], num_words); + 80057f8: ab34 add r3, sp, #208 ; 0xd0 + 80057fa: eb03 0686 add.w r6, r3, r6, lsl #2 + 80057fe: 462a mov r2, r5 + 8005800: f856 1cc8 ldr.w r1, [r6, #-200] + 8005804: 4640 mov r0, r8 + 8005806: f7ff ff88 bl 800571a + uECC_vli_mmod(result, product, mod, num_words); +} + 800580a: b035 add sp, #212 ; 0xd4 + 800580c: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + 8005810: f857 0023 ldr.w r0, [r7, r3, lsl #2] + 8005814: fa00 f109 lsl.w r1, r0, r9 + 8005818: 430a orrs r2, r1 + 800581a: f846 2b04 str.w r2, [r6], #4 + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 800581e: 3301 adds r3, #1 + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + 8005820: fa20 f20c lsr.w r2, r0, ip + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 8005824: e7da b.n 80057dc + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + 8005826: ab14 add r3, sp, #80 ; 0x50 + 8005828: 462a mov r2, r5 + 800582a: 4639 mov r1, r7 + 800582c: eb03 0086 add.w r0, r3, r6, lsl #2 + 8005830: f7ff ff73 bl 800571a + 8005834: e7d4 b.n 80057e0 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 8005836: fa0f fe82 sxth.w lr, r2 + 800583a: f85a 3cc8 ldr.w r3, [sl, #-200] + 800583e: f853 b02e ldr.w fp, [r3, lr, lsl #2] + 8005842: ab34 add r3, sp, #208 ; 0xd0 + 8005844: eb03 0282 add.w r2, r3, r2, lsl #2 + 8005848: 3001 adds r0, #1 + 800584a: f852 3c80 ldr.w r3, [r2, #-128] + 800584e: 440b add r3, r1 + 8005850: ebbb 0303 subs.w r3, fp, r3 + 8005854: bf34 ite cc + 8005856: 2201 movcc r2, #1 + 8005858: 2200 movcs r2, #0 + if (diff != v[index][i]) { + 800585a: 459b cmp fp, r3 + borrow = (diff > v[index][i]); + 800585c: bf18 it ne + 800585e: 4611 movne r1, r2 + v[1 - index][i] = diff; + 8005860: f85c 2cc8 ldr.w r2, [ip, #-200] + 8005864: f842 302e str.w r3, [r2, lr, lsl #2] + for (i = 0; i < num_words * 2; ++i) { + 8005868: 9b01 ldr r3, [sp, #4] + 800586a: b242 sxtb r2, r0 + 800586c: 429a cmp r2, r3 + 800586e: dbe2 blt.n 8005836 + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + 8005870: 1a73 subs r3, r6, r1 + 8005872: 425e negs r6, r3 + uECC_vli_rshift1(mod_multiple, num_words); + 8005874: 4629 mov r1, r5 + 8005876: a814 add r0, sp, #80 ; 0x50 + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + 8005878: 415e adcs r6, r3 + uECC_vli_rshift1(mod_multiple, num_words); + 800587a: f7ff ff6f bl 800575c + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 800587e: ab34 add r3, sp, #208 ; 0xd0 + 8005880: eb03 0385 add.w r3, r3, r5, lsl #2 + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005884: 4629 mov r1, r5 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 8005886: f853 2c80 ldr.w r2, [r3, #-128] + 800588a: f857 3c80 ldr.w r3, [r7, #-128] + 800588e: ea43 73c2 orr.w r3, r3, r2, lsl #31 + 8005892: f847 3c80 str.w r3, [r7, #-128] + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005896: 4648 mov r0, r9 + 8005898: 3c01 subs r4, #1 + 800589a: f7ff ff5f bl 800575c + for (index = 1; shift >= 0; --shift) { + 800589e: b224 sxth r4, r4 + 80058a0: e7a8 b.n 80057f4 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 80058a2: ab34 add r3, sp, #208 ; 0xd0 + 80058a4: 2000 movs r0, #0 + v[1 - index][i] = diff; + 80058a6: f1c6 0c01 rsb ip, r6, #1 + uECC_word_t borrow = 0; + 80058aa: 4601 mov r1, r0 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 80058ac: eb03 0a86 add.w sl, r3, r6, lsl #2 + v[1 - index][i] = diff; + 80058b0: eb03 0c8c add.w ip, r3, ip, lsl #2 + 80058b4: e7d8 b.n 8005868 + +080058b6 : + +uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve) { + 80058b6: b530 push {r4, r5, lr} + 80058b8: 461c mov r4, r3 + 80058ba: b091 sub sp, #68 ; 0x44 + 80058bc: 4605 mov r5, r0 + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + 80058be: f993 3000 ldrsb.w r3, [r3] + 80058c2: 4668 mov r0, sp + 80058c4: f7ff feaa bl 800561c +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); + 80058c8: 4601 mov r1, r0 + 80058ca: f8d4 30b0 ldr.w r3, [r4, #176] ; 0xb0 + 80058ce: 4628 mov r0, r5 + 80058d0: 4798 blx r3 +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + 80058d2: b011 add sp, #68 ; 0x44 + 80058d4: bd30 pop {r4, r5, pc} + +080058d6 : +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + 80058d6: 4613 mov r3, r2 + uECC_vli_modMult_fast(result, left, left, curve); + 80058d8: 460a mov r2, r1 + 80058da: f7ff bfec b.w 80058b6 + +080058de : + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(uECC_word_t * X1, + uECC_word_t * Y1, + const uECC_word_t * const Z, + uECC_Curve curve) { + 80058de: b570 push {r4, r5, r6, lr} + 80058e0: 4614 mov r4, r2 + 80058e2: b08a sub sp, #40 ; 0x28 + 80058e4: 4606 mov r6, r0 + 80058e6: 460d mov r5, r1 + uECC_word_t t1[uECC_MAX_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + 80058e8: 461a mov r2, r3 + 80058ea: 4621 mov r1, r4 + 80058ec: a802 add r0, sp, #8 + 80058ee: 9301 str r3, [sp, #4] + 80058f0: f7ff fff1 bl 80058d6 + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + 80058f4: 9b01 ldr r3, [sp, #4] + 80058f6: aa02 add r2, sp, #8 + 80058f8: 4631 mov r1, r6 + 80058fa: 4630 mov r0, r6 + 80058fc: f7ff ffdb bl 80058b6 + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + 8005900: a902 add r1, sp, #8 + 8005902: 9b01 ldr r3, [sp, #4] + 8005904: 4622 mov r2, r4 + 8005906: 4608 mov r0, r1 + 8005908: f7ff ffd5 bl 80058b6 + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ + 800590c: 9b01 ldr r3, [sp, #4] + 800590e: aa02 add r2, sp, #8 + 8005910: 4629 mov r1, r5 + 8005912: 4628 mov r0, r5 + 8005914: f7ff ffcf bl 80058b6 +} + 8005918: b00a add sp, #40 ; 0x28 + 800591a: bd70 pop {r4, r5, r6, pc} + +0800591c : + +#else + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uECC_word_t *native) { + 800591c: b5f0 push {r4, r5, r6, r7, lr} + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + 800591e: 2500 movs r5, #0 + unsigned b = num_bytes - 1 - i; + 8005920: 1e4f subs r7, r1, #1 + 8005922: b26c sxtb r4, r5 + for (i = 0; i < num_bytes; ++i) { + 8005924: 428c cmp r4, r1 + 8005926: f105 0501 add.w r5, r5, #1 + 800592a: db00 blt.n 800592e + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + 800592c: bdf0 pop {r4, r5, r6, r7, pc} + unsigned b = num_bytes - 1 - i; + 800592e: 1b3b subs r3, r7, r4 + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + 8005930: f023 0603 bic.w r6, r3, #3 + 8005934: f003 0303 and.w r3, r3, #3 + 8005938: 5996 ldr r6, [r2, r6] + 800593a: 00db lsls r3, r3, #3 + 800593c: fa26 f303 lsr.w r3, r6, r3 + 8005940: 5503 strb r3, [r0, r4] + for (i = 0; i < num_bytes; ++i) { + 8005942: e7ee b.n 8005922 + +08005944 : + +uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, + const uint8_t *bytes, + int num_bytes) { + 8005944: b5f8 push {r3, r4, r5, r6, r7, lr} + 8005946: 460e mov r6, r1 + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005948: 1cd1 adds r1, r2, #3 + 800594a: bf48 it mi + 800594c: 1d91 addmi r1, r2, #6 + int num_bytes) { + 800594e: 4614 mov r4, r2 + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005950: f341 0187 sbfx r1, r1, #2, #8 + int num_bytes) { + 8005954: 4605 mov r5, r0 + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + 8005956: 1e67 subs r7, r4, #1 + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005958: f7ff fea0 bl 800569c + for (i = 0; i < num_bytes; ++i) { + 800595c: 2000 movs r0, #0 + 800595e: b242 sxtb r2, r0 + 8005960: 42a2 cmp r2, r4 + 8005962: f100 0001 add.w r0, r0, #1 + 8005966: db00 blt.n 800596a + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + 8005968: bdf8 pop {r3, r4, r5, r6, r7, pc} + unsigned b = num_bytes - 1 - i; + 800596a: 1abb subs r3, r7, r2 + native[b / uECC_WORD_SIZE] |= + 800596c: f023 0103 bic.w r1, r3, #3 + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + 8005970: 5cb2 ldrb r2, [r6, r2] + 8005972: f003 0303 and.w r3, r3, #3 + 8005976: 00db lsls r3, r3, #3 + 8005978: fa02 f303 lsl.w r3, r2, r3 + native[b / uECC_WORD_SIZE] |= + 800597c: 586a ldr r2, [r5, r1] + 800597e: 431a orrs r2, r3 + 8005980: 506a str r2, [r5, r1] + for (i = 0; i < num_bytes; ++i) { + 8005982: e7ec b.n 800595e + +08005984 : + return 0; +} + +/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always + the same size as the hash result size. */ +static void HMAC_init(uECC_HashContext *hash_context, const uint8_t *K) { + 8005984: b570 push {r4, r5, r6, lr} + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 8005986: e9d0 3504 ldrd r3, r5, [r0, #16] +static void HMAC_init(uECC_HashContext *hash_context, const uint8_t *K) { + 800598a: 4604 mov r4, r0 + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 800598c: eb05 0543 add.w r5, r5, r3, lsl #1 + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + 8005990: 2300 movs r3, #0 + 8005992: 6922 ldr r2, [r4, #16] + 8005994: 429a cmp r2, r3 + 8005996: d80d bhi.n 80059b4 + pad[i] = K[i] ^ 0x36; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x36; + 8005998: 2136 movs r1, #54 ; 0x36 + for (; i < hash_context->block_size; ++i) + 800599a: 68e2 ldr r2, [r4, #12] + 800599c: 429a cmp r2, r3 + 800599e: d80f bhi.n 80059c0 + + hash_context->init_hash(hash_context); + 80059a0: 6823 ldr r3, [r4, #0] + 80059a2: 4620 mov r0, r4 + 80059a4: 4798 blx r3 + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 80059a6: 6863 ldr r3, [r4, #4] + 80059a8: 68e2 ldr r2, [r4, #12] + 80059aa: 4629 mov r1, r5 + 80059ac: 4620 mov r0, r4 +} + 80059ae: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 80059b2: 4718 bx r3 + pad[i] = K[i] ^ 0x36; + 80059b4: 5cca ldrb r2, [r1, r3] + 80059b6: f082 0236 eor.w r2, r2, #54 ; 0x36 + 80059ba: 54ea strb r2, [r5, r3] + for (i = 0; i < hash_context->result_size; ++i) + 80059bc: 3301 adds r3, #1 + 80059be: e7e8 b.n 8005992 + pad[i] = 0x36; + 80059c0: 54e9 strb r1, [r5, r3] + for (; i < hash_context->block_size; ++i) + 80059c2: 3301 adds r3, #1 + 80059c4: e7e9 b.n 800599a + +080059c6 : + +static void HMAC_update(uECC_HashContext *hash_context, + const uint8_t *message, + unsigned message_size) { + hash_context->update_hash(hash_context, message, message_size); + 80059c6: 6843 ldr r3, [r0, #4] + 80059c8: 4718 bx r3 + +080059ca : +} + +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 80059ca: b570 push {r4, r5, r6, lr} + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 80059cc: e9d0 3604 ldrd r3, r6, [r0, #16] +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 80059d0: 4604 mov r4, r0 + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 80059d2: eb06 0643 add.w r6, r6, r3, lsl #1 +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 80059d6: 4615 mov r5, r2 + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + 80059d8: 2300 movs r3, #0 + 80059da: 6922 ldr r2, [r4, #16] + 80059dc: 429a cmp r2, r3 + 80059de: d81a bhi.n 8005a16 + pad[i] = K[i] ^ 0x5c; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x5c; + 80059e0: 215c movs r1, #92 ; 0x5c + for (; i < hash_context->block_size; ++i) + 80059e2: 68e2 ldr r2, [r4, #12] + 80059e4: 429a cmp r2, r3 + 80059e6: d81c bhi.n 8005a22 + + hash_context->finish_hash(hash_context, result); + 80059e8: 4629 mov r1, r5 + 80059ea: 68a3 ldr r3, [r4, #8] + 80059ec: 4620 mov r0, r4 + 80059ee: 4798 blx r3 + + hash_context->init_hash(hash_context); + 80059f0: 6823 ldr r3, [r4, #0] + 80059f2: 4620 mov r0, r4 + 80059f4: 4798 blx r3 + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 80059f6: 6863 ldr r3, [r4, #4] + 80059f8: 68e2 ldr r2, [r4, #12] + 80059fa: 4631 mov r1, r6 + 80059fc: 4620 mov r0, r4 + 80059fe: 4798 blx r3 + hash_context->update_hash(hash_context, result, hash_context->result_size); + 8005a00: 6863 ldr r3, [r4, #4] + 8005a02: 6922 ldr r2, [r4, #16] + 8005a04: 4629 mov r1, r5 + 8005a06: 4620 mov r0, r4 + 8005a08: 4798 blx r3 + hash_context->finish_hash(hash_context, result); + 8005a0a: 68a3 ldr r3, [r4, #8] + 8005a0c: 4629 mov r1, r5 + 8005a0e: 4620 mov r0, r4 +} + 8005a10: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + hash_context->finish_hash(hash_context, result); + 8005a14: 4718 bx r3 + pad[i] = K[i] ^ 0x5c; + 8005a16: 5cca ldrb r2, [r1, r3] + 8005a18: f082 025c eor.w r2, r2, #92 ; 0x5c + 8005a1c: 54f2 strb r2, [r6, r3] + for (i = 0; i < hash_context->result_size; ++i) + 8005a1e: 3301 adds r3, #1 + 8005a20: e7db b.n 80059da + pad[i] = 0x5c; + 8005a22: 54f1 strb r1, [r6, r3] + for (; i < hash_context->block_size; ++i) + 8005a24: 3301 adds r3, #1 + 8005a26: e7dc b.n 80059e2 + +08005a28 : + +/* V = HMAC_K(V) */ +static void update_V(uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { + 8005a28: b570 push {r4, r5, r6, lr} + 8005a2a: 4604 mov r4, r0 + 8005a2c: 4615 mov r5, r2 + 8005a2e: 460e mov r6, r1 + HMAC_init(hash_context, K); + 8005a30: f7ff ffa8 bl 8005984 + HMAC_update(hash_context, V, hash_context->result_size); + 8005a34: 6922 ldr r2, [r4, #16] + 8005a36: 4629 mov r1, r5 + 8005a38: 4620 mov r0, r4 + 8005a3a: f7ff ffc4 bl 80059c6 + HMAC_finish(hash_context, K, V); + 8005a3e: 462a mov r2, r5 + 8005a40: 4631 mov r1, r6 + 8005a42: 4620 mov r0, r4 +} + 8005a44: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + HMAC_finish(hash_context, K, V); + 8005a48: f7ff bfbf b.w 80059ca + +08005a4c : +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + 8005a4c: b530 push {r4, r5, lr} + __asm__ volatile ( + 8005a4e: 2300 movs r3, #0 + 8005a50: c910 ldmia r1!, {r4} + 8005a52: ca20 ldmia r2!, {r5} + 8005a54: 1b64 subs r4, r4, r5 + 8005a56: c010 stmia r0!, {r4} + 8005a58: c910 ldmia r1!, {r4} + 8005a5a: ca20 ldmia r2!, {r5} + 8005a5c: 41ac sbcs r4, r5 + 8005a5e: c010 stmia r0!, {r4} + 8005a60: c910 ldmia r1!, {r4} + 8005a62: ca20 ldmia r2!, {r5} + 8005a64: 41ac sbcs r4, r5 + 8005a66: c010 stmia r0!, {r4} + 8005a68: c910 ldmia r1!, {r4} + 8005a6a: ca20 ldmia r2!, {r5} + 8005a6c: 41ac sbcs r4, r5 + 8005a6e: c010 stmia r0!, {r4} + 8005a70: c910 ldmia r1!, {r4} + 8005a72: ca20 ldmia r2!, {r5} + 8005a74: 41ac sbcs r4, r5 + 8005a76: c010 stmia r0!, {r4} + 8005a78: c910 ldmia r1!, {r4} + 8005a7a: ca20 ldmia r2!, {r5} + 8005a7c: 41ac sbcs r4, r5 + 8005a7e: c010 stmia r0!, {r4} + 8005a80: c910 ldmia r1!, {r4} + 8005a82: ca20 ldmia r2!, {r5} + 8005a84: 41ac sbcs r4, r5 + 8005a86: c010 stmia r0!, {r4} + 8005a88: c910 ldmia r1!, {r4} + 8005a8a: ca20 ldmia r2!, {r5} + 8005a8c: 41ac sbcs r4, r5 + 8005a8e: c010 stmia r0!, {r4} + 8005a90: 415b adcs r3, r3 +} + 8005a92: fab3 f083 clz r0, r3 + 8005a96: 0940 lsrs r0, r0, #5 + 8005a98: bd30 pop {r4, r5, pc} + +08005a9a : + uECC_Curve curve) { + 8005a9a: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 8005a9e: 4698 mov r8, r3 + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005aa0: f9b3 3002 ldrsh.w r3, [r3, #2] + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005aa4: f113 041f adds.w r4, r3, #31 + 8005aa8: bf48 it mi + 8005aaa: f103 043e addmi.w r4, r3, #62 ; 0x3e + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005aae: 1ddd adds r5, r3, #7 + 8005ab0: bf48 it mi + 8005ab2: f103 050e addmi.w r5, r3, #14 + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005ab6: 1166 asrs r6, r4, #5 + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005ab8: 10ec asrs r4, r5, #3 + 8005aba: 4294 cmp r4, r2 + uECC_vli_clear(native, num_n_words); + 8005abc: b275 sxtb r5, r6 + 8005abe: bf28 it cs + 8005ac0: 4614 movcs r4, r2 + uECC_Curve curve) { + 8005ac2: 4607 mov r7, r0 + 8005ac4: 4689 mov r9, r1 + uECC_vli_clear(native, num_n_words); + 8005ac6: 4629 mov r1, r5 + 8005ac8: f7ff fde8 bl 800569c + uECC_vli_bytesToNative(native, bits, bits_size); + 8005acc: 4622 mov r2, r4 + 8005ace: 4649 mov r1, r9 + 8005ad0: 4638 mov r0, r7 + 8005ad2: f7ff ff37 bl 8005944 + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + 8005ad6: f9b8 2002 ldrsh.w r2, [r8, #2] + 8005ada: ebb2 0fc4 cmp.w r2, r4, lsl #3 + 8005ade: ea4f 03c4 mov.w r3, r4, lsl #3 + 8005ae2: d21f bcs.n 8005b24 + int shift = bits_size * 8 - curve->num_n_bits; + 8005ae4: 1a9b subs r3, r3, r2 + uECC_word_t *ptr = native + num_n_words; + 8005ae6: eb07 0486 add.w r4, r7, r6, lsl #2 + uECC_word_t carry = 0; + 8005aea: 2100 movs r1, #0 + carry = temp << (uECC_WORD_BITS - shift); + 8005aec: f1c3 0620 rsb r6, r3, #32 + while (ptr-- > native) { + 8005af0: 42a7 cmp r7, r4 + 8005af2: d30e bcc.n 8005b12 + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + 8005af4: f108 0824 add.w r8, r8, #36 ; 0x24 + 8005af8: 462a mov r2, r5 + 8005afa: 4639 mov r1, r7 + 8005afc: 4640 mov r0, r8 + 8005afe: f7ff fe18 bl 8005732 + 8005b02: 2801 cmp r0, #1 + 8005b04: d00e beq.n 8005b24 + uECC_vli_sub(native, native, curve->n, num_n_words); + 8005b06: 4642 mov r2, r8 + 8005b08: 4638 mov r0, r7 +} + 8005b0a: e8bd 43f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + uECC_vli_sub(native, native, curve->n, num_n_words); + 8005b0e: f7ff bf9d b.w 8005a4c + uECC_word_t temp = *ptr; + 8005b12: f854 0d04 ldr.w r0, [r4, #-4]! + *ptr = (temp >> shift) | carry; + 8005b16: fa20 f203 lsr.w r2, r0, r3 + 8005b1a: 430a orrs r2, r1 + 8005b1c: 6022 str r2, [r4, #0] + carry = temp << (uECC_WORD_BITS - shift); + 8005b1e: fa00 f106 lsl.w r1, r0, r6 + 8005b22: e7e5 b.n 8005af0 +} + 8005b24: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + +08005b28 : + wordcount_t num_words) { + 8005b28: b530 push {r4, r5, lr} + 8005b2a: b089 sub sp, #36 ; 0x24 + 8005b2c: 4615 mov r5, r2 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005b2e: 460a mov r2, r1 + 8005b30: 4601 mov r1, r0 + 8005b32: 4668 mov r0, sp + 8005b34: f7ff ff8a bl 8005a4c + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + 8005b38: 4629 mov r1, r5 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005b3a: 4604 mov r4, r0 + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + 8005b3c: 4668 mov r0, sp + 8005b3e: f7ff fdb3 bl 80056a8 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005b42: 3c00 subs r4, #0 + 8005b44: bf18 it ne + 8005b46: 2401 movne r4, #1 + return (!equal - 2 * neg); + 8005b48: 0064 lsls r4, r4, #1 +} + 8005b4a: 2800 cmp r0, #0 + 8005b4c: bf14 ite ne + 8005b4e: 4260 negne r0, r4 + 8005b50: f1c4 0001 rsbeq r0, r4, #1 + 8005b54: b009 add sp, #36 ; 0x24 + 8005b56: bd30 pop {r4, r5, pc} + +08005b58 : + wordcount_t num_words) { + 8005b58: e92d 4ff8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8005b5c: 460f mov r7, r1 + if (!g_rng_function) { + 8005b5e: f8df a06c ldr.w sl, [pc, #108] ; 8005bcc + wordcount_t num_words) { + 8005b62: 4606 mov r6, r0 + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + 8005b64: 4611 mov r1, r2 + 8005b66: 4638 mov r0, r7 + wordcount_t num_words) { + 8005b68: 4614 mov r4, r2 + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + 8005b6a: f7ff fdb6 bl 80056da + if (!g_rng_function) { + 8005b6e: f8da 3000 ldr.w r3, [sl] + 8005b72: b303 cbz r3, 8005bb6 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005b74: 2504 movs r5, #4 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005b76: ebc0 1044 rsb r0, r0, r4, lsl #5 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005b7a: fb14 fb05 smulbb fp, r4, r5 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005b7e: b200 sxth r0, r0 + 8005b80: fb05 6504 mla r5, r5, r4, r6 + 8005b84: f04f 38ff mov.w r8, #4294967295 ; 0xffffffff + 8005b88: 3d04 subs r5, #4 + 8005b8a: fa28 f800 lsr.w r8, r8, r0 + 8005b8e: f04f 0940 mov.w r9, #64 ; 0x40 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005b92: f8da 3000 ldr.w r3, [sl] + 8005b96: 4659 mov r1, fp + 8005b98: 4630 mov r0, r6 + 8005b9a: 4798 blx r3 + 8005b9c: b158 cbz r0, 8005bb6 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005b9e: 682b ldr r3, [r5, #0] + 8005ba0: ea03 0308 and.w r3, r3, r8 + 8005ba4: 602b str r3, [r5, #0] + if (!uECC_vli_isZero(random, num_words) && + 8005ba6: 4621 mov r1, r4 + 8005ba8: 4630 mov r0, r6 + 8005baa: f7ff fd7d bl 80056a8 + 8005bae: b120 cbz r0, 8005bba + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8005bb0: f1b9 0901 subs.w r9, r9, #1 + 8005bb4: d1ed bne.n 8005b92 + return 0; + 8005bb6: 2000 movs r0, #0 + 8005bb8: e006 b.n 8005bc8 + uECC_vli_cmp(top, random, num_words) == 1) { + 8005bba: 4622 mov r2, r4 + 8005bbc: 4631 mov r1, r6 + 8005bbe: 4638 mov r0, r7 + 8005bc0: f7ff ffb2 bl 8005b28 + if (!uECC_vli_isZero(random, num_words) && + 8005bc4: 2801 cmp r0, #1 + 8005bc6: d1f3 bne.n 8005bb0 +} + 8005bc8: e8bd 8ff8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, pc} + 8005bcc: 2009e2a0 .word 0x2009e2a0 + +08005bd0 : +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + 8005bd0: b530 push {r4, r5, lr} + __asm__ volatile ( + 8005bd2: 4603 mov r3, r0 + 8005bd4: 2000 movs r0, #0 + 8005bd6: c910 ldmia r1!, {r4} + 8005bd8: ca20 ldmia r2!, {r5} + 8005bda: 1964 adds r4, r4, r5 + 8005bdc: c310 stmia r3!, {r4} + 8005bde: c910 ldmia r1!, {r4} + 8005be0: ca20 ldmia r2!, {r5} + 8005be2: 416c adcs r4, r5 + 8005be4: c310 stmia r3!, {r4} + 8005be6: c910 ldmia r1!, {r4} + 8005be8: ca20 ldmia r2!, {r5} + 8005bea: 416c adcs r4, r5 + 8005bec: c310 stmia r3!, {r4} + 8005bee: c910 ldmia r1!, {r4} + 8005bf0: ca20 ldmia r2!, {r5} + 8005bf2: 416c adcs r4, r5 + 8005bf4: c310 stmia r3!, {r4} + 8005bf6: c910 ldmia r1!, {r4} + 8005bf8: ca20 ldmia r2!, {r5} + 8005bfa: 416c adcs r4, r5 + 8005bfc: c310 stmia r3!, {r4} + 8005bfe: c910 ldmia r1!, {r4} + 8005c00: ca20 ldmia r2!, {r5} + 8005c02: 416c adcs r4, r5 + 8005c04: c310 stmia r3!, {r4} + 8005c06: c910 ldmia r1!, {r4} + 8005c08: ca20 ldmia r2!, {r5} + 8005c0a: 416c adcs r4, r5 + 8005c0c: c310 stmia r3!, {r4} + 8005c0e: c910 ldmia r1!, {r4} + 8005c10: ca20 ldmia r2!, {r5} + 8005c12: 416c adcs r4, r5 + 8005c14: c310 stmia r3!, {r4} + 8005c16: 4140 adcs r0, r0 +} + 8005c18: bd30 pop {r4, r5, pc} + +08005c1a : + uECC_Curve curve) { + 8005c1a: b573 push {r0, r1, r4, r5, r6, lr} + 8005c1c: 460d mov r5, r1 + 8005c1e: 4616 mov r6, r2 + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005c20: 4601 mov r1, r0 + 8005c22: f103 0224 add.w r2, r3, #36 ; 0x24 + 8005c26: 4628 mov r0, r5 + 8005c28: 9201 str r2, [sp, #4] + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005c2a: f9b3 4002 ldrsh.w r4, [r3, #2] + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005c2e: f7ff ffcf bl 8005bd0 + 8005c32: 9a01 ldr r2, [sp, #4] + 8005c34: b9c8 cbnz r0, 8005c6a + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005c36: f114 031f adds.w r3, r4, #31 + 8005c3a: bf48 it mi + 8005c3c: f104 033e addmi.w r3, r4, #62 ; 0x3e + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + 8005c40: f343 1347 sbfx r3, r3, #5, #8 + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005c44: ebb4 1f43 cmp.w r4, r3, lsl #5 + 8005c48: da11 bge.n 8005c6e + uECC_vli_testBit(k0, num_n_bits)); + 8005c4a: 4621 mov r1, r4 + 8005c4c: 4628 mov r0, r5 + 8005c4e: 9201 str r2, [sp, #4] + 8005c50: f7ff fd39 bl 80056c6 + 8005c54: 9a01 ldr r2, [sp, #4] + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + 8005c56: 1e04 subs r4, r0, #0 + 8005c58: bf18 it ne + 8005c5a: 2401 movne r4, #1 + uECC_vli_add(k1, k0, curve->n, num_n_words); + 8005c5c: 4629 mov r1, r5 + 8005c5e: 4630 mov r0, r6 + 8005c60: f7ff ffb6 bl 8005bd0 +} + 8005c64: 4620 mov r0, r4 + 8005c66: b002 add sp, #8 + 8005c68: bd70 pop {r4, r5, r6, pc} + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005c6a: 2401 movs r4, #1 + 8005c6c: e7f6 b.n 8005c5c + 8005c6e: 2400 movs r4, #0 + 8005c70: e7f4 b.n 8005c5c + +08005c72 : + /* add the 2^32 multiple */ + result[4 + num_words_secp256k1] = + uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + 8005c72: b5f8 push {r3, r4, r5, r6, r7, lr} + 8005c74: 460a mov r2, r1 + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uint32_t carry = 0; + 8005c76: 2300 movs r3, #0 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + 8005c78: 4604 mov r4, r0 + 8005c7a: 3904 subs r1, #4 + 8005c7c: 3804 subs r0, #4 + 8005c7e: f102 071c add.w r7, r2, #28 + wordcount_t k; + + for (k = 0; k < num_words_secp256k1; ++k) { + uint64_t p = (uint64_t)0x3D1 * right[k] + carry; + 8005c82: 469e mov lr, r3 + 8005c84: f240 35d1 movw r5, #977 ; 0x3d1 + 8005c88: f851 6f04 ldr.w r6, [r1, #4]! + 8005c8c: 46f4 mov ip, lr + 8005c8e: fbe6 3c05 umlal r3, ip, r6, r5 + for (k = 0; k < num_words_secp256k1; ++k) { + 8005c92: 428f cmp r7, r1 + result[k] = p; + 8005c94: f840 3f04 str.w r3, [r0, #4]! + carry = p >> 32; + 8005c98: 4663 mov r3, ip + for (k = 0; k < num_words_secp256k1; ++k) { + 8005c9a: d1f5 bne.n 8005c88 + } + result[num_words_secp256k1] = carry; + /* add the 2^32 multiple */ + result[1 + num_words_secp256k1] = + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); + 8005c9c: 1d21 adds r1, r4, #4 + result[num_words_secp256k1] = carry; + 8005c9e: f8c4 c020 str.w ip, [r4, #32] + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); + 8005ca2: 4608 mov r0, r1 + 8005ca4: f7ff ff94 bl 8005bd0 + result[1 + num_words_secp256k1] = + 8005ca8: 6260 str r0, [r4, #36] ; 0x24 +} + 8005caa: bdf8 pop {r3, r4, r5, r6, r7, pc} + +08005cac : +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + 8005cac: b570 push {r4, r5, r6, lr} + 8005cae: b090 sub sp, #64 ; 0x40 + 8005cb0: 460e mov r6, r1 + 8005cb2: 4604 mov r4, r0 + uECC_vli_clear(tmp, num_words_secp256k1); + 8005cb4: 2108 movs r1, #8 + 8005cb6: 4668 mov r0, sp + 8005cb8: f7ff fcf0 bl 800569c + uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); + 8005cbc: 2108 movs r1, #8 + 8005cbe: a808 add r0, sp, #32 + 8005cc0: f7ff fcec bl 800569c + omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ + 8005cc4: f106 0120 add.w r1, r6, #32 + 8005cc8: 4668 mov r0, sp + 8005cca: f7ff ffd2 bl 8005c72 + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + 8005cce: 466a mov r2, sp + 8005cd0: 4631 mov r1, r6 + 8005cd2: 4620 mov r0, r4 + 8005cd4: f7ff ff7c bl 8005bd0 + uECC_vli_clear(product, num_words_secp256k1); + 8005cd8: 2108 movs r1, #8 + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + 8005cda: 4605 mov r5, r0 + uECC_vli_clear(product, num_words_secp256k1); + 8005cdc: 4630 mov r0, r6 + 8005cde: f7ff fcdd bl 800569c + omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ + 8005ce2: 4630 mov r0, r6 + 8005ce4: a908 add r1, sp, #32 + 8005ce6: f7ff ffc4 bl 8005c72 + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + 8005cea: 4632 mov r2, r6 + 8005cec: 4621 mov r1, r4 + 8005cee: 4620 mov r0, r4 + 8005cf0: f7ff ff6e bl 8005bd0 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005cf4: 4e0b ldr r6, [pc, #44] ; (8005d24 ) + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + 8005cf6: 4405 add r5, r0 + while (carry > 0) { + 8005cf8: b96d cbnz r5, 8005d16 + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { + 8005cfa: 490a ldr r1, [pc, #40] ; (8005d24 ) + 8005cfc: 2208 movs r2, #8 + 8005cfe: 4620 mov r0, r4 + 8005d00: f7ff fd17 bl 8005732 + 8005d04: 2800 cmp r0, #0 + 8005d06: dd04 ble.n 8005d12 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005d08: 460a mov r2, r1 + 8005d0a: 4620 mov r0, r4 + 8005d0c: 4621 mov r1, r4 + 8005d0e: f7ff fe9d bl 8005a4c +} + 8005d12: b010 add sp, #64 ; 0x40 + 8005d14: bd70 pop {r4, r5, r6, pc} + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005d16: 4632 mov r2, r6 + 8005d18: 4621 mov r1, r4 + 8005d1a: 4620 mov r0, r4 + --carry; + 8005d1c: 3d01 subs r5, #1 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005d1e: f7ff fe95 bl 8005a4c + 8005d22: e7e9 b.n 8005cf8 + 8005d24: 0800e878 .word 0x0800e878 + +08005d28 : +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 8005d28: e92d 44f0 stmdb sp!, {r4, r5, r6, r7, sl, lr} + uECC_vli_set(result, product, num_words_secp256r1); + 8005d2c: 2208 movs r2, #8 +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 8005d2e: b088 sub sp, #32 + uECC_vli_set(result, product, num_words_secp256r1); + 8005d30: f7ff fcf3 bl 800571a + tmp[3] = product[11]; + 8005d34: 6acb ldr r3, [r1, #44] ; 0x2c + 8005d36: 9303 str r3, [sp, #12] + tmp[4] = product[12]; + 8005d38: 6b0b ldr r3, [r1, #48] ; 0x30 + 8005d3a: 9304 str r3, [sp, #16] + tmp[5] = product[13]; + 8005d3c: 6b4b ldr r3, [r1, #52] ; 0x34 + 8005d3e: 9305 str r3, [sp, #20] + tmp[6] = product[14]; + 8005d40: 6b8b ldr r3, [r1, #56] ; 0x38 + 8005d42: 9306 str r3, [sp, #24] +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 8005d44: 460c mov r4, r1 + 8005d46: 4682 mov sl, r0 + tmp[0] = tmp[1] = tmp[2] = 0; + 8005d48: 2700 movs r7, #0 + tmp[7] = product[15]; + 8005d4a: 6bcb ldr r3, [r1, #60] ; 0x3c + 8005d4c: 9307 str r3, [sp, #28] + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d4e: 466a mov r2, sp + 8005d50: 4669 mov r1, sp + 8005d52: 4668 mov r0, sp + tmp[0] = tmp[1] = tmp[2] = 0; + 8005d54: e9cd 7701 strd r7, r7, [sp, #4] + 8005d58: 9700 str r7, [sp, #0] + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d5a: f7ff ff39 bl 8005bd0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005d5e: 466a mov r2, sp + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d60: 4605 mov r5, r0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005d62: 4651 mov r1, sl + 8005d64: 4650 mov r0, sl + 8005d66: f7ff ff33 bl 8005bd0 + tmp[3] = product[12]; + 8005d6a: 6b23 ldr r3, [r4, #48] ; 0x30 + 8005d6c: 9303 str r3, [sp, #12] + tmp[4] = product[13]; + 8005d6e: 6b63 ldr r3, [r4, #52] ; 0x34 + 8005d70: 9304 str r3, [sp, #16] + tmp[5] = product[14]; + 8005d72: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8005d74: 9305 str r3, [sp, #20] + tmp[6] = product[15]; + 8005d76: 6be3 ldr r3, [r4, #60] ; 0x3c + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005d78: 4405 add r5, r0 + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d7a: 466a mov r2, sp + 8005d7c: 4669 mov r1, sp + 8005d7e: 4668 mov r0, sp + tmp[7] = 0; + 8005d80: e9cd 3706 strd r3, r7, [sp, #24] + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d84: f7ff ff24 bl 8005bd0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005d88: 466a mov r2, sp + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8005d8a: 4405 add r5, r0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005d8c: 4651 mov r1, sl + 8005d8e: 4650 mov r0, sl + 8005d90: f7ff ff1e bl 8005bd0 + tmp[0] = product[8]; + 8005d94: 6a23 ldr r3, [r4, #32] + 8005d96: 9300 str r3, [sp, #0] + tmp[1] = product[9]; + 8005d98: 6a63 ldr r3, [r4, #36] ; 0x24 + 8005d9a: 9301 str r3, [sp, #4] + tmp[2] = product[10]; + 8005d9c: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8005d9e: 9302 str r3, [sp, #8] + tmp[6] = product[14]; + 8005da0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8005da2: 9306 str r3, [sp, #24] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005da4: 4405 add r5, r0 + tmp[7] = product[15]; + 8005da6: 6be3 ldr r3, [r4, #60] ; 0x3c + 8005da8: 9307 str r3, [sp, #28] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005daa: 466a mov r2, sp + 8005dac: 4651 mov r1, sl + 8005dae: 4650 mov r0, sl + tmp[3] = tmp[4] = tmp[5] = 0; + 8005db0: e9cd 7704 strd r7, r7, [sp, #16] + 8005db4: 9703 str r7, [sp, #12] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005db6: f7ff ff0b bl 8005bd0 + tmp[0] = product[9]; + 8005dba: 6a63 ldr r3, [r4, #36] ; 0x24 + 8005dbc: 9300 str r3, [sp, #0] + tmp[1] = product[10]; + 8005dbe: 6aa3 ldr r3, [r4, #40] ; 0x28 + tmp[4] = product[14]; + 8005dc0: 6ba2 ldr r2, [r4, #56] ; 0x38 + tmp[1] = product[10]; + 8005dc2: 9301 str r3, [sp, #4] + tmp[2] = product[11]; + 8005dc4: 6ae3 ldr r3, [r4, #44] ; 0x2c + 8005dc6: 9302 str r3, [sp, #8] + tmp[4] = product[14]; + 8005dc8: 9204 str r2, [sp, #16] + tmp[3] = product[13]; + 8005dca: 6b63 ldr r3, [r4, #52] ; 0x34 + tmp[5] = product[15]; + 8005dcc: 6be2 ldr r2, [r4, #60] ; 0x3c + tmp[3] = product[13]; + 8005dce: 9303 str r3, [sp, #12] + tmp[6] = product[13]; + 8005dd0: e9cd 2305 strd r2, r3, [sp, #20] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005dd4: 182e adds r6, r5, r0 + tmp[7] = product[8]; + 8005dd6: 6a23 ldr r3, [r4, #32] + 8005dd8: 9307 str r3, [sp, #28] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005dda: 466a mov r2, sp + 8005ddc: 4651 mov r1, sl + 8005dde: 4650 mov r0, sl + 8005de0: f7ff fef6 bl 8005bd0 + tmp[0] = product[11]; + 8005de4: 6ae3 ldr r3, [r4, #44] ; 0x2c + 8005de6: 9300 str r3, [sp, #0] + tmp[1] = product[12]; + 8005de8: 6b23 ldr r3, [r4, #48] ; 0x30 + 8005dea: 9301 str r3, [sp, #4] + tmp[2] = product[13]; + 8005dec: 6b63 ldr r3, [r4, #52] ; 0x34 + 8005dee: 9302 str r3, [sp, #8] + tmp[6] = product[8]; + 8005df0: 6a23 ldr r3, [r4, #32] + 8005df2: 9306 str r3, [sp, #24] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8005df4: 1835 adds r5, r6, r0 + tmp[7] = product[10]; + 8005df6: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8005df8: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005dfa: 466a mov r2, sp + 8005dfc: 4651 mov r1, sl + 8005dfe: 4650 mov r0, sl + tmp[3] = tmp[4] = tmp[5] = 0; + 8005e00: e9cd 7704 strd r7, r7, [sp, #16] + 8005e04: 9703 str r7, [sp, #12] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e06: f7ff fe21 bl 8005a4c + tmp[0] = product[12]; + 8005e0a: 6b23 ldr r3, [r4, #48] ; 0x30 + 8005e0c: 9300 str r3, [sp, #0] + tmp[1] = product[13]; + 8005e0e: 6b63 ldr r3, [r4, #52] ; 0x34 + 8005e10: 9301 str r3, [sp, #4] + tmp[2] = product[14]; + 8005e12: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8005e14: 9302 str r3, [sp, #8] + tmp[3] = product[15]; + 8005e16: 6be3 ldr r3, [r4, #60] ; 0x3c + 8005e18: 9303 str r3, [sp, #12] + tmp[6] = product[9]; + 8005e1a: 6a63 ldr r3, [r4, #36] ; 0x24 + 8005e1c: 9306 str r3, [sp, #24] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e1e: 1a2e subs r6, r5, r0 + tmp[7] = product[11]; + 8005e20: 6ae3 ldr r3, [r4, #44] ; 0x2c + 8005e22: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e24: 466a mov r2, sp + 8005e26: 4651 mov r1, sl + 8005e28: 4650 mov r0, sl + tmp[4] = tmp[5] = 0; + 8005e2a: e9cd 7704 strd r7, r7, [sp, #16] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e2e: f7ff fe0d bl 8005a4c + tmp[0] = product[13]; + 8005e32: 6b63 ldr r3, [r4, #52] ; 0x34 + 8005e34: 9300 str r3, [sp, #0] + tmp[1] = product[14]; + 8005e36: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8005e38: 9301 str r3, [sp, #4] + tmp[2] = product[15]; + 8005e3a: 6be3 ldr r3, [r4, #60] ; 0x3c + 8005e3c: 9302 str r3, [sp, #8] + tmp[3] = product[8]; + 8005e3e: 6a23 ldr r3, [r4, #32] + 8005e40: 9303 str r3, [sp, #12] + tmp[4] = product[9]; + 8005e42: 6a63 ldr r3, [r4, #36] ; 0x24 + 8005e44: 9304 str r3, [sp, #16] + tmp[5] = product[10]; + 8005e46: 6aa3 ldr r3, [r4, #40] ; 0x28 + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e48: 1a36 subs r6, r6, r0 + tmp[6] = 0; + 8005e4a: e9cd 3705 strd r3, r7, [sp, #20] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e4e: 466a mov r2, sp + tmp[7] = product[12]; + 8005e50: 6b23 ldr r3, [r4, #48] ; 0x30 + 8005e52: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e54: 4651 mov r1, sl + 8005e56: 4650 mov r0, sl + 8005e58: f7ff fdf8 bl 8005a4c + tmp[0] = product[14]; + 8005e5c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8005e5e: 9300 str r3, [sp, #0] + tmp[1] = product[15]; + 8005e60: 6be3 ldr r3, [r4, #60] ; 0x3c + tmp[2] = 0; + 8005e62: e9cd 3701 strd r3, r7, [sp, #4] + tmp[3] = product[9]; + 8005e66: 6a63 ldr r3, [r4, #36] ; 0x24 + 8005e68: 9303 str r3, [sp, #12] + tmp[4] = product[10]; + 8005e6a: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8005e6c: 9304 str r3, [sp, #16] + tmp[5] = product[11]; + 8005e6e: 6ae3 ldr r3, [r4, #44] ; 0x2c + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e70: 1a36 subs r6, r6, r0 + tmp[6] = 0; + 8005e72: e9cd 3705 strd r3, r7, [sp, #20] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e76: 466a mov r2, sp + tmp[7] = product[13]; + 8005e78: 6b63 ldr r3, [r4, #52] ; 0x34 + 8005e7a: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8005e7c: 4651 mov r1, sl + 8005e7e: 4650 mov r0, sl + 8005e80: f7ff fde4 bl 8005a4c + if (carry < 0) { + 8005e84: 1a36 subs r6, r6, r0 + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + 8005e86: 4c0d ldr r4, [pc, #52] ; (8005ebc ) + if (carry < 0) { + 8005e88: d40e bmi.n 8005ea8 + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + 8005e8a: b936 cbnz r6, 8005e9a + 8005e8c: 2208 movs r2, #8 + 8005e8e: 4651 mov r1, sl + 8005e90: 4620 mov r0, r4 + 8005e92: f7ff fc4e bl 8005732 + 8005e96: 2801 cmp r0, #1 + 8005e98: d00d beq.n 8005eb6 + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + 8005e9a: 4622 mov r2, r4 + 8005e9c: 4651 mov r1, sl + 8005e9e: 4650 mov r0, sl + 8005ea0: f7ff fdd4 bl 8005a4c + 8005ea4: 1a36 subs r6, r6, r0 + 8005ea6: e7f0 b.n 8005e8a + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + 8005ea8: 4622 mov r2, r4 + 8005eaa: 4651 mov r1, sl + 8005eac: 4650 mov r0, sl + 8005eae: f7ff fe8f bl 8005bd0 + } while (carry < 0); + 8005eb2: 1836 adds r6, r6, r0 + 8005eb4: d4f8 bmi.n 8005ea8 +} + 8005eb6: b008 add sp, #32 + 8005eb8: e8bd 84f0 ldmia.w sp!, {r4, r5, r6, r7, sl, pc} + 8005ebc: 0800e92c .word 0x0800e92c + +08005ec0 : +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + 8005ec0: b5f0 push {r4, r5, r6, r7, lr} + 8005ec2: b091 sub sp, #68 ; 0x44 + 8005ec4: 460d mov r5, r1 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 8005ec6: 221c movs r2, #28 + 8005ec8: 2100 movs r1, #0 +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + 8005eca: 4606 mov r6, r0 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 8005ecc: a801 add r0, sp, #4 + 8005ece: f007 fbd1 bl 800d674 + 8005ed2: 2401 movs r4, #1 + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 8005ed4: 221c movs r2, #28 + 8005ed6: 2100 movs r1, #0 + 8005ed8: a809 add r0, sp, #36 ; 0x24 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 8005eda: 9400 str r4, [sp, #0] + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 8005edc: f007 fbca bl 800d674 + wordcount_t num_words = curve->num_words; + 8005ee0: 4629 mov r1, r5 + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + 8005ee2: 466a mov r2, sp + wordcount_t num_words = curve->num_words; + 8005ee4: f911 7b04 ldrsb.w r7, [r1], #4 + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 8005ee8: 9408 str r4, [sp, #32] + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + 8005eea: 4668 mov r0, sp + 8005eec: f7ff fe70 bl 8005bd0 + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + 8005ef0: 4639 mov r1, r7 + 8005ef2: 4668 mov r0, sp + 8005ef4: f7ff fbf1 bl 80056da + 8005ef8: 1e44 subs r4, r0, #1 + 8005efa: b224 sxth r4, r4 + 8005efc: 2c01 cmp r4, #1 + 8005efe: dc06 bgt.n 8005f0e + uECC_vli_set(a, l_result, num_words); + 8005f00: 463a mov r2, r7 + 8005f02: a908 add r1, sp, #32 + 8005f04: 4630 mov r0, r6 + 8005f06: f7ff fc08 bl 800571a +} + 8005f0a: b011 add sp, #68 ; 0x44 + 8005f0c: bdf0 pop {r4, r5, r6, r7, pc} + uECC_vli_modSquare_fast(l_result, l_result, curve); + 8005f0e: a908 add r1, sp, #32 + 8005f10: 4608 mov r0, r1 + 8005f12: 462a mov r2, r5 + 8005f14: f7ff fcdf bl 80058d6 + if (uECC_vli_testBit(p1, i)) { + 8005f18: 4621 mov r1, r4 + 8005f1a: 4668 mov r0, sp + 8005f1c: f7ff fbd3 bl 80056c6 + 8005f20: b128 cbz r0, 8005f2e + uECC_vli_modMult_fast(l_result, l_result, a, curve); + 8005f22: a908 add r1, sp, #32 + 8005f24: 462b mov r3, r5 + 8005f26: 4632 mov r2, r6 + 8005f28: 4608 mov r0, r1 + 8005f2a: f7ff fcc4 bl 80058b6 + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + 8005f2e: 3c01 subs r4, #1 + 8005f30: e7e3 b.n 8005efa + +08005f32 : + if (!EVEN(uv)) { + 8005f32: 6803 ldr r3, [r0, #0] + wordcount_t num_words) { + 8005f34: b570 push {r4, r5, r6, lr} + if (!EVEN(uv)) { + 8005f36: f013 0601 ands.w r6, r3, #1 + wordcount_t num_words) { + 8005f3a: 4605 mov r5, r0 + 8005f3c: 4614 mov r4, r2 + if (!EVEN(uv)) { + 8005f3e: d004 beq.n 8005f4a + carry = uECC_vli_add(uv, uv, mod, num_words); + 8005f40: 460a mov r2, r1 + 8005f42: 4601 mov r1, r0 + 8005f44: f7ff fe44 bl 8005bd0 + 8005f48: 4606 mov r6, r0 + uECC_vli_rshift1(uv, num_words); + 8005f4a: 4621 mov r1, r4 + 8005f4c: 4628 mov r0, r5 + 8005f4e: f7ff fc05 bl 800575c + if (carry) { + 8005f52: b146 cbz r6, 8005f66 + uv[num_words - 1] |= HIGH_BIT_SET; + 8005f54: f104 4280 add.w r2, r4, #1073741824 ; 0x40000000 + 8005f58: 3a01 subs r2, #1 + 8005f5a: f855 3022 ldr.w r3, [r5, r2, lsl #2] + 8005f5e: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 8005f62: f845 3022 str.w r3, [r5, r2, lsl #2] +} + 8005f66: bd70 pop {r4, r5, r6, pc} + +08005f68 : + wordcount_t num_words) { + 8005f68: b5f0 push {r4, r5, r6, r7, lr} + 8005f6a: 460f mov r7, r1 + 8005f6c: b0a1 sub sp, #132 ; 0x84 + 8005f6e: 4606 mov r6, r0 + if (uECC_vli_isZero(input, num_words)) { + 8005f70: 4619 mov r1, r3 + 8005f72: 4638 mov r0, r7 + wordcount_t num_words) { + 8005f74: 4615 mov r5, r2 + 8005f76: 461c mov r4, r3 + if (uECC_vli_isZero(input, num_words)) { + 8005f78: f7ff fb96 bl 80056a8 + 8005f7c: b128 cbz r0, 8005f8a + uECC_vli_clear(result, num_words); + 8005f7e: 4630 mov r0, r6 +} + 8005f80: b021 add sp, #132 ; 0x84 + 8005f82: e8bd 40f0 ldmia.w sp!, {r4, r5, r6, r7, lr} + uECC_vli_clear(result, num_words); + 8005f86: f7ff bb89 b.w 800569c + uECC_vli_set(a, input, num_words); + 8005f8a: 4622 mov r2, r4 + 8005f8c: 4639 mov r1, r7 + 8005f8e: 4668 mov r0, sp + 8005f90: f7ff fbc3 bl 800571a + uECC_vli_set(b, mod, num_words); + 8005f94: 4629 mov r1, r5 + 8005f96: a808 add r0, sp, #32 + 8005f98: f7ff fbbf bl 800571a + uECC_vli_clear(u, num_words); + 8005f9c: 4621 mov r1, r4 + 8005f9e: a810 add r0, sp, #64 ; 0x40 + 8005fa0: f7ff fb7c bl 800569c + u[0] = 1; + 8005fa4: 2301 movs r3, #1 + uECC_vli_clear(v, num_words); + 8005fa6: 4621 mov r1, r4 + 8005fa8: a818 add r0, sp, #96 ; 0x60 + u[0] = 1; + 8005faa: 9310 str r3, [sp, #64] ; 0x40 + uECC_vli_clear(v, num_words); + 8005fac: f7ff fb76 bl 800569c + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + 8005fb0: 4622 mov r2, r4 + 8005fb2: a908 add r1, sp, #32 + 8005fb4: 4668 mov r0, sp + 8005fb6: f7ff fbbc bl 8005732 + 8005fba: b930 cbnz r0, 8005fca + uECC_vli_set(result, u, num_words); + 8005fbc: 4622 mov r2, r4 + 8005fbe: a910 add r1, sp, #64 ; 0x40 + 8005fc0: 4630 mov r0, r6 + 8005fc2: f7ff fbaa bl 800571a +} + 8005fc6: b021 add sp, #132 ; 0x84 + 8005fc8: bdf0 pop {r4, r5, r6, r7, pc} + if (EVEN(a)) { + 8005fca: 9b00 ldr r3, [sp, #0] + 8005fcc: 07da lsls r2, r3, #31 + 8005fce: d409 bmi.n 8005fe4 + uECC_vli_rshift1(a, num_words); + 8005fd0: 4621 mov r1, r4 + 8005fd2: 4668 mov r0, sp + 8005fd4: f7ff fbc2 bl 800575c + vli_modInv_update(u, mod, num_words); + 8005fd8: 4622 mov r2, r4 + 8005fda: 4629 mov r1, r5 + 8005fdc: a810 add r0, sp, #64 ; 0x40 + vli_modInv_update(v, mod, num_words); + 8005fde: f7ff ffa8 bl 8005f32 + 8005fe2: e7e5 b.n 8005fb0 + } else if (EVEN(b)) { + 8005fe4: 9b08 ldr r3, [sp, #32] + 8005fe6: 07db lsls r3, r3, #31 + 8005fe8: d407 bmi.n 8005ffa + uECC_vli_rshift1(b, num_words); + 8005fea: 4621 mov r1, r4 + 8005fec: a808 add r0, sp, #32 + 8005fee: f7ff fbb5 bl 800575c + vli_modInv_update(v, mod, num_words); + 8005ff2: 4622 mov r2, r4 + 8005ff4: 4629 mov r1, r5 + 8005ff6: a818 add r0, sp, #96 ; 0x60 + 8005ff8: e7f1 b.n 8005fde + } else if (cmpResult > 0) { + 8005ffa: 2800 cmp r0, #0 + 8005ffc: dd1a ble.n 8006034 + uECC_vli_sub(a, a, b, num_words); + 8005ffe: aa08 add r2, sp, #32 + 8006000: 4669 mov r1, sp + 8006002: 4668 mov r0, sp + 8006004: f7ff fd22 bl 8005a4c + uECC_vli_rshift1(a, num_words); + 8006008: 4621 mov r1, r4 + 800600a: 4668 mov r0, sp + 800600c: f7ff fba6 bl 800575c + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + 8006010: 4622 mov r2, r4 + 8006012: a918 add r1, sp, #96 ; 0x60 + 8006014: a810 add r0, sp, #64 ; 0x40 + 8006016: f7ff fb8c bl 8005732 + 800601a: 2800 cmp r0, #0 + 800601c: da04 bge.n 8006028 + uECC_vli_add(u, u, mod, num_words); + 800601e: a910 add r1, sp, #64 ; 0x40 + 8006020: 462a mov r2, r5 + 8006022: 4608 mov r0, r1 + 8006024: f7ff fdd4 bl 8005bd0 + uECC_vli_sub(u, u, v, num_words); + 8006028: a910 add r1, sp, #64 ; 0x40 + 800602a: aa18 add r2, sp, #96 ; 0x60 + 800602c: 4608 mov r0, r1 + 800602e: f7ff fd0d bl 8005a4c + 8006032: e7d1 b.n 8005fd8 + uECC_vli_sub(b, b, a, num_words); + 8006034: 466a mov r2, sp + 8006036: a808 add r0, sp, #32 + 8006038: f7ff fd08 bl 8005a4c + uECC_vli_rshift1(b, num_words); + 800603c: 4621 mov r1, r4 + 800603e: a808 add r0, sp, #32 + 8006040: f7ff fb8c bl 800575c + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + 8006044: 4622 mov r2, r4 + 8006046: a910 add r1, sp, #64 ; 0x40 + 8006048: a818 add r0, sp, #96 ; 0x60 + 800604a: f7ff fb72 bl 8005732 + 800604e: 2800 cmp r0, #0 + 8006050: da04 bge.n 800605c + uECC_vli_add(v, v, mod, num_words); + 8006052: a918 add r1, sp, #96 ; 0x60 + 8006054: 462a mov r2, r5 + 8006056: 4608 mov r0, r1 + 8006058: f7ff fdba bl 8005bd0 + uECC_vli_sub(v, v, u, num_words); + 800605c: a918 add r1, sp, #96 ; 0x60 + 800605e: aa10 add r2, sp, #64 ; 0x40 + 8006060: 4608 mov r0, r1 + 8006062: f7ff fcf3 bl 8005a4c + 8006066: e7c4 b.n 8005ff2 + +08006068 : + wordcount_t num_words) { + 8006068: b570 push {r4, r5, r6, lr} + 800606a: 4604 mov r4, r0 + 800606c: f99d 6010 ldrsb.w r6, [sp, #16] + 8006070: 461d mov r5, r3 + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + 8006072: f7ff fdad bl 8005bd0 + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + 8006076: b930 cbnz r0, 8006086 + 8006078: 4632 mov r2, r6 + 800607a: 4621 mov r1, r4 + 800607c: 4628 mov r0, r5 + 800607e: f7ff fb58 bl 8005732 + 8006082: 2801 cmp r0, #1 + 8006084: d006 beq.n 8006094 + uECC_vli_sub(result, result, mod, num_words); + 8006086: 462a mov r2, r5 + 8006088: 4621 mov r1, r4 + 800608a: 4620 mov r0, r4 +} + 800608c: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + uECC_vli_sub(result, result, mod, num_words); + 8006090: f7ff bcdc b.w 8005a4c +} + 8006094: bd70 pop {r4, r5, r6, pc} + +08006096 : +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + 8006096: b573 push {r0, r1, r4, r5, r6, lr} + 8006098: 4604 mov r4, r0 + 800609a: 4615 mov r5, r2 + 800609c: 460e mov r6, r1 + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 800609e: f7ff fc1a bl 80058d6 + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ + 80060a2: 462b mov r3, r5 + 80060a4: 4632 mov r2, r6 + 80060a6: 4621 mov r1, r4 + 80060a8: 4620 mov r0, r4 + 80060aa: f7ff fc04 bl 80058b6 + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ + 80060ae: 2308 movs r3, #8 + 80060b0: 9300 str r3, [sp, #0] + 80060b2: f105 0284 add.w r2, r5, #132 ; 0x84 + 80060b6: 1d2b adds r3, r5, #4 + 80060b8: 4621 mov r1, r4 + 80060ba: 4620 mov r0, r4 + 80060bc: f7ff ffd4 bl 8006068 +} + 80060c0: b002 add sp, #8 + 80060c2: bd70 pop {r4, r5, r6, pc} + +080060c4 : +uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, + 80060c4: b538 push {r3, r4, r5, lr} + 80060c6: 4604 mov r4, r0 + 80060c8: 461d mov r5, r3 + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + 80060ca: f7ff fcbf bl 8005a4c + if (l_borrow) { + 80060ce: b130 cbz r0, 80060de + uECC_vli_add(result, result, mod, num_words); + 80060d0: 462a mov r2, r5 + 80060d2: 4621 mov r1, r4 + 80060d4: 4620 mov r0, r4 +} + 80060d6: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + uECC_vli_add(result, result, mod, num_words); + 80060da: f7ff bd79 b.w 8005bd0 +} + 80060de: bd38 pop {r3, r4, r5, pc} + +080060e0 : + uECC_Curve curve) { + 80060e0: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80060e4: b09a sub sp, #104 ; 0x68 + 80060e6: 4615 mov r5, r2 + 80060e8: 9f22 ldr r7, [sp, #136] ; 0x88 + wordcount_t num_words = curve->num_words; + 80060ea: 463c mov r4, r7 + uECC_Curve curve) { + 80060ec: 4698 mov r8, r3 + wordcount_t num_words = curve->num_words; + 80060ee: f914 ab04 ldrsb.w sl, [r4], #4 + uECC_Curve curve) { + 80060f2: 4606 mov r6, r0 + 80060f4: 4689 mov r9, r1 + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + 80060f6: 4623 mov r3, r4 + 80060f8: 4602 mov r2, r0 + 80060fa: 4629 mov r1, r5 + 80060fc: a802 add r0, sp, #8 + 80060fe: f7ff ffe1 bl 80060c4 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + 8006102: a902 add r1, sp, #8 + 8006104: 463a mov r2, r7 + 8006106: 4608 mov r0, r1 + 8006108: f7ff fbe5 bl 80058d6 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + 800610c: 463b mov r3, r7 + 800610e: aa02 add r2, sp, #8 + 8006110: 4631 mov r1, r6 + 8006112: 4630 mov r0, r6 + 8006114: f7ff fbcf bl 80058b6 + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + 8006118: 463b mov r3, r7 + 800611a: aa02 add r2, sp, #8 + 800611c: 4629 mov r1, r5 + 800611e: 4628 mov r0, r5 + 8006120: f7ff fbc9 bl 80058b6 + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + 8006124: 4623 mov r3, r4 + 8006126: 464a mov r2, r9 + 8006128: 4641 mov r1, r8 + 800612a: a802 add r0, sp, #8 + 800612c: f8cd a000 str.w sl, [sp] + 8006130: f7ff ff9a bl 8006068 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + 8006134: 4623 mov r3, r4 + 8006136: 464a mov r2, r9 + 8006138: 4641 mov r1, r8 + 800613a: 4640 mov r0, r8 + 800613c: f7ff ffc2 bl 80060c4 + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + 8006140: 4623 mov r3, r4 + 8006142: 4632 mov r2, r6 + 8006144: 4629 mov r1, r5 + 8006146: a80a add r0, sp, #40 ; 0x28 + 8006148: f7ff ffbc bl 80060c4 + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + 800614c: 463b mov r3, r7 + 800614e: aa0a add r2, sp, #40 ; 0x28 + 8006150: 4649 mov r1, r9 + 8006152: 4648 mov r0, r9 + 8006154: f7ff fbaf bl 80058b6 + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + 8006158: 4623 mov r3, r4 + 800615a: 462a mov r2, r5 + 800615c: 4631 mov r1, r6 + 800615e: a80a add r0, sp, #40 ; 0x28 + 8006160: f8cd a000 str.w sl, [sp] + 8006164: f7ff ff80 bl 8006068 + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + 8006168: 463a mov r2, r7 + 800616a: 4641 mov r1, r8 + 800616c: 4628 mov r0, r5 + 800616e: f7ff fbb2 bl 80058d6 + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + 8006172: 4623 mov r3, r4 + 8006174: aa0a add r2, sp, #40 ; 0x28 + 8006176: 4629 mov r1, r5 + 8006178: 4628 mov r0, r5 + 800617a: f7ff ffa3 bl 80060c4 + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + 800617e: 4623 mov r3, r4 + 8006180: 462a mov r2, r5 + 8006182: 4631 mov r1, r6 + 8006184: a812 add r0, sp, #72 ; 0x48 + 8006186: f7ff ff9d bl 80060c4 + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + 800618a: 463b mov r3, r7 + 800618c: aa12 add r2, sp, #72 ; 0x48 + 800618e: 4641 mov r1, r8 + 8006190: 4640 mov r0, r8 + 8006192: f7ff fb90 bl 80058b6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ + 8006196: 4623 mov r3, r4 + 8006198: 464a mov r2, r9 + 800619a: 4641 mov r1, r8 + 800619c: 4640 mov r0, r8 + 800619e: f7ff ff91 bl 80060c4 + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + 80061a2: 463a mov r2, r7 + 80061a4: a902 add r1, sp, #8 + 80061a6: a812 add r0, sp, #72 ; 0x48 + 80061a8: f7ff fb95 bl 80058d6 + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + 80061ac: a912 add r1, sp, #72 ; 0x48 + 80061ae: 4623 mov r3, r4 + 80061b0: aa0a add r2, sp, #40 ; 0x28 + 80061b2: 4608 mov r0, r1 + 80061b4: f7ff ff86 bl 80060c4 + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + 80061b8: 4623 mov r3, r4 + 80061ba: 4632 mov r2, r6 + 80061bc: a912 add r1, sp, #72 ; 0x48 + 80061be: a80a add r0, sp, #40 ; 0x28 + 80061c0: f7ff ff80 bl 80060c4 + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + 80061c4: a90a add r1, sp, #40 ; 0x28 + 80061c6: 463b mov r3, r7 + 80061c8: aa02 add r2, sp, #8 + 80061ca: 4608 mov r0, r1 + 80061cc: f7ff fb73 bl 80058b6 + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ + 80061d0: 4623 mov r3, r4 + 80061d2: 464a mov r2, r9 + 80061d4: a90a add r1, sp, #40 ; 0x28 + 80061d6: 4648 mov r0, r9 + 80061d8: f7ff ff74 bl 80060c4 + uECC_vli_set(X1, t7, num_words); + 80061dc: 4652 mov r2, sl + 80061de: a912 add r1, sp, #72 ; 0x48 + 80061e0: 4630 mov r0, r6 + 80061e2: f7ff fa9a bl 800571a +} + 80061e6: b01a add sp, #104 ; 0x68 + 80061e8: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + +080061ec : + uECC_Curve curve) { + 80061ec: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80061f0: b088 sub sp, #32 + 80061f2: 4614 mov r4, r2 + 80061f4: f8dd 8040 ldr.w r8, [sp, #64] ; 0x40 + wordcount_t num_words = curve->num_words; + 80061f8: 4645 mov r5, r8 + uECC_Curve curve) { + 80061fa: 461e mov r6, r3 + wordcount_t num_words = curve->num_words; + 80061fc: f915 ab04 ldrsb.w sl, [r5], #4 + uECC_Curve curve) { + 8006200: 4607 mov r7, r0 + 8006202: 4689 mov r9, r1 + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + 8006204: 462b mov r3, r5 + 8006206: 4602 mov r2, r0 + 8006208: 4621 mov r1, r4 + 800620a: 4668 mov r0, sp + 800620c: f7ff ff5a bl 80060c4 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + 8006210: 4642 mov r2, r8 + 8006212: 4669 mov r1, sp + 8006214: 4668 mov r0, sp + 8006216: f7ff fb5e bl 80058d6 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + 800621a: 4643 mov r3, r8 + 800621c: 466a mov r2, sp + 800621e: 4639 mov r1, r7 + 8006220: 4638 mov r0, r7 + 8006222: f7ff fb48 bl 80058b6 + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + 8006226: 4643 mov r3, r8 + 8006228: 466a mov r2, sp + 800622a: 4621 mov r1, r4 + 800622c: 4620 mov r0, r4 + 800622e: f7ff fb42 bl 80058b6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + 8006232: 462b mov r3, r5 + 8006234: 464a mov r2, r9 + 8006236: 4631 mov r1, r6 + 8006238: 4630 mov r0, r6 + 800623a: f7ff ff43 bl 80060c4 + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + 800623e: 4642 mov r2, r8 + 8006240: 4631 mov r1, r6 + 8006242: 4668 mov r0, sp + 8006244: f7ff fb47 bl 80058d6 + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + 8006248: 462b mov r3, r5 + 800624a: 463a mov r2, r7 + 800624c: 4669 mov r1, sp + 800624e: 4668 mov r0, sp + 8006250: f7ff ff38 bl 80060c4 + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + 8006254: 462b mov r3, r5 + 8006256: 4622 mov r2, r4 + 8006258: 4669 mov r1, sp + 800625a: 4668 mov r0, sp + 800625c: f7ff ff32 bl 80060c4 + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + 8006260: 462b mov r3, r5 + 8006262: 463a mov r2, r7 + 8006264: 4621 mov r1, r4 + 8006266: 4620 mov r0, r4 + 8006268: f7ff ff2c bl 80060c4 + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + 800626c: 4643 mov r3, r8 + 800626e: 4622 mov r2, r4 + 8006270: 4649 mov r1, r9 + 8006272: 4648 mov r0, r9 + 8006274: f7ff fb1f bl 80058b6 + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + 8006278: 462b mov r3, r5 + 800627a: 466a mov r2, sp + 800627c: 4639 mov r1, r7 + 800627e: 4620 mov r0, r4 + 8006280: f7ff ff20 bl 80060c4 + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + 8006284: 4643 mov r3, r8 + 8006286: 4622 mov r2, r4 + 8006288: 4631 mov r1, r6 + 800628a: 4630 mov r0, r6 + 800628c: f7ff fb13 bl 80058b6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + 8006290: 462b mov r3, r5 + 8006292: 464a mov r2, r9 + 8006294: 4631 mov r1, r6 + 8006296: 4630 mov r0, r6 + 8006298: f7ff ff14 bl 80060c4 + uECC_vli_set(X2, t5, num_words); + 800629c: 4652 mov r2, sl + 800629e: 4669 mov r1, sp + 80062a0: 4620 mov r0, r4 + 80062a2: f7ff fa3a bl 800571a +} + 80062a6: b008 add sp, #32 + 80062a8: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + +080062ac : + uECC_Curve curve) { + 80062ac: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 80062b0: b0b1 sub sp, #196 ; 0xc4 + 80062b2: e9cd 0102 strd r0, r1, [sp, #8] + 80062b6: 9c3b ldr r4, [sp, #236] ; 0xec + 80062b8: 9204 str r2, [sp, #16] + wordcount_t num_words = curve->num_words; + 80062ba: f994 9000 ldrsb.w r9, [r4] + uECC_vli_set(Rx[1], point, num_words); + 80062be: a818 add r0, sp, #96 ; 0x60 + 80062c0: 464a mov r2, r9 + uECC_Curve curve) { + 80062c2: 461d mov r5, r3 + uECC_vli_set(Rx[1], point, num_words); + 80062c4: f7ff fa29 bl 800571a + uECC_vli_set(Ry[1], point + num_words, num_words); + 80062c8: ea4f 0389 mov.w r3, r9, lsl #2 + 80062cc: 9305 str r3, [sp, #20] + 80062ce: 9b03 ldr r3, [sp, #12] + 80062d0: eb03 0a89 add.w sl, r3, r9, lsl #2 + 80062d4: 4651 mov r1, sl + 80062d6: a828 add r0, sp, #160 ; 0xa0 + 80062d8: f7ff fa1f bl 800571a + wordcount_t num_words = curve->num_words; + 80062dc: f994 2000 ldrsb.w r2, [r4] + if (initial_Z) { + 80062e0: 2d00 cmp r5, #0 + 80062e2: f000 8082 beq.w 80063ea + uECC_vli_set(z, initial_Z, num_words); + 80062e6: 4629 mov r1, r5 + 80062e8: a808 add r0, sp, #32 + 80062ea: f7ff fa16 bl 800571a + uECC_vli_set(X2, X1, num_words); + 80062ee: af10 add r7, sp, #64 ; 0x40 + 80062f0: a918 add r1, sp, #96 ; 0x60 + 80062f2: 4638 mov r0, r7 + uECC_vli_set(Y2, Y1, num_words); + 80062f4: f10d 0880 add.w r8, sp, #128 ; 0x80 + uECC_vli_set(X2, X1, num_words); + 80062f8: f7ff fa0f bl 800571a + uECC_vli_set(Y2, Y1, num_words); + 80062fc: a928 add r1, sp, #160 ; 0xa0 + 80062fe: 4640 mov r0, r8 + 8006300: f7ff fa0b bl 800571a + apply_z(X1, Y1, z, curve); + 8006304: 4623 mov r3, r4 + 8006306: aa08 add r2, sp, #32 + 8006308: a818 add r0, sp, #96 ; 0x60 + 800630a: f7ff fae8 bl 80058de + curve->double_jacobian(X1, Y1, z, curve); + 800630e: f8d4 50a4 ldr.w r5, [r4, #164] ; 0xa4 + 8006312: 4623 mov r3, r4 + 8006314: aa08 add r2, sp, #32 + 8006316: a928 add r1, sp, #160 ; 0xa0 + 8006318: a818 add r0, sp, #96 ; 0x60 + 800631a: 47a8 blx r5 + apply_z(X2, Y2, z, curve); + 800631c: 4623 mov r3, r4 + 800631e: aa08 add r2, sp, #32 + 8006320: 4641 mov r1, r8 + 8006322: 4638 mov r0, r7 + 8006324: f7ff fadb bl 80058de + for (i = num_bits - 2; i > 0; --i) { + 8006328: f9bd 50e8 ldrsh.w r5, [sp, #232] ; 0xe8 + 800632c: 3d02 subs r5, #2 + 800632e: b22d sxth r5, r5 + 8006330: 2d00 cmp r5, #0 + 8006332: dc63 bgt.n 80063fc + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 8006334: 9b04 ldr r3, [sp, #16] + 8006336: 681d ldr r5, [r3, #0] + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 8006338: 9400 str r4, [sp, #0] + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 800633a: f005 0601 and.w r6, r5, #1 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 800633e: ab10 add r3, sp, #64 ; 0x40 + 8006340: eb03 1746 add.w r7, r3, r6, lsl #5 + 8006344: 43ed mvns r5, r5 + 8006346: ab20 add r3, sp, #128 ; 0x80 + 8006348: eb03 1646 add.w r6, r3, r6, lsl #5 + 800634c: f005 0501 and.w r5, r5, #1 + 8006350: ab10 add r3, sp, #64 ; 0x40 + 8006352: eb03 1845 add.w r8, r3, r5, lsl #5 + 8006356: ab20 add r3, sp, #128 ; 0x80 + 8006358: eb03 1545 add.w r5, r3, r5, lsl #5 + 800635c: 462b mov r3, r5 + 800635e: 4642 mov r2, r8 + 8006360: 4631 mov r1, r6 + 8006362: 4638 mov r0, r7 + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + 8006364: f104 0b04 add.w fp, r4, #4 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 8006368: f7ff feba bl 80060e0 + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + 800636c: 465b mov r3, fp + 800636e: aa10 add r2, sp, #64 ; 0x40 + 8006370: a918 add r1, sp, #96 ; 0x60 + 8006372: a808 add r0, sp, #32 + 8006374: f7ff fea6 bl 80060c4 + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + 8006378: a908 add r1, sp, #32 + 800637a: 4623 mov r3, r4 + 800637c: 4632 mov r2, r6 + 800637e: 4608 mov r0, r1 + 8006380: f7ff fa99 bl 80058b6 + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + 8006384: a908 add r1, sp, #32 + 8006386: 9a03 ldr r2, [sp, #12] + 8006388: 4623 mov r3, r4 + 800638a: 4608 mov r0, r1 + 800638c: f7ff fa93 bl 80058b6 + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ + 8006390: a908 add r1, sp, #32 + 8006392: 464b mov r3, r9 + 8006394: 465a mov r2, fp + 8006396: 4608 mov r0, r1 + 8006398: f7ff fde6 bl 8005f68 + uECC_vli_modMult_fast(z, z, point + num_words, curve); + 800639c: a908 add r1, sp, #32 + 800639e: 4623 mov r3, r4 + 80063a0: 4652 mov r2, sl + 80063a2: 4608 mov r0, r1 + 80063a4: f7ff fa87 bl 80058b6 + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + 80063a8: a908 add r1, sp, #32 + 80063aa: 4623 mov r3, r4 + 80063ac: 463a mov r2, r7 + 80063ae: 4608 mov r0, r1 + 80063b0: f7ff fa81 bl 80058b6 + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + 80063b4: 4633 mov r3, r6 + 80063b6: 463a mov r2, r7 + 80063b8: 4629 mov r1, r5 + 80063ba: 4640 mov r0, r8 + 80063bc: 9400 str r4, [sp, #0] + 80063be: f7ff ff15 bl 80061ec + apply_z(Rx[0], Ry[0], z, curve); + 80063c2: 4623 mov r3, r4 + 80063c4: aa08 add r2, sp, #32 + 80063c6: a920 add r1, sp, #128 ; 0x80 + 80063c8: a810 add r0, sp, #64 ; 0x40 + 80063ca: f7ff fa88 bl 80058de + uECC_vli_set(result, Rx[0], num_words); + 80063ce: 9802 ldr r0, [sp, #8] + 80063d0: 464a mov r2, r9 + 80063d2: a910 add r1, sp, #64 ; 0x40 + 80063d4: f7ff f9a1 bl 800571a + uECC_vli_set(result + num_words, Ry[0], num_words); + 80063d8: 9802 ldr r0, [sp, #8] + 80063da: 9b05 ldr r3, [sp, #20] + 80063dc: a920 add r1, sp, #128 ; 0x80 + 80063de: 4418 add r0, r3 + 80063e0: f7ff f99b bl 800571a +} + 80063e4: b031 add sp, #196 ; 0xc4 + 80063e6: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + uECC_vli_clear(z, num_words); + 80063ea: 4611 mov r1, r2 + 80063ec: a808 add r0, sp, #32 + 80063ee: 9206 str r2, [sp, #24] + 80063f0: f7ff f954 bl 800569c + z[0] = 1; + 80063f4: 2301 movs r3, #1 + 80063f6: 9a06 ldr r2, [sp, #24] + 80063f8: 9308 str r3, [sp, #32] + 80063fa: e778 b.n 80062ee + nb = !uECC_vli_testBit(scalar, i); + 80063fc: 4629 mov r1, r5 + 80063fe: 9804 ldr r0, [sp, #16] + 8006400: f7ff f961 bl 80056c6 + 8006404: fab0 f680 clz r6, r0 + 8006408: 0976 lsrs r6, r6, #5 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 800640a: f1c6 0101 rsb r1, r6, #1 + 800640e: eb07 1b46 add.w fp, r7, r6, lsl #5 + 8006412: eb08 1646 add.w r6, r8, r6, lsl #5 + 8006416: eb07 1041 add.w r0, r7, r1, lsl #5 + 800641a: 4633 mov r3, r6 + 800641c: eb08 1141 add.w r1, r8, r1, lsl #5 + 8006420: 465a mov r2, fp + 8006422: 9400 str r4, [sp, #0] + 8006424: e9cd 0106 strd r0, r1, [sp, #24] + 8006428: f7ff fe5a bl 80060e0 + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + 800642c: 9907 ldr r1, [sp, #28] + 800642e: 9806 ldr r0, [sp, #24] + 8006430: 9400 str r4, [sp, #0] + 8006432: 460b mov r3, r1 + 8006434: 4602 mov r2, r0 + 8006436: 4631 mov r1, r6 + 8006438: 4658 mov r0, fp + 800643a: f7ff fed7 bl 80061ec + for (i = num_bits - 2; i > 0; --i) { + 800643e: 3d01 subs r5, #1 + 8006440: e775 b.n 800632e + +08006442 : + uECC_Curve curve) { + 8006442: b530 push {r4, r5, lr} + 8006444: 4614 mov r4, r2 + 8006446: b095 sub sp, #84 ; 0x54 + 8006448: 4605 mov r5, r0 + uECC_word_t *p2[2] = {tmp1, tmp2}; + 800644a: aa0c add r2, sp, #48 ; 0x30 + carry = regularize_k(private, tmp1, tmp2, curve); + 800644c: 4623 mov r3, r4 + uECC_Curve curve) { + 800644e: 4608 mov r0, r1 + uECC_word_t *p2[2] = {tmp1, tmp2}; + 8006450: a904 add r1, sp, #16 + 8006452: 9102 str r1, [sp, #8] + 8006454: 9203 str r2, [sp, #12] + carry = regularize_k(private, tmp1, tmp2, curve); + 8006456: f7ff fbe0 bl 8005c1a + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + 800645a: fab0 f380 clz r3, r0 + 800645e: 095b lsrs r3, r3, #5 + 8006460: aa14 add r2, sp, #80 ; 0x50 + 8006462: eb02 0283 add.w r2, r2, r3, lsl #2 + 8006466: 8863 ldrh r3, [r4, #2] + 8006468: 9401 str r4, [sp, #4] + 800646a: 3301 adds r3, #1 + 800646c: b21b sxth r3, r3 + 800646e: 9300 str r3, [sp, #0] + 8006470: f852 2c48 ldr.w r2, [r2, #-72] + 8006474: 2300 movs r3, #0 + 8006476: f104 0144 add.w r1, r4, #68 ; 0x44 + 800647a: 4628 mov r0, r5 + 800647c: f7ff ff16 bl 80062ac + if (EccPoint_isZero(result, curve)) { + 8006480: 7821 ldrb r1, [r4, #0] + 8006482: 0049 lsls r1, r1, #1 + 8006484: b249 sxtb r1, r1 + 8006486: 4628 mov r0, r5 + 8006488: f7ff f90e bl 80056a8 +} + 800648c: fab0 f080 clz r0, r0 + 8006490: 0940 lsrs r0, r0, #5 + 8006492: b015 add sp, #84 ; 0x54 + 8006494: bd30 pop {r4, r5, pc} + ... + +08006498 : + uECC_Curve curve) { + 8006498: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800649c: ed2d 8b02 vpush {d8} + 80064a0: b0a7 sub sp, #156 ; 0x9c + 80064a2: 461e mov r6, r3 + 80064a4: 9d33 ldr r5, [sp, #204] ; 0xcc + wordcount_t num_words = curve->num_words; + 80064a6: f995 a000 ldrsb.w sl, [r5] + uECC_Curve curve) { + 80064aa: ee08 1a10 vmov s16, r1 + 80064ae: 4683 mov fp, r0 + uECC_word_t *k2[2] = {tmp, s}; + 80064b0: f10d 0918 add.w r9, sp, #24 + 80064b4: ab0e add r3, sp, #56 ; 0x38 + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80064b6: 4651 mov r1, sl + 80064b8: 4630 mov r0, r6 + uECC_Curve curve) { + 80064ba: ee08 2a90 vmov s17, r2 + uECC_word_t *k2[2] = {tmp, s}; + 80064be: f8cd 9010 str.w r9, [sp, #16] + 80064c2: 9305 str r3, [sp, #20] + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80064c4: f7ff f8f0 bl 80056a8 + 80064c8: b128 cbz r0, 80064d6 + return 0; + 80064ca: 2000 movs r0, #0 +} + 80064cc: b027 add sp, #156 ; 0x9c + 80064ce: ecbd 8b02 vpop {d8} + 80064d2: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 80064d6: f9b5 8002 ldrsh.w r8, [r5, #2] + 80064da: f118 041f adds.w r4, r8, #31 + 80064de: bf48 it mi + 80064e0: f108 043e addmi.w r4, r8, #62 ; 0x3e + 80064e4: f344 1447 sbfx r4, r4, #5, #8 + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80064e8: f105 0724 add.w r7, r5, #36 ; 0x24 + 80064ec: 4622 mov r2, r4 + 80064ee: 4631 mov r1, r6 + 80064f0: 4638 mov r0, r7 + 80064f2: f7ff fb19 bl 8005b28 + 80064f6: 2801 cmp r0, #1 + 80064f8: 9003 str r0, [sp, #12] + 80064fa: d1e6 bne.n 80064ca + carry = regularize_k(k, tmp, s, curve); + 80064fc: 462b mov r3, r5 + 80064fe: aa0e add r2, sp, #56 ; 0x38 + 8006500: 4649 mov r1, r9 + 8006502: 4630 mov r0, r6 + 8006504: f7ff fb89 bl 8005c1a + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + 8006508: fab0 f080 clz r0, r0 + 800650c: ab26 add r3, sp, #152 ; 0x98 + 800650e: 0940 lsrs r0, r0, #5 + 8006510: f108 0801 add.w r8, r8, #1 + 8006514: eb03 0080 add.w r0, r3, r0, lsl #2 + 8006518: fa0f f388 sxth.w r3, r8 + 800651c: 9300 str r3, [sp, #0] + 800651e: 9501 str r5, [sp, #4] + 8006520: f850 2c88 ldr.w r2, [r0, #-136] + 8006524: f105 0144 add.w r1, r5, #68 ; 0x44 + 8006528: a816 add r0, sp, #88 ; 0x58 + 800652a: 2300 movs r3, #0 + 800652c: f7ff febe bl 80062ac + if (uECC_vli_isZero(p, num_words)) { + 8006530: 4651 mov r1, sl + 8006532: a816 add r0, sp, #88 ; 0x58 + 8006534: f7ff f8b8 bl 80056a8 + 8006538: 2800 cmp r0, #0 + 800653a: d1c6 bne.n 80064ca + uECC_recid = (p[curve->num_words] & 0x01); + 800653c: f995 3000 ldrsb.w r3, [r5] + 8006540: aa26 add r2, sp, #152 ; 0x98 + 8006542: eb02 0383 add.w r3, r2, r3, lsl #2 + 8006546: 4a3b ldr r2, [pc, #236] ; (8006634 ) + 8006548: f853 3c40 ldr.w r3, [r3, #-64] + 800654c: f003 0301 and.w r3, r3, #1 + 8006550: 7013 strb r3, [r2, #0] + if (!g_rng_function) { + 8006552: 4b39 ldr r3, [pc, #228] ; (8006638 ) + 8006554: 681b ldr r3, [r3, #0] + 8006556: 2b00 cmp r3, #0 + 8006558: d163 bne.n 8006622 + uECC_vli_clear(tmp, num_n_words); + 800655a: 4621 mov r1, r4 + 800655c: 4648 mov r0, r9 + 800655e: f7ff f89d bl 800569c + tmp[0] = 1; + 8006562: 9b03 ldr r3, [sp, #12] + 8006564: 9306 str r3, [sp, #24] + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + 8006566: 463b mov r3, r7 + 8006568: aa06 add r2, sp, #24 + 800656a: 4631 mov r1, r6 + 800656c: 4630 mov r0, r6 + 800656e: 9400 str r4, [sp, #0] + 8006570: f7ff f901 bl 8005776 + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + 8006574: 4623 mov r3, r4 + 8006576: 463a mov r2, r7 + 8006578: 4631 mov r1, r6 + 800657a: 4630 mov r0, r6 + 800657c: f7ff fcf4 bl 8005f68 + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + 8006580: 463b mov r3, r7 + 8006582: aa06 add r2, sp, #24 + 8006584: 4631 mov r1, r6 + 8006586: 4630 mov r0, r6 + 8006588: 9400 str r4, [sp, #0] + 800658a: f7ff f8f4 bl 8005776 + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + 800658e: f995 1001 ldrsb.w r1, [r5, #1] + 8006592: 9832 ldr r0, [sp, #200] ; 0xc8 + 8006594: aa16 add r2, sp, #88 ; 0x58 + 8006596: f7ff f9c1 bl 800591c + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ + 800659a: f9b5 3002 ldrsh.w r3, [r5, #2] + 800659e: 1dda adds r2, r3, #7 + 80065a0: bf48 it mi + 80065a2: f103 020e addmi.w r2, r3, #14 + 80065a6: 10d2 asrs r2, r2, #3 + 80065a8: 4659 mov r1, fp + 80065aa: a806 add r0, sp, #24 + 80065ac: f7ff f9ca bl 8005944 + s[num_n_words - 1] = 0; + 80065b0: aa26 add r2, sp, #152 ; 0x98 + 80065b2: 1e63 subs r3, r4, #1 + 80065b4: eb02 0383 add.w r3, r2, r3, lsl #2 + 80065b8: 2200 movs r2, #0 + uECC_vli_set(s, p, num_words); + 80065ba: a80e add r0, sp, #56 ; 0x38 + s[num_n_words - 1] = 0; + 80065bc: f843 2c60 str.w r2, [r3, #-96] + uECC_vli_set(s, p, num_words); + 80065c0: a916 add r1, sp, #88 ; 0x58 + 80065c2: 4652 mov r2, sl + 80065c4: f7ff f8a9 bl 800571a + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + 80065c8: 4602 mov r2, r0 + 80065ca: 463b mov r3, r7 + 80065cc: a906 add r1, sp, #24 + 80065ce: 9400 str r4, [sp, #0] + 80065d0: f7ff f8d1 bl 8005776 + bits2int(tmp, message_hash, hash_size, curve); + 80065d4: ee18 2a90 vmov r2, s17 + 80065d8: ee18 1a10 vmov r1, s16 + 80065dc: 462b mov r3, r5 + 80065de: a806 add r0, sp, #24 + 80065e0: f7ff fa5b bl 8005a9a + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + 80065e4: aa0e add r2, sp, #56 ; 0x38 + 80065e6: 4610 mov r0, r2 + 80065e8: 463b mov r3, r7 + 80065ea: a906 add r1, sp, #24 + 80065ec: 9400 str r4, [sp, #0] + 80065ee: f7ff fd3b bl 8006068 + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + 80065f2: a90e add r1, sp, #56 ; 0x38 + 80065f4: 4608 mov r0, r1 + 80065f6: 463b mov r3, r7 + 80065f8: 4632 mov r2, r6 + 80065fa: 9400 str r4, [sp, #0] + 80065fc: f7ff f8bb bl 8005776 + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + 8006600: 4621 mov r1, r4 + 8006602: a80e add r0, sp, #56 ; 0x38 + 8006604: f7ff f869 bl 80056da + 8006608: f995 1001 ldrsb.w r1, [r5, #1] + 800660c: ebb0 0fc1 cmp.w r0, r1, lsl #3 + 8006610: f73f af5b bgt.w 80064ca + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + 8006614: 9b32 ldr r3, [sp, #200] ; 0xc8 + 8006616: aa0e add r2, sp, #56 ; 0x38 + 8006618: 1858 adds r0, r3, r1 + 800661a: f7ff f97f bl 800591c + return 1; + 800661e: 2001 movs r0, #1 + 8006620: e754 b.n 80064cc + } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + 8006622: 4622 mov r2, r4 + 8006624: 4639 mov r1, r7 + 8006626: 4648 mov r0, r9 + 8006628: f7ff fa96 bl 8005b58 + 800662c: 2800 cmp r0, #0 + 800662e: d19a bne.n 8006566 + 8006630: e74b b.n 80064ca + 8006632: bf00 nop + 8006634: 2009e2a4 .word 0x2009e2a4 + 8006638: 2009e2a0 .word 0x2009e2a0 + +0800663c : + uECC_Curve curve) { + 800663c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8006640: 4605 mov r5, r0 + 8006642: b093 sub sp, #76 ; 0x4c + 8006644: 460c mov r4, r1 + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + 8006646: 4610 mov r0, r2 + 8006648: 2108 movs r1, #8 + uECC_Curve curve) { + 800664a: 4617 mov r7, r2 + 800664c: 461e mov r6, r3 + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + 800664e: f7ff f82b bl 80056a8 + 8006652: 2800 cmp r0, #0 + 8006654: d161 bne.n 800671a + uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ + 8006656: 4632 mov r2, r6 + 8006658: 4621 mov r1, r4 + 800665a: a80a add r0, sp, #40 ; 0x28 + 800665c: f7ff f93b bl 80058d6 + uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ + 8006660: 4633 mov r3, r6 + 8006662: aa0a add r2, sp, #40 ; 0x28 + 8006664: 4629 mov r1, r5 + 8006666: a802 add r0, sp, #8 + 8006668: f7ff f925 bl 80058b6 + uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ + 800666c: 4632 mov r2, r6 + 800666e: 4629 mov r1, r5 + 8006670: 4628 mov r0, r5 + 8006672: f7ff f930 bl 80058d6 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ + 8006676: a90a add r1, sp, #40 ; 0x28 + 8006678: 4608 mov r0, r1 + 800667a: 4632 mov r2, r6 + 800667c: f7ff f92b bl 80058d6 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 8006680: f04f 0808 mov.w r8, #8 + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + 8006684: 463a mov r2, r7 + 8006686: 4638 mov r0, r7 + 8006688: 4633 mov r3, r6 + 800668a: 4621 mov r1, r4 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 800668c: 1d37 adds r7, r6, #4 + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + 800668e: f7ff f912 bl 80058b6 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 8006692: 463b mov r3, r7 + 8006694: 462a mov r2, r5 + 8006696: 4629 mov r1, r5 + 8006698: 4620 mov r0, r4 + 800669a: f8cd 8000 str.w r8, [sp] + 800669e: f7ff fce3 bl 8006068 + uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ + 80066a2: 463b mov r3, r7 + 80066a4: f8cd 8000 str.w r8, [sp] + 80066a8: 462a mov r2, r5 + 80066aa: 4621 mov r1, r4 + 80066ac: 4620 mov r0, r4 + 80066ae: f7ff fcdb bl 8006068 + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 80066b2: 6823 ldr r3, [r4, #0] + if (uECC_vli_testBit(Y1, 0)) { + 80066b4: 07db lsls r3, r3, #31 + 80066b6: d533 bpl.n 8006720 + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + 80066b8: 463a mov r2, r7 + 80066ba: 4621 mov r1, r4 + 80066bc: 4620 mov r0, r4 + 80066be: f7ff fa87 bl 8005bd0 + uECC_vli_rshift1(Y1, num_words_secp256k1); + 80066c2: 4641 mov r1, r8 + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + 80066c4: 4681 mov r9, r0 + uECC_vli_rshift1(Y1, num_words_secp256k1); + 80066c6: 4620 mov r0, r4 + 80066c8: f7ff f848 bl 800575c + Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); + 80066cc: 69e3 ldr r3, [r4, #28] + 80066ce: ea43 73c9 orr.w r3, r3, r9, lsl #31 + 80066d2: 61e3 str r3, [r4, #28] + uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ + 80066d4: 4632 mov r2, r6 + 80066d6: 4621 mov r1, r4 + 80066d8: 4628 mov r0, r5 + 80066da: f7ff f8fc bl 80058d6 + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ + 80066de: 463b mov r3, r7 + 80066e0: aa02 add r2, sp, #8 + 80066e2: 4629 mov r1, r5 + 80066e4: 4628 mov r0, r5 + 80066e6: f7ff fced bl 80060c4 + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ + 80066ea: 463b mov r3, r7 + 80066ec: aa02 add r2, sp, #8 + 80066ee: 4629 mov r1, r5 + 80066f0: 4628 mov r0, r5 + 80066f2: f7ff fce7 bl 80060c4 + uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ + 80066f6: a902 add r1, sp, #8 + 80066f8: 4608 mov r0, r1 + 80066fa: 463b mov r3, r7 + 80066fc: 462a mov r2, r5 + 80066fe: f7ff fce1 bl 80060c4 + uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ + 8006702: 4633 mov r3, r6 + 8006704: aa02 add r2, sp, #8 + 8006706: 4621 mov r1, r4 + 8006708: 4620 mov r0, r4 + 800670a: f7ff f8d4 bl 80058b6 + uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ + 800670e: 463b mov r3, r7 + 8006710: aa0a add r2, sp, #40 ; 0x28 + 8006712: 4621 mov r1, r4 + 8006714: 4620 mov r0, r4 + 8006716: f7ff fcd5 bl 80060c4 +} + 800671a: b013 add sp, #76 ; 0x4c + 800671c: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + uECC_vli_rshift1(Y1, num_words_secp256k1); + 8006720: 4641 mov r1, r8 + 8006722: 4620 mov r0, r4 + 8006724: f7ff f81a bl 800575c + 8006728: e7d4 b.n 80066d4 + +0800672a : +static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + 800672a: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800672e: b08a sub sp, #40 ; 0x28 + 8006730: 4604 mov r4, r0 + 8006732: 4615 mov r5, r2 + 8006734: 460e mov r6, r1 + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 8006736: 221c movs r2, #28 + 8006738: 2100 movs r1, #0 + 800673a: a803 add r0, sp, #12 + 800673c: f006 ff9a bl 800d674 + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + 8006740: 1d2f adds r7, r5, #4 + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 8006742: 2303 movs r3, #3 + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 8006744: 462a mov r2, r5 + 8006746: 4631 mov r1, r6 + 8006748: 4620 mov r0, r4 + wordcount_t num_words = curve->num_words; + 800674a: f995 8000 ldrsb.w r8, [r5] + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 800674e: 9302 str r3, [sp, #8] + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 8006750: f7ff f8c1 bl 80058d6 + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + 8006754: 463b mov r3, r7 + 8006756: aa02 add r2, sp, #8 + 8006758: 4621 mov r1, r4 + 800675a: 4620 mov r0, r4 + 800675c: f7ff fcb2 bl 80060c4 + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + 8006760: 462b mov r3, r5 + 8006762: 4632 mov r2, r6 + 8006764: 4621 mov r1, r4 + 8006766: 4620 mov r0, r4 + 8006768: f7ff f8a5 bl 80058b6 + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ + 800676c: f8cd 8000 str.w r8, [sp] + 8006770: 463b mov r3, r7 + 8006772: f105 0284 add.w r2, r5, #132 ; 0x84 + 8006776: 4621 mov r1, r4 + 8006778: 4620 mov r0, r4 + 800677a: f7ff fc75 bl 8006068 +} + 800677e: b00a add sp, #40 ; 0x28 + 8006780: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +08006784 : + uECC_Curve curve) { + 8006784: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + wordcount_t num_words = curve->num_words; + 8006788: f993 8000 ldrsb.w r8, [r3] + uECC_Curve curve) { + 800678c: b092 sub sp, #72 ; 0x48 + 800678e: 4604 mov r4, r0 + 8006790: 4689 mov r9, r1 + if (uECC_vli_isZero(Z1, num_words)) { + 8006792: 4610 mov r0, r2 + 8006794: 4641 mov r1, r8 + uECC_Curve curve) { + 8006796: 4615 mov r5, r2 + 8006798: 461e mov r6, r3 + if (uECC_vli_isZero(Z1, num_words)) { + 800679a: f7fe ff85 bl 80056a8 + 800679e: 2800 cmp r0, #0 + 80067a0: f040 808e bne.w 80068c0 + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + 80067a4: 4632 mov r2, r6 + 80067a6: 4649 mov r1, r9 + 80067a8: a802 add r0, sp, #8 + 80067aa: f7ff f894 bl 80058d6 + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + 80067ae: 4633 mov r3, r6 + 80067b0: aa02 add r2, sp, #8 + 80067b2: 4621 mov r1, r4 + 80067b4: a80a add r0, sp, #40 ; 0x28 + 80067b6: f7ff f87e bl 80058b6 + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + 80067ba: a902 add r1, sp, #8 + 80067bc: 4608 mov r0, r1 + 80067be: 4632 mov r2, r6 + 80067c0: f7ff f889 bl 80058d6 + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + 80067c4: 4633 mov r3, r6 + 80067c6: 462a mov r2, r5 + 80067c8: 4649 mov r1, r9 + 80067ca: 4648 mov r0, r9 + 80067cc: f7ff f873 bl 80058b6 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + 80067d0: 1d37 adds r7, r6, #4 + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + 80067d2: 4632 mov r2, r6 + 80067d4: 4629 mov r1, r5 + 80067d6: 4628 mov r0, r5 + 80067d8: f7ff f87d bl 80058d6 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + 80067dc: 463b mov r3, r7 + 80067de: 462a mov r2, r5 + 80067e0: 4621 mov r1, r4 + 80067e2: 4620 mov r0, r4 + 80067e4: f8cd 8000 str.w r8, [sp] + 80067e8: f7ff fc3e bl 8006068 + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + 80067ec: 463b mov r3, r7 + 80067ee: 462a mov r2, r5 + 80067f0: 4629 mov r1, r5 + 80067f2: 4628 mov r0, r5 + 80067f4: f8cd 8000 str.w r8, [sp] + 80067f8: f7ff fc36 bl 8006068 + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + 80067fc: 463b mov r3, r7 + 80067fe: 462a mov r2, r5 + 8006800: 4621 mov r1, r4 + 8006802: 4628 mov r0, r5 + 8006804: f7ff fc5e bl 80060c4 + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + 8006808: 4633 mov r3, r6 + 800680a: 462a mov r2, r5 + 800680c: 4621 mov r1, r4 + 800680e: 4620 mov r0, r4 + 8006810: f7ff f851 bl 80058b6 + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + 8006814: 463b mov r3, r7 + 8006816: 4622 mov r2, r4 + 8006818: 4621 mov r1, r4 + 800681a: 4628 mov r0, r5 + 800681c: f8cd 8000 str.w r8, [sp] + 8006820: f7ff fc22 bl 8006068 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + 8006824: 463b mov r3, r7 + 8006826: f8cd 8000 str.w r8, [sp] + 800682a: 462a mov r2, r5 + 800682c: 4621 mov r1, r4 + 800682e: 4620 mov r0, r4 + 8006830: f7ff fc1a bl 8006068 + 8006834: 6823 ldr r3, [r4, #0] + if (uECC_vli_testBit(X1, 0)) { + 8006836: 07db lsls r3, r3, #31 + 8006838: d545 bpl.n 80068c6 + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + 800683a: 463a mov r2, r7 + 800683c: 4621 mov r1, r4 + 800683e: 4620 mov r0, r4 + 8006840: f7ff f9c6 bl 8005bd0 + uECC_vli_rshift1(X1, num_words); + 8006844: 4641 mov r1, r8 + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + 8006846: 4682 mov sl, r0 + uECC_vli_rshift1(X1, num_words); + 8006848: 4620 mov r0, r4 + 800684a: f7fe ff87 bl 800575c + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + 800684e: f108 4380 add.w r3, r8, #1073741824 ; 0x40000000 + 8006852: 3b01 subs r3, #1 + 8006854: f854 2023 ldr.w r2, [r4, r3, lsl #2] + 8006858: ea42 72ca orr.w r2, r2, sl, lsl #31 + 800685c: f844 2023 str.w r2, [r4, r3, lsl #2] + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + 8006860: 4632 mov r2, r6 + 8006862: 4621 mov r1, r4 + 8006864: 4628 mov r0, r5 + 8006866: f7ff f836 bl 80058d6 + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + 800686a: 463b mov r3, r7 + 800686c: aa0a add r2, sp, #40 ; 0x28 + 800686e: 4629 mov r1, r5 + 8006870: 4628 mov r0, r5 + 8006872: f7ff fc27 bl 80060c4 + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + 8006876: 463b mov r3, r7 + 8006878: aa0a add r2, sp, #40 ; 0x28 + 800687a: 4629 mov r1, r5 + 800687c: 4628 mov r0, r5 + 800687e: f7ff fc21 bl 80060c4 + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + 8006882: a90a add r1, sp, #40 ; 0x28 + 8006884: 4608 mov r0, r1 + 8006886: 463b mov r3, r7 + 8006888: 462a mov r2, r5 + 800688a: f7ff fc1b bl 80060c4 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + 800688e: 4633 mov r3, r6 + 8006890: aa0a add r2, sp, #40 ; 0x28 + 8006892: 4621 mov r1, r4 + 8006894: 4620 mov r0, r4 + 8006896: f7ff f80e bl 80058b6 + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ + 800689a: aa02 add r2, sp, #8 + 800689c: 463b mov r3, r7 + 800689e: 4610 mov r0, r2 + 80068a0: 4621 mov r1, r4 + 80068a2: f7ff fc0f bl 80060c4 + uECC_vli_set(X1, Z1, num_words); + 80068a6: 4642 mov r2, r8 + 80068a8: 4629 mov r1, r5 + 80068aa: 4620 mov r0, r4 + 80068ac: f7fe ff35 bl 800571a + uECC_vli_set(Z1, Y1, num_words); + 80068b0: 4649 mov r1, r9 + 80068b2: 4628 mov r0, r5 + 80068b4: f7fe ff31 bl 800571a + uECC_vli_set(Y1, t4, num_words); + 80068b8: a902 add r1, sp, #8 + 80068ba: 4648 mov r0, r9 + 80068bc: f7fe ff2d bl 800571a +} + 80068c0: b012 add sp, #72 ; 0x48 + 80068c2: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + uECC_vli_rshift1(X1, num_words); + 80068c6: 4641 mov r1, r8 + 80068c8: 4620 mov r0, r4 + 80068ca: f7fe ff47 bl 800575c + 80068ce: e7c7 b.n 8006860 + +080068d0 : + g_rng_function = rng_function; + 80068d0: 4b01 ldr r3, [pc, #4] ; (80068d8 ) + 80068d2: 6018 str r0, [r3, #0] +} + 80068d4: 4770 bx lr + 80068d6: bf00 nop + 80068d8: 2009e2a0 .word 0x2009e2a0 + +080068dc : +uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } + 80068dc: 4800 ldr r0, [pc, #0] ; (80068e0 ) + 80068de: 4770 bx lr + 80068e0: 0800e928 .word 0x0800e928 + +080068e4 : +uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } + 80068e4: 4800 ldr r0, [pc, #0] ; (80068e8 ) + 80068e6: 4770 bx lr + 80068e8: 0800e874 .word 0x0800e874 + +080068ec : + uECC_Curve curve) { + 80068ec: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 80068f0: 4605 mov r5, r0 + 80068f2: b098 sub sp, #96 ; 0x60 + 80068f4: 460f mov r7, r1 + 80068f6: 4614 mov r4, r2 + 80068f8: 2640 movs r6, #64 ; 0x40 + if (!uECC_generate_random_int(private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + 80068fa: f102 0824 add.w r8, r2, #36 ; 0x24 + 80068fe: f9b4 3002 ldrsh.w r3, [r4, #2] + 8006902: f113 021f adds.w r2, r3, #31 + 8006906: bf48 it mi + 8006908: f103 023e addmi.w r2, r3, #62 ; 0x3e + 800690c: f342 1247 sbfx r2, r2, #5, #8 + 8006910: 4641 mov r1, r8 + 8006912: 4668 mov r0, sp + 8006914: f7ff f920 bl 8005b58 + 8006918: b330 cbz r0, 8006968 + if (EccPoint_compute_public_key(public, private, curve)) { + 800691a: 4622 mov r2, r4 + 800691c: 4669 mov r1, sp + 800691e: a808 add r0, sp, #32 + 8006920: f7ff fd8f bl 8006442 + 8006924: b1f0 cbz r0, 8006964 + uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), private); + 8006926: f9b4 3002 ldrsh.w r3, [r4, #2] + 800692a: 1dd9 adds r1, r3, #7 + 800692c: bf48 it mi + 800692e: f103 010e addmi.w r1, r3, #14 + 8006932: 466a mov r2, sp + 8006934: 10c9 asrs r1, r1, #3 + 8006936: 4638 mov r0, r7 + 8006938: f7fe fff0 bl 800591c + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + 800693c: f994 1001 ldrsb.w r1, [r4, #1] + 8006940: aa08 add r2, sp, #32 + 8006942: 4628 mov r0, r5 + 8006944: f7fe ffea bl 800591c + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + 8006948: f994 1001 ldrsb.w r1, [r4, #1] + 800694c: f994 2000 ldrsb.w r2, [r4] + uECC_vli_nativeToBytes( + 8006950: ab08 add r3, sp, #32 + 8006952: 1868 adds r0, r5, r1 + 8006954: eb03 0282 add.w r2, r3, r2, lsl #2 + 8006958: f7fe ffe0 bl 800591c + return 1; + 800695c: 2001 movs r0, #1 +} + 800695e: b018 add sp, #96 ; 0x60 + 8006960: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8006964: 3e01 subs r6, #1 + 8006966: d1ca bne.n 80068fe + return 0; + 8006968: 2000 movs r0, #0 + 800696a: e7f8 b.n 800695e + +0800696c : + uECC_Curve curve) { + 800696c: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8006970: 461c mov r4, r3 + wordcount_t num_bytes = curve->num_bytes; + 8006972: f993 6001 ldrsb.w r6, [r3, #1] + wordcount_t num_words = curve->num_words; + 8006976: f993 9000 ldrsb.w r9, [r3] + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 800697a: f9b3 3002 ldrsh.w r3, [r3, #2] + uECC_Curve curve) { + 800697e: b0a6 sub sp, #152 ; 0x98 + 8006980: 4617 mov r7, r2 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006982: 1dda adds r2, r3, #7 + 8006984: bf48 it mi + 8006986: f103 020e addmi.w r2, r3, #14 + uECC_word_t *p2[2] = {private, tmp}; + 800698a: f10d 0818 add.w r8, sp, #24 + uECC_Curve curve) { + 800698e: 4605 mov r5, r0 + uECC_word_t *p2[2] = {private, tmp}; + 8006990: f10d 0a38 add.w sl, sp, #56 ; 0x38 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006994: 10d2 asrs r2, r2, #3 + 8006996: 4640 mov r0, r8 + uECC_word_t *p2[2] = {private, tmp}; + 8006998: f8cd 8010 str.w r8, [sp, #16] + 800699c: f8cd a014 str.w sl, [sp, #20] + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 80069a0: f7fe ffd0 bl 8005944 + uECC_vli_bytesToNative(public, public_key, num_bytes); + 80069a4: 4629 mov r1, r5 + 80069a6: 4632 mov r2, r6 + 80069a8: a816 add r0, sp, #88 ; 0x58 + 80069aa: f7fe ffcb bl 8005944 + uECC_vli_bytesToNative(public + num_words, public_key + num_bytes, num_bytes); + 80069ae: ab16 add r3, sp, #88 ; 0x58 + 80069b0: 19a9 adds r1, r5, r6 + 80069b2: eb03 0089 add.w r0, r3, r9, lsl #2 + 80069b6: 4632 mov r2, r6 + 80069b8: f7fe ffc4 bl 8005944 + carry = regularize_k(private, private, tmp, curve); + 80069bc: 4623 mov r3, r4 + 80069be: 4652 mov r2, sl + 80069c0: 4641 mov r1, r8 + 80069c2: 4640 mov r0, r8 + 80069c4: f7ff f929 bl 8005c1a + if (g_rng_function) { + 80069c8: 4b19 ldr r3, [pc, #100] ; (8006a30 ) + 80069ca: 681b ldr r3, [r3, #0] + carry = regularize_k(private, private, tmp, curve); + 80069cc: 4605 mov r5, r0 + if (g_rng_function) { + 80069ce: b163 cbz r3, 80069ea + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + 80069d0: ab26 add r3, sp, #152 ; 0x98 + 80069d2: eb03 0380 add.w r3, r3, r0, lsl #2 + 80069d6: 464a mov r2, r9 + 80069d8: f853 3c88 ldr.w r3, [r3, #-136] + 80069dc: 9303 str r3, [sp, #12] + 80069de: 4618 mov r0, r3 + 80069e0: 1d21 adds r1, r4, #4 + 80069e2: f7ff f8b9 bl 8005b58 + 80069e6: 9b03 ldr r3, [sp, #12] + 80069e8: b1f0 cbz r0, 8006a28 + EccPoint_mult(public, public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); + 80069ea: fab5 f185 clz r1, r5 + 80069ee: aa26 add r2, sp, #152 ; 0x98 + 80069f0: 0949 lsrs r1, r1, #5 + 80069f2: eb02 0181 add.w r1, r2, r1, lsl #2 + 80069f6: 8862 ldrh r2, [r4, #2] + 80069f8: 9401 str r4, [sp, #4] + 80069fa: 3201 adds r2, #1 + 80069fc: b212 sxth r2, r2 + 80069fe: 9200 str r2, [sp, #0] + 8006a00: f851 2c88 ldr.w r2, [r1, #-136] + 8006a04: a916 add r1, sp, #88 ; 0x58 + 8006a06: 4608 mov r0, r1 + 8006a08: f7ff fc50 bl 80062ac + uECC_vli_nativeToBytes(secret, num_bytes, public); + 8006a0c: aa16 add r2, sp, #88 ; 0x58 + 8006a0e: 4631 mov r1, r6 + 8006a10: 4638 mov r0, r7 + 8006a12: f7fe ff83 bl 800591c + return !EccPoint_isZero(public, curve); + 8006a16: 7821 ldrb r1, [r4, #0] + 8006a18: 0049 lsls r1, r1, #1 + 8006a1a: b249 sxtb r1, r1 + 8006a1c: 4610 mov r0, r2 + 8006a1e: f7fe fe43 bl 80056a8 + 8006a22: fab0 f080 clz r0, r0 + 8006a26: 0940 lsrs r0, r0, #5 +} + 8006a28: b026 add sp, #152 ; 0x98 + 8006a2a: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 8006a2e: bf00 nop + 8006a30: 2009e2a0 .word 0x2009e2a0 + +08006a34 : +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { + 8006a34: b530 push {r4, r5, lr} + for (i = 0; i < curve->num_bytes; ++i) { + 8006a36: 2400 movs r4, #0 + 8006a38: f992 5001 ldrsb.w r5, [r2, #1] + 8006a3c: b263 sxtb r3, r4 + 8006a3e: 429d cmp r5, r3 + 8006a40: dc08 bgt.n 8006a54 + compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); + 8006a42: eb00 0045 add.w r0, r0, r5, lsl #1 + 8006a46: f810 3c01 ldrb.w r3, [r0, #-1] + 8006a4a: f003 0301 and.w r3, r3, #1 + 8006a4e: 3302 adds r3, #2 + 8006a50: 700b strb r3, [r1, #0] +} + 8006a52: bd30 pop {r4, r5, pc} + compressed[i+1] = public_key[i]; + 8006a54: 5cc5 ldrb r5, [r0, r3] + 8006a56: 440b add r3, r1 + 8006a58: 3401 adds r4, #1 + 8006a5a: 705d strb r5, [r3, #1] + for (i = 0; i < curve->num_bytes; ++i) { + 8006a5c: e7ec b.n 8006a38 + +08006a5e : +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006a5e: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + uECC_word_t *y = point + curve->num_words; + 8006a62: f992 8000 ldrsb.w r8, [r2] +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006a66: b090 sub sp, #64 ; 0x40 + 8006a68: 4614 mov r4, r2 + 8006a6a: 4607 mov r7, r0 + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); + 8006a6c: f992 2001 ldrsb.w r2, [r2, #1] + uECC_word_t *y = point + curve->num_words; + 8006a70: eb0d 0588 add.w r5, sp, r8, lsl #2 +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006a74: 460e mov r6, r1 + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); + 8006a76: 1c41 adds r1, r0, #1 + 8006a78: 4668 mov r0, sp + 8006a7a: f7fe ff63 bl 8005944 + curve->x_side(y, point, curve); + 8006a7e: 4622 mov r2, r4 + 8006a80: f8d4 30ac ldr.w r3, [r4, #172] ; 0xac + 8006a84: 4669 mov r1, sp + 8006a86: 4628 mov r0, r5 + 8006a88: 4798 blx r3 + curve->mod_sqrt(y, curve); + 8006a8a: f8d4 30a8 ldr.w r3, [r4, #168] ; 0xa8 + 8006a8e: 4621 mov r1, r4 + 8006a90: 4628 mov r0, r5 + 8006a92: 4798 blx r3 + if ((y[0] & 0x01) != (compressed[0] & 0x01)) { + 8006a94: 783b ldrb r3, [r7, #0] + 8006a96: f85d 2028 ldr.w r2, [sp, r8, lsl #2] + 8006a9a: 4053 eors r3, r2 + 8006a9c: 07db lsls r3, r3, #31 + 8006a9e: d504 bpl.n 8006aaa + uECC_vli_sub(y, curve->p, y, curve->num_words); + 8006aa0: 462a mov r2, r5 + 8006aa2: 1d21 adds r1, r4, #4 + 8006aa4: 4628 mov r0, r5 + 8006aa6: f7fe ffd1 bl 8005a4c + uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); + 8006aaa: f994 1001 ldrsb.w r1, [r4, #1] + 8006aae: 466a mov r2, sp + 8006ab0: 4630 mov r0, r6 + 8006ab2: f7fe ff33 bl 800591c + uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); + 8006ab6: f994 1001 ldrsb.w r1, [r4, #1] + 8006aba: 462a mov r2, r5 + 8006abc: 1870 adds r0, r6, r1 + 8006abe: f7fe ff2d bl 800591c +} + 8006ac2: b010 add sp, #64 ; 0x40 + 8006ac4: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +08006ac8 : +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006ac8: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + if (EccPoint_isZero(point, curve)) { + 8006acc: 780d ldrb r5, [r1, #0] + wordcount_t num_words = curve->num_words; + 8006ace: f991 2000 ldrsb.w r2, [r1] +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006ad2: b092 sub sp, #72 ; 0x48 + 8006ad4: 460e mov r6, r1 + if (EccPoint_isZero(point, curve)) { + 8006ad6: 0069 lsls r1, r5, #1 + 8006ad8: b249 sxtb r1, r1 +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006ada: 4607 mov r7, r0 + wordcount_t num_words = curve->num_words; + 8006adc: 9201 str r2, [sp, #4] + if (EccPoint_isZero(point, curve)) { + 8006ade: f7fe fde3 bl 80056a8 + 8006ae2: 4604 mov r4, r0 + 8006ae4: bb80 cbnz r0, 8006b48 + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + 8006ae6: f106 0804 add.w r8, r6, #4 + 8006aea: 9a01 ldr r2, [sp, #4] + 8006aec: 4639 mov r1, r7 + 8006aee: 4640 mov r0, r8 + 8006af0: f7fe fe1f bl 8005732 + 8006af4: 2801 cmp r0, #1 + 8006af6: d11a bne.n 8006b2e + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + 8006af8: 9a01 ldr r2, [sp, #4] + 8006afa: 4640 mov r0, r8 + 8006afc: eb07 0182 add.w r1, r7, r2, lsl #2 + 8006b00: f7fe fe17 bl 8005732 + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + 8006b04: 2801 cmp r0, #1 + 8006b06: d112 bne.n 8006b2e + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + 8006b08: 4632 mov r2, r6 + 8006b0a: a802 add r0, sp, #8 + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + 8006b0c: f10d 0828 add.w r8, sp, #40 ; 0x28 + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + 8006b10: f7fe fee1 bl 80058d6 + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + 8006b14: f8d6 30ac ldr.w r3, [r6, #172] ; 0xac + 8006b18: 4632 mov r2, r6 + 8006b1a: 4639 mov r1, r7 + 8006b1c: 4640 mov r0, r8 + 8006b1e: 4798 blx r3 + for (i = num_words - 1; i >= 0; --i) { + 8006b20: 1e6b subs r3, r5, #1 + 8006b22: b25b sxtb r3, r3 + 8006b24: 061a lsls r2, r3, #24 + 8006b26: d506 bpl.n 8006b36 + return (diff == 0); + 8006b28: fab4 f484 clz r4, r4 + 8006b2c: 0964 lsrs r4, r4, #5 +} + 8006b2e: 4620 mov r0, r4 + 8006b30: b012 add sp, #72 ; 0x48 + 8006b32: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + diff |= (left[i] ^ right[i]); + 8006b36: aa02 add r2, sp, #8 + 8006b38: f858 1023 ldr.w r1, [r8, r3, lsl #2] + 8006b3c: f852 2023 ldr.w r2, [r2, r3, lsl #2] + 8006b40: 404a eors r2, r1 + 8006b42: 4314 orrs r4, r2 + for (i = num_words - 1; i >= 0; --i) { + 8006b44: 3b01 subs r3, #1 + 8006b46: e7ed b.n 8006b24 + return 0; + 8006b48: 2400 movs r4, #0 + 8006b4a: e7f0 b.n 8006b2e + +08006b4c : +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { + 8006b4c: b530 push {r4, r5, lr} + 8006b4e: 460c mov r4, r1 + 8006b50: b091 sub sp, #68 ; 0x44 + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8006b52: f991 2001 ldrsb.w r2, [r1, #1] +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { + 8006b56: 4605 mov r5, r0 + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8006b58: 4601 mov r1, r0 + 8006b5a: 4668 mov r0, sp + 8006b5c: f7fe fef2 bl 8005944 + public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); + 8006b60: f994 2001 ldrsb.w r2, [r4, #1] + 8006b64: f994 0000 ldrsb.w r0, [r4] + uECC_vli_bytesToNative( + 8006b68: 18a9 adds r1, r5, r2 + 8006b6a: eb0d 0080 add.w r0, sp, r0, lsl #2 + 8006b6e: f7fe fee9 bl 8005944 + return uECC_valid_point(public, curve); + 8006b72: 4621 mov r1, r4 + 8006b74: 4668 mov r0, sp + 8006b76: f7ff ffa7 bl 8006ac8 +} + 8006b7a: b011 add sp, #68 ; 0x44 + 8006b7c: bd30 pop {r4, r5, pc} + +08006b7e : +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006b7e: b570 push {r4, r5, r6, lr} + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006b80: f9b2 3002 ldrsh.w r3, [r2, #2] +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006b84: 4614 mov r4, r2 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006b86: 1dda adds r2, r3, #7 + 8006b88: bf48 it mi + 8006b8a: f103 020e addmi.w r2, r3, #14 +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006b8e: b098 sub sp, #96 ; 0x60 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006b90: 10d2 asrs r2, r2, #3 +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006b92: 460e mov r6, r1 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006b94: 4601 mov r1, r0 + 8006b96: 4668 mov r0, sp + 8006b98: f7fe fed4 bl 8005944 + if (uECC_vli_isZero(private, BITS_TO_WORDS(curve->num_n_bits))) { + 8006b9c: f9b4 3002 ldrsh.w r3, [r4, #2] + 8006ba0: f113 021f adds.w r2, r3, #31 + 8006ba4: bf48 it mi + 8006ba6: f103 023e addmi.w r2, r3, #62 ; 0x3e + 8006baa: f342 1147 sbfx r1, r2, #5, #8 + 8006bae: 4668 mov r0, sp + 8006bb0: f7fe fd7a bl 80056a8 + 8006bb4: b110 cbz r0, 8006bbc + return 0; + 8006bb6: 2000 movs r0, #0 +} + 8006bb8: b018 add sp, #96 ; 0x60 + 8006bba: bd70 pop {r4, r5, r6, pc} + if (uECC_vli_cmp(curve->n, private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + 8006bbc: 460a mov r2, r1 + 8006bbe: f104 0024 add.w r0, r4, #36 ; 0x24 + 8006bc2: 4669 mov r1, sp + 8006bc4: f7fe ffb0 bl 8005b28 + 8006bc8: 2801 cmp r0, #1 + 8006bca: 4605 mov r5, r0 + 8006bcc: d1f3 bne.n 8006bb6 + if (!EccPoint_compute_public_key(public, private, curve)) { + 8006bce: 4622 mov r2, r4 + 8006bd0: 4669 mov r1, sp + 8006bd2: a808 add r0, sp, #32 + 8006bd4: f7ff fc35 bl 8006442 + 8006bd8: 2800 cmp r0, #0 + 8006bda: d0ec beq.n 8006bb6 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + 8006bdc: f994 1001 ldrsb.w r1, [r4, #1] + 8006be0: aa08 add r2, sp, #32 + 8006be2: 4630 mov r0, r6 + 8006be4: f7fe fe9a bl 800591c + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + 8006be8: f994 1001 ldrsb.w r1, [r4, #1] + 8006bec: f994 2000 ldrsb.w r2, [r4] + uECC_vli_nativeToBytes( + 8006bf0: ab08 add r3, sp, #32 + 8006bf2: 1870 adds r0, r6, r1 + 8006bf4: eb03 0282 add.w r2, r3, r2, lsl #2 + 8006bf8: f7fe fe90 bl 800591c + return 1; + 8006bfc: 4628 mov r0, r5 + 8006bfe: e7db b.n 8006bb8 + +08006c00 : + uECC_Curve curve) { + 8006c00: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8006c04: b08a sub sp, #40 ; 0x28 + 8006c06: 4605 mov r5, r0 + 8006c08: f8dd 9048 ldr.w r9, [sp, #72] ; 0x48 + 8006c0c: 460e mov r6, r1 + 8006c0e: 4617 mov r7, r2 + 8006c10: 4698 mov r8, r3 + 8006c12: 2440 movs r4, #64 ; 0x40 + if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + 8006c14: f109 0a24 add.w sl, r9, #36 ; 0x24 + 8006c18: f9b9 3002 ldrsh.w r3, [r9, #2] + 8006c1c: f113 021f adds.w r2, r3, #31 + 8006c20: bf48 it mi + 8006c22: f103 023e addmi.w r2, r3, #62 ; 0x3e + 8006c26: f342 1247 sbfx r2, r2, #5, #8 + 8006c2a: 4651 mov r1, sl + 8006c2c: a802 add r0, sp, #8 + 8006c2e: f7fe ff93 bl 8005b58 + 8006c32: b150 cbz r0, 8006c4a + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, curve)) { + 8006c34: e9cd 8900 strd r8, r9, [sp] + 8006c38: ab02 add r3, sp, #8 + 8006c3a: 463a mov r2, r7 + 8006c3c: 4631 mov r1, r6 + 8006c3e: 4628 mov r0, r5 + 8006c40: f7ff fc2a bl 8006498 + 8006c44: b928 cbnz r0, 8006c52 + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8006c46: 3c01 subs r4, #1 + 8006c48: d1e6 bne.n 8006c18 + return 0; + 8006c4a: 2000 movs r0, #0 +} + 8006c4c: b00a add sp, #40 ; 0x28 + 8006c4e: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + return 1; + 8006c52: 2001 movs r0, #1 + 8006c54: e7fa b.n 8006c4c + +08006c56 : +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve) { + 8006c56: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8006c5a: b091 sub sp, #68 ; 0x44 + 8006c5c: 4693 mov fp, r2 + uint8_t *K = hash_context->tmp; + uint8_t *V = K + hash_context->result_size; + wordcount_t num_bytes = curve->num_bytes; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006c5e: 9a1b ldr r2, [sp, #108] ; 0x6c + 8006c60: f9b2 8002 ldrsh.w r8, [r2, #2] + uint8_t *V = K + hash_context->result_size; + 8006c64: e9d3 6504 ldrd r6, r5, [r3, #16] + uECC_Curve curve) { + 8006c68: 461c mov r4, r3 + wordcount_t num_bytes = curve->num_bytes; + 8006c6a: 9b1b ldr r3, [sp, #108] ; 0x6c + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006c6c: f118 071f adds.w r7, r8, #31 + 8006c70: bf48 it mi + 8006c72: f108 073e addmi.w r7, r8, #62 ; 0x3e + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t tries; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) { + 8006c76: 2200 movs r2, #0 + wordcount_t num_bytes = curve->num_bytes; + 8006c78: f993 3001 ldrsb.w r3, [r3, #1] + uECC_Curve curve) { + 8006c7c: 4681 mov r9, r0 + 8006c7e: 468a mov sl, r1 + uint8_t *V = K + hash_context->result_size; + 8006c80: 442e add r6, r5 + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006c82: f347 1747 sbfx r7, r7, #5, #8 + V[i] = 0x01; + 8006c86: 2001 movs r0, #1 + K[i] = 0; + 8006c88: 4694 mov ip, r2 + for (i = 0; i < hash_context->result_size; ++i) { + 8006c8a: 6921 ldr r1, [r4, #16] + 8006c8c: 4291 cmp r1, r2 + 8006c8e: f200 8086 bhi.w 8006d9e + } + + /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + 8006c92: 4629 mov r1, r5 + 8006c94: 4620 mov r0, r4 + 8006c96: 9303 str r3, [sp, #12] + 8006c98: f7fe fe74 bl 8005984 + V[hash_context->result_size] = 0x00; + 8006c9c: 6922 ldr r2, [r4, #16] + 8006c9e: 2100 movs r1, #0 + 8006ca0: 54b1 strb r1, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 8006ca2: 6922 ldr r2, [r4, #16] + 8006ca4: 4631 mov r1, r6 + 8006ca6: 3201 adds r2, #1 + 8006ca8: 4620 mov r0, r4 + 8006caa: f7fe fe8c bl 80059c6 + HMAC_update(hash_context, private_key, num_bytes); + 8006cae: 9b03 ldr r3, [sp, #12] + 8006cb0: 4649 mov r1, r9 + 8006cb2: 461a mov r2, r3 + 8006cb4: 4620 mov r0, r4 + 8006cb6: f7fe fe86 bl 80059c6 + HMAC_update(hash_context, message_hash, hash_size); + 8006cba: 465a mov r2, fp + 8006cbc: 4651 mov r1, sl + 8006cbe: 4620 mov r0, r4 + 8006cc0: f7fe fe81 bl 80059c6 + HMAC_finish(hash_context, K, K); + 8006cc4: 462a mov r2, r5 + 8006cc6: 4629 mov r1, r5 + 8006cc8: 4620 mov r0, r4 + 8006cca: f7fe fe7e bl 80059ca + + update_V(hash_context, K, V); + 8006cce: 4632 mov r2, r6 + 8006cd0: 4629 mov r1, r5 + 8006cd2: 4620 mov r0, r4 + 8006cd4: f7fe fea8 bl 8005a28 + + /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + 8006cd8: 4629 mov r1, r5 + 8006cda: 4620 mov r0, r4 + 8006cdc: f7fe fe52 bl 8005984 + V[hash_context->result_size] = 0x01; + 8006ce0: 6922 ldr r2, [r4, #16] + 8006ce2: 2101 movs r1, #1 + 8006ce4: 54b1 strb r1, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 8006ce6: 6922 ldr r2, [r4, #16] + 8006ce8: 4620 mov r0, r4 + 8006cea: 440a add r2, r1 + 8006cec: 4631 mov r1, r6 + 8006cee: f7fe fe6a bl 80059c6 + HMAC_update(hash_context, private_key, num_bytes); + 8006cf2: 9b03 ldr r3, [sp, #12] + 8006cf4: 4649 mov r1, r9 + 8006cf6: 461a mov r2, r3 + 8006cf8: 4620 mov r0, r4 + 8006cfa: f7fe fe64 bl 80059c6 + HMAC_update(hash_context, message_hash, hash_size); + 8006cfe: 465a mov r2, fp + 8006d00: 4651 mov r1, sl + 8006d02: 4620 mov r0, r4 + 8006d04: f7fe fe5f bl 80059c6 + HMAC_finish(hash_context, K, K); + 8006d08: 462a mov r2, r5 + 8006d0a: 4629 mov r1, r5 + 8006d0c: 4620 mov r0, r4 + 8006d0e: f7fe fe5c bl 80059ca + + update_V(hash_context, K, V); + 8006d12: 4632 mov r2, r6 + 8006d14: 4629 mov r1, r5 + 8006d16: 4620 mov r0, r4 + 8006d18: f7fe fe86 bl 8005a28 + wordcount_t T_bytes = 0; + for (;;) { + update_V(hash_context, K, V); + for (i = 0; i < hash_context->result_size; ++i) { + T_ptr[T_bytes++] = V[i]; + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + 8006d1c: 00bb lsls r3, r7, #2 + 8006d1e: 9304 str r3, [sp, #16] + goto filled; + } + } + } + filled: + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + 8006d20: 017b lsls r3, r7, #5 + 8006d22: 9305 str r3, [sp, #20] + uECC_word_t mask = (uECC_word_t)-1; + T[num_n_words - 1] &= + mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); + 8006d24: ebc8 1347 rsb r3, r8, r7, lsl #5 + 8006d28: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 8006d2c: b21b sxth r3, r3 + 8006d2e: fa22 f303 lsr.w r3, r2, r3 + 8006d32: 9306 str r3, [sp, #24] + 8006d34: 2340 movs r3, #64 ; 0x40 + 8006d36: 9303 str r3, [sp, #12] + T[num_n_words - 1] &= + 8006d38: 4417 add r7, r2 + 8006d3a: 446b add r3, sp + 8006d3c: eb03 0787 add.w r7, r3, r7, lsl #2 + wordcount_t T_bytes = 0; + 8006d40: 2300 movs r3, #0 + update_V(hash_context, K, V); + 8006d42: 4632 mov r2, r6 + 8006d44: 4629 mov r1, r5 + 8006d46: 4620 mov r0, r4 + 8006d48: 9307 str r3, [sp, #28] + 8006d4a: f7fe fe6d bl 8005a28 + for (i = 0; i < hash_context->result_size; ++i) { + 8006d4e: 6920 ldr r0, [r4, #16] + 8006d50: 9b07 ldr r3, [sp, #28] + 8006d52: 4631 mov r1, r6 + 8006d54: 4430 add r0, r6 + 8006d56: 461a mov r2, r3 + T_ptr[T_bytes++] = V[i]; + 8006d58: ab08 add r3, sp, #32 + 8006d5a: eb03 0c02 add.w ip, r3, r2 + for (i = 0; i < hash_context->result_size; ++i) { + 8006d5e: 4288 cmp r0, r1 + 8006d60: 4613 mov r3, r2 + 8006d62: f102 0201 add.w r2, r2, #1 + 8006d66: b252 sxtb r2, r2 + 8006d68: d0eb beq.n 8006d42 + T_ptr[T_bytes++] = V[i]; + 8006d6a: f811 3b01 ldrb.w r3, [r1], #1 + 8006d6e: f88c 3000 strb.w r3, [ip] + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + 8006d72: 9b04 ldr r3, [sp, #16] + 8006d74: 4293 cmp r3, r2 + 8006d76: dcef bgt.n 8006d58 + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + 8006d78: 9b05 ldr r3, [sp, #20] + 8006d7a: 4598 cmp r8, r3 + 8006d7c: db14 blt.n 8006da8 + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, T, signature, curve)) { + 8006d7e: 9b1b ldr r3, [sp, #108] ; 0x6c + 8006d80: 9301 str r3, [sp, #4] + 8006d82: 9b1a ldr r3, [sp, #104] ; 0x68 + 8006d84: 9300 str r3, [sp, #0] + 8006d86: 465a mov r2, fp + 8006d88: ab08 add r3, sp, #32 + 8006d8a: 4651 mov r1, sl + 8006d8c: 4648 mov r0, r9 + 8006d8e: f7ff fb83 bl 8006498 + 8006d92: b180 cbz r0, 8006db6 + return 1; + 8006d94: 2301 movs r3, #1 + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + } + return 0; +} + 8006d96: 4618 mov r0, r3 + 8006d98: b011 add sp, #68 ; 0x44 + 8006d9a: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + V[i] = 0x01; + 8006d9e: 54b0 strb r0, [r6, r2] + K[i] = 0; + 8006da0: f805 c002 strb.w ip, [r5, r2] + for (i = 0; i < hash_context->result_size; ++i) { + 8006da4: 3201 adds r2, #1 + 8006da6: e770 b.n 8006c8a + T[num_n_words - 1] &= + 8006da8: f857 3c20 ldr.w r3, [r7, #-32] + 8006dac: 9a06 ldr r2, [sp, #24] + 8006dae: 4013 ands r3, r2 + 8006db0: f847 3c20 str.w r3, [r7, #-32] + 8006db4: e7e3 b.n 8006d7e + 8006db6: 9007 str r0, [sp, #28] + HMAC_init(hash_context, K); + 8006db8: 4629 mov r1, r5 + 8006dba: 4620 mov r0, r4 + 8006dbc: f7fe fde2 bl 8005984 + V[hash_context->result_size] = 0x00; + 8006dc0: 6922 ldr r2, [r4, #16] + 8006dc2: 9b07 ldr r3, [sp, #28] + 8006dc4: 54b3 strb r3, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 8006dc6: 6922 ldr r2, [r4, #16] + 8006dc8: 4631 mov r1, r6 + 8006dca: 3201 adds r2, #1 + 8006dcc: 4620 mov r0, r4 + 8006dce: f7fe fdfa bl 80059c6 + HMAC_finish(hash_context, K, K); + 8006dd2: 462a mov r2, r5 + 8006dd4: 4629 mov r1, r5 + 8006dd6: 4620 mov r0, r4 + 8006dd8: f7fe fdf7 bl 80059ca + update_V(hash_context, K, V); + 8006ddc: 4632 mov r2, r6 + 8006dde: 4629 mov r1, r5 + 8006de0: 4620 mov r0, r4 + 8006de2: f7fe fe21 bl 8005a28 + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8006de6: 9b03 ldr r3, [sp, #12] + 8006de8: 3b01 subs r3, #1 + 8006dea: 9303 str r3, [sp, #12] + 8006dec: 9b07 ldr r3, [sp, #28] + 8006dee: d1a7 bne.n 8006d40 + 8006df0: e7d1 b.n 8006d96 + +08006df2 : + +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve) { + 8006df2: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8006df6: ed2d 8b02 vpush {d8} + 8006dfa: b0fb sub sp, #492 ; 0x1ec + 8006dfc: 461c mov r4, r3 + 8006dfe: 9d86 ldr r5, [sp, #536] ; 0x218 + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006e00: f9b5 3002 ldrsh.w r3, [r5, #2] + wordcount_t num_words = curve->num_words; + 8006e04: f995 8000 ldrsb.w r8, [r5] + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006e08: f113 061f adds.w r6, r3, #31 + 8006e0c: bf48 it mi + 8006e0e: f103 063e addmi.w r6, r3, #62 ; 0x3e + 8006e12: f346 1647 sbfx r6, r6, #5, #8 + + rx[num_n_words - 1] = 0; + 8006e16: f106 3aff add.w sl, r6, #4294967295 ; 0xffffffff + uECC_Curve curve) { + 8006e1a: 4691 mov r9, r2 + rx[num_n_words - 1] = 0; + 8006e1c: aa22 add r2, sp, #136 ; 0x88 + 8006e1e: 2300 movs r3, #0 + 8006e20: f842 302a str.w r3, [r2, sl, lsl #2] + r[num_n_words - 1] = 0; + 8006e24: aa7a add r2, sp, #488 ; 0x1e8 + 8006e26: eb02 028a add.w r2, r2, sl, lsl #2 + uECC_Curve curve) { + 8006e2a: 4607 mov r7, r0 + r[num_n_words - 1] = 0; + 8006e2c: f842 3cc0 str.w r3, [r2, #-192] + s[num_n_words - 1] = 0; + 8006e30: f842 3ca0 str.w r3, [r2, #-160] + uECC_Curve curve) { + 8006e34: ee08 1a90 vmov s17, r1 + + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8006e38: f995 2001 ldrsb.w r2, [r5, #1] + 8006e3c: 4601 mov r1, r0 + 8006e3e: a85a add r0, sp, #360 ; 0x168 + 8006e40: f7fe fd80 bl 8005944 + uECC_vli_bytesToNative( + public + num_words, public_key + curve->num_bytes, curve->num_bytes); + 8006e44: ea4f 0388 mov.w r3, r8, lsl #2 + 8006e48: f995 2001 ldrsb.w r2, [r5, #1] + 8006e4c: 9304 str r3, [sp, #16] + uECC_vli_bytesToNative( + 8006e4e: ab5a add r3, sp, #360 ; 0x168 + 8006e50: eb03 0388 add.w r3, r3, r8, lsl #2 + 8006e54: 4618 mov r0, r3 + 8006e56: 18b9 adds r1, r7, r2 + 8006e58: ee08 3a10 vmov s16, r3 + 8006e5c: f7fe fd72 bl 8005944 + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + 8006e60: 4621 mov r1, r4 + 8006e62: f995 2001 ldrsb.w r2, [r5, #1] + 8006e66: a84a add r0, sp, #296 ; 0x128 + 8006e68: f7fe fd6c bl 8005944 + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + 8006e6c: f995 2001 ldrsb.w r2, [r5, #1] + 8006e70: a852 add r0, sp, #328 ; 0x148 + 8006e72: 18a1 adds r1, r4, r2 + 8006e74: f7fe fd66 bl 8005944 + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + 8006e78: 4641 mov r1, r8 + 8006e7a: a84a add r0, sp, #296 ; 0x128 + 8006e7c: f7fe fc14 bl 80056a8 + 8006e80: 2300 movs r3, #0 + 8006e82: 4604 mov r4, r0 + 8006e84: 2800 cmp r0, #0 + 8006e86: f040 812b bne.w 80070e0 + 8006e8a: a852 add r0, sp, #328 ; 0x148 + 8006e8c: f7fe fc0c bl 80056a8 + 8006e90: 9002 str r0, [sp, #8] + 8006e92: 2800 cmp r0, #0 + 8006e94: f040 8126 bne.w 80070e4 + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 8006e98: f105 0b24 add.w fp, r5, #36 ; 0x24 + 8006e9c: 4632 mov r2, r6 + 8006e9e: a94a add r1, sp, #296 ; 0x128 + 8006ea0: 4658 mov r0, fp + 8006ea2: f7fe fc46 bl 8005732 + 8006ea6: 2801 cmp r0, #1 + 8006ea8: f040 811e bne.w 80070e8 + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + 8006eac: 4632 mov r2, r6 + 8006eae: a952 add r1, sp, #328 ; 0x148 + 8006eb0: 4658 mov r0, fp + 8006eb2: f7fe fc3e bl 8005732 + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 8006eb6: 2801 cmp r0, #1 + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + 8006eb8: 9005 str r0, [sp, #20] + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 8006eba: f040 8115 bne.w 80070e8 + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + 8006ebe: ac1a add r4, sp, #104 ; 0x68 + u1[num_n_words - 1] = 0; + 8006ec0: af0a add r7, sp, #40 ; 0x28 + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + 8006ec2: 4633 mov r3, r6 + 8006ec4: 465a mov r2, fp + 8006ec6: 4620 mov r0, r4 + 8006ec8: f7ff f84e bl 8005f68 + u1[num_n_words - 1] = 0; + 8006ecc: 9b02 ldr r3, [sp, #8] + 8006ece: f847 302a str.w r3, [r7, sl, lsl #2] + bits2int(u1, message_hash, hash_size, curve); + 8006ed2: 464a mov r2, r9 + 8006ed4: 4638 mov r0, r7 + 8006ed6: ee18 1a90 vmov r1, s17 + 8006eda: 462b mov r3, r5 + 8006edc: f7fe fddd bl 8005a9a + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + 8006ee0: 4639 mov r1, r7 + 8006ee2: 4638 mov r0, r7 + 8006ee4: 465b mov r3, fp + 8006ee6: 4622 mov r2, r4 + 8006ee8: 9600 str r6, [sp, #0] + 8006eea: f7fe fc44 bl 8005776 + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, public, num_words); + 8006eee: f50d 7ad4 add.w sl, sp, #424 ; 0x1a8 + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + 8006ef2: 465b mov r3, fp + 8006ef4: 4622 mov r2, r4 + 8006ef6: a94a add r1, sp, #296 ; 0x128 + 8006ef8: a812 add r0, sp, #72 ; 0x48 + 8006efa: 9600 str r6, [sp, #0] + 8006efc: f7fe fc3b bl 8005776 + uECC_vli_set(sum, public, num_words); + 8006f00: 4642 mov r2, r8 + 8006f02: 4650 mov r0, sl + 8006f04: a95a add r1, sp, #360 ; 0x168 + 8006f06: f7fe fc08 bl 800571a + uECC_vli_set(sum + num_words, public + num_words, num_words); + 8006f0a: 9b04 ldr r3, [sp, #16] + 8006f0c: eb0a 0903 add.w r9, sl, r3 + 8006f10: ee18 1a10 vmov r1, s16 + 8006f14: 4648 mov r0, r9 + 8006f16: f7fe fc00 bl 800571a + uECC_vli_set(tx, curve->G, num_words); + 8006f1a: f105 0344 add.w r3, r5, #68 ; 0x44 + 8006f1e: 4619 mov r1, r3 + 8006f20: a832 add r0, sp, #200 ; 0xc8 + 8006f22: 9303 str r3, [sp, #12] + 8006f24: f7fe fbf9 bl 800571a + uECC_vli_set(ty, curve->G + num_words, num_words); + 8006f28: e9dd 3103 ldrd r3, r1, [sp, #12] + 8006f2c: a83a add r0, sp, #232 ; 0xe8 + 8006f2e: 1859 adds r1, r3, r1 + 8006f30: f7fe fbf3 bl 800571a + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + 8006f34: 1d2b adds r3, r5, #4 + 8006f36: ee08 3a10 vmov s16, r3 + 8006f3a: 4651 mov r1, sl + 8006f3c: aa32 add r2, sp, #200 ; 0xc8 + 8006f3e: 4620 mov r0, r4 + 8006f40: f7ff f8c0 bl 80060c4 + XYcZ_add(tx, ty, sum, sum + num_words, curve); + 8006f44: 464b mov r3, r9 + 8006f46: 4652 mov r2, sl + 8006f48: a93a add r1, sp, #232 ; 0xe8 + 8006f4a: a832 add r0, sp, #200 ; 0xc8 + 8006f4c: 9500 str r5, [sp, #0] + 8006f4e: f7ff f94d bl 80061ec + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + 8006f52: ee18 2a10 vmov r2, s16 + 8006f56: 4643 mov r3, r8 + 8006f58: 4621 mov r1, r4 + 8006f5a: 4620 mov r0, r4 + 8006f5c: f7ff f804 bl 8005f68 + apply_z(sum, sum + num_words, z, curve); + 8006f60: 462b mov r3, r5 + 8006f62: 4649 mov r1, r9 + 8006f64: 4650 mov r0, sl + 8006f66: 4622 mov r2, r4 + 8006f68: f7fe fcb9 bl 80058de + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + 8006f6c: 9a02 ldr r2, [sp, #8] + 8006f6e: 9206 str r2, [sp, #24] + points[1] = curve->G; + 8006f70: 9a03 ldr r2, [sp, #12] + 8006f72: 9207 str r2, [sp, #28] + points[2] = public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 8006f74: 4631 mov r1, r6 + points[2] = public; + 8006f76: aa5a add r2, sp, #360 ; 0x168 + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 8006f78: 4638 mov r0, r7 + points[3] = sum; + 8006f7a: e9cd 2a08 strd r2, sl, [sp, #32] + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 8006f7e: f7fe fbac bl 80056da + 8006f82: 4631 mov r1, r6 + 8006f84: 4682 mov sl, r0 + 8006f86: a812 add r0, sp, #72 ; 0x48 + 8006f88: f7fe fba7 bl 80056da + return (a > b ? a : b); + 8006f8c: 4550 cmp r0, sl + 8006f8e: bfb8 it lt + 8006f90: 4650 movlt r0, sl + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8006f92: fa1f f980 uxth.w r9, r0 + 8006f96: f109 31ff add.w r1, r9, #4294967295 ; 0xffffffff + 8006f9a: b209 sxth r1, r1 + 8006f9c: 4638 mov r0, r7 + 8006f9e: 9103 str r1, [sp, #12] + 8006fa0: f7fe fb91 bl 80056c6 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 8006fa4: 9903 ldr r1, [sp, #12] + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8006fa6: 1e07 subs r7, r0, #0 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 8006fa8: a812 add r0, sp, #72 ; 0x48 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8006faa: bf18 it ne + 8006fac: 2701 movne r7, #1 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 8006fae: f7fe fb8a bl 80056c6 + 8006fb2: 2800 cmp r0, #0 + 8006fb4: bf14 ite ne + 8006fb6: 2002 movne r0, #2 + 8006fb8: 2000 moveq r0, #0 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8006fba: ab06 add r3, sp, #24 + 8006fbc: 4307 orrs r7, r0 + uECC_vli_set(rx, point, num_words); + 8006fbe: 4642 mov r2, r8 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8006fc0: f853 1027 ldr.w r1, [r3, r7, lsl #2] + uECC_vli_set(rx, point, num_words); + 8006fc4: a822 add r0, sp, #136 ; 0x88 + 8006fc6: f7fe fba8 bl 800571a + uECC_vli_set(ry, point + num_words, num_words); + 8006fca: 9b04 ldr r3, [sp, #16] + 8006fcc: f10d 0aa8 add.w sl, sp, #168 ; 0xa8 + 8006fd0: 4419 add r1, r3 + 8006fd2: 4650 mov r0, sl + 8006fd4: f7fe fba1 bl 800571a + uECC_vli_clear(z, num_words); + 8006fd8: 4641 mov r1, r8 + 8006fda: 4620 mov r0, r4 + 8006fdc: f7fe fb5e bl 800569c + z[0] = 1; + 8006fe0: 9b05 ldr r3, [sp, #20] + 8006fe2: 6023 str r3, [r4, #0] + + for (i = num_bits - 2; i >= 0; --i) { + 8006fe4: f1a9 0902 sub.w r9, r9, #2 + 8006fe8: ab22 add r3, sp, #136 ; 0x88 + 8006fea: fa0f f989 sxth.w r9, r9 + 8006fee: 9303 str r3, [sp, #12] + 8006ff0: f1b9 0f00 cmp.w r9, #0 + 8006ff4: da26 bge.n 8007044 + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + 8006ff6: ee18 2a10 vmov r2, s16 + 8006ffa: 4643 mov r3, r8 + 8006ffc: 4621 mov r1, r4 + 8006ffe: 4620 mov r0, r4 + 8007000: f7fe ffb2 bl 8005f68 + apply_z(rx, ry, z, curve); + 8007004: 9803 ldr r0, [sp, #12] + 8007006: 462b mov r3, r5 + 8007008: 4622 mov r2, r4 + 800700a: 4651 mov r1, sl + 800700c: f7fe fc67 bl 80058de + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + 8007010: 9903 ldr r1, [sp, #12] + 8007012: 4632 mov r2, r6 + 8007014: 4658 mov r0, fp + 8007016: f7fe fb8c bl 8005732 + 800701a: 2801 cmp r0, #1 + 800701c: d003 beq.n 8007026 + uECC_vli_sub(rx, rx, curve->n, num_n_words); + 800701e: 465a mov r2, fp + 8007020: 4608 mov r0, r1 + 8007022: f7fe fd13 bl 8005a4c + for (i = num_words - 1; i >= 0; --i) { + 8007026: f108 33ff add.w r3, r8, #4294967295 ; 0xffffffff + 800702a: b25b sxtb r3, r3 + diff |= (left[i] ^ right[i]); + 800702c: a94a add r1, sp, #296 ; 0x128 + for (i = num_words - 1; i >= 0; --i) { + 800702e: 061a lsls r2, r3, #24 + 8007030: d54b bpl.n 80070ca + return (diff == 0); + 8007032: 9b02 ldr r3, [sp, #8] + 8007034: fab3 f083 clz r0, r3 + 8007038: 0940 lsrs r0, r0, #5 + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words)); +} + 800703a: b07b add sp, #492 ; 0x1ec + 800703c: ecbd 8b02 vpop {d8} + 8007040: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + curve->double_jacobian(rx, ry, z, curve); + 8007044: 462b mov r3, r5 + 8007046: 4622 mov r2, r4 + 8007048: f8d5 70a4 ldr.w r7, [r5, #164] ; 0xa4 + 800704c: 9803 ldr r0, [sp, #12] + 800704e: 4651 mov r1, sl + 8007050: 47b8 blx r7 + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + 8007052: 4649 mov r1, r9 + 8007054: a80a add r0, sp, #40 ; 0x28 + 8007056: f7fe fb36 bl 80056c6 + 800705a: 4649 mov r1, r9 + 800705c: 1e07 subs r7, r0, #0 + 800705e: a812 add r0, sp, #72 ; 0x48 + 8007060: bf18 it ne + 8007062: 2701 movne r7, #1 + 8007064: f7fe fb2f bl 80056c6 + 8007068: 2800 cmp r0, #0 + 800706a: bf14 ite ne + 800706c: 2002 movne r0, #2 + 800706e: 2000 moveq r0, #0 + 8007070: 4307 orrs r7, r0 + point = points[index]; + 8007072: ab06 add r3, sp, #24 + 8007074: f853 1027 ldr.w r1, [r3, r7, lsl #2] + if (point) { + 8007078: b311 cbz r1, 80070c0 + uECC_vli_set(tx, point, num_words); + 800707a: 4642 mov r2, r8 + 800707c: a832 add r0, sp, #200 ; 0xc8 + 800707e: f7fe fb4c bl 800571a + uECC_vli_set(ty, point + num_words, num_words); + 8007082: 9b04 ldr r3, [sp, #16] + 8007084: a83a add r0, sp, #232 ; 0xe8 + 8007086: 4419 add r1, r3 + 8007088: f7fe fb47 bl 800571a + apply_z(tx, ty, z, curve); + 800708c: 4601 mov r1, r0 + 800708e: 462b mov r3, r5 + 8007090: 4622 mov r2, r4 + 8007092: a832 add r0, sp, #200 ; 0xc8 + 8007094: f7fe fc23 bl 80058de + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + 8007098: ee18 3a10 vmov r3, s16 + 800709c: 9903 ldr r1, [sp, #12] + 800709e: aa32 add r2, sp, #200 ; 0xc8 + 80070a0: a842 add r0, sp, #264 ; 0x108 + 80070a2: f7ff f80f bl 80060c4 + XYcZ_add(tx, ty, rx, ry, curve); + 80070a6: 9a03 ldr r2, [sp, #12] + 80070a8: 9500 str r5, [sp, #0] + 80070aa: 4653 mov r3, sl + 80070ac: a93a add r1, sp, #232 ; 0xe8 + 80070ae: a832 add r0, sp, #200 ; 0xc8 + 80070b0: f7ff f89c bl 80061ec + uECC_vli_modMult_fast(z, z, tz, curve); + 80070b4: 462b mov r3, r5 + 80070b6: aa42 add r2, sp, #264 ; 0x108 + 80070b8: 4621 mov r1, r4 + 80070ba: 4620 mov r0, r4 + 80070bc: f7fe fbfb bl 80058b6 + for (i = num_bits - 2; i >= 0; --i) { + 80070c0: f109 39ff add.w r9, r9, #4294967295 ; 0xffffffff + 80070c4: fa0f f989 sxth.w r9, r9 + 80070c8: e792 b.n 8006ff0 + diff |= (left[i] ^ right[i]); + 80070ca: 9a03 ldr r2, [sp, #12] + 80070cc: f851 0023 ldr.w r0, [r1, r3, lsl #2] + 80070d0: f852 2023 ldr.w r2, [r2, r3, lsl #2] + 80070d4: 4042 eors r2, r0 + 80070d6: 9802 ldr r0, [sp, #8] + 80070d8: 4310 orrs r0, r2 + 80070da: 9002 str r0, [sp, #8] + for (i = num_words - 1; i >= 0; --i) { + 80070dc: 3b01 subs r3, #1 + 80070de: e7a6 b.n 800702e + return 0; + 80070e0: 4618 mov r0, r3 + 80070e2: e7aa b.n 800703a + 80070e4: 4620 mov r0, r4 + 80070e6: e7a8 b.n 800703a + 80070e8: 9802 ldr r0, [sp, #8] + 80070ea: e7a6 b.n 800703a + +080070ec : +const uint32_t MSIRangeTable[12] = {100000, 200000, 400000, 800000, 1000000, 2000000, \ + 4000000, 8000000, 16000000, 24000000, 32000000, 48000000}; +uint32_t SystemCoreClock; + +// TODO: cleanup HAL stuff to not use this +uint32_t HAL_GetTick(void) { return 53; } + 80070ec: 2035 movs r0, #53 ; 0x35 + 80070ee: 4770 bx lr + +080070f0 : +uint32_t uwTickPrio = 0; /* (1UL << __NVIC_PRIO_BITS); * Invalid priority */ + +// unwanted junk from stm32l4xx_hal_rcc.c +HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority) { return 0; } + 80070f0: 2000 movs r0, #0 + 80070f2: 4770 bx lr + +080070f4 : + * or PWR_REGULATOR_VOLTAGE_SCALE1_BOOST when applicable) + */ +uint32_t HAL_PWREx_GetVoltageRange(void) +{ +#if defined(PWR_CR5_R1MODE) + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 80070f4: 4b07 ldr r3, [pc, #28] ; (8007114 ) + 80070f6: 6818 ldr r0, [r3, #0] + 80070f8: f400 60c0 and.w r0, r0, #1536 ; 0x600 + 80070fc: f5b0 6f80 cmp.w r0, #1024 ; 0x400 + 8007100: d006 beq.n 8007110 + { + return PWR_REGULATOR_VOLTAGE_SCALE2; + } + else if (READ_BIT(PWR->CR5, PWR_CR5_R1MODE) == PWR_CR5_R1MODE) + 8007102: f8d3 0080 ldr.w r0, [r3, #128] ; 0x80 + { + /* PWR_CR5_R1MODE bit set means that Range 1 Boost is disabled */ + return PWR_REGULATOR_VOLTAGE_SCALE1; + 8007106: f410 7080 ands.w r0, r0, #256 ; 0x100 + 800710a: bf18 it ne + 800710c: f44f 7000 movne.w r0, #512 ; 0x200 + return PWR_REGULATOR_VOLTAGE_SCALE1_BOOST; + } +#else + return (PWR->CR1 & PWR_CR1_VOS); +#endif +} + 8007110: 4770 bx lr + 8007112: bf00 nop + 8007114: 40007000 .word 0x40007000 + +08007118 : + uint32_t wait_loop_index; + + assert_param(IS_PWR_VOLTAGE_SCALING_RANGE(VoltageScaling)); + +#if defined(PWR_CR5_R1MODE) + if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1_BOOST) + 8007118: 4b29 ldr r3, [pc, #164] ; (80071c0 ) + { + /* If current range is range 2 */ + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 800711a: 681a ldr r2, [r3, #0] + if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1_BOOST) + 800711c: bb30 cbnz r0, 800716c + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 800711e: f402 62c0 and.w r2, r2, #1536 ; 0x600 + 8007122: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + { + /* Make sure Range 1 Boost is enabled */ + CLEAR_BIT(PWR->CR5, PWR_CR5_R1MODE); + 8007126: f8d3 2080 ldr.w r2, [r3, #128] ; 0x80 + 800712a: f422 7280 bic.w r2, r2, #256 ; 0x100 + 800712e: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007132: d11a bne.n 800716a + + /* Set Range 1 */ + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE1); + 8007134: 681a ldr r2, [r3, #0] + 8007136: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 800713a: f442 7200 orr.w r2, r2, #512 ; 0x200 + 800713e: 601a str r2, [r3, #0] + + /* Wait until VOSF is cleared */ + wait_loop_index = ((PWR_FLAG_SETTING_DELAY_US * SystemCoreClock) / 1000000U) + 1; + 8007140: 4a20 ldr r2, [pc, #128] ; (80071c4 ) + 8007142: 6812 ldr r2, [r2, #0] + 8007144: 2132 movs r1, #50 ; 0x32 + 8007146: 434a muls r2, r1 + 8007148: 491f ldr r1, [pc, #124] ; (80071c8 ) + 800714a: fbb2 f2f1 udiv r2, r2, r1 + 800714e: 3201 adds r2, #1 + while ((HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) && (wait_loop_index != 0U)) + 8007150: 6959 ldr r1, [r3, #20] + 8007152: 0549 lsls r1, r1, #21 + 8007154: d500 bpl.n 8007158 + 8007156: b922 cbnz r2, 8007162 + { + wait_loop_index--; + } + if (HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) + 8007158: 695b ldr r3, [r3, #20] + 800715a: 0558 lsls r0, r3, #21 + 800715c: d403 bmi.n 8007166 + /* No need to wait for VOSF to be cleared for this transition */ + } + } +#endif + + return HAL_OK; + 800715e: 2000 movs r0, #0 +} + 8007160: 4770 bx lr + wait_loop_index--; + 8007162: 3a01 subs r2, #1 + 8007164: e7f4 b.n 8007150 + return HAL_TIMEOUT; + 8007166: 2003 movs r0, #3 + 8007168: 4770 bx lr + CLEAR_BIT(PWR->CR5, PWR_CR5_R1MODE); + 800716a: 4770 bx lr + else if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1) + 800716c: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8007170: d11f bne.n 80071b2 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007172: f402 62c0 and.w r2, r2, #1536 ; 0x600 + 8007176: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + SET_BIT(PWR->CR5, PWR_CR5_R1MODE); + 800717a: f8d3 2080 ldr.w r2, [r3, #128] ; 0x80 + 800717e: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8007182: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007186: d1ea bne.n 800715e + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE1); + 8007188: 681a ldr r2, [r3, #0] + 800718a: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 800718e: f442 7200 orr.w r2, r2, #512 ; 0x200 + 8007192: 601a str r2, [r3, #0] + wait_loop_index = ((PWR_FLAG_SETTING_DELAY_US * SystemCoreClock) / 1000000U) + 1; + 8007194: 4a0b ldr r2, [pc, #44] ; (80071c4 ) + 8007196: 6812 ldr r2, [r2, #0] + 8007198: 2132 movs r1, #50 ; 0x32 + 800719a: 434a muls r2, r1 + 800719c: 490a ldr r1, [pc, #40] ; (80071c8 ) + 800719e: fbb2 f2f1 udiv r2, r2, r1 + 80071a2: 3201 adds r2, #1 + while ((HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) && (wait_loop_index != 0U)) + 80071a4: 6959 ldr r1, [r3, #20] + 80071a6: 0549 lsls r1, r1, #21 + 80071a8: d5d6 bpl.n 8007158 + 80071aa: 2a00 cmp r2, #0 + 80071ac: d0d4 beq.n 8007158 + wait_loop_index--; + 80071ae: 3a01 subs r2, #1 + 80071b0: e7f8 b.n 80071a4 + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE2); + 80071b2: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 80071b6: f442 6280 orr.w r2, r2, #1024 ; 0x400 + 80071ba: 601a str r2, [r3, #0] + 80071bc: e7cf b.n 800715e + 80071be: bf00 nop + 80071c0: 40007000 .word 0x40007000 + 80071c4: 2009e2a8 .word 0x2009e2a8 + 80071c8: 000f4240 .word 0x000f4240 + +080071cc : + +__weak void HAL_SDEx_DriveTransceiver_1_8V_Callback(FlagStatus status) +{ + // unused? +} + 80071cc: 4770 bx lr + ... + +080071d0 <__NVIC_SystemReset>: + 80071d0: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80071d4: 4905 ldr r1, [pc, #20] ; (80071ec <__NVIC_SystemReset+0x1c>) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80071d6: 4b06 ldr r3, [pc, #24] ; (80071f0 <__NVIC_SystemReset+0x20>) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80071d8: 68ca ldr r2, [r1, #12] + 80071da: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80071de: 4313 orrs r3, r2 + 80071e0: 60cb str r3, [r1, #12] + 80071e2: f3bf 8f4f dsb sy + __NOP(); + 80071e6: bf00 nop + for(;;) /* wait until reset */ + 80071e8: e7fd b.n 80071e6 <__NVIC_SystemReset+0x16> + 80071ea: bf00 nop + 80071ec: e000ed00 .word 0xe000ed00 + 80071f0: 05fa0004 .word 0x05fa0004 + +080071f4 : +{ + 80071f4: b510 push {r4, lr} + 80071f6: 3801 subs r0, #1 + 80071f8: 440a add r2, r1 + *(acc) ^= *(more); + 80071fa: f811 4b01 ldrb.w r4, [r1], #1 + 80071fe: f810 3f01 ldrb.w r3, [r0, #1]! + for(; len; len--, more++, acc++) { + 8007202: 4291 cmp r1, r2 + *(acc) ^= *(more); + 8007204: ea83 0304 eor.w r3, r3, r4 + 8007208: 7003 strb r3, [r0, #0] + for(; len; len--, more++, acc++) { + 800720a: d1f6 bne.n 80071fa + } +} + 800720c: bd10 pop {r4, pc} + ... + +08007210 : + +// se2_write1() +// + static bool +se2_write1(uint8_t cmd, uint8_t arg) +{ + 8007210: b51f push {r0, r1, r2, r3, r4, lr} + uint8_t data[3] = { cmd, 1, arg }; + 8007212: 2301 movs r3, #1 + 8007214: f88d 300d strb.w r3, [sp, #13] + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 8007218: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + uint8_t data[3] = { cmd, 1, arg }; + 800721c: f88d 000c strb.w r0, [sp, #12] + 8007220: f88d 100e strb.w r1, [sp, #14] + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 8007224: 9300 str r3, [sp, #0] + 8007226: aa03 add r2, sp, #12 + 8007228: 2303 movs r3, #3 + 800722a: 2136 movs r1, #54 ; 0x36 + 800722c: 4804 ldr r0, [pc, #16] ; (8007240 ) + 800722e: f004 fb7f bl 800b930 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 8007232: 3800 subs r0, #0 + 8007234: bf18 it ne + 8007236: 2001 movne r0, #1 + 8007238: b005 add sp, #20 + 800723a: f85d fb04 ldr.w pc, [sp], #4 + 800723e: bf00 nop + 8007240: 2009e3ec .word 0x2009e3ec + +08007244 : + +// se2_write2() +// + static bool +se2_write2(uint8_t cmd, uint8_t arg1, uint8_t arg2) +{ + 8007244: b51f push {r0, r1, r2, r3, r4, lr} + uint8_t data[4] = { cmd, 2, arg1, arg2 }; + 8007246: 2302 movs r3, #2 + 8007248: f88d 300d strb.w r3, [sp, #13] + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 800724c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + uint8_t data[4] = { cmd, 2, arg1, arg2 }; + 8007250: f88d 000c strb.w r0, [sp, #12] + 8007254: f88d 100e strb.w r1, [sp, #14] + 8007258: f88d 200f strb.w r2, [sp, #15] + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 800725c: 9300 str r3, [sp, #0] + 800725e: aa03 add r2, sp, #12 + 8007260: 2304 movs r3, #4 + 8007262: 2136 movs r1, #54 ; 0x36 + 8007264: 4804 ldr r0, [pc, #16] ; (8007278 ) + 8007266: f004 fb63 bl 800b930 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 800726a: 3800 subs r0, #0 + 800726c: bf18 it ne + 800726e: 2001 movne r0, #1 + 8007270: b005 add sp, #20 + 8007272: f85d fb04 ldr.w pc, [sp], #4 + 8007276: bf00 nop + 8007278: 2009e3ec .word 0x2009e3ec + +0800727c : + +// se2_write_n() +// + static bool +se2_write_n(uint8_t cmd, uint8_t *param1, const uint8_t *data_in, uint8_t len) +{ + 800727c: b5f0 push {r4, r5, r6, r7, lr} + 800727e: 460d mov r5, r1 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007280: 2d00 cmp r5, #0 + 8007282: bf14 ite ne + 8007284: 2403 movne r4, #3 + 8007286: 2402 moveq r4, #2 + 8007288: 441c add r4, r3 +{ + 800728a: 4611 mov r1, r2 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 800728c: f104 0207 add.w r2, r4, #7 +{ + 8007290: b083 sub sp, #12 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007292: f402 727e and.w r2, r2, #1016 ; 0x3f8 +{ + 8007296: af02 add r7, sp, #8 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007298: ebad 0d02 sub.w sp, sp, r2 + 800729c: ae02 add r6, sp, #8 + + *(p++) = cmd; + *(p++) = sizeof(data) - 2; + 800729e: f1a4 0202 sub.w r2, r4, #2 + *(p++) = cmd; + 80072a2: f88d 0008 strb.w r0, [sp, #8] + *(p++) = sizeof(data) - 2; + 80072a6: 7072 strb r2, [r6, #1] + if(param1) { + *(p++) = *param1; + 80072a8: bf1b ittet ne + 80072aa: 782a ldrbne r2, [r5, #0] + 80072ac: 70b2 strbne r2, [r6, #2] + *(p++) = sizeof(data) - 2; + 80072ae: f10d 000a addeq.w r0, sp, #10 + *(p++) = *param1; + 80072b2: f10d 000b addne.w r0, sp, #11 + } + if(len) { + 80072b6: b113 cbz r3, 80072be + memcpy(p, data_in, len); + 80072b8: 461a mov r2, r3 + 80072ba: f006 f9b3 bl 800d624 + } + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 80072be: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 80072c2: 9300 str r3, [sp, #0] + 80072c4: 4632 mov r2, r6 + 80072c6: 4623 mov r3, r4 + 80072c8: 2136 movs r1, #54 ; 0x36 + 80072ca: 4804 ldr r0, [pc, #16] ; (80072dc ) + 80072cc: f004 fb30 bl 800b930 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 80072d0: 3800 subs r0, #0 + 80072d2: bf18 it ne + 80072d4: 2001 movne r0, #1 + 80072d6: 3704 adds r7, #4 + 80072d8: 46bd mov sp, r7 + 80072da: bdf0 pop {r4, r5, r6, r7, pc} + 80072dc: 2009e3ec .word 0x2009e3ec + +080072e0 : + +// rng_for_uECC() +// + static int +rng_for_uECC(uint8_t *dest, unsigned size) +{ + 80072e0: b508 push {r3, lr} + 'dest' was filled with random data, or 0 if the random data could not be generated. + The filled-in values should be either truly random, or from a cryptographically-secure PRNG. + + typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); + */ + rng_buffer(dest, size); + 80072e2: f7fb fa35 bl 8002750 + + return 1; +} + 80072e6: 2001 movs r0, #1 + 80072e8: bd08 pop {r3, pc} + ... + +080072ec : +{ + 80072ec: b508 push {r3, lr} + 80072ee: 4602 mov r2, r0 + CALL_CHECK(se2_write_n(0x87, NULL, data, len)); + 80072f0: b2cb uxtb r3, r1 + 80072f2: 2087 movs r0, #135 ; 0x87 + 80072f4: 2100 movs r1, #0 + 80072f6: f7ff ffc1 bl 800727c + 80072fa: b118 cbz r0, 8007304 + 80072fc: 4802 ldr r0, [pc, #8] ; (8007308 ) + 80072fe: 21c1 movs r1, #193 ; 0xc1 + 8007300: f006 f9c6 bl 800d690 +} + 8007304: bd08 pop {r3, pc} + 8007306: bf00 nop + 8007308: 2009e390 .word 0x2009e390 + +0800730c : +{ + 800730c: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + HAL_StatusTypeDef rv = HAL_I2C_Master_Receive(&i2c_port, I2C_ADDR, rx, len, HAL_MAX_DELAY); + 8007310: f8df 9044 ldr.w r9, [pc, #68] ; 8007358 +{ + 8007314: 4604 mov r4, r0 + 8007316: 460d mov r5, r1 + 8007318: f44f 7696 mov.w r6, #300 ; 0x12c + HAL_StatusTypeDef rv = HAL_I2C_Master_Receive(&i2c_port, I2C_ADDR, rx, len, HAL_MAX_DELAY); + 800731c: b287 uxth r7, r0 + 800731e: f04f 38ff mov.w r8, #4294967295 ; 0xffffffff + 8007322: f8cd 8000 str.w r8, [sp] + 8007326: 463b mov r3, r7 + 8007328: 462a mov r2, r5 + 800732a: 2136 movs r1, #54 ; 0x36 + 800732c: 4648 mov r0, r9 + 800732e: f004 fbb3 bl 800ba98 + if(rv == HAL_OK) { + 8007332: b938 cbnz r0, 8007344 + if(rx[0] != len-1) { + 8007334: 782b ldrb r3, [r5, #0] + 8007336: 3c01 subs r4, #1 + 8007338: 42a3 cmp r3, r4 + 800733a: d10a bne.n 8007352 + return rx[1]; + 800733c: 7868 ldrb r0, [r5, #1] +} + 800733e: b003 add sp, #12 + 8007340: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + delay_ms(1); + 8007344: 2001 movs r0, #1 + 8007346: f7fc fad7 bl 80038f8 + for(int tries=0; tries<300; tries++) { + 800734a: 3e01 subs r6, #1 + 800734c: d1e9 bne.n 8007322 + return RC_NO_ACK; + 800734e: 200f movs r0, #15 + 8007350: e7f5 b.n 800733e + return RC_WRONG_SIZE; + 8007352: 201f movs r0, #31 + 8007354: e7f3 b.n 800733e + 8007356: bf00 nop + 8007358: 2009e3ec .word 0x2009e3ec + +0800735c : +{ + 800735c: b507 push {r0, r1, r2, lr} + return se2_read_n(2, rx); + 800735e: 2002 movs r0, #2 + 8007360: a901 add r1, sp, #4 + 8007362: f7ff ffd3 bl 800730c +} + 8007366: b003 add sp, #12 + 8007368: f85d fb04 ldr.w pc, [sp], #4 + +0800736c : +{ + 800736c: b507 push {r0, r1, r2, lr} + CALL_CHECK(se2_write_n(0x96, &page_num, data, 32)); + 800736e: 2320 movs r3, #32 +{ + 8007370: 460a mov r2, r1 + 8007372: f88d 0007 strb.w r0, [sp, #7] + CALL_CHECK(se2_write_n(0x96, &page_num, data, 32)); + 8007376: f10d 0107 add.w r1, sp, #7 + 800737a: 2096 movs r0, #150 ; 0x96 + 800737c: f7ff ff7e bl 800727c + 8007380: b118 cbz r0, 800738a + 8007382: 21cb movs r1, #203 ; 0xcb + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007384: 4805 ldr r0, [pc, #20] ; (800739c ) + 8007386: f006 f983 bl 800d690 + 800738a: f7ff ffe7 bl 800735c + 800738e: 28aa cmp r0, #170 ; 0xaa + 8007390: d001 beq.n 8007396 + 8007392: 21cd movs r1, #205 ; 0xcd + 8007394: e7f6 b.n 8007384 +} + 8007396: b003 add sp, #12 + 8007398: f85d fb04 ldr.w pc, [sp], #4 + 800739c: 2009e390 .word 0x2009e390 + +080073a0 : + ASSERT(pubkey_num < 2); + 80073a0: 2801 cmp r0, #1 +{ + 80073a2: b508 push {r3, lr} + ASSERT(pubkey_num < 2); + 80073a4: d902 bls.n 80073ac + 80073a6: 480a ldr r0, [pc, #40] ; (80073d0 ) + 80073a8: f7f9 fb4e bl 8000a48 + CALL_CHECK(se2_write1(0xcb, (wpe <<6) | pubkey_num)); + 80073ac: ea40 1181 orr.w r1, r0, r1, lsl #6 + 80073b0: b2c9 uxtb r1, r1 + 80073b2: 20cb movs r0, #203 ; 0xcb + 80073b4: f7ff ff2c bl 8007210 + 80073b8: b118 cbz r0, 80073c2 + 80073ba: 21d9 movs r1, #217 ; 0xd9 + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 80073bc: 4805 ldr r0, [pc, #20] ; (80073d4 ) + 80073be: f006 f967 bl 800d690 + 80073c2: f7ff ffcb bl 800735c + 80073c6: 28aa cmp r0, #170 ; 0xaa + 80073c8: d001 beq.n 80073ce + 80073ca: 21db movs r1, #219 ; 0xdb + 80073cc: e7f6 b.n 80073bc +} + 80073ce: bd08 pop {r3, pc} + 80073d0: 0800e3e0 .word 0x0800e3e0 + 80073d4: 2009e390 .word 0x2009e390 + +080073d8 : +{ + 80073d8: b570 push {r4, r5, r6, lr} + 80073da: b0dc sub sp, #368 ; 0x170 + 80073dc: 460d mov r5, r1 + 80073de: f88d 0007 strb.w r0, [sp, #7] + rng_buffer(chal, sizeof(chal)); + 80073e2: 2120 movs r1, #32 + 80073e4: a802 add r0, sp, #8 +{ + 80073e6: 4616 mov r6, r2 + 80073e8: 461c mov r4, r3 + rng_buffer(chal, sizeof(chal)); + 80073ea: f7fb f9b1 bl 8002750 + se2_write_buffer(chal, sizeof(chal)); + 80073ee: 2120 movs r1, #32 + 80073f0: a802 add r0, sp, #8 + 80073f2: f7ff ff7b bl 80072ec + CALL_CHECK(se2_write1(0xa5, (keynum<<5) | page_num)); + 80073f6: f89d 3007 ldrb.w r3, [sp, #7] + 80073fa: ea43 1146 orr.w r1, r3, r6, lsl #5 + 80073fe: b2c9 uxtb r1, r1 + 8007400: 20a5 movs r0, #165 ; 0xa5 + 8007402: f7ff ff05 bl 8007210 + 8007406: b118 cbz r0, 8007410 + 8007408: 21eb movs r1, #235 ; 0xeb + CHECK_RIGHT(se2_read_n(sizeof(check), check) == RC_SUCCESS); + 800740a: 481e ldr r0, [pc, #120] ; (8007484 ) + 800740c: f006 f940 bl 800d690 + 8007410: a912 add r1, sp, #72 ; 0x48 + 8007412: 2022 movs r0, #34 ; 0x22 + 8007414: f7ff ff7a bl 800730c + 8007418: 28aa cmp r0, #170 ; 0xaa + 800741a: d001 beq.n 8007420 + 800741c: 21ee movs r1, #238 ; 0xee + 800741e: e7f4 b.n 800740a + hmac_sha256_init(&ctx); + 8007420: a81b add r0, sp, #108 ; 0x6c + 8007422: f7fe f8bb bl 800559c + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 8007426: 4b18 ldr r3, [pc, #96] ; (8007488 ) + 8007428: 4918 ldr r1, [pc, #96] ; (800748c ) + 800742a: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 800742e: 33b0 adds r3, #176 ; 0xb0 + 8007430: 2aff cmp r2, #255 ; 0xff + 8007432: bf18 it ne + 8007434: 4619 movne r1, r3 + 8007436: a81b add r0, sp, #108 ; 0x6c + 8007438: 2208 movs r2, #8 + 800743a: 3160 adds r1, #96 ; 0x60 + 800743c: f7fe f8b4 bl 80055a8 + hmac_sha256_update(&ctx, data, 32); + 8007440: 4629 mov r1, r5 + 8007442: a81b add r0, sp, #108 ; 0x6c + 8007444: 2220 movs r2, #32 + 8007446: f7fe f8af bl 80055a8 + hmac_sha256_update(&ctx, chal, 32); + 800744a: a902 add r1, sp, #8 + 800744c: a81b add r0, sp, #108 ; 0x6c + 800744e: 2220 movs r2, #32 + 8007450: f7fe f8aa bl 80055a8 + hmac_sha256_update(&ctx, &page_num, 1); + 8007454: f10d 0107 add.w r1, sp, #7 + 8007458: a81b add r0, sp, #108 ; 0x6c + 800745a: 2201 movs r2, #1 + 800745c: f7fe f8a4 bl 80055a8 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 8007460: a81b add r0, sp, #108 ; 0x6c + 8007462: 490b ldr r1, [pc, #44] ; (8007490 ) + 8007464: 2202 movs r2, #2 + 8007466: f7fe f89f bl 80055a8 + hmac_sha256_final(&ctx, secret, expect); + 800746a: aa0a add r2, sp, #40 ; 0x28 + 800746c: 4621 mov r1, r4 + 800746e: a81b add r0, sp, #108 ; 0x6c + 8007470: f7fe f8b0 bl 80055d4 + return check_equal(expect, check+2, 32); + 8007474: 2220 movs r2, #32 + 8007476: f10d 014a add.w r1, sp, #74 ; 0x4a + 800747a: a80a add r0, sp, #40 ; 0x28 + 800747c: f7fb f919 bl 80026b2 +} + 8007480: b05c add sp, #368 ; 0x170 + 8007482: bd70 pop {r4, r5, r6, pc} + 8007484: 2009e390 .word 0x2009e390 + 8007488: 0801c000 .word 0x0801c000 + 800748c: 2009e2b0 .word 0x2009e2b0 + 8007490: 0800ea44 .word 0x0800ea44 + +08007494 : +{ + 8007494: b570 push {r4, r5, r6, lr} + 8007496: 4604 mov r4, r0 + 8007498: b08a sub sp, #40 ; 0x28 + 800749a: 460d mov r5, r1 + CALL_CHECK(se2_write1(0x69, page_num)); + 800749c: 4601 mov r1, r0 + 800749e: 2069 movs r0, #105 ; 0x69 +{ + 80074a0: 4616 mov r6, r2 + CALL_CHECK(se2_write1(0x69, page_num)); + 80074a2: f7ff feb5 bl 8007210 + 80074a6: b120 cbz r0, 80074b2 + 80074a8: f44f 7185 mov.w r1, #266 ; 0x10a + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 80074ac: 481c ldr r0, [pc, #112] ; (8007520 ) + 80074ae: f006 f8ef bl 800d690 + 80074b2: a901 add r1, sp, #4 + 80074b4: 2022 movs r0, #34 ; 0x22 + 80074b6: f7ff ff29 bl 800730c + 80074ba: 28aa cmp r0, #170 ; 0xaa + 80074bc: d002 beq.n 80074c4 + 80074be: f240 110d movw r1, #269 ; 0x10d + 80074c2: e7f3 b.n 80074ac + CHECK_RIGHT(rx[0] == 33); + 80074c4: f89d 3004 ldrb.w r3, [sp, #4] + 80074c8: 2b21 cmp r3, #33 ; 0x21 + 80074ca: d002 beq.n 80074d2 + 80074cc: f240 110f movw r1, #271 ; 0x10f + 80074d0: e7ec b.n 80074ac + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 80074d2: f89d 3005 ldrb.w r3, [sp, #5] + 80074d6: 2baa cmp r3, #170 ; 0xaa + 80074d8: d002 beq.n 80074e0 + 80074da: f44f 7188 mov.w r1, #272 ; 0x110 + 80074de: e7e5 b.n 80074ac + memcpy(data, rx+2, 32); + 80074e0: f10d 0306 add.w r3, sp, #6 + 80074e4: 462a mov r2, r5 + 80074e6: f10d 0126 add.w r1, sp, #38 ; 0x26 + 80074ea: f853 0b04 ldr.w r0, [r3], #4 + 80074ee: f842 0b04 str.w r0, [r2], #4 + 80074f2: 428b cmp r3, r1 + 80074f4: d1f9 bne.n 80074ea + if(!verify) return; + 80074f6: b186 cbz r6, 800751a + CHECK_RIGHT(se2_verify_page(page_num, data, 0, SE2_SECRETS->pairing)); + 80074f8: 4b0a ldr r3, [pc, #40] ; (8007524 ) + 80074fa: 4a0b ldr r2, [pc, #44] ; (8007528 ) + 80074fc: f893 10b0 ldrb.w r1, [r3, #176] ; 0xb0 + 8007500: 4b0a ldr r3, [pc, #40] ; (800752c ) + 8007502: 4620 mov r0, r4 + 8007504: 29ff cmp r1, #255 ; 0xff + 8007506: bf18 it ne + 8007508: 4613 movne r3, r2 + 800750a: 2200 movs r2, #0 + 800750c: 4629 mov r1, r5 + 800750e: f7ff ff63 bl 80073d8 + 8007512: b910 cbnz r0, 800751a + 8007514: f44f 718b mov.w r1, #278 ; 0x116 + 8007518: e7c8 b.n 80074ac +} + 800751a: b00a add sp, #40 ; 0x28 + 800751c: bd70 pop {r4, r5, r6, pc} + 800751e: bf00 nop + 8007520: 2009e390 .word 0x2009e390 + 8007524: 0801c000 .word 0x0801c000 + 8007528: 0801c0b0 .word 0x0801c0b0 + 800752c: 2009e2b0 .word 0x2009e2b0 + +08007530 : +{ + 8007530: b570 push {r4, r5, r6, lr} + 8007532: b0d6 sub sp, #344 ; 0x158 + 8007534: 461e mov r6, r3 + ASSERT((keynum == 0) || (keynum == 2)); + 8007536: f032 0302 bics.w r3, r2, #2 +{ + 800753a: 460c mov r4, r1 + 800753c: 4615 mov r5, r2 + 800753e: f88d 0007 strb.w r0, [sp, #7] + ASSERT((keynum == 0) || (keynum == 2)); + 8007542: d002 beq.n 800754a + 8007544: 4831 ldr r0, [pc, #196] ; (800760c ) + 8007546: f7f9 fa7f bl 8000a48 + CALL_CHECK(se2_write1(0x4b, (keynum << 6) | page_num)); + 800754a: f89d 1007 ldrb.w r1, [sp, #7] + 800754e: ea41 1182 orr.w r1, r1, r2, lsl #6 + 8007552: b2c9 uxtb r1, r1 + 8007554: 204b movs r0, #75 ; 0x4b + 8007556: f7ff fe5b bl 8007210 + 800755a: b120 cbz r0, 8007566 + 800755c: f44f 71b3 mov.w r1, #358 ; 0x166 + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 8007560: 482b ldr r0, [pc, #172] ; (8007610 ) + 8007562: f006 f895 bl 800d690 + 8007566: a90a add r1, sp, #40 ; 0x28 + 8007568: 202a movs r0, #42 ; 0x2a + 800756a: f7ff fecf bl 800730c + 800756e: 28aa cmp r0, #170 ; 0xaa + 8007570: d002 beq.n 8007578 + 8007572: f240 1169 movw r1, #361 ; 0x169 + 8007576: e7f3 b.n 8007560 + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 8007578: f89d 3029 ldrb.w r3, [sp, #41] ; 0x29 + 800757c: 2baa cmp r3, #170 ; 0xaa + 800757e: d002 beq.n 8007586 + 8007580: f240 116b movw r1, #363 ; 0x16b + 8007584: e7ec b.n 8007560 + memcpy(data, rx+2+8, 32); + 8007586: f10d 0332 add.w r3, sp, #50 ; 0x32 + 800758a: 4622 mov r2, r4 + 800758c: f10d 0152 add.w r1, sp, #82 ; 0x52 + 8007590: f853 0b04 ldr.w r0, [r3], #4 + 8007594: f842 0b04 str.w r0, [r2], #4 + 8007598: 428b cmp r3, r1 + 800759a: d1f9 bne.n 8007590 + hmac_sha256_init(&ctx); + 800759c: a815 add r0, sp, #84 ; 0x54 + 800759e: f7fd fffd bl 800559c + hmac_sha256_update(&ctx, chal, 8); + 80075a2: 2208 movs r2, #8 + 80075a4: f10d 012a add.w r1, sp, #42 ; 0x2a + 80075a8: a815 add r0, sp, #84 ; 0x54 + 80075aa: f7fd fffd bl 80055a8 + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 80075ae: 4b19 ldr r3, [pc, #100] ; (8007614 ) + 80075b0: 4919 ldr r1, [pc, #100] ; (8007618 ) + 80075b2: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80075b6: 33b0 adds r3, #176 ; 0xb0 + 80075b8: 2aff cmp r2, #255 ; 0xff + 80075ba: bf18 it ne + 80075bc: 4619 movne r1, r3 + 80075be: 3160 adds r1, #96 ; 0x60 + 80075c0: 2208 movs r2, #8 + 80075c2: a815 add r0, sp, #84 ; 0x54 + 80075c4: f7fd fff0 bl 80055a8 + hmac_sha256_update(&ctx, &page_num, 1); + 80075c8: 2201 movs r2, #1 + 80075ca: f10d 0107 add.w r1, sp, #7 + 80075ce: a815 add r0, sp, #84 ; 0x54 + 80075d0: f7fd ffea bl 80055a8 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 80075d4: 4911 ldr r1, [pc, #68] ; (800761c ) + 80075d6: 2202 movs r2, #2 + 80075d8: a815 add r0, sp, #84 ; 0x54 + 80075da: f7fd ffe5 bl 80055a8 + hmac_sha256_final(&ctx, secret, otp); + 80075de: aa02 add r2, sp, #8 + 80075e0: 4631 mov r1, r6 + 80075e2: a815 add r0, sp, #84 ; 0x54 + 80075e4: f7fd fff6 bl 80055d4 + xor_mixin(data, otp, 32); + 80075e8: 2220 movs r2, #32 + 80075ea: a902 add r1, sp, #8 + 80075ec: 4620 mov r0, r4 + 80075ee: f7ff fe01 bl 80071f4 + CHECK_RIGHT(se2_verify_page(page_num, data, keynum, secret)); + 80075f2: f89d 0007 ldrb.w r0, [sp, #7] + 80075f6: 4633 mov r3, r6 + 80075f8: 462a mov r2, r5 + 80075fa: 4621 mov r1, r4 + 80075fc: f7ff feec bl 80073d8 + 8007600: b910 cbnz r0, 8007608 + 8007602: f44f 71c0 mov.w r1, #384 ; 0x180 + 8007606: e7ab b.n 8007560 +} + 8007608: b056 add sp, #344 ; 0x158 + 800760a: bd70 pop {r4, r5, r6, pc} + 800760c: 0800e3e0 .word 0x0800e3e0 + 8007610: 2009e390 .word 0x2009e390 + 8007614: 0801c000 .word 0x0801c000 + 8007618: 2009e2b0 .word 0x2009e2b0 + 800761c: 0800ea44 .word 0x0800ea44 + +08007620 : +{ + 8007620: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8007624: 460e mov r6, r1 + ASSERT((keynum == 0) || (keynum == 2)); + 8007626: f032 0102 bics.w r1, r2, #2 +{ + 800762a: b0e4 sub sp, #400 ; 0x190 + 800762c: 4604 mov r4, r0 + 800762e: 4617 mov r7, r2 + 8007630: 4698 mov r8, r3 + ASSERT((keynum == 0) || (keynum == 2)); + 8007632: d002 beq.n 800763a + 8007634: 4849 ldr r0, [pc, #292] ; (800775c ) + 8007636: f7f9 fa07 bl 8000a48 + se2_read_encrypted(page_num, old_data, keynum, secret); + 800763a: a901 add r1, sp, #4 + 800763c: f7ff ff78 bl 8007530 + uint8_t PGDV = page_num | 0x80; + 8007640: f064 037f orn r3, r4, #127 ; 0x7f + rng_buffer(&chal_check[32], 8); + 8007644: 2108 movs r1, #8 + 8007646: a821 add r0, sp, #132 ; 0x84 + uint8_t PGDV = page_num | 0x80; + 8007648: f88d 3002 strb.w r3, [sp, #2] + rng_buffer(&chal_check[32], 8); + 800764c: f7fb f880 bl 8002750 + hmac_sha256_init(&ctx); + 8007650: a823 add r0, sp, #140 ; 0x8c + 8007652: f7fd ffa3 bl 800559c + hmac_sha256_update(&ctx, &chal_check[32], 8); + 8007656: 2208 movs r2, #8 + 8007658: a921 add r1, sp, #132 ; 0x84 + 800765a: a823 add r0, sp, #140 ; 0x8c + 800765c: f7fd ffa4 bl 80055a8 + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 8007660: 4b3f ldr r3, [pc, #252] ; (8007760 ) + 8007662: 4940 ldr r1, [pc, #256] ; (8007764 ) + 8007664: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 8007668: 33b0 adds r3, #176 ; 0xb0 + 800766a: 2aff cmp r2, #255 ; 0xff + 800766c: bf18 it ne + 800766e: 4619 movne r1, r3 + 8007670: 3160 adds r1, #96 ; 0x60 + 8007672: 2208 movs r2, #8 + 8007674: a823 add r0, sp, #140 ; 0x8c + 8007676: f7fd ff97 bl 80055a8 + hmac_sha256_update(&ctx, &PGDV, 1); + 800767a: 2201 movs r2, #1 + 800767c: f10d 0102 add.w r1, sp, #2 + 8007680: a823 add r0, sp, #140 ; 0x8c + 8007682: f7fd ff91 bl 80055a8 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 8007686: 4938 ldr r1, [pc, #224] ; (8007768 ) + 8007688: 2202 movs r2, #2 + 800768a: a823 add r0, sp, #140 ; 0x8c + 800768c: f7fd ff8c bl 80055a8 + ASSERT(ctx.num_pending == 19); + 8007690: 9b63 ldr r3, [sp, #396] ; 0x18c + 8007692: 2b13 cmp r3, #19 + 8007694: d1ce bne.n 8007634 + hmac_sha256_final(&ctx, secret, otp); + 8007696: aa09 add r2, sp, #36 ; 0x24 + 8007698: 4641 mov r1, r8 + 800769a: a823 add r0, sp, #140 ; 0x8c + 800769c: f7fd ff9a bl 80055d4 + memcpy(tmp, data, 32); + 80076a0: 4635 mov r5, r6 + 80076a2: aa11 add r2, sp, #68 ; 0x44 + 80076a4: f106 0c20 add.w ip, r6, #32 + 80076a8: 6828 ldr r0, [r5, #0] + 80076aa: 6869 ldr r1, [r5, #4] + 80076ac: 4613 mov r3, r2 + 80076ae: c303 stmia r3!, {r0, r1} + 80076b0: 3508 adds r5, #8 + 80076b2: 4565 cmp r5, ip + 80076b4: 461a mov r2, r3 + 80076b6: d1f7 bne.n 80076a8 + xor_mixin(tmp, otp, 32); + 80076b8: 2220 movs r2, #32 + 80076ba: a909 add r1, sp, #36 ; 0x24 + 80076bc: a811 add r0, sp, #68 ; 0x44 + 80076be: f7ff fd99 bl 80071f4 + hmac_sha256_init(&ctx); + 80076c2: a823 add r0, sp, #140 ; 0x8c + 80076c4: f7fd ff6a bl 800559c + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 80076c8: 4b25 ldr r3, [pc, #148] ; (8007760 ) + 80076ca: 4926 ldr r1, [pc, #152] ; (8007764 ) + 80076cc: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80076d0: 33b0 adds r3, #176 ; 0xb0 + 80076d2: 2aff cmp r2, #255 ; 0xff + 80076d4: bf18 it ne + 80076d6: 4619 movne r1, r3 + 80076d8: 3160 adds r1, #96 ; 0x60 + 80076da: 2208 movs r2, #8 + 80076dc: a823 add r0, sp, #140 ; 0x8c + 80076de: f7fd ff63 bl 80055a8 + hmac_sha256_update(&ctx, old_data, 32); + 80076e2: 2220 movs r2, #32 + 80076e4: a901 add r1, sp, #4 + 80076e6: a823 add r0, sp, #140 ; 0x8c + 80076e8: f7fd ff5e bl 80055a8 + hmac_sha256_update(&ctx, data, 32); + 80076ec: 2220 movs r2, #32 + 80076ee: 4631 mov r1, r6 + 80076f0: a823 add r0, sp, #140 ; 0x8c + 80076f2: f7fd ff59 bl 80055a8 + hmac_sha256_update(&ctx, &PGDV, 1); + 80076f6: 2201 movs r2, #1 + 80076f8: f10d 0102 add.w r1, sp, #2 + 80076fc: a823 add r0, sp, #140 ; 0x8c + 80076fe: f7fd ff53 bl 80055a8 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 8007702: 4919 ldr r1, [pc, #100] ; (8007768 ) + 8007704: 2202 movs r2, #2 + 8007706: a823 add r0, sp, #140 ; 0x8c + 8007708: f7fd ff4e bl 80055a8 + ASSERT(ctx.num_pending == 75); + 800770c: 9b63 ldr r3, [sp, #396] ; 0x18c + 800770e: 2b4b cmp r3, #75 ; 0x4b + 8007710: d190 bne.n 8007634 + hmac_sha256_final(&ctx, secret, chal_check); + 8007712: aa19 add r2, sp, #100 ; 0x64 + 8007714: 4641 mov r1, r8 + 8007716: a823 add r0, sp, #140 ; 0x8c + 8007718: f7fd ff5c bl 80055d4 + se2_write_buffer(chal_check, sizeof(chal_check)); + 800771c: 2128 movs r1, #40 ; 0x28 + 800771e: a819 add r0, sp, #100 ; 0x64 + 8007720: f7ff fde4 bl 80072ec + uint8_t pn = (keynum << 6) | page_num; + 8007724: ea44 1487 orr.w r4, r4, r7, lsl #6 + CALL_CHECK(se2_write_n(0x99, &pn, tmp, 32)); + 8007728: 2320 movs r3, #32 + 800772a: aa11 add r2, sp, #68 ; 0x44 + 800772c: f10d 0103 add.w r1, sp, #3 + 8007730: 2099 movs r0, #153 ; 0x99 + uint8_t pn = (keynum << 6) | page_num; + 8007732: f88d 4003 strb.w r4, [sp, #3] + CALL_CHECK(se2_write_n(0x99, &pn, tmp, 32)); + 8007736: f7ff fda1 bl 800727c + 800773a: b120 cbz r0, 8007746 + 800773c: f44f 71aa mov.w r1, #340 ; 0x154 + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007740: 480a ldr r0, [pc, #40] ; (800776c ) + 8007742: f005 ffa5 bl 800d690 + 8007746: f7ff fe09 bl 800735c + 800774a: 28aa cmp r0, #170 ; 0xaa + 800774c: d002 beq.n 8007754 + 800774e: f44f 71ab mov.w r1, #342 ; 0x156 + 8007752: e7f5 b.n 8007740 +} + 8007754: b064 add sp, #400 ; 0x190 + 8007756: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + 800775a: bf00 nop + 800775c: 0800e3e0 .word 0x0800e3e0 + 8007760: 0801c000 .word 0x0801c000 + 8007764: 2009e2b0 .word 0x2009e2b0 + 8007768: 0800ea44 .word 0x0800ea44 + 800776c: 2009e390 .word 0x2009e390 + +08007770 : +{ + 8007770: b508 push {r3, lr} + 8007772: 4601 mov r1, r0 + CALL_CHECK(se2_write1(0xaa, page_num)); + 8007774: 20aa movs r0, #170 ; 0xaa + 8007776: f7ff fd4b bl 8007210 + 800777a: b120 cbz r0, 8007786 + 800777c: 4804 ldr r0, [pc, #16] ; (8007790 ) + 800777e: f240 118b movw r1, #395 ; 0x18b + 8007782: f005 ff85 bl 800d690 +} + 8007786: e8bd 4008 ldmia.w sp!, {r3, lr} + return se2_read1(); + 800778a: f7ff bde7 b.w 800735c + 800778e: bf00 nop + 8007790: 2009e390 .word 0x2009e390 + +08007794 : +{ + 8007794: b538 push {r3, r4, r5, lr} + 8007796: 460c mov r4, r1 + 8007798: 4605 mov r5, r0 + if(se2_get_protection(page_num) == flags) { + 800779a: f7ff ffe9 bl 8007770 + 800779e: 42a0 cmp r0, r4 + 80077a0: d011 beq.n 80077c6 + CALL_CHECK(se2_write2(0xc3, page_num, flags)); + 80077a2: 4622 mov r2, r4 + 80077a4: 4629 mov r1, r5 + 80077a6: 20c3 movs r0, #195 ; 0xc3 + 80077a8: f7ff fd4c bl 8007244 + 80077ac: b120 cbz r0, 80077b8 + 80077ae: f240 119b movw r1, #411 ; 0x19b + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 80077b2: 4805 ldr r0, [pc, #20] ; (80077c8 ) + 80077b4: f005 ff6c bl 800d690 + 80077b8: f7ff fdd0 bl 800735c + 80077bc: 28aa cmp r0, #170 ; 0xaa + 80077be: d002 beq.n 80077c6 + 80077c0: f240 119d movw r1, #413 ; 0x19d + 80077c4: e7f5 b.n 80077b2 +} + 80077c6: bd38 pop {r3, r4, r5, pc} + 80077c8: 2009e390 .word 0x2009e390 + +080077cc : +{ + 80077cc: b500 push {lr} + if(setjmp(error_env)) { + 80077ce: 4812 ldr r0, [pc, #72] ; (8007818 ) +{ + 80077d0: b089 sub sp, #36 ; 0x24 + if(setjmp(error_env)) { + 80077d2: f005 ff57 bl 800d684 + 80077d6: b120 cbz r0, 80077e2 + oled_show(screen_se2_issue); + 80077d8: 4810 ldr r0, [pc, #64] ; (800781c ) + 80077da: f7f9 fb33 bl 8000e44 + LOCKUP_FOREVER(); + 80077de: bf30 wfi + 80077e0: e7fd b.n 80077de + rng_delay(); + 80077e2: f7fa ffcb bl 800277c + if(rom_secrets->se2.pairing[0] == 0xff) { + 80077e6: 4b0e ldr r3, [pc, #56] ; (8007820 ) + 80077e8: f893 30b0 ldrb.w r3, [r3, #176] ; 0xb0 + 80077ec: 2bff cmp r3, #255 ; 0xff + 80077ee: d00f beq.n 8007810 + se2_read_page(PGN_ROM_OPTIONS, tmp, true); + 80077f0: 2201 movs r2, #1 + 80077f2: 4669 mov r1, sp + 80077f4: 201c movs r0, #28 + 80077f6: f7ff fe4d bl 8007494 + CHECK_RIGHT(check_equal(&tmp[24], rom_secrets->se2.romid, 8)); + 80077fa: 490a ldr r1, [pc, #40] ; (8007824 ) + 80077fc: 2208 movs r2, #8 + 80077fe: a806 add r0, sp, #24 + 8007800: f7fa ff57 bl 80026b2 + 8007804: b920 cbnz r0, 8007810 + 8007806: 4804 ldr r0, [pc, #16] ; (8007818 ) + 8007808: f240 11b5 movw r1, #437 ; 0x1b5 + 800780c: f005 ff40 bl 800d690 +} + 8007810: b009 add sp, #36 ; 0x24 + 8007812: f85d fb04 ldr.w pc, [sp], #4 + 8007816: bf00 nop + 8007818: 2009e390 .word 0x2009e390 + 800781c: 0800dfce .word 0x0800dfce + 8007820: 0801c000 .word 0x0801c000 + 8007824: 0801c110 .word 0x0801c110 + +08007828 : +{ + 8007828: b510 push {r4, lr} + if(setjmp(error_env)) fatal_mitm(); + 800782a: 4817 ldr r0, [pc, #92] ; (8007888 ) +{ + 800782c: b088 sub sp, #32 + if(setjmp(error_env)) fatal_mitm(); + 800782e: f005 ff29 bl 800d684 + 8007832: 4604 mov r4, r0 + 8007834: b108 cbz r0, 800783a + 8007836: f7f9 f911 bl 8000a5c + uint8_t z32[32] = {0}; + 800783a: 221c movs r2, #28 + 800783c: 4601 mov r1, r0 + 800783e: 9000 str r0, [sp, #0] + 8007840: a801 add r0, sp, #4 + 8007842: f005 ff17 bl 800d674 + se2_write_page(PGN_PUBKEY_S+0, z32); + 8007846: 4669 mov r1, sp + 8007848: 201e movs r0, #30 + 800784a: f7ff fd8f bl 800736c + se2_write_page(PGN_PUBKEY_S+1, z32); + 800784e: 4669 mov r1, sp + 8007850: 201f movs r0, #31 + 8007852: f7ff fd8b bl 800736c + se2_write_buffer(z32, 32); + 8007856: 2120 movs r1, #32 + 8007858: 4668 mov r0, sp + 800785a: f7ff fd47 bl 80072ec + CALL_CHECK(se2_write2(0x3c, (2<<6), 0)); + 800785e: 4622 mov r2, r4 + 8007860: 2180 movs r1, #128 ; 0x80 + 8007862: 203c movs r0, #60 ; 0x3c + 8007864: f7ff fcee bl 8007244 + 8007868: b120 cbz r0, 8007874 + 800786a: f240 11cd movw r1, #461 ; 0x1cd + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 800786e: 4806 ldr r0, [pc, #24] ; (8007888 ) + 8007870: f005 ff0e bl 800d690 + 8007874: f7ff fd72 bl 800735c + 8007878: 28aa cmp r0, #170 ; 0xaa + 800787a: d002 beq.n 8007882 + 800787c: f44f 71e7 mov.w r1, #462 ; 0x1ce + 8007880: e7f5 b.n 800786e +} + 8007882: b008 add sp, #32 + 8007884: bd10 pop {r4, pc} + 8007886: bf00 nop + 8007888: 2009e390 .word 0x2009e390 + +0800788c : +{ + 800788c: b570 push {r4, r5, r6, lr} + if((setjmp(error_env))) { + 800788e: 485b ldr r0, [pc, #364] ; (80079fc ) +{ + 8007890: b090 sub sp, #64 ; 0x40 + if((setjmp(error_env))) { + 8007892: f005 fef7 bl 800d684 + 8007896: 4604 mov r4, r0 + 8007898: b120 cbz r0, 80078a4 + oled_show(screen_se2_issue); + 800789a: 4859 ldr r0, [pc, #356] ; (8007a00 ) + 800789c: f7f9 fad2 bl 8000e44 + LOCKUP_FOREVER(); + 80078a0: bf30 wfi + 80078a2: e7fd b.n 80078a0 + if(rom_secrets->se2.pairing[0] != 0xff) { + 80078a4: 4b57 ldr r3, [pc, #348] ; (8007a04 ) + 80078a6: f893 10b0 ldrb.w r1, [r3, #176] ; 0xb0 + 80078aa: 29ff cmp r1, #255 ; 0xff + 80078ac: f040 80a0 bne.w 80079f0 + memset(&_tbd, 0xff, sizeof(_tbd)); + 80078b0: 4d55 ldr r5, [pc, #340] ; (8007a08 ) + 80078b2: 22e0 movs r2, #224 ; 0xe0 + 80078b4: 4628 mov r0, r5 + 80078b6: f005 fedd bl 800d674 + rng_buffer(_tbd.tpin_key, 32); + 80078ba: 2120 movs r1, #32 + 80078bc: f105 0080 add.w r0, r5, #128 ; 0x80 + 80078c0: f7fa ff46 bl 8002750 + se2_read_page(PGN_ROM_OPTIONS, tmp, false); + 80078c4: 4622 mov r2, r4 + 80078c6: 4669 mov r1, sp + 80078c8: 201c movs r0, #28 + 80078ca: f7ff fde3 bl 8007494 + ASSERT(tmp[1] == 0x00); // check ANON is not set + 80078ce: f89d 3001 ldrb.w r3, [sp, #1] + 80078d2: b113 cbz r3, 80078da + 80078d4: 484d ldr r0, [pc, #308] ; (8007a0c ) + 80078d6: f7f9 f8b7 bl 8000a48 + memcpy(_tbd.romid, tmp+24, 8); + 80078da: ab06 add r3, sp, #24 + 80078dc: cb03 ldmia r3!, {r0, r1} + 80078de: 6628 str r0, [r5, #96] ; 0x60 + 80078e0: 6669 str r1, [r5, #100] ; 0x64 + rng_buffer(tmp, 32); + 80078e2: 4668 mov r0, sp + 80078e4: 2120 movs r1, #32 + 80078e6: f7fa ff33 bl 8002750 + se2_write_page(PGN_SECRET_B, tmp); + 80078ea: 4669 mov r1, sp + 80078ec: 201a movs r0, #26 + 80078ee: f7ff fd3d bl 800736c + se2_pick_keypair(0, true); + 80078f2: 2101 movs r1, #1 + 80078f4: 4620 mov r0, r4 + 80078f6: f7ff fd53 bl 80073a0 + se2_read_page(PGN_PUBKEY_A, &_tbd.pubkey_A[0], false); + 80078fa: 4622 mov r2, r4 + 80078fc: f105 0120 add.w r1, r5, #32 + 8007900: 2010 movs r0, #16 + 8007902: f7ff fdc7 bl 8007494 + memset(tmp, 0, 32); + 8007906: 2620 movs r6, #32 + se2_read_page(PGN_PUBKEY_A+1, &_tbd.pubkey_A[32], false); + 8007908: 4622 mov r2, r4 + 800790a: f105 0140 add.w r1, r5, #64 ; 0x40 + 800790e: 2011 movs r0, #17 + 8007910: f7ff fdc0 bl 8007494 + memset(tmp, 0, 32); + 8007914: 4632 mov r2, r6 + 8007916: 4621 mov r1, r4 + 8007918: 4668 mov r0, sp + 800791a: f005 feab bl 800d674 + se2_write_page(PGN_PRIVKEY_B, tmp); + 800791e: 4669 mov r1, sp + 8007920: 2017 movs r0, #23 + 8007922: f7ff fd23 bl 800736c + se2_write_page(PGN_PRIVKEY_B+1, tmp); + 8007926: 4669 mov r1, sp + 8007928: 2018 movs r0, #24 + 800792a: f7ff fd1f bl 800736c + se2_write_page(PGN_PUBKEY_B, tmp); + 800792e: 4669 mov r1, sp + 8007930: 2012 movs r0, #18 + 8007932: f7ff fd1b bl 800736c + se2_write_page(PGN_PUBKEY_B+1, tmp); + 8007936: 4669 mov r1, sp + 8007938: 2013 movs r0, #19 + 800793a: f7ff fd17 bl 800736c + rng_buffer(_tbd.pairing, 32); + 800793e: 4631 mov r1, r6 + 8007940: 4628 mov r0, r5 + 8007942: f7fa ff05 bl 8002750 + } while(_tbd.pairing[0] == 0xff); + 8007946: 782b ldrb r3, [r5, #0] + 8007948: 2bff cmp r3, #255 ; 0xff + 800794a: d0f8 beq.n 800793e + se2_write_page(PGN_SECRET_A, _tbd.pairing); + 800794c: 4629 mov r1, r5 + 800794e: 2019 movs r0, #25 + rng_buffer(tmp, 32); + 8007950: 466d mov r5, sp + se2_write_page(PGN_SECRET_A, _tbd.pairing); + 8007952: f7ff fd0b bl 800736c + rng_buffer(tmp, 32); + 8007956: 2120 movs r1, #32 + 8007958: 4628 mov r0, r5 + 800795a: f7fa fef9 bl 8002750 + se2_write_page(PGN_SE2_EASY_KEY, tmp); + 800795e: 4629 mov r1, r5 + 8007960: 200e movs r0, #14 + 8007962: f7ff fd03 bl 800736c + memset(tmp, 0, 32); + 8007966: 2220 movs r2, #32 + 8007968: 2100 movs r1, #0 + 800796a: 4628 mov r0, r5 + 800796c: f005 fe82 bl 800d674 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007970: 4626 mov r6, r4 + se2_write_page(pn, tmp); + 8007972: b2f0 uxtb r0, r6 + 8007974: 4629 mov r1, r5 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007976: 3601 adds r6, #1 + se2_write_page(pn, tmp); + 8007978: f7ff fcf8 bl 800736c + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 800797c: 2e0e cmp r6, #14 + 800797e: d1f8 bne.n 8007972 + flash_save_se2_data(&_tbd); + 8007980: 4821 ldr r0, [pc, #132] ; (8007a08 ) + 8007982: f7fa fc2b bl 80021dc + se2_set_protection(PGN_SECRET_A, PROT_WP); + 8007986: 2102 movs r1, #2 + 8007988: 2019 movs r0, #25 + 800798a: f7ff ff03 bl 8007794 + se2_set_protection(PGN_SECRET_B, PROT_WP); + 800798e: 2102 movs r1, #2 + 8007990: 201a movs r0, #26 + 8007992: f7ff feff bl 8007794 + se2_set_protection(PGN_PUBKEY_A, PROT_WP); + 8007996: 2102 movs r1, #2 + 8007998: 2010 movs r0, #16 + 800799a: f7ff fefb bl 8007794 + se2_set_protection(PGN_PUBKEY_B, PROT_WP); + 800799e: 2102 movs r1, #2 + 80079a0: 2012 movs r0, #18 + 80079a2: f7ff fef7 bl 8007794 + se2_set_protection(PGN_SE2_EASY_KEY, PROT_EPH); + 80079a6: 2110 movs r1, #16 + 80079a8: 4630 mov r0, r6 + 80079aa: f7ff fef3 bl 8007794 + se2_set_protection(pn, PROT_EPH); + 80079ae: 2510 movs r5, #16 + 80079b0: b2e0 uxtb r0, r4 + 80079b2: 4629 mov r1, r5 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 80079b4: 3401 adds r4, #1 + se2_set_protection(pn, PROT_EPH); + 80079b6: f7ff feed bl 8007794 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 80079ba: 2c0e cmp r4, #14 + 80079bc: d1f8 bne.n 80079b0 + se2_set_protection(PGN_ROM_OPTIONS, PROT_APH); // not planning to change + 80079be: 2108 movs r1, #8 + 80079c0: 201c movs r0, #28 + 80079c2: f7ff fee7 bl 8007794 + se2_read_page(PGN_DEC_COUNTER, tmp, false); + 80079c6: 2200 movs r2, #0 + 80079c8: a908 add r1, sp, #32 + 80079ca: 201b movs r0, #27 + 80079cc: f7ff fd62 bl 8007494 + if(tmp[2] == 0xff) { + 80079d0: f89d 3022 ldrb.w r3, [sp, #34] ; 0x22 + 80079d4: 2bff cmp r3, #255 ; 0xff + 80079d6: d10d bne.n 80079f4 + tmp[0] = val & 0x0ff; + 80079d8: 2380 movs r3, #128 ; 0x80 + 80079da: f88d 3020 strb.w r3, [sp, #32] + se2_write_page(PGN_DEC_COUNTER, tmp); + 80079de: a908 add r1, sp, #32 + tmp[1] = (val >> 8) & 0x0ff; + 80079e0: 2300 movs r3, #0 + se2_write_page(PGN_DEC_COUNTER, tmp); + 80079e2: 201b movs r0, #27 + tmp[1] = (val >> 8) & 0x0ff; + 80079e4: f88d 3021 strb.w r3, [sp, #33] ; 0x21 + tmp[2] = (val >> 16) & 0x01; + 80079e8: f88d 3022 strb.w r3, [sp, #34] ; 0x22 + se2_write_page(PGN_DEC_COUNTER, tmp); + 80079ec: f7ff fcbe bl 800736c +} + 80079f0: b010 add sp, #64 ; 0x40 + 80079f2: bd70 pop {r4, r5, r6, pc} + puts("ctr set?"); // not expected, but keep going + 80079f4: 4806 ldr r0, [pc, #24] ; (8007a10 ) + 80079f6: f7fd f9d9 bl 8004dac + 80079fa: e7f9 b.n 80079f0 + 80079fc: 2009e390 .word 0x2009e390 + 8007a00: 0800dfce .word 0x0800dfce + 8007a04: 0801c000 .word 0x0801c000 + 8007a08: 2009e2b0 .word 0x2009e2b0 + 8007a0c: 0800e3e0 .word 0x0800e3e0 + 8007a10: 0800ea24 .word 0x0800ea24 + +08007a14 : +{ + 8007a14: b510 push {r4, lr} + 8007a16: b08a sub sp, #40 ; 0x28 + 8007a18: 9001 str r0, [sp, #4] + if(setjmp(error_env)) fatal_mitm(); + 8007a1a: 481e ldr r0, [pc, #120] ; (8007a94 ) + 8007a1c: f005 fe32 bl 800d684 + 8007a20: b108 cbz r0, 8007a26 + 8007a22: f7f9 f81b bl 8000a5c + ASSERT(check_all_ones(rom_secrets->se2.auth_pubkey, 64)); + 8007a26: 481c ldr r0, [pc, #112] ; (8007a98 ) + 8007a28: 2140 movs r1, #64 ; 0x40 + 8007a2a: f7fa fe29 bl 8002680 + 8007a2e: b910 cbnz r0, 8007a36 + 8007a30: 481a ldr r0, [pc, #104] ; (8007a9c ) + 8007a32: f7f9 f809 bl 8000a48 + memcpy(&_tbd, &rom_secrets->se2, sizeof(_tbd)); + 8007a36: 4c1a ldr r4, [pc, #104] ; (8007aa0 ) + 8007a38: 491a ldr r1, [pc, #104] ; (8007aa4 ) + 8007a3a: 22e0 movs r2, #224 ; 0xe0 + 8007a3c: 4620 mov r0, r4 + 8007a3e: f005 fdf1 bl 800d624 + rng_buffer(tmp, 32); + 8007a42: 2120 movs r1, #32 + 8007a44: a802 add r0, sp, #8 + 8007a46: f7fa fe83 bl 8002750 + se2_write_page(PGN_SE2_HARD_KEY, tmp); + 8007a4a: a902 add r1, sp, #8 + 8007a4c: 200f movs r0, #15 + 8007a4e: f7ff fc8d bl 800736c + se2_write_page(PGN_PUBKEY_C, &pubkey[0]); + 8007a52: 9901 ldr r1, [sp, #4] + 8007a54: 2014 movs r0, #20 + 8007a56: f7ff fc89 bl 800736c + se2_write_page(PGN_PUBKEY_C+1, &pubkey[32]); + 8007a5a: 9b01 ldr r3, [sp, #4] + 8007a5c: 2015 movs r0, #21 + 8007a5e: f103 0120 add.w r1, r3, #32 + 8007a62: f7ff fc83 bl 800736c + memcpy(_tbd.auth_pubkey, pubkey, 64); + 8007a66: 9b01 ldr r3, [sp, #4] + 8007a68: 34a0 adds r4, #160 ; 0xa0 + 8007a6a: f103 0240 add.w r2, r3, #64 ; 0x40 + 8007a6e: f853 1b04 ldr.w r1, [r3], #4 + 8007a72: f844 1b04 str.w r1, [r4], #4 + 8007a76: 4293 cmp r3, r2 + 8007a78: d1f9 bne.n 8007a6e + flash_save_se2_data(&_tbd); + 8007a7a: 4809 ldr r0, [pc, #36] ; (8007aa0 ) + 8007a7c: f7fa fbae bl 80021dc + se2_set_protection(PGN_SE2_HARD_KEY, PROT_WP | PROT_ECH | PROT_ECW); + 8007a80: 21c2 movs r1, #194 ; 0xc2 + 8007a82: 200f movs r0, #15 + 8007a84: f7ff fe86 bl 8007794 + se2_set_protection(PGN_PUBKEY_C, PROT_WP | PROT_RP | PROT_AUTH); + 8007a88: 2123 movs r1, #35 ; 0x23 + 8007a8a: 2014 movs r0, #20 + 8007a8c: f7ff fe82 bl 8007794 +} + 8007a90: b00a add sp, #40 ; 0x28 + 8007a92: bd10 pop {r4, pc} + 8007a94: 2009e390 .word 0x2009e390 + 8007a98: 0801c150 .word 0x0801c150 + 8007a9c: 0800e3e0 .word 0x0800e3e0 + 8007aa0: 2009e2b0 .word 0x2009e2b0 + 8007aa4: 0801c0b0 .word 0x0801c0b0 + +08007aa8 : +{ + 8007aa8: b530 push {r4, r5, lr} + 8007aaa: 4614 mov r4, r2 + ASSERT(pin_len >= 0); // 12-12 typical, but empty = blank PIN + 8007aac: 1e0a subs r2, r1, #0 +{ + 8007aae: b0c5 sub sp, #276 ; 0x114 + 8007ab0: 4605 mov r5, r0 + ASSERT(pin_len >= 0); // 12-12 typical, but empty = blank PIN + 8007ab2: da02 bge.n 8007aba + 8007ab4: 4812 ldr r0, [pc, #72] ; (8007b00 ) + 8007ab6: f7f8 ffc7 bl 8000a48 + hmac_sha256_init(&ctx); + 8007aba: a803 add r0, sp, #12 + 8007abc: 9201 str r2, [sp, #4] + 8007abe: f7fd fd6d bl 800559c + hmac_sha256_update(&ctx, (uint8_t *)pin, pin_len); + 8007ac2: 9a01 ldr r2, [sp, #4] + 8007ac4: 4629 mov r1, r5 + 8007ac6: a803 add r0, sp, #12 + 8007ac8: f7fd fd6e bl 80055a8 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, tpin_hash); + 8007acc: 4b0d ldr r3, [pc, #52] ; (8007b04 ) + 8007ace: 490e ldr r1, [pc, #56] ; (8007b08 ) + 8007ad0: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 8007ad4: 33b0 adds r3, #176 ; 0xb0 + 8007ad6: 2aff cmp r2, #255 ; 0xff + 8007ad8: bf18 it ne + 8007ada: 4619 movne r1, r3 + 8007adc: a803 add r0, sp, #12 + 8007ade: 4622 mov r2, r4 + 8007ae0: 3180 adds r1, #128 ; 0x80 + 8007ae2: f7fd fd77 bl 80055d4 + sha256_single(tpin_hash, 32, tpin_hash); + 8007ae6: 4622 mov r2, r4 + 8007ae8: 4620 mov r0, r4 + 8007aea: 2120 movs r1, #32 + 8007aec: f7fd fd36 bl 800555c + sha256_single(tpin_hash, 32, tpin_hash); + 8007af0: 4622 mov r2, r4 + 8007af2: 2120 movs r1, #32 + 8007af4: 4620 mov r0, r4 + 8007af6: f7fd fd31 bl 800555c +} + 8007afa: b045 add sp, #276 ; 0x114 + 8007afc: bd30 pop {r4, r5, pc} + 8007afe: bf00 nop + 8007b00: 0800e3e0 .word 0x0800e3e0 + 8007b04: 0801c000 .word 0x0801c000 + 8007b08: 2009e2b0 .word 0x2009e2b0 + +08007b0c : + +// p256_gen_keypair() +// + void +p256_gen_keypair(uint8_t privkey[32], uint8_t pubkey[64]) +{ + 8007b0c: b538 push {r3, r4, r5, lr} + 8007b0e: 4605 mov r5, r0 + uECC_set_rng(rng_for_uECC); + 8007b10: 4808 ldr r0, [pc, #32] ; (8007b34 ) +{ + 8007b12: 460c mov r4, r1 + uECC_set_rng(rng_for_uECC); + 8007b14: f7fe fedc bl 80068d0 + + int ok = uECC_make_key(pubkey, privkey, uECC_secp256r1()); + 8007b18: f7fe fee0 bl 80068dc + 8007b1c: 4629 mov r1, r5 + 8007b1e: 4602 mov r2, r0 + 8007b20: 4620 mov r0, r4 + 8007b22: f7fe fee3 bl 80068ec + ASSERT(ok == 1); + 8007b26: 2801 cmp r0, #1 + 8007b28: d002 beq.n 8007b30 + 8007b2a: 4803 ldr r0, [pc, #12] ; (8007b38 ) + 8007b2c: f7f8 ff8c bl 8000a48 +} + 8007b30: bd38 pop {r3, r4, r5, pc} + 8007b32: bf00 nop + 8007b34: 080072e1 .word 0x080072e1 + 8007b38: 0800e3e0 .word 0x0800e3e0 + +08007b3c : + +// ps256_ecdh() +// + void +ps256_ecdh(const uint8_t pubkey[64], const uint8_t privkey[32], uint8_t result[32]) +{ + 8007b3c: b513 push {r0, r1, r4, lr} + 8007b3e: 4604 mov r4, r0 + uECC_set_rng(rng_for_uECC); + 8007b40: 4809 ldr r0, [pc, #36] ; (8007b68 ) +{ + 8007b42: e9cd 2100 strd r2, r1, [sp] + uECC_set_rng(rng_for_uECC); + 8007b46: f7fe fec3 bl 80068d0 + + int ok = uECC_shared_secret(pubkey, privkey, result, uECC_secp256r1()); + 8007b4a: f7fe fec7 bl 80068dc + 8007b4e: e9dd 2100 ldrd r2, r1, [sp] + 8007b52: 4603 mov r3, r0 + 8007b54: 4620 mov r0, r4 + 8007b56: f7fe ff09 bl 800696c + ASSERT(ok == 1); + 8007b5a: 2801 cmp r0, #1 + 8007b5c: d002 beq.n 8007b64 + 8007b5e: 4803 ldr r0, [pc, #12] ; (8007b6c ) + 8007b60: f7f8 ff72 bl 8000a48 +} + 8007b64: b002 add sp, #8 + 8007b66: bd10 pop {r4, pc} + 8007b68: 080072e1 .word 0x080072e1 + 8007b6c: 0800e3e0 .word 0x0800e3e0 + +08007b70 : + +// se2_read_hard_secret() +// + static bool +se2_read_hard_secret(uint8_t hard_key[32], const uint8_t pin_digest[32]) +{ + 8007b70: b510 push {r4, lr} + 8007b72: b0e8 sub sp, #416 ; 0x1a0 + 8007b74: e9cd 0102 strd r0, r1, [sp, #8] + if(setjmp(error_env)) { + 8007b78: 4836 ldr r0, [pc, #216] ; (8007c54 ) + 8007b7a: f005 fd83 bl 800d684 + 8007b7e: 2800 cmp r0, #0 + 8007b80: d165 bne.n 8007c4e + // + SHA256_CTX ctx; + + // pick a temp key pair, share public part w/ SE2 + uint8_t tmp_privkey[32], tmp_pubkey[64]; + p256_gen_keypair(tmp_privkey, tmp_pubkey); + 8007b82: a925 add r1, sp, #148 ; 0x94 + 8007b84: a805 add r0, sp, #20 + 8007b86: f7ff ffc1 bl 8007b0c + + // - this can be mitm-ed, but we sign it next so doesn't matter + se2_write_page(PGN_PUBKEY_S, &tmp_pubkey[0]); + 8007b8a: a925 add r1, sp, #148 ; 0x94 + 8007b8c: 201e movs r0, #30 + 8007b8e: f7ff fbed bl 800736c + se2_write_page(PGN_PUBKEY_S+1, &tmp_pubkey[32]); + 8007b92: a92d add r1, sp, #180 ; 0xb4 + 8007b94: 201f movs r0, #31 + 8007b96: f7ff fbe9 bl 800736c + + // pick nonce + uint8_t chal[32+32]; + rng_buffer(chal, sizeof(chal)); + 8007b9a: 2140 movs r1, #64 ; 0x40 + 8007b9c: a835 add r0, sp, #212 ; 0xd4 + 8007b9e: f7fa fdd7 bl 8002750 + se2_write_buffer(chal, sizeof(chal)); + 8007ba2: 2140 movs r1, #64 ; 0x40 + 8007ba4: a835 add r0, sp, #212 ; 0xd4 + 8007ba6: f7ff fba1 bl 80072ec + + // md = ngu.hash.sha256s(T_pubkey + chal[0:32]) + sha256_init(&ctx); + 8007baa: a855 add r0, sp, #340 ; 0x154 + 8007bac: f7fd fc6e bl 800548c + sha256_update(&ctx, tmp_pubkey, 64); + 8007bb0: 2240 movs r2, #64 ; 0x40 + 8007bb2: a925 add r1, sp, #148 ; 0x94 + 8007bb4: a855 add r0, sp, #340 ; 0x154 + 8007bb6: f7fd fc77 bl 80054a8 + sha256_update(&ctx, chal, 32); // only first 32 bytes + 8007bba: 2220 movs r2, #32 + 8007bbc: a935 add r1, sp, #212 ; 0xd4 + 8007bbe: a855 add r0, sp, #340 ; 0x154 + 8007bc0: f7fd fc72 bl 80054a8 + + uint8_t md[32]; + sha256_final(&ctx, md); + 8007bc4: a90d add r1, sp, #52 ; 0x34 + 8007bc6: a855 add r0, sp, #340 ; 0x154 + 8007bc8: f7fd fcb4 bl 8005534 + // Get that digest signed by SE1 now, and doing that requires + // the main pin, because the required slot requires auth by that key. + // - this is the critical step attackers would not be able to emulate w/o SE1 contents + // - fails here if PIN wrong + uint8_t signature[64]; + int arc = ae_sign_authed(KEYNUM_joiner_key, md, signature, KEYNUM_main_pin, pin_digest); + 8007bcc: 9b03 ldr r3, [sp, #12] + 8007bce: 9300 str r3, [sp, #0] + 8007bd0: aa45 add r2, sp, #276 ; 0x114 + 8007bd2: 2303 movs r3, #3 + 8007bd4: a90d add r1, sp, #52 ; 0x34 + 8007bd6: 2007 movs r0, #7 + 8007bd8: f7fb f8f0 bl 8002dbc + CHECK_RIGHT(arc == 0); + 8007bdc: 4604 mov r4, r0 + 8007bde: b120 cbz r0, 8007bea + 8007be0: f240 4152 movw r1, #1106 ; 0x452 + + // "Authenticate ECDSA Public Key" = 0xA8 + // cs_offset=32 ecdh_keynum=0=pubA ECDH=1 WR=0 + uint8_t param = ((32-1) << 3) | (0 << 2) | 0x2; + se2_write_n(0xA8, ¶m, signature, 64); + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007be4: 481b ldr r0, [pc, #108] ; (8007c54 ) + 8007be6: f005 fd53 bl 800d690 + uint8_t param = ((32-1) << 3) | (0 << 2) | 0x2; + 8007bea: 23fa movs r3, #250 ; 0xfa + 8007bec: f88d 3013 strb.w r3, [sp, #19] + se2_write_n(0xA8, ¶m, signature, 64); + 8007bf0: aa45 add r2, sp, #276 ; 0x114 + 8007bf2: 2340 movs r3, #64 ; 0x40 + 8007bf4: f10d 0113 add.w r1, sp, #19 + 8007bf8: 20a8 movs r0, #168 ; 0xa8 + 8007bfa: f7ff fb3f bl 800727c + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007bfe: f7ff fbad bl 800735c + 8007c02: 28aa cmp r0, #170 ; 0xaa + 8007c04: d002 beq.n 8007c0c + 8007c06: f44f 618b mov.w r1, #1112 ; 0x458 + 8007c0a: e7eb b.n 8007be4 + + uint8_t shared_x[32], shared_secret[32]; + ps256_ecdh(rom_secrets->se2.pubkey_A, tmp_privkey, shared_x); + 8007c0c: aa15 add r2, sp, #84 ; 0x54 + 8007c0e: a905 add r1, sp, #20 + 8007c10: 4811 ldr r0, [pc, #68] ; (8007c58 ) + 8007c12: f7ff ff93 bl 8007b3c + + // shared secret S will be SHA over X of shared ECDH point + chal[32:] + // s = ngu.hash.sha256s(x + chal[32:]) + sha256_init(&ctx); + 8007c16: a855 add r0, sp, #340 ; 0x154 + 8007c18: f7fd fc38 bl 800548c + sha256_update(&ctx, shared_x, 32); + 8007c1c: 2220 movs r2, #32 + 8007c1e: a915 add r1, sp, #84 ; 0x54 + 8007c20: a855 add r0, sp, #340 ; 0x154 + 8007c22: f7fd fc41 bl 80054a8 + sha256_update(&ctx, &chal[32], 32); // second half + 8007c26: 2220 movs r2, #32 + 8007c28: a93d add r1, sp, #244 ; 0xf4 + 8007c2a: a855 add r0, sp, #340 ; 0x154 + 8007c2c: f7fd fc3c bl 80054a8 + sha256_final(&ctx, shared_secret); + 8007c30: a91d add r1, sp, #116 ; 0x74 + 8007c32: a855 add r0, sp, #340 ; 0x154 + 8007c34: f7fd fc7e bl 8005534 + + se2_read_encrypted(PGN_SE2_HARD_KEY, hard_key, 2, shared_secret); + 8007c38: 200f movs r0, #15 + 8007c3a: 9902 ldr r1, [sp, #8] + 8007c3c: ab1d add r3, sp, #116 ; 0x74 + 8007c3e: 2202 movs r2, #2 + 8007c40: f7ff fc76 bl 8007530 + + // CONCERN: secret "S" is retained in SE2's sram. No API to clear it. + // - but you'd need to see our copy of that value to make use of it + // - and PIN checked already to get here, so you could re-do anyway + se2_clear_volatile(); + 8007c44: f7ff fdf0 bl 8007828 + + return false; + 8007c48: 4620 mov r0, r4 +} + 8007c4a: b068 add sp, #416 ; 0x1a0 + 8007c4c: bd10 pop {r4, pc} + return true; + 8007c4e: 2001 movs r0, #1 + 8007c50: e7fb b.n 8007c4a + 8007c52: bf00 nop + 8007c54: 2009e390 .word 0x2009e390 + 8007c58: 0801c0d0 .word 0x0801c0d0 + +08007c5c : + +// se2_calc_seed_key() +// + static bool +se2_calc_seed_key(uint8_t aes_key[32], const mcu_key_t *mcu_key, const uint8_t pin_digest[32]) +{ + 8007c5c: b570 push {r4, r5, r6, lr} + 8007c5e: b0d2 sub sp, #328 ; 0x148 + 8007c60: 4614 mov r4, r2 + // Gather key parts from all over. Combine them w/ HMAC into a AES-256 key + uint8_t se1_easy_key[32], se1_hard_key[32]; + se2_read_encrypted(PGN_SE2_EASY_KEY, se1_easy_key, 0, rom_secrets->se2.pairing); + 8007c62: 4b15 ldr r3, [pc, #84] ; (8007cb8 ) + 8007c64: 2200 movs r2, #0 +{ + 8007c66: 4605 mov r5, r0 + 8007c68: 460e mov r6, r1 + se2_read_encrypted(PGN_SE2_EASY_KEY, se1_easy_key, 0, rom_secrets->se2.pairing); + 8007c6a: 200e movs r0, #14 + 8007c6c: a901 add r1, sp, #4 + 8007c6e: f7ff fc5f bl 8007530 + + if(se2_read_hard_secret(se1_hard_key, pin_digest)) return true; + 8007c72: 4621 mov r1, r4 + 8007c74: a809 add r0, sp, #36 ; 0x24 + 8007c76: f7ff ff7b bl 8007b70 + 8007c7a: 4604 mov r4, r0 + 8007c7c: b9c8 cbnz r0, 8007cb2 + + HMAC_CTX ctx; + hmac_sha256_init(&ctx); + 8007c7e: a811 add r0, sp, #68 ; 0x44 + 8007c80: f7fd fc8c bl 800559c + hmac_sha256_update(&ctx, mcu_key->value, 32); + 8007c84: 2220 movs r2, #32 + 8007c86: 4631 mov r1, r6 + 8007c88: a811 add r0, sp, #68 ; 0x44 + 8007c8a: f7fd fc8d bl 80055a8 + hmac_sha256_update(&ctx, se1_hard_key, 32); + 8007c8e: 2220 movs r2, #32 + 8007c90: a909 add r1, sp, #36 ; 0x24 + 8007c92: a811 add r0, sp, #68 ; 0x44 + 8007c94: f7fd fc88 bl 80055a8 + hmac_sha256_update(&ctx, se1_easy_key, 32); + 8007c98: 2220 movs r2, #32 + 8007c9a: a901 add r1, sp, #4 + 8007c9c: a811 add r0, sp, #68 ; 0x44 + 8007c9e: f7fd fc83 bl 80055a8 + + // combine them all using anther MCU key via HMAC-SHA256 + hmac_sha256_final(&ctx, rom_secrets->mcu_hmac_key, aes_key); + 8007ca2: a811 add r0, sp, #68 ; 0x44 + 8007ca4: 4905 ldr r1, [pc, #20] ; (8007cbc ) + 8007ca6: 462a mov r2, r5 + 8007ca8: f7fd fc94 bl 80055d4 + hmac_sha256_init(&ctx); // clear secrets + 8007cac: a811 add r0, sp, #68 ; 0x44 + 8007cae: f7fd fc75 bl 800559c + + return false; +} + 8007cb2: 4620 mov r0, r4 + 8007cb4: b052 add sp, #328 ; 0x148 + 8007cb6: bd70 pop {r4, r5, r6, pc} + 8007cb8: 0801c0b0 .word 0x0801c0b0 + 8007cbc: 0801c090 .word 0x0801c090 + +08007cc0 : +{ + 8007cc0: b5f0 push {r4, r5, r6, r7, lr} + if(i2c_port.Instance == I2C2) { + 8007cc2: 4e1b ldr r6, [pc, #108] ; (8007d30 ) + 8007cc4: 4f1b ldr r7, [pc, #108] ; (8007d34 ) + 8007cc6: 6833 ldr r3, [r6, #0] + 8007cc8: 42bb cmp r3, r7 +{ + 8007cca: b089 sub sp, #36 ; 0x24 + if(i2c_port.Instance == I2C2) { + 8007ccc: d02e beq.n 8007d2c + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8007cce: 4b1a ldr r3, [pc, #104] ; (8007d38 ) + GPIO_InitTypeDef setup = { + 8007cd0: 4d1a ldr r5, [pc, #104] ; (8007d3c ) + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8007cd2: 6cda ldr r2, [r3, #76] ; 0x4c + 8007cd4: f042 0202 orr.w r2, r2, #2 + 8007cd8: 64da str r2, [r3, #76] ; 0x4c + 8007cda: 6cda ldr r2, [r3, #76] ; 0x4c + 8007cdc: f002 0202 and.w r2, r2, #2 + 8007ce0: 9201 str r2, [sp, #4] + 8007ce2: 9a01 ldr r2, [sp, #4] + __HAL_RCC_I2C2_CLK_ENABLE(); + 8007ce4: 6d9a ldr r2, [r3, #88] ; 0x58 + 8007ce6: f442 0280 orr.w r2, r2, #4194304 ; 0x400000 + 8007cea: 659a str r2, [r3, #88] ; 0x58 + 8007cec: 6d9b ldr r3, [r3, #88] ; 0x58 + 8007cee: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 8007cf2: 9302 str r3, [sp, #8] + 8007cf4: 9b02 ldr r3, [sp, #8] + GPIO_InitTypeDef setup = { + 8007cf6: cd0f ldmia r5!, {r0, r1, r2, r3} + 8007cf8: ac03 add r4, sp, #12 + 8007cfa: c40f stmia r4!, {r0, r1, r2, r3} + 8007cfc: 682b ldr r3, [r5, #0] + HAL_GPIO_Init(GPIOB, &setup); + 8007cfe: 4810 ldr r0, [pc, #64] ; (8007d40 ) + GPIO_InitTypeDef setup = { + 8007d00: 6023 str r3, [r4, #0] + HAL_GPIO_Init(GPIOB, &setup); + 8007d02: a903 add r1, sp, #12 + 8007d04: f7f9 f984 bl 8001010 + memset(&i2c_port, 0, sizeof(i2c_port)); + 8007d08: 2244 movs r2, #68 ; 0x44 + 8007d0a: 2100 movs r1, #0 + 8007d0c: f106 0008 add.w r0, r6, #8 + 8007d10: f005 fcb0 bl 800d674 + i2c_port.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + 8007d14: 2301 movs r3, #1 + 8007d16: 60f3 str r3, [r6, #12] + HAL_StatusTypeDef rv = HAL_I2C_Init(&i2c_port); + 8007d18: 4630 mov r0, r6 + i2c_port.Init.Timing = 0x00b03fb8; // 400khz "fast mode" in CubeMX @ 120Mhz (measured ok) + 8007d1a: 4b0a ldr r3, [pc, #40] ; (8007d44 ) + i2c_port.Instance = I2C2; + 8007d1c: 6037 str r7, [r6, #0] + i2c_port.Init.Timing = 0x00b03fb8; // 400khz "fast mode" in CubeMX @ 120Mhz (measured ok) + 8007d1e: 6073 str r3, [r6, #4] + HAL_StatusTypeDef rv = HAL_I2C_Init(&i2c_port); + 8007d20: f003 fdb4 bl 800b88c + ASSERT(rv == HAL_OK); + 8007d24: b110 cbz r0, 8007d2c + 8007d26: 4808 ldr r0, [pc, #32] ; (8007d48 ) + 8007d28: f7f8 fe8e bl 8000a48 +} + 8007d2c: b009 add sp, #36 ; 0x24 + 8007d2e: bdf0 pop {r4, r5, r6, r7, pc} + 8007d30: 2009e3ec .word 0x2009e3ec + 8007d34: 40005800 .word 0x40005800 + 8007d38: 40021000 .word 0x40021000 + 8007d3c: 0800ea30 .word 0x0800ea30 + 8007d40: 48000400 .word 0x48000400 + 8007d44: 00b03fb8 .word 0x00b03fb8 + 8007d48: 0800e3e0 .word 0x0800e3e0 + +08007d4c : +{ + 8007d4c: b5f0 push {r4, r5, r6, r7, lr} + 8007d4e: b089 sub sp, #36 ; 0x24 + se2_setup(); + 8007d50: f7ff ffb6 bl 8007cc0 + if(setjmp(error_env)) fatal_mitm(); + 8007d54: 480f ldr r0, [pc, #60] ; (8007d94 ) + 8007d56: f005 fc95 bl 800d684 + 8007d5a: 4604 mov r4, r0 + 8007d5c: b108 cbz r0, 8007d62 + 8007d5e: f7f8 fe7d bl 8000a5c + uint8_t tmp[32] = {0}; + 8007d62: 9000 str r0, [sp, #0] + 8007d64: 4601 mov r1, r0 + 8007d66: 221c movs r2, #28 + 8007d68: a801 add r0, sp, #4 + 8007d6a: f005 fc83 bl 800d674 + se2_write_encrypted(pn, tmp, 0, SE2_SECRETS->pairing); + 8007d6e: 4f0a ldr r7, [pc, #40] ; (8007d98 ) + 8007d70: 4e0a ldr r6, [pc, #40] ; (8007d9c ) + 8007d72: 4d0b ldr r5, [pc, #44] ; (8007da0 ) + 8007d74: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 8007d78: b2e0 uxtb r0, r4 + 8007d7a: 2bff cmp r3, #255 ; 0xff + 8007d7c: bf0c ite eq + 8007d7e: 4633 moveq r3, r6 + 8007d80: 462b movne r3, r5 + 8007d82: 2200 movs r2, #0 + 8007d84: 4669 mov r1, sp + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007d86: 3401 adds r4, #1 + se2_write_encrypted(pn, tmp, 0, SE2_SECRETS->pairing); + 8007d88: f7ff fc4a bl 8007620 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007d8c: 2c0e cmp r4, #14 + 8007d8e: d1f1 bne.n 8007d74 +} + 8007d90: b009 add sp, #36 ; 0x24 + 8007d92: bdf0 pop {r4, r5, r6, r7, pc} + 8007d94: 2009e390 .word 0x2009e390 + 8007d98: 0801c000 .word 0x0801c000 + 8007d9c: 2009e2b0 .word 0x2009e2b0 + 8007da0: 0801c0b0 .word 0x0801c0b0 + +08007da4 : +{ + 8007da4: b5f0 push {r4, r5, r6, r7, lr} + 8007da6: b087 sub sp, #28 + 8007da8: e9cd 0102 strd r0, r1, [sp, #8] + if(setjmp(error_env)) fatal_mitm(); + 8007dac: 4816 ldr r0, [pc, #88] ; (8007e08 ) +{ + 8007dae: 9201 str r2, [sp, #4] + if(setjmp(error_env)) fatal_mitm(); + 8007db0: f005 fc68 bl 800d684 + 8007db4: b108 cbz r0, 8007dba + 8007db6: f7f8 fe51 bl 8000a5c + se2_read_encrypted(slot_num+1, &data[0], 0, SE2_SECRETS->pairing); + 8007dba: 4f14 ldr r7, [pc, #80] ; (8007e0c ) + 8007dbc: 9005 str r0, [sp, #20] + se2_setup(); + 8007dbe: f7ff ff7f bl 8007cc0 + se2_read_encrypted(slot_num+1, &data[0], 0, SE2_SECRETS->pairing); + 8007dc2: f89d 4008 ldrb.w r4, [sp, #8] + 8007dc6: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 8007dca: 4e11 ldr r6, [pc, #68] ; (8007e10 ) + 8007dcc: 4d11 ldr r5, [pc, #68] ; (8007e14 ) + 8007dce: 9a05 ldr r2, [sp, #20] + 8007dd0: 9901 ldr r1, [sp, #4] + 8007dd2: 9204 str r2, [sp, #16] + 8007dd4: 1c60 adds r0, r4, #1 + 8007dd6: 2bff cmp r3, #255 ; 0xff + 8007dd8: bf0c ite eq + 8007dda: 4633 moveq r3, r6 + 8007ddc: 462b movne r3, r5 + 8007dde: b2c0 uxtb r0, r0 + 8007de0: f7ff fba6 bl 8007530 + if(tc_flags & TC_XPRV_WALLET) { + 8007de4: 9b03 ldr r3, [sp, #12] + 8007de6: 051b lsls r3, r3, #20 + 8007de8: d50c bpl.n 8007e04 + se2_read_encrypted(slot_num+2, &data[32], 0, SE2_SECRETS->pairing); + 8007dea: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 8007dee: 9901 ldr r1, [sp, #4] + 8007df0: 9a04 ldr r2, [sp, #16] + 8007df2: 3402 adds r4, #2 + 8007df4: 2bff cmp r3, #255 ; 0xff + 8007df6: bf0c ite eq + 8007df8: 4633 moveq r3, r6 + 8007dfa: 462b movne r3, r5 + 8007dfc: 3120 adds r1, #32 + 8007dfe: b2e0 uxtb r0, r4 + 8007e00: f7ff fb96 bl 8007530 +} + 8007e04: b007 add sp, #28 + 8007e06: bdf0 pop {r4, r5, r6, r7, pc} + 8007e08: 2009e390 .word 0x2009e390 + 8007e0c: 0801c000 .word 0x0801c000 + 8007e10: 2009e2b0 .word 0x2009e2b0 + 8007e14: 0801c0b0 .word 0x0801c0b0 + +08007e18 : +{ + 8007e18: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8007e1c: b0fe sub sp, #504 ; 0x1f8 + 8007e1e: e9cd 1002 strd r1, r0, [sp, #8] + 8007e22: e9cd 2300 strd r2, r3, [sp] + se2_setup(); + 8007e26: f7ff ff4b bl 8007cc0 + if(setjmp(error_env)) { + 8007e2a: 4864 ldr r0, [pc, #400] ; (8007fbc ) + 8007e2c: f005 fc2a bl 800d684 + 8007e30: 4604 mov r4, r0 + 8007e32: b138 cbz r0, 8007e44 + if(!safety_mode) fatal_mitm(); + 8007e34: 9b01 ldr r3, [sp, #4] + 8007e36: b11b cbz r3, 8007e40 + return false; + 8007e38: 2000 movs r0, #0 +} + 8007e3a: b07e add sp, #504 ; 0x1f8 + 8007e3c: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if(!safety_mode) fatal_mitm(); + 8007e40: f7f8 fe0c bl 8000a5c + if(!pin_len) return false; + 8007e44: 9b02 ldr r3, [sp, #8] + 8007e46: 2b00 cmp r3, #0 + 8007e48: d0f6 beq.n 8007e38 + trick_pin_hash(pin, pin_len, tpin_hash); + 8007e4a: 9803 ldr r0, [sp, #12] + se2_read_encrypted(pn, slots[i], 0, SE2_SECRETS->pairing); + 8007e4c: f8df a174 ldr.w sl, [pc, #372] ; 8007fc4 + 8007e50: f8df 9174 ldr.w r9, [pc, #372] ; 8007fc8 + 8007e54: f8df 8174 ldr.w r8, [pc, #372] ; 8007fcc + trick_pin_hash(pin, pin_len, tpin_hash); + 8007e58: aa06 add r2, sp, #24 + 8007e5a: 4619 mov r1, r3 + 8007e5c: f7ff fe24 bl 8007aa8 + 8007e60: ad0e add r5, sp, #56 ; 0x38 + 8007e62: 462f mov r7, r5 + int pn = PGN_TRICK(0); + 8007e64: 4626 mov r6, r4 + se2_read_encrypted(pn, slots[i], 0, SE2_SECRETS->pairing); + 8007e66: f89a 30b0 ldrb.w r3, [sl, #176] ; 0xb0 + 8007e6a: 4639 mov r1, r7 + 8007e6c: 2bff cmp r3, #255 ; 0xff + 8007e6e: bf0c ite eq + 8007e70: 464b moveq r3, r9 + 8007e72: 4643 movne r3, r8 + 8007e74: b2f0 uxtb r0, r6 + 8007e76: 2200 movs r2, #0 + for(int i=0; ipairing); + 8007e7a: f7ff fb59 bl 8007530 + for(int i=0; i + se2_clear_volatile(); + 8007e86: f7ff fccf bl 8007828 + uint32_t blank = 0; + 8007e8a: 2700 movs r7, #0 + int found = -1; + 8007e8c: f04f 36ff mov.w r6, #4294967295 ; 0xffffffff + if(check_equal(here, tpin_hash, 28)) { + 8007e90: f04f 091c mov.w r9, #28 + blank |= (!!check_all_zeros(here, 32)) << i; + 8007e94: f04f 0820 mov.w r8, #32 + if(check_equal(here, tpin_hash, 28)) { + 8007e98: 464a mov r2, r9 + 8007e9a: a906 add r1, sp, #24 + 8007e9c: 4628 mov r0, r5 + 8007e9e: f7fa fc08 bl 80026b2 + blank |= (!!check_all_zeros(here, 32)) << i; + 8007ea2: 4641 mov r1, r8 + if(check_equal(here, tpin_hash, 28)) { + 8007ea4: 2800 cmp r0, #0 + 8007ea6: bf18 it ne + 8007ea8: 4626 movne r6, r4 + blank |= (!!check_all_zeros(here, 32)) << i; + 8007eaa: 4628 mov r0, r5 + 8007eac: f7fa fbf2 bl 8002694 + 8007eb0: 40a0 lsls r0, r4 + for(int i=0; i + rng_delay(); + 8007ec0: f7fa fc5c bl 800277c + memset(found_slot, 0, sizeof(trick_slot_t)); + 8007ec4: 9800 ldr r0, [sp, #0] + 8007ec6: 2280 movs r2, #128 ; 0x80 + 8007ec8: 2100 movs r1, #0 + 8007eca: f005 fbd3 bl 800d674 + if(safety_mode) { + 8007ece: 9b01 ldr r3, [sp, #4] + 8007ed0: b10b cbz r3, 8007ed6 + found_slot->blank_slots = blank; + 8007ed2: 9b00 ldr r3, [sp, #0] + 8007ed4: 65df str r7, [r3, #92] ; 0x5c + if(found >= 0) { + 8007ed6: 1c72 adds r2, r6, #1 + 8007ed8: d069 beq.n 8007fae + found_slot->slot_num = found; + 8007eda: 9b00 ldr r3, [sp, #0] + 8007edc: 0174 lsls r4, r6, #5 + 8007ede: 601e str r6, [r3, #0] + memcpy(meta, &slots[found][28], 4); + 8007ee0: ab15 add r3, sp, #84 ; 0x54 + xor_mixin(meta, &tpin_hash[28], 4); + 8007ee2: 2204 movs r2, #4 + memcpy(meta, &slots[found][28], 4); + 8007ee4: 591b ldr r3, [r3, r4] + 8007ee6: 9305 str r3, [sp, #20] + xor_mixin(meta, &tpin_hash[28], 4); + 8007ee8: a90d add r1, sp, #52 ; 0x34 + 8007eea: a805 add r0, sp, #20 + 8007eec: f7ff f982 bl 80071f4 + memcpy(&found_slot->tc_flags, &meta[0], 2); + 8007ef0: 9b00 ldr r3, [sp, #0] + 8007ef2: f8bd 5014 ldrh.w r5, [sp, #20] + memcpy(&found_slot->tc_arg, &meta[2], 2); + 8007ef6: 9a00 ldr r2, [sp, #0] + memcpy(&found_slot->tc_flags, &meta[0], 2); + 8007ef8: 809d strh r5, [r3, #4] + memcpy(&found_slot->tc_arg, &meta[2], 2); + 8007efa: f8bd 3016 ldrh.w r3, [sp, #22] + 8007efe: 80d3 strh r3, [r2, #6] + if(todo & TC_WORD_WALLET) { + 8007f00: 04eb lsls r3, r5, #19 + 8007f02: d513 bpl.n 8007f2c + if(found+1 < NUM_TRICKS) { + 8007f04: 2e0c cmp r6, #12 + 8007f06: dc0e bgt.n 8007f26 + memcpy(found_slot->xdata, &slots[found+1][0], 32); + 8007f08: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 8007f0c: eb0d 0403 add.w r4, sp, r3 + 8007f10: f5a4 73d0 sub.w r3, r4, #416 ; 0x1a0 + 8007f14: 3208 adds r2, #8 + 8007f16: f5a4 74c0 sub.w r4, r4, #384 ; 0x180 + 8007f1a: f853 1b04 ldr.w r1, [r3], #4 + 8007f1e: f842 1b04 str.w r1, [r2], #4 + 8007f22: 42a3 cmp r3, r4 + 8007f24: d1f9 bne.n 8007f1a + if(!safety_mode && todo) { + 8007f26: 9b01 ldr r3, [sp, #4] + 8007f28: b33b cbz r3, 8007f7a + 8007f2a: e03e b.n 8007faa + } else if(todo & TC_XPRV_WALLET) { + 8007f2c: 052f lsls r7, r5, #20 + 8007f2e: d521 bpl.n 8007f74 + if(found+2 < NUM_TRICKS) { + 8007f30: 2e0b cmp r6, #11 + 8007f32: dcf8 bgt.n 8007f26 + memcpy(&found_slot->xdata[0], &slots[found+1][0], 32); + 8007f34: 9900 ldr r1, [sp, #0] + 8007f36: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 8007f3a: 446b add r3, sp + 8007f3c: f5a3 72d0 sub.w r2, r3, #416 ; 0x1a0 + 8007f40: 3108 adds r1, #8 + 8007f42: f5a3 73c0 sub.w r3, r3, #384 ; 0x180 + 8007f46: f852 0b04 ldr.w r0, [r2], #4 + 8007f4a: f841 0b04 str.w r0, [r1], #4 + 8007f4e: 429a cmp r2, r3 + 8007f50: d1f9 bne.n 8007f46 + memcpy(&found_slot->xdata[32], &slots[found+2][0], 32); + 8007f52: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 8007f56: 9a00 ldr r2, [sp, #0] + 8007f58: eb0d 0403 add.w r4, sp, r3 + 8007f5c: f5a4 73c0 sub.w r3, r4, #384 ; 0x180 + 8007f60: 3228 adds r2, #40 ; 0x28 + 8007f62: f5a4 74b0 sub.w r4, r4, #352 ; 0x160 + 8007f66: f853 1b04 ldr.w r1, [r3], #4 + 8007f6a: f842 1b04 str.w r1, [r2], #4 + 8007f6e: 42a3 cmp r3, r4 + 8007f70: d1f9 bne.n 8007f66 + 8007f72: e7d8 b.n 8007f26 + if(!safety_mode && todo) { + 8007f74: 9b01 ldr r3, [sp, #4] + 8007f76: b9c3 cbnz r3, 8007faa + 8007f78: b1bd cbz r5, 8007faa + if(todo & TC_WIPE) { + 8007f7a: 0428 lsls r0, r5, #16 + 8007f7c: d50a bpl.n 8007f94 + mcu_key_clear(NULL); + 8007f7e: 2000 movs r0, #0 + 8007f80: f7fa fa6a bl 8002458 + if(todo == TC_WIPE) { + 8007f84: f5b5 4f00 cmp.w r5, #32768 ; 0x8000 + 8007f88: d104 bne.n 8007f94 + oled_show(screen_wiped); + 8007f8a: 480d ldr r0, [pc, #52] ; (8007fc0 ) + 8007f8c: f7f8 ff5a bl 8000e44 + LOCKUP_FOREVER(); + 8007f90: bf30 wfi + 8007f92: e7fd b.n 8007f90 + if(todo & TC_BRICK) { + 8007f94: 0469 lsls r1, r5, #17 + 8007f96: d403 bmi.n 8007fa0 + if(todo & TC_REBOOT) { + 8007f98: 05aa lsls r2, r5, #22 + 8007f9a: d504 bpl.n 8007fa6 + NVIC_SystemReset(); + 8007f9c: f7ff f918 bl 80071d0 <__NVIC_SystemReset> + fast_brick(); + 8007fa0: f7fa fb1e bl 80025e0 + 8007fa4: e7f8 b.n 8007f98 + if(todo & TC_FAKE_OUT) { + 8007fa6: 04ab lsls r3, r5, #18 + 8007fa8: d401 bmi.n 8007fae + return true; + 8007faa: 2001 movs r0, #1 + 8007fac: e745 b.n 8007e3a + found_slot->slot_num = -1; + 8007fae: 9a00 ldr r2, [sp, #0] + 8007fb0: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8007fb4: 6013 str r3, [r2, #0] + rng_delay(); + 8007fb6: f7fa fbe1 bl 800277c + 8007fba: e73d b.n 8007e38 + 8007fbc: 2009e390 .word 0x2009e390 + 8007fc0: 0800e310 .word 0x0800e310 + 8007fc4: 0801c000 .word 0x0801c000 + 8007fc8: 2009e2b0 .word 0x2009e2b0 + 8007fcc: 0801c0b0 .word 0x0801c0b0 + +08007fd0 : +{ + 8007fd0: b510 push {r4, lr} + 8007fd2: b0a2 sub sp, #136 ; 0x88 + 8007fd4: 4604 mov r4, r0 + bool is_trick = se2_test_trick_pin("!p", 2, &slot, true); + 8007fd6: 2301 movs r3, #1 + 8007fd8: 481d ldr r0, [pc, #116] ; (8008050 ) + 8007fda: aa02 add r2, sp, #8 + 8007fdc: 2102 movs r1, #2 + 8007fde: f7ff ff1b bl 8007e18 + if(!is_trick) return; + 8007fe2: b390 cbz r0, 800804a + if(num_fails >= slot.tc_arg) { + 8007fe4: f8bd 300e ldrh.w r3, [sp, #14] + 8007fe8: 42a3 cmp r3, r4 + 8007fea: dc2e bgt.n 800804a + if(slot.tc_flags & TC_WIPE) { + 8007fec: f9bd 300c ldrsh.w r3, [sp, #12] + 8007ff0: f8bd 000c ldrh.w r0, [sp, #12] + 8007ff4: 2b00 cmp r3, #0 + 8007ff6: da1c bge.n 8008032 + if(slot.tc_flags & TC_BRICK) { + 8007ff8: f410 4080 ands.w r0, r0, #16384 ; 0x4000 + 8007ffc: d00d beq.n 800801a + const mcu_key_t *cur = mcu_key_get(&valid); + 8007ffe: f10d 0007 add.w r0, sp, #7 + 8008002: f7fa fa09 bl 8002418 + if(valid) { + 8008006: f89d 3007 ldrb.w r3, [sp, #7] + 800800a: b193 cbz r3, 8008032 + mcu_key_clear(cur); + 800800c: f7fa fa24 bl 8002458 + oled_show(screen_wiped); + 8008010: 4810 ldr r0, [pc, #64] ; (8008054 ) + 8008012: f7f8 ff17 bl 8000e44 + LOCKUP_FOREVER(); + 8008016: bf30 wfi + 8008018: e7fd b.n 8008016 + mcu_key_clear(NULL); // does valid key check + 800801a: f7fa fa1d bl 8002458 + if(slot.tc_flags == TC_WIPE) { + 800801e: f8bd 300c ldrh.w r3, [sp, #12] + 8008022: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + 8008026: d104 bne.n 8008032 + oled_show(screen_wiped); + 8008028: 480a ldr r0, [pc, #40] ; (8008054 ) + 800802a: f7f8 ff0b bl 8000e44 + LOCKUP_FOREVER(); + 800802e: bf30 wfi + 8008030: e7fd b.n 800802e + if(slot.tc_flags & TC_BRICK) { + 8008032: f8bd 300c ldrh.w r3, [sp, #12] + 8008036: 045a lsls r2, r3, #17 + 8008038: d501 bpl.n 800803e + fast_brick(); + 800803a: f7fa fad1 bl 80025e0 + if(slot.tc_flags & TC_REBOOT) { + 800803e: f8bd 300c ldrh.w r3, [sp, #12] + 8008042: 059b lsls r3, r3, #22 + 8008044: d501 bpl.n 800804a + NVIC_SystemReset(); + 8008046: f7ff f8c3 bl 80071d0 <__NVIC_SystemReset> +} + 800804a: b022 add sp, #136 ; 0x88 + 800804c: bd10 pop {r4, pc} + 800804e: bf00 nop + 8008050: 0800ea2d .word 0x0800ea2d + 8008054: 0800e310 .word 0x0800e310 + +08008058 : +{ + 8008058: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800805c: b094 sub sp, #80 ; 0x50 + 800805e: 9001 str r0, [sp, #4] + se2_setup(); + 8008060: f7ff fe2e bl 8007cc0 + if(setjmp(error_env)) { + 8008064: 4848 ldr r0, [pc, #288] ; (8008188 ) + 8008066: f005 fb0d bl 800d684 + 800806a: 4604 mov r4, r0 + 800806c: 2800 cmp r0, #0 + 800806e: f040 8088 bne.w 8008182 + if((config->slot_num < 0) || (config->slot_num >= NUM_TRICKS) ) { + 8008072: 9b01 ldr r3, [sp, #4] + 8008074: 681b ldr r3, [r3, #0] + 8008076: 2b0d cmp r3, #13 + 8008078: d804 bhi.n 8008084 + if((config->slot_num >= NUM_TRICKS-1) && (config->tc_flags & TC_WORD_WALLET) ) { + 800807a: d106 bne.n 800808a + 800807c: 9b01 ldr r3, [sp, #4] + 800807e: 889b ldrh r3, [r3, #4] + 8008080: 04d9 lsls r1, r3, #19 + 8008082: d504 bpl.n 800808e + return EPIN_RANGE_ERR; + 8008084: f06f 0466 mvn.w r4, #102 ; 0x66 + 8008088: e01f b.n 80080ca + if((config->slot_num >= NUM_TRICKS-2) && (config->tc_flags & TC_XPRV_WALLET) ) { + 800808a: 2b0c cmp r3, #12 + 800808c: d103 bne.n 8008096 + 800808e: 9b01 ldr r3, [sp, #4] + 8008090: 889b ldrh r3, [r3, #4] + 8008092: 051a lsls r2, r3, #20 + 8008094: d4f6 bmi.n 8008084 + if(config->pin_len > sizeof(config->pin)) { + 8008096: 9b01 ldr r3, [sp, #4] + 8008098: 6d99 ldr r1, [r3, #88] ; 0x58 + 800809a: 2910 cmp r1, #16 + 800809c: d8f2 bhi.n 8008084 + if(config->blank_slots) { + 800809e: 6ddd ldr r5, [r3, #92] ; 0x5c + 80080a0: b31d cbz r5, 80080ea + uint8_t zeros[32] = { 0 }; + 80080a2: 2100 movs r1, #0 + 80080a4: 221c movs r2, #28 + 80080a6: a805 add r0, sp, #20 + 80080a8: 9104 str r1, [sp, #16] + 80080aa: f005 fae3 bl 800d674 + se2_write_encrypted(PGN_TRICK(i), zeros, 0, SE2_SECRETS->pairing); + 80080ae: f8df 80e4 ldr.w r8, [pc, #228] ; 8008194 + 80080b2: 4f36 ldr r7, [pc, #216] ; (800818c ) + 80080b4: 4e36 ldr r6, [pc, #216] ; (8008190 ) + for(int i=0; iblank_slots) { + 80080b8: 9a01 ldr r2, [sp, #4] + uint32_t mask = (1 << i); + 80080ba: 2301 movs r3, #1 + if(mask & config->blank_slots) { + 80080bc: 6dd2 ldr r2, [r2, #92] ; 0x5c + uint32_t mask = (1 << i); + 80080be: 40ab lsls r3, r5 + if(mask & config->blank_slots) { + 80080c0: 4213 tst r3, r2 + 80080c2: d106 bne.n 80080d2 + for(int i=0; i +} + 80080ca: 4620 mov r0, r4 + 80080cc: b014 add sp, #80 ; 0x50 + 80080ce: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + se2_write_encrypted(PGN_TRICK(i), zeros, 0, SE2_SECRETS->pairing); + 80080d2: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + 80080d6: 2200 movs r2, #0 + 80080d8: 2bff cmp r3, #255 ; 0xff + 80080da: bf0c ite eq + 80080dc: 463b moveq r3, r7 + 80080de: 4633 movne r3, r6 + 80080e0: a904 add r1, sp, #16 + 80080e2: b2e8 uxtb r0, r5 + 80080e4: f7ff fa9c bl 8007620 + 80080e8: e7ec b.n 80080c4 + trick_pin_hash(config->pin, config->pin_len, tpin_digest); + 80080ea: 9b01 ldr r3, [sp, #4] + se2_write_encrypted(PGN_TRICK(config->slot_num), tpin_digest, 0, SE2_SECRETS->pairing); + 80080ec: f8df 80a4 ldr.w r8, [pc, #164] ; 8008194 + 80080f0: 4f26 ldr r7, [pc, #152] ; (800818c ) + 80080f2: 4e27 ldr r6, [pc, #156] ; (8008190 ) + trick_pin_hash(config->pin, config->pin_len, tpin_digest); + 80080f4: f103 0048 add.w r0, r3, #72 ; 0x48 + 80080f8: aa0c add r2, sp, #48 ; 0x30 + 80080fa: f7ff fcd5 bl 8007aa8 + memcpy(&meta[0], &config->tc_flags, 2); + 80080fe: 9b01 ldr r3, [sp, #4] + 8008100: 889b ldrh r3, [r3, #4] + 8008102: f8ad 300c strh.w r3, [sp, #12] + memcpy(&meta[2], &config->tc_arg, 2); + 8008106: 9b01 ldr r3, [sp, #4] + xor_mixin(&tpin_digest[28], meta, 4); + 8008108: 2204 movs r2, #4 + memcpy(&meta[2], &config->tc_arg, 2); + 800810a: 88db ldrh r3, [r3, #6] + 800810c: f8ad 300e strh.w r3, [sp, #14] + xor_mixin(&tpin_digest[28], meta, 4); + 8008110: a903 add r1, sp, #12 + 8008112: a813 add r0, sp, #76 ; 0x4c + 8008114: f7ff f86e bl 80071f4 + se2_write_encrypted(PGN_TRICK(config->slot_num), tpin_digest, 0, SE2_SECRETS->pairing); + 8008118: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + 800811c: 9801 ldr r0, [sp, #4] + 800811e: 2bff cmp r3, #255 ; 0xff + 8008120: bf0c ite eq + 8008122: 463b moveq r3, r7 + 8008124: 4633 movne r3, r6 + 8008126: 7800 ldrb r0, [r0, #0] + 8008128: 462a mov r2, r5 + 800812a: a90c add r1, sp, #48 ; 0x30 + 800812c: f7ff fa78 bl 8007620 + if(config->tc_flags & (TC_WORD_WALLET | TC_XPRV_WALLET)) { + 8008130: 9b01 ldr r3, [sp, #4] + 8008132: 889b ldrh r3, [r3, #4] + 8008134: f403 53c0 and.w r3, r3, #6144 ; 0x1800 + 8008138: b9a3 cbnz r3, 8008164 + if(config->tc_flags & TC_XPRV_WALLET) { + 800813a: 9b01 ldr r3, [sp, #4] + 800813c: 889b ldrh r3, [r3, #4] + 800813e: 051b lsls r3, r3, #20 + 8008140: d5c3 bpl.n 80080ca + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 8008142: 9901 ldr r1, [sp, #4] + 0, SE2_SECRETS->pairing); + 8008144: 4b13 ldr r3, [pc, #76] ; (8008194 ) + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 8008146: f851 0b28 ldr.w r0, [r1], #40 + 0, SE2_SECRETS->pairing); + 800814a: f893 50b0 ldrb.w r5, [r3, #176] ; 0xb0 + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 800814e: 4a10 ldr r2, [pc, #64] ; (8008190 ) + 8008150: 4b0e ldr r3, [pc, #56] ; (800818c ) + 8008152: 3002 adds r0, #2 + 8008154: 2dff cmp r5, #255 ; 0xff + 8008156: bf18 it ne + 8008158: 4613 movne r3, r2 + 800815a: b2c0 uxtb r0, r0 + 800815c: 2200 movs r2, #0 + 800815e: f7ff fa5f bl 8007620 + 8008162: e7b2 b.n 80080ca + se2_write_encrypted(PGN_TRICK(config->slot_num+1), &config->xdata[0], + 8008164: 9901 ldr r1, [sp, #4] + 0, SE2_SECRETS->pairing); + 8008166: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + se2_write_encrypted(PGN_TRICK(config->slot_num+1), &config->xdata[0], + 800816a: f851 0b08 ldr.w r0, [r1], #8 + 800816e: 3001 adds r0, #1 + 8008170: 2bff cmp r3, #255 ; 0xff + 8008172: bf0c ite eq + 8008174: 463b moveq r3, r7 + 8008176: 4633 movne r3, r6 + 8008178: 462a mov r2, r5 + 800817a: b2c0 uxtb r0, r0 + 800817c: f7ff fa50 bl 8007620 + 8008180: e7db b.n 800813a + return EPIN_SE2_FAIL; + 8008182: f06f 0472 mvn.w r4, #114 ; 0x72 + 8008186: e7a0 b.n 80080ca + 8008188: 2009e390 .word 0x2009e390 + 800818c: 2009e2b0 .word 0x2009e2b0 + 8008190: 0801c0b0 .word 0x0801c0b0 + 8008194: 0801c000 .word 0x0801c000 + +08008198 : +// + bool +se2_encrypt_secret(const uint8_t secret[], int secret_len, int offset, + uint8_t main_slot[], uint8_t *check_value, + const uint8_t pin_digest[32]) +{ + 8008198: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 800819c: f5ad 7d10 sub.w sp, sp, #576 ; 0x240 + 80081a0: 4699 mov r9, r3 + 80081a2: 4682 mov sl, r0 + 80081a4: 460f mov r7, r1 + 80081a6: 4614 mov r4, r2 + 80081a8: f8dd 8260 ldr.w r8, [sp, #608] ; 0x260 + se2_setup(); + 80081ac: f7ff fd88 bl 8007cc0 + + bool is_valid; + const mcu_key_t *cur = mcu_key_get(&is_valid); + 80081b0: f10d 000b add.w r0, sp, #11 + 80081b4: f7fa f930 bl 8002418 + + if(!is_valid) { + 80081b8: f89d 300b ldrb.w r3, [sp, #11] + 80081bc: b953 cbnz r3, 80081d4 + if(!check_value) { + 80081be: f1b8 0f00 cmp.w r8, #0 + 80081c2: d105 bne.n 80081d0 + // problem: we are not writing the check value but it would be changed. + // ie: change long secret before real secret--unlikely + return true; + 80081c4: 2501 movs r5, #1 + ctx.num_pending = 32; + aes_done(&ctx, check_value, 32, aes_key, nonce); + } + + return false; +} + 80081c6: 4628 mov r0, r5 + 80081c8: f50d 7d10 add.w sp, sp, #576 ; 0x240 + 80081cc: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + cur = mcu_key_pick(); + 80081d0: f7fa f98a bl 80024e8 + if(se2_calc_seed_key(aes_key, cur, pin_digest)) return true; + 80081d4: 4601 mov r1, r0 + 80081d6: 9a99 ldr r2, [sp, #612] ; 0x264 + 80081d8: a807 add r0, sp, #28 + 80081da: f7ff fd3f bl 8007c5c + 80081de: 4605 mov r5, r0 + 80081e0: 2800 cmp r0, #0 + 80081e2: d1ef bne.n 80081c4 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80081e4: 4b16 ldr r3, [pc, #88] ; (8008240 ) + 80081e6: cb0f ldmia r3, {r0, r1, r2, r3} + 80081e8: ae03 add r6, sp, #12 + 80081ea: 46b4 mov ip, r6 + 80081ec: e8ac 0007 stmia.w ip!, {r0, r1, r2} + nonce[15] = offset / AES_BLOCK_SIZE; + 80081f0: 2c00 cmp r4, #0 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80081f2: f82c 3b02 strh.w r3, [ip], #2 + nonce[15] = offset / AES_BLOCK_SIZE; + 80081f6: bfb8 it lt + 80081f8: 340f addlt r4, #15 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80081fa: 0c1b lsrs r3, r3, #16 + 80081fc: f88c 3000 strb.w r3, [ip] + aes_init(&ctx); + 8008200: a80f add r0, sp, #60 ; 0x3c + nonce[15] = offset / AES_BLOCK_SIZE; + 8008202: 1124 asrs r4, r4, #4 + 8008204: 73f4 strb r4, [r6, #15] + aes_init(&ctx); + 8008206: f000 f92b bl 8008460 + aes_add(&ctx, secret, secret_len); + 800820a: 463a mov r2, r7 + 800820c: 4651 mov r1, sl + 800820e: a80f add r0, sp, #60 ; 0x3c + 8008210: f000 f92c bl 800846c + aes_done(&ctx, main_slot, secret_len, aes_key, nonce); + 8008214: 9600 str r6, [sp, #0] + 8008216: ab07 add r3, sp, #28 + 8008218: 463a mov r2, r7 + 800821a: 4649 mov r1, r9 + 800821c: a80f add r0, sp, #60 ; 0x3c + 800821e: f000 f93b bl 8008498 + if(check_value) { + 8008222: f1b8 0f00 cmp.w r8, #0 + 8008226: d0ce beq.n 80081c6 + aes_init(&ctx); + 8008228: a80f add r0, sp, #60 ; 0x3c + 800822a: f000 f919 bl 8008460 + ctx.num_pending = 32; + 800822e: 2220 movs r2, #32 + aes_done(&ctx, check_value, 32, aes_key, nonce); + 8008230: 9600 str r6, [sp, #0] + 8008232: ab07 add r3, sp, #28 + 8008234: 4641 mov r1, r8 + 8008236: a80f add r0, sp, #60 ; 0x3c + ctx.num_pending = 32; + 8008238: 928f str r2, [sp, #572] ; 0x23c + aes_done(&ctx, check_value, 32, aes_key, nonce); + 800823a: f000 f92d bl 8008498 + 800823e: e7c2 b.n 80081c6 + 8008240: 0801c090 .word 0x0801c090 + +08008244 : +// + void +se2_decrypt_secret(uint8_t secret[], int secret_len, int offset, + const uint8_t main_slot[], const uint8_t *check_value, + const uint8_t pin_digest[32], bool *is_valid) +{ + 8008244: b530 push {r4, r5, lr} + 8008246: f5ad 7d1f sub.w sp, sp, #636 ; 0x27c + 800824a: e9cd 2306 strd r2, r3, [sp, #24] + 800824e: 9005 str r0, [sp, #20] + 8008250: 9103 str r1, [sp, #12] + se2_setup(); + 8008252: f7ff fd35 bl 8007cc0 + + const mcu_key_t *cur = mcu_key_get(is_valid); + 8008256: 98a4 ldr r0, [sp, #656] ; 0x290 + 8008258: f7fa f8de bl 8002418 + if(!*is_valid) { + 800825c: 9ba4 ldr r3, [sp, #656] ; 0x290 + const mcu_key_t *cur = mcu_key_get(is_valid); + 800825e: 9004 str r0, [sp, #16] + if(!*is_valid) { + 8008260: 781b ldrb r3, [r3, #0] + 8008262: b133 cbz r3, 8008272 + // no key set? won't be able to decrypt. + return; + } + + int line_num; + if((line_num = setjmp(error_env))) { + 8008264: 4825 ldr r0, [pc, #148] ; (80082fc ) + 8008266: f005 fa0d bl 800d684 + 800826a: b128 cbz r0, 8008278 + // internal failures / broken i2c buses will come here + *is_valid = false; + 800826c: 9aa4 ldr r2, [sp, #656] ; 0x290 + 800826e: 2300 movs r3, #0 + 8008270: 7013 strb r3, [r2, #0] + + // decrypt the real data + aes_init(&ctx); + aes_add(&ctx, main_slot, secret_len); + aes_done(&ctx, secret, secret_len, aes_key, nonce); +} + 8008272: f50d 7d1f add.w sp, sp, #636 ; 0x27c + 8008276: bd30 pop {r4, r5, pc} + if(se2_calc_seed_key(aes_key, cur, pin_digest)) { + 8008278: 9aa3 ldr r2, [sp, #652] ; 0x28c + 800827a: 9904 ldr r1, [sp, #16] + 800827c: a80d add r0, sp, #52 ; 0x34 + 800827e: f7ff fced bl 8007c5c + 8008282: 2800 cmp r0, #0 + 8008284: d1f2 bne.n 800826c + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 8008286: 4b1e ldr r3, [pc, #120] ; (8008300 ) + 8008288: cb0f ldmia r3, {r0, r1, r2, r3} + 800828a: ad09 add r5, sp, #36 ; 0x24 + 800828c: 462c mov r4, r5 + 800828e: c407 stmia r4!, {r0, r1, r2} + 8008290: f824 3b02 strh.w r3, [r4], #2 + 8008294: 0c1b lsrs r3, r3, #16 + 8008296: 7023 strb r3, [r4, #0] + nonce[15] = offset / AES_BLOCK_SIZE; + 8008298: 9b06 ldr r3, [sp, #24] + 800829a: 2b00 cmp r3, #0 + 800829c: bfb8 it lt + 800829e: 330f addlt r3, #15 + 80082a0: 111b asrs r3, r3, #4 + 80082a2: 73eb strb r3, [r5, #15] + if(check_value) { + 80082a4: 9ba2 ldr r3, [sp, #648] ; 0x288 + 80082a6: b1bb cbz r3, 80082d8 + aes_init(&ctx); + 80082a8: a81d add r0, sp, #116 ; 0x74 + 80082aa: f000 f8d9 bl 8008460 + aes_add(&ctx, check_value, 32); + 80082ae: 99a2 ldr r1, [sp, #648] ; 0x288 + 80082b0: 2220 movs r2, #32 + 80082b2: a81d add r0, sp, #116 ; 0x74 + 80082b4: f000 f8da bl 800846c + aes_done(&ctx, got, 32, aes_key, nonce); + 80082b8: ab09 add r3, sp, #36 ; 0x24 + 80082ba: 9300 str r3, [sp, #0] + 80082bc: a915 add r1, sp, #84 ; 0x54 + 80082be: a81d add r0, sp, #116 ; 0x74 + 80082c0: ab0d add r3, sp, #52 ; 0x34 + 80082c2: 2220 movs r2, #32 + 80082c4: f000 f8e8 bl 8008498 + if(!check_all_zeros(got, 32)) { + 80082c8: 2120 movs r1, #32 + 80082ca: a815 add r0, sp, #84 ; 0x54 + 80082cc: f7fa f9e2 bl 8002694 + 80082d0: b910 cbnz r0, 80082d8 + *is_valid = false; + 80082d2: 9ba4 ldr r3, [sp, #656] ; 0x290 + 80082d4: 7018 strb r0, [r3, #0] + return; + 80082d6: e7cc b.n 8008272 + aes_init(&ctx); + 80082d8: a81d add r0, sp, #116 ; 0x74 + 80082da: f000 f8c1 bl 8008460 + aes_add(&ctx, main_slot, secret_len); + 80082de: 9a03 ldr r2, [sp, #12] + 80082e0: 9907 ldr r1, [sp, #28] + 80082e2: a81d add r0, sp, #116 ; 0x74 + 80082e4: f000 f8c2 bl 800846c + aes_done(&ctx, secret, secret_len, aes_key, nonce); + 80082e8: ab09 add r3, sp, #36 ; 0x24 + 80082ea: 9300 str r3, [sp, #0] + 80082ec: 9a03 ldr r2, [sp, #12] + 80082ee: 9905 ldr r1, [sp, #20] + 80082f0: ab0d add r3, sp, #52 ; 0x34 + 80082f2: a81d add r0, sp, #116 ; 0x74 + 80082f4: f000 f8d0 bl 8008498 + 80082f8: e7bb b.n 8008272 + 80082fa: bf00 nop + 80082fc: 2009e390 .word 0x2009e390 + 8008300: 0801c090 .word 0x0801c090 + +08008304 : +// +// Hash up a PIN code for login attempt: to tie it into SE2's contents. +// + void +se2_pin_hash(uint8_t digest_io[32], uint32_t purpose) +{ + 8008304: b5f0 push {r4, r5, r6, r7, lr} + if(purpose != PIN_PURPOSE_NORMAL) { + 8008306: 4b41 ldr r3, [pc, #260] ; (800840c ) +{ + 8008308: b0d5 sub sp, #340 ; 0x154 + if(purpose != PIN_PURPOSE_NORMAL) { + 800830a: 4299 cmp r1, r3 +{ + 800830c: e9cd 0100 strd r0, r1, [sp] + if(purpose != PIN_PURPOSE_NORMAL) { + 8008310: d17a bne.n 8008408 + // do nothing except for real PIN case (ie. not for prefix words) + return; + } + + se2_setup(); + 8008312: f7ff fcd5 bl 8007cc0 + if((setjmp(error_env))) { + 8008316: 483e ldr r0, [pc, #248] ; (8008410 ) + 8008318: f005 f9b4 bl 800d684 + 800831c: 4604 mov r4, r0 + 800831e: b120 cbz r0, 800832a + oled_show(screen_se2_issue); + 8008320: 483c ldr r0, [pc, #240] ; (8008414 ) + 8008322: f7f8 fd8f bl 8000e44 + + LOCKUP_FOREVER(); + 8008326: bf30 wfi + 8008328: e7fd b.n 8008326 + uint8_t rx[34]; // 2 bytes of len+status, then 32 bytes of data + uint8_t tmp[32]; + HMAC_CTX ctx; + + // HMAC(key=tpin_key, msg=given hash so far) + hmac_sha256_init(&ctx); + 800832a: a813 add r0, sp, #76 ; 0x4c + 800832c: f7fd f936 bl 800559c + hmac_sha256_update(&ctx, digest_io, 32); + 8008330: 9900 ldr r1, [sp, #0] + 8008332: 2220 movs r2, #32 + 8008334: a813 add r0, sp, #76 ; 0x4c + 8008336: f7fd f937 bl 80055a8 + hmac_sha256_update(&ctx, (uint8_t *)&purpose, 4); + 800833a: 2204 movs r2, #4 + 800833c: eb0d 0102 add.w r1, sp, r2 + 8008340: a813 add r0, sp, #76 ; 0x4c + 8008342: f7fd f931 bl 80055a8 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, tmp); + 8008346: 4b34 ldr r3, [pc, #208] ; (8008418 ) + 8008348: 4934 ldr r1, [pc, #208] ; (800841c ) + 800834a: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 800834e: 33b0 adds r3, #176 ; 0xb0 + 8008350: 2aff cmp r2, #255 ; 0xff + 8008352: bf18 it ne + 8008354: 4619 movne r1, r3 + 8008356: 3180 adds r1, #128 ; 0x80 + 8008358: aa02 add r2, sp, #8 + 800835a: a813 add r0, sp, #76 ; 0x4c + 800835c: f7fd f93a bl 80055d4 + + // NOTE: exposed as cleartext here + se2_write_buffer(tmp, 32); + 8008360: 2120 movs r1, #32 + 8008362: a802 add r0, sp, #8 + 8008364: f7fe ffc2 bl 80072ec + 8008368: 25aa movs r5, #170 ; 0xaa + se2_write_buffer(rx+2, 32); + } + + // HMAC(key=secret-B, msg=consts+easy_key+buffer+consts) + // - result put in secret-S (ram) + CALL_CHECK(se2_write2(0x3c, (2<<6) | (1<<4) | PGN_SE2_EASY_KEY, 0)); + 800836a: 269e movs r6, #158 ; 0x9e + 800836c: 273c movs r7, #60 ; 0x3c + 800836e: 4622 mov r2, r4 + 8008370: 4631 mov r1, r6 + 8008372: 4638 mov r0, r7 + 8008374: f7fe ff66 bl 8007244 + 8008378: b150 cbz r0, 8008390 + 800837a: f240 511d movw r1, #1309 ; 0x51d + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 800837e: 4824 ldr r0, [pc, #144] ; (8008410 ) + 8008380: f005 f986 bl 800d690 + se2_write_buffer(rx+2, 32); + 8008384: 2120 movs r1, #32 + 8008386: f10d 002a add.w r0, sp, #42 ; 0x2a + 800838a: f7fe ffaf bl 80072ec + 800838e: e7ee b.n 800836e + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8008390: f7fe ffe4 bl 800735c + 8008394: 28aa cmp r0, #170 ; 0xaa + 8008396: d002 beq.n 800839e + 8008398: f240 511e movw r1, #1310 ; 0x51e + 800839c: e7ef b.n 800837e + + // HMAC(key=S, msg=counter+junk), so we have something to read out + // - not 100% clear what contents of 'buffer' are here, but seems + // to be deterministic and unchanged from prev command + CALL_CHECK(se2_write1(0xa5, (2<<5) | PGN_DEC_COUNTER)); + 800839e: 215b movs r1, #91 ; 0x5b + 80083a0: 20a5 movs r0, #165 ; 0xa5 + 80083a2: f7fe ff35 bl 8007210 + 80083a6: b110 cbz r0, 80083ae + 80083a8: f240 5123 movw r1, #1315 ; 0x523 + 80083ac: e7e7 b.n 800837e + + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 80083ae: a90a add r1, sp, #40 ; 0x28 + 80083b0: 2022 movs r0, #34 ; 0x22 + 80083b2: f7fe ffab bl 800730c + 80083b6: 28aa cmp r0, #170 ; 0xaa + 80083b8: d002 beq.n 80083c0 + 80083ba: f240 5125 movw r1, #1317 ; 0x525 + 80083be: e7de b.n 800837e + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 80083c0: f89d 3029 ldrb.w r3, [sp, #41] ; 0x29 + 80083c4: 2baa cmp r3, #170 ; 0xaa + 80083c6: d002 beq.n 80083ce + 80083c8: f240 5126 movw r1, #1318 ; 0x526 + 80083cc: e7d7 b.n 800837e + for(int i=0; i + } + + // one final HMAC because we had to read cleartext from bus + hmac_sha256_init(&ctx); + 80083d2: a813 add r0, sp, #76 ; 0x4c + 80083d4: f7fd f8e2 bl 800559c + hmac_sha256_update(&ctx, rx+2, 32); + 80083d8: 2220 movs r2, #32 + 80083da: f10d 012a add.w r1, sp, #42 ; 0x2a + 80083de: a813 add r0, sp, #76 ; 0x4c + 80083e0: f7fd f8e2 bl 80055a8 + hmac_sha256_update(&ctx, digest_io, 32); + 80083e4: 9900 ldr r1, [sp, #0] + 80083e6: 2220 movs r2, #32 + 80083e8: a813 add r0, sp, #76 ; 0x4c + 80083ea: f7fd f8dd bl 80055a8 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, digest_io); + 80083ee: 4b0a ldr r3, [pc, #40] ; (8008418 ) + 80083f0: 490a ldr r1, [pc, #40] ; (800841c ) + 80083f2: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80083f6: 33b0 adds r3, #176 ; 0xb0 + 80083f8: 2aff cmp r2, #255 ; 0xff + 80083fa: bf18 it ne + 80083fc: 4619 movne r1, r3 + 80083fe: 3180 adds r1, #128 ; 0x80 + 8008400: 9a00 ldr r2, [sp, #0] + 8008402: a813 add r0, sp, #76 ; 0x4c + 8008404: f7fd f8e6 bl 80055d4 +} + 8008408: b055 add sp, #340 ; 0x154 + 800840a: bdf0 pop {r4, r5, r6, r7, pc} + 800840c: 334d1858 .word 0x334d1858 + 8008410: 2009e390 .word 0x2009e390 + 8008414: 0800dfce .word 0x0800dfce + 8008418: 0801c000 .word 0x0801c000 + 800841c: 2009e2b0 .word 0x2009e2b0 + +08008420 : +// +// Read some random bytes, which we know cannot be MitM'ed. +// + void +se2_read_rng(uint8_t value[8]) +{ + 8008420: b500 push {lr} + 8008422: b08b sub sp, #44 ; 0x2c + 8008424: 9001 str r0, [sp, #4] + // funny business means MitM here + se2_setup(); + 8008426: f7ff fc4b bl 8007cc0 + if(setjmp(error_env)) fatal_mitm(); + 800842a: 4809 ldr r0, [pc, #36] ; (8008450 ) + 800842c: f005 f92a bl 800d684 + 8008430: b108 cbz r0, 8008436 + 8008432: f7f8 fb13 bl 8000a5c + + // read a field with "RPS" bytes, and verify those were read true + uint8_t tmp[32]; + se2_read_page(PGN_ROM_OPTIONS, tmp, true); + 8008436: a902 add r1, sp, #8 + 8008438: 2201 movs r2, #1 + 800843a: 201c movs r0, #28 + 800843c: f7ff f82a bl 8007494 + + memcpy(value, &tmp[4], 8); + 8008440: ab03 add r3, sp, #12 + 8008442: cb03 ldmia r3!, {r0, r1} + 8008444: 9b01 ldr r3, [sp, #4] + 8008446: 6018 str r0, [r3, #0] + 8008448: 6059 str r1, [r3, #4] +} + 800844a: b00b add sp, #44 ; 0x2c + 800844c: f85d fb04 ldr.w pc, [sp], #4 + 8008450: 2009e390 .word 0x2009e390 + +08008454 : + uint32_t rv; + + if(((uint32_t)src) & 0x3) { + memcpy(&rv, *src, 4); + } else { + rv = *(uint32_t *)(*src); + 8008454: 6803 ldr r3, [r0, #0] + 8008456: f853 2b04 ldr.w r2, [r3], #4 + } + (*src) += 4; + 800845a: 6003 str r3, [r0, #0] + + return __REV(rv); +} + 800845c: ba10 rev r0, r2 + 800845e: 4770 bx lr + +08008460 : + memset(ctx, 0, sizeof(AES_CTX)); + 8008460: f44f 7201 mov.w r2, #516 ; 0x204 + 8008464: 2100 movs r1, #0 + 8008466: f005 b905 b.w 800d674 + ... + +0800846c : +{ + 800846c: b538 push {r3, r4, r5, lr} + 800846e: 4605 mov r5, r0 + memcpy(ctx->pending+ctx->num_pending, data_in, len); + 8008470: f8d0 0200 ldr.w r0, [r0, #512] ; 0x200 + 8008474: 4428 add r0, r5 +{ + 8008476: 4614 mov r4, r2 + memcpy(ctx->pending+ctx->num_pending, data_in, len); + 8008478: f005 f8d4 bl 800d624 + ctx->num_pending += len; + 800847c: f8d5 2200 ldr.w r2, [r5, #512] ; 0x200 + 8008480: 4422 add r2, r4 + ASSERT(ctx->num_pending < sizeof(ctx->pending)); + 8008482: f5b2 7f00 cmp.w r2, #512 ; 0x200 + ctx->num_pending += len; + 8008486: f8c5 2200 str.w r2, [r5, #512] ; 0x200 + ASSERT(ctx->num_pending < sizeof(ctx->pending)); + 800848a: d302 bcc.n 8008492 + 800848c: 4801 ldr r0, [pc, #4] ; (8008494 ) + 800848e: f7f8 fadb bl 8000a48 +} + 8008492: bd38 pop {r3, r4, r5, pc} + 8008494: 0800e3e0 .word 0x0800e3e0 + +08008498 : +// +// Do the decryption. +// + void +aes_done(AES_CTX *ctx, uint8_t data_out[], uint32_t len, const uint8_t key[32], const uint8_t nonce[AES_BLOCK_SIZE]) +{ + 8008498: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 800849c: 4688 mov r8, r1 + 800849e: 4611 mov r1, r2 + ASSERT(len <= ctx->num_pending); + 80084a0: f8d0 2200 ldr.w r2, [r0, #512] ; 0x200 +{ + 80084a4: b085 sub sp, #20 + ASSERT(len <= ctx->num_pending); + 80084a6: 428a cmp r2, r1 +{ + 80084a8: f8dd 9030 ldr.w r9, [sp, #48] ; 0x30 + 80084ac: 4606 mov r6, r0 + ASSERT(len <= ctx->num_pending); + 80084ae: d202 bcs.n 80084b6 + 80084b0: 4858 ldr r0, [pc, #352] ; (8008614 ) + 80084b2: f7f8 fac9 bl 8000a48 + + // enable clock to block + __HAL_RCC_AES_CLK_ENABLE(); + 80084b6: 4d58 ldr r5, [pc, #352] ; (8008618 ) + + // most changes have to be made w/ module disabled + AES->CR &= ~AES_CR_EN; + 80084b8: 4c58 ldr r4, [pc, #352] ; (800861c ) + __HAL_RCC_AES_CLK_ENABLE(); + 80084ba: 6cea ldr r2, [r5, #76] ; 0x4c + 80084bc: f442 3280 orr.w r2, r2, #65536 ; 0x10000 + 80084c0: 64ea str r2, [r5, #76] ; 0x4c + 80084c2: 6cea ldr r2, [r5, #76] ; 0x4c + 80084c4: f402 3280 and.w r2, r2, #65536 ; 0x10000 + 80084c8: 9201 str r2, [sp, #4] + 80084ca: 9a01 ldr r2, [sp, #4] + AES->CR &= ~AES_CR_EN; + 80084cc: 6822 ldr r2, [r4, #0] + 80084ce: f022 0201 bic.w r2, r2, #1 + 80084d2: 6022 str r2, [r4, #0] + + // set the key size and operation mode + MODIFY_REG(AES->CR, AES_CR_KEYSIZE, CRYP_KEYSIZE_256B); + 80084d4: 6822 ldr r2, [r4, #0] + 80084d6: f442 2280 orr.w r2, r2, #262144 ; 0x40000 + 80084da: 6022 str r2, [r4, #0] + MODIFY_REG(AES->CR, AES_CR_DATATYPE|AES_CR_MODE|AES_CR_CHMOD, + 80084dc: 6827 ldr r7, [r4, #0] + 80084de: f427 3780 bic.w r7, r7, #65536 ; 0x10000 + 80084e2: f027 077e bic.w r7, r7, #126 ; 0x7e + 80084e6: f047 0744 orr.w r7, r7, #68 ; 0x44 + 80084ea: 6027 str r7, [r4, #0] + CRYP_DATATYPE_8B | CRYP_ALGOMODE_ENCRYPT | CRYP_CHAINMODE_AES_CTR); + + // load key and IV values + const uint8_t *K = key; + AES->KEYR7 = word_pump_bytes(&K); + 80084ec: a802 add r0, sp, #8 + const uint8_t *K = key; + 80084ee: 9302 str r3, [sp, #8] + AES->KEYR7 = word_pump_bytes(&K); + 80084f0: f7ff ffb0 bl 8008454 + 80084f4: 63e0 str r0, [r4, #60] ; 0x3c + AES->KEYR6 = word_pump_bytes(&K); + 80084f6: a802 add r0, sp, #8 + 80084f8: f7ff ffac bl 8008454 + 80084fc: 63a0 str r0, [r4, #56] ; 0x38 + AES->KEYR5 = word_pump_bytes(&K); + 80084fe: a802 add r0, sp, #8 + 8008500: f7ff ffa8 bl 8008454 + 8008504: 6360 str r0, [r4, #52] ; 0x34 + AES->KEYR4 = word_pump_bytes(&K); + 8008506: a802 add r0, sp, #8 + 8008508: f7ff ffa4 bl 8008454 + 800850c: 6320 str r0, [r4, #48] ; 0x30 + AES->KEYR3 = word_pump_bytes(&K); + 800850e: a802 add r0, sp, #8 + 8008510: f7ff ffa0 bl 8008454 + 8008514: 61e0 str r0, [r4, #28] + AES->KEYR2 = word_pump_bytes(&K); + 8008516: a802 add r0, sp, #8 + 8008518: f7ff ff9c bl 8008454 + 800851c: 61a0 str r0, [r4, #24] + AES->KEYR1 = word_pump_bytes(&K); + 800851e: a802 add r0, sp, #8 + 8008520: f7ff ff98 bl 8008454 + 8008524: 6160 str r0, [r4, #20] + AES->KEYR0 = word_pump_bytes(&K); + 8008526: a802 add r0, sp, #8 + 8008528: f7ff ff94 bl 8008454 + 800852c: 6120 str r0, [r4, #16] + + if(nonce) { + 800852e: f1b9 0f00 cmp.w r9, #0 + 8008532: d045 beq.n 80085c0 + const uint8_t *N = nonce; + AES->IVR3 = word_pump_bytes(&N); + 8008534: a803 add r0, sp, #12 + const uint8_t *N = nonce; + 8008536: f8cd 900c str.w r9, [sp, #12] + AES->IVR3 = word_pump_bytes(&N); + 800853a: f7ff ff8b bl 8008454 + 800853e: 62e0 str r0, [r4, #44] ; 0x2c + AES->IVR2 = word_pump_bytes(&N); + 8008540: a803 add r0, sp, #12 + 8008542: f7ff ff87 bl 8008454 + 8008546: 62a0 str r0, [r4, #40] ; 0x28 + AES->IVR1 = word_pump_bytes(&N); + 8008548: a803 add r0, sp, #12 + 800854a: f7ff ff83 bl 8008454 + 800854e: 6260 str r0, [r4, #36] ; 0x24 + AES->IVR0 = word_pump_bytes(&N); + 8008550: a803 add r0, sp, #12 + 8008552: f7ff ff7f bl 8008454 + 8008556: 6220 str r0, [r4, #32] + AES->IVR1 = 0; + AES->IVR0 = 0; // maybe should be byte-swapped one, but whatever + } + + // Enable the Peripheral + AES->CR |= AES_CR_EN; + 8008558: 4b30 ldr r3, [pc, #192] ; (800861c ) + 800855a: 681a ldr r2, [r3, #0] + + ASSERT((((uint32_t)&ctx->pending) & 3) == 0); // safe because of special attr + 800855c: 07b0 lsls r0, r6, #30 + AES->CR |= AES_CR_EN; + 800855e: f042 0201 orr.w r2, r2, #1 + 8008562: 601a str r2, [r3, #0] + ASSERT((((uint32_t)&ctx->pending) & 3) == 0); // safe because of special attr + 8008564: d1a4 bne.n 80084b0 + + uint32_t *p = (uint32_t *)ctx->pending; + for(int i=0; i < ctx->num_pending; i += 16) { + 8008566: f06f 070f mvn.w r7, #15 + 800856a: f8d6 0200 ldr.w r0, [r6, #512] ; 0x200 + 800856e: f106 0410 add.w r4, r6, #16 + 8008572: 1bbf subs r7, r7, r6 + 8008574: 193a adds r2, r7, r4 + 8008576: 4282 cmp r2, r0 + 8008578: db2b blt.n 80085d2 + *out = AES->DOUTR; out++; + *out = AES->DOUTR; out++; + *out = AES->DOUTR; + } + + memcpy(data_out, ctx->pending, len); + 800857a: 460a mov r2, r1 + 800857c: 4640 mov r0, r8 + 800857e: 4631 mov r1, r6 + 8008580: f005 f850 bl 800d624 + + memset(ctx, 0, sizeof(AES_CTX)); + 8008584: f44f 7201 mov.w r2, #516 ; 0x204 + 8008588: 2100 movs r1, #0 + 800858a: 4630 mov r0, r6 + 800858c: f005 f872 bl 800d674 + + // reset state of chip block, and leave clock off as well + __HAL_RCC_AES_CLK_ENABLE(); + 8008590: 6ceb ldr r3, [r5, #76] ; 0x4c + 8008592: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8008596: 64eb str r3, [r5, #76] ; 0x4c + 8008598: 6ceb ldr r3, [r5, #76] ; 0x4c + 800859a: f403 3380 and.w r3, r3, #65536 ; 0x10000 + 800859e: 9303 str r3, [sp, #12] + 80085a0: 9b03 ldr r3, [sp, #12] + __HAL_RCC_AES_FORCE_RESET(); + 80085a2: 6aeb ldr r3, [r5, #44] ; 0x2c + 80085a4: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 80085a8: 62eb str r3, [r5, #44] ; 0x2c + __HAL_RCC_AES_RELEASE_RESET(); + 80085aa: 6aeb ldr r3, [r5, #44] ; 0x2c + 80085ac: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 80085b0: 62eb str r3, [r5, #44] ; 0x2c + __HAL_RCC_AES_CLK_DISABLE(); + 80085b2: 6ceb ldr r3, [r5, #76] ; 0x4c + 80085b4: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 80085b8: 64eb str r3, [r5, #76] ; 0x4c +} + 80085ba: b005 add sp, #20 + 80085bc: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + AES->IVR3 = 0; + 80085c0: f8c4 902c str.w r9, [r4, #44] ; 0x2c + AES->IVR2 = 0; + 80085c4: f8c4 9028 str.w r9, [r4, #40] ; 0x28 + AES->IVR1 = 0; + 80085c8: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + AES->IVR0 = 0; // maybe should be byte-swapped one, but whatever + 80085cc: f8c4 9020 str.w r9, [r4, #32] + 80085d0: e7c2 b.n 8008558 + AES->DINR = *p; p++; + 80085d2: f854 2c10 ldr.w r2, [r4, #-16] + 80085d6: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80085d8: f854 2c0c ldr.w r2, [r4, #-12] + 80085dc: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80085de: f854 2c08 ldr.w r2, [r4, #-8] + 80085e2: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80085e4: f854 2c04 ldr.w r2, [r4, #-4] + 80085e8: 609a str r2, [r3, #8] + while(HAL_IS_BIT_CLR(AES->SR, AES_SR_CCF)) { + 80085ea: 685a ldr r2, [r3, #4] + 80085ec: 07d2 lsls r2, r2, #31 + 80085ee: d5fc bpl.n 80085ea + SET_BIT(AES->CR, CRYP_CCF_CLEAR); + 80085f0: 681a ldr r2, [r3, #0] + 80085f2: f042 0280 orr.w r2, r2, #128 ; 0x80 + 80085f6: 601a str r2, [r3, #0] + *out = AES->DOUTR; out++; + 80085f8: 68da ldr r2, [r3, #12] + 80085fa: f844 2c10 str.w r2, [r4, #-16] + *out = AES->DOUTR; out++; + 80085fe: 68da ldr r2, [r3, #12] + 8008600: f844 2c0c str.w r2, [r4, #-12] + *out = AES->DOUTR; out++; + 8008604: 68da ldr r2, [r3, #12] + 8008606: f844 2c08 str.w r2, [r4, #-8] + *out = AES->DOUTR; + 800860a: 68da ldr r2, [r3, #12] + 800860c: f844 2c04 str.w r2, [r4, #-4] + for(int i=0; i < ctx->num_pending; i += 16) { + 8008610: 3410 adds r4, #16 + 8008612: e7af b.n 8008574 + 8008614: 0800e3e0 .word 0x0800e3e0 + 8008618: 40021000 .word 0x40021000 + 800861c: 50060000 .word 0x50060000 + +08008620 : + voltage range. + * @param msirange MSI range value from RCC_MSIRANGE_0 to RCC_MSIRANGE_11 + * @retval HAL status + */ +static HAL_StatusTypeDef RCC_SetFlashLatencyFromMSIRange(uint32_t msirange) +{ + 8008620: b537 push {r0, r1, r2, r4, r5, lr} + uint32_t vos; + uint32_t latency = FLASH_LATENCY_0; /* default value 0WS */ + + if(__HAL_RCC_PWR_IS_CLK_ENABLED()) + 8008622: 4d1c ldr r5, [pc, #112] ; (8008694 ) + 8008624: 6dab ldr r3, [r5, #88] ; 0x58 + 8008626: 00da lsls r2, r3, #3 +{ + 8008628: 4604 mov r4, r0 + if(__HAL_RCC_PWR_IS_CLK_ENABLED()) + 800862a: d518 bpl.n 800865e + { + vos = HAL_PWREx_GetVoltageRange(); + 800862c: f7fe fd62 bl 80070f4 + __HAL_RCC_PWR_CLK_ENABLE(); + vos = HAL_PWREx_GetVoltageRange(); + __HAL_RCC_PWR_CLK_DISABLE(); + } + + if(vos == PWR_REGULATOR_VOLTAGE_SCALE1) + 8008630: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8008634: d123 bne.n 800867e + { + if(msirange > RCC_MSIRANGE_8) + 8008636: 2c80 cmp r4, #128 ; 0x80 + 8008638: d928 bls.n 800868c + latency = FLASH_LATENCY_2; /* 2WS */ + } + else + { + /* MSI 24Mhz or 32Mhz */ + latency = FLASH_LATENCY_1; /* 1WS */ + 800863a: 2ca0 cmp r4, #160 ; 0xa0 + 800863c: bf8c ite hi + 800863e: 2002 movhi r0, #2 + 8008640: 2001 movls r0, #1 + /* else MSI < 8Mhz default FLASH_LATENCY_0 0WS */ + } +#endif + } + + __HAL_FLASH_SET_LATENCY(latency); + 8008642: 4a15 ldr r2, [pc, #84] ; (8008698 ) + 8008644: 6813 ldr r3, [r2, #0] + 8008646: f023 030f bic.w r3, r3, #15 + 800864a: 4303 orrs r3, r0 + 800864c: 6013 str r3, [r2, #0] + + /* Check that the new number of wait states is taken into account to access the Flash + memory by reading the FLASH_ACR register */ + if(__HAL_FLASH_GET_LATENCY() != latency) + 800864e: 6813 ldr r3, [r2, #0] + 8008650: f003 030f and.w r3, r3, #15 + { + return HAL_ERROR; + } + + return HAL_OK; +} + 8008654: 1a18 subs r0, r3, r0 + 8008656: bf18 it ne + 8008658: 2001 movne r0, #1 + 800865a: b003 add sp, #12 + 800865c: bd30 pop {r4, r5, pc} + __HAL_RCC_PWR_CLK_ENABLE(); + 800865e: 6dab ldr r3, [r5, #88] ; 0x58 + 8008660: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 8008664: 65ab str r3, [r5, #88] ; 0x58 + 8008666: 6dab ldr r3, [r5, #88] ; 0x58 + 8008668: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 800866c: 9301 str r3, [sp, #4] + 800866e: 9b01 ldr r3, [sp, #4] + vos = HAL_PWREx_GetVoltageRange(); + 8008670: f7fe fd40 bl 80070f4 + __HAL_RCC_PWR_CLK_DISABLE(); + 8008674: 6dab ldr r3, [r5, #88] ; 0x58 + 8008676: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800867a: 65ab str r3, [r5, #88] ; 0x58 + 800867c: e7d8 b.n 8008630 + if(msirange >= RCC_MSIRANGE_8) + 800867e: 2c7f cmp r4, #127 ; 0x7f + 8008680: d806 bhi.n 8008690 + if(msirange == RCC_MSIRANGE_7) + 8008682: f1a4 0370 sub.w r3, r4, #112 ; 0x70 + 8008686: 4258 negs r0, r3 + 8008688: 4158 adcs r0, r3 + 800868a: e7da b.n 8008642 + uint32_t latency = FLASH_LATENCY_0; /* default value 0WS */ + 800868c: 2000 movs r0, #0 + 800868e: e7d8 b.n 8008642 + latency = FLASH_LATENCY_2; /* 2WS */ + 8008690: 2002 movs r0, #2 + 8008692: e7d6 b.n 8008642 + 8008694: 40021000 .word 0x40021000 + 8008698: 40022000 .word 0x40022000 + +0800869c : +{ + 800869c: b5f8 push {r3, r4, r5, r6, r7, lr} + SET_BIT(RCC->CR, RCC_CR_MSION); + 800869e: 4c32 ldr r4, [pc, #200] ; (8008768 ) + 80086a0: 6823 ldr r3, [r4, #0] + 80086a2: f043 0301 orr.w r3, r3, #1 + 80086a6: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80086a8: f7fe fd20 bl 80070ec + 80086ac: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 80086ae: 6823 ldr r3, [r4, #0] + 80086b0: 079b lsls r3, r3, #30 + 80086b2: d543 bpl.n 800873c + MODIFY_REG(RCC->CR, RCC_CR_MSIRANGE, RCC_MSIRANGE_6); + 80086b4: 6823 ldr r3, [r4, #0] + SystemCoreClock = MSI_VALUE; + 80086b6: 4a2d ldr r2, [pc, #180] ; (800876c ) + MODIFY_REG(RCC->CR, RCC_CR_MSIRANGE, RCC_MSIRANGE_6); + 80086b8: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 80086bc: f043 0360 orr.w r3, r3, #96 ; 0x60 + 80086c0: 6023 str r3, [r4, #0] + CLEAR_REG(RCC->CFGR); + 80086c2: 2300 movs r3, #0 + 80086c4: 60a3 str r3, [r4, #8] + SystemCoreClock = MSI_VALUE; + 80086c6: 4b2a ldr r3, [pc, #168] ; (8008770 ) + 80086c8: 601a str r2, [r3, #0] + if(HAL_InitTick(uwTickPrio) != HAL_OK) + 80086ca: 4b2a ldr r3, [pc, #168] ; (8008774 ) + 80086cc: 6818 ldr r0, [r3, #0] + 80086ce: f7fe fd0f bl 80070f0 + 80086d2: 4605 mov r5, r0 + 80086d4: 2800 cmp r0, #0 + 80086d6: d145 bne.n 8008764 + tickstart = HAL_GetTick(); + 80086d8: f7fe fd08 bl 80070ec + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 80086dc: f241 3788 movw r7, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 80086e0: 4606 mov r6, r0 + while(READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RCC_CFGR_SWS_MSI) + 80086e2: 68a3 ldr r3, [r4, #8] + 80086e4: f013 0f0c tst.w r3, #12 + 80086e8: d130 bne.n 800874c + CLEAR_BIT(RCC->CR, RCC_CR_HSEON | RCC_CR_HSION | RCC_CR_HSIKERON| RCC_CR_HSIASFS | RCC_CR_PLLON | RCC_CR_PLLSAI1ON | RCC_CR_PLLSAI2ON); + 80086ea: 6822 ldr r2, [r4, #0] + 80086ec: 4b22 ldr r3, [pc, #136] ; (8008778 ) + 80086ee: 4013 ands r3, r2 + 80086f0: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80086f2: f7fe fcfb bl 80070ec + 80086f6: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY | RCC_CR_PLLSAI1RDY | RCC_CR_PLLSAI2RDY) != 0U) + 80086f8: 6823 ldr r3, [r4, #0] + 80086fa: f013 5328 ands.w r3, r3, #704643072 ; 0x2a000000 + 80086fe: d12b bne.n 8008758 + CLEAR_REG(RCC->PLLCFGR); + 8008700: 60e3 str r3, [r4, #12] + SET_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN_4 ); + 8008702: 68e2 ldr r2, [r4, #12] + 8008704: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8008708: 60e2 str r2, [r4, #12] + CLEAR_REG(RCC->PLLSAI1CFGR); + 800870a: 6123 str r3, [r4, #16] + SET_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N_4 ); + 800870c: 6922 ldr r2, [r4, #16] + 800870e: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8008712: 6122 str r2, [r4, #16] + CLEAR_REG(RCC->PLLSAI2CFGR); + 8008714: 6163 str r3, [r4, #20] + SET_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N_4 ); + 8008716: 6962 ldr r2, [r4, #20] + 8008718: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800871c: 6162 str r2, [r4, #20] + CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); + 800871e: 6822 ldr r2, [r4, #0] + 8008720: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 8008724: 6022 str r2, [r4, #0] + CLEAR_REG(RCC->CIER); + 8008726: 61a3 str r3, [r4, #24] + WRITE_REG(RCC->CICR, 0xFFFFFFFFU); + 8008728: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800872c: 6223 str r3, [r4, #32] + SET_BIT(RCC->CSR, RCC_CSR_RMVF); + 800872e: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008732: f443 0300 orr.w r3, r3, #8388608 ; 0x800000 + 8008736: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + return HAL_OK; + 800873a: e005 b.n 8008748 + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 800873c: f7fe fcd6 bl 80070ec + 8008740: 1b40 subs r0, r0, r5 + 8008742: 2802 cmp r0, #2 + 8008744: d9b3 bls.n 80086ae + return HAL_TIMEOUT; + 8008746: 2503 movs r5, #3 +} + 8008748: 4628 mov r0, r5 + 800874a: bdf8 pop {r3, r4, r5, r6, r7, pc} + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 800874c: f7fe fcce bl 80070ec + 8008750: 1b80 subs r0, r0, r6 + 8008752: 42b8 cmp r0, r7 + 8008754: d9c5 bls.n 80086e2 + 8008756: e7f6 b.n 8008746 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008758: f7fe fcc8 bl 80070ec + 800875c: 1b80 subs r0, r0, r6 + 800875e: 2802 cmp r0, #2 + 8008760: d9ca bls.n 80086f8 + 8008762: e7f0 b.n 8008746 + return HAL_ERROR; + 8008764: 2501 movs r5, #1 + 8008766: e7ef b.n 8008748 + 8008768: 40021000 .word 0x40021000 + 800876c: 003d0900 .word 0x003d0900 + 8008770: 2009e2a8 .word 0x2009e2a8 + 8008774: 2009e2ac .word 0x2009e2ac + 8008778: eafef4ff .word 0xeafef4ff + +0800877c : +{ + 800877c: b570 push {r4, r5, r6, lr} + __MCO1_CLK_ENABLE(); + 800877e: 4c12 ldr r4, [pc, #72] ; (80087c8 ) + 8008780: 6ce3 ldr r3, [r4, #76] ; 0x4c + 8008782: f043 0301 orr.w r3, r3, #1 + 8008786: 64e3 str r3, [r4, #76] ; 0x4c + 8008788: 6ce3 ldr r3, [r4, #76] ; 0x4c +{ + 800878a: b086 sub sp, #24 + __MCO1_CLK_ENABLE(); + 800878c: f003 0301 and.w r3, r3, #1 + 8008790: 9300 str r3, [sp, #0] + 8008792: 9b00 ldr r3, [sp, #0] +{ + 8008794: 4616 mov r6, r2 + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + 8008796: 2302 movs r3, #2 + 8008798: f44f 7280 mov.w r2, #256 ; 0x100 + 800879c: e9cd 2301 strd r2, r3, [sp, #4] +{ + 80087a0: 460d mov r5, r1 + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + 80087a2: 9304 str r3, [sp, #16] + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 80087a4: a901 add r1, sp, #4 + GPIO_InitStruct.Pull = GPIO_NOPULL; + 80087a6: 2300 movs r3, #0 + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 80087a8: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + GPIO_InitStruct.Pull = GPIO_NOPULL; + 80087ac: 9303 str r3, [sp, #12] + GPIO_InitStruct.Alternate = GPIO_AF0_MCO; + 80087ae: 9305 str r3, [sp, #20] + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 80087b0: f7f8 fc2e bl 8001010 + MODIFY_REG(RCC->CFGR, (RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE), (RCC_MCOSource | RCC_MCODiv )); + 80087b4: 68a3 ldr r3, [r4, #8] + 80087b6: f023 43fe bic.w r3, r3, #2130706432 ; 0x7f000000 + 80087ba: ea43 0206 orr.w r2, r3, r6 + 80087be: 432a orrs r2, r5 + 80087c0: 60a2 str r2, [r4, #8] +} + 80087c2: b006 add sp, #24 + 80087c4: bd70 pop {r4, r5, r6, pc} + 80087c6: bf00 nop + 80087c8: 40021000 .word 0x40021000 + +080087cc : + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 80087cc: 4b22 ldr r3, [pc, #136] ; (8008858 ) + 80087ce: 689a ldr r2, [r3, #8] + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 80087d0: 68d9 ldr r1, [r3, #12] + if((sysclk_source == RCC_CFGR_SWS_MSI) || + 80087d2: f012 020c ands.w r2, r2, #12 + 80087d6: d005 beq.n 80087e4 + 80087d8: 2a0c cmp r2, #12 + 80087da: d115 bne.n 8008808 + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 80087dc: f001 0103 and.w r1, r1, #3 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_oscsource == RCC_PLLSOURCE_MSI))) + 80087e0: 2901 cmp r1, #1 + 80087e2: d118 bne.n 8008816 + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 80087e4: 6819 ldr r1, [r3, #0] + msirange = MSIRangeTable[msirange]; + 80087e6: 481d ldr r0, [pc, #116] ; (800885c ) + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 80087e8: 0709 lsls r1, r1, #28 + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 80087ea: bf55 itete pl + 80087ec: f8d3 1094 ldrpl.w r1, [r3, #148] ; 0x94 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 80087f0: 6819 ldrmi r1, [r3, #0] + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 80087f2: f3c1 2103 ubfxpl r1, r1, #8, #4 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 80087f6: f3c1 1103 ubfxmi r1, r1, #4, #4 + msirange = MSIRangeTable[msirange]; + 80087fa: f850 0021 ldr.w r0, [r0, r1, lsl #2] + if(sysclk_source == RCC_CFGR_SWS_MSI) + 80087fe: b34a cbz r2, 8008854 + if(sysclk_source == RCC_CFGR_SWS_PLL) + 8008800: 2a0c cmp r2, #12 + 8008802: d009 beq.n 8008818 + 8008804: 2000 movs r0, #0 + return sysclockfreq; + 8008806: 4770 bx lr + else if(sysclk_source == RCC_CFGR_SWS_HSI) + 8008808: 2a04 cmp r2, #4 + 800880a: d022 beq.n 8008852 + else if(sysclk_source == RCC_CFGR_SWS_HSE) + 800880c: 2a08 cmp r2, #8 + 800880e: 4814 ldr r0, [pc, #80] ; (8008860 ) + 8008810: bf18 it ne + 8008812: 2000 movne r0, #0 + 8008814: 4770 bx lr + uint32_t msirange = 0U, sysclockfreq = 0U; + 8008816: 2000 movs r0, #0 + pllsource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8008818: 68da ldr r2, [r3, #12] + 800881a: f002 0203 and.w r2, r2, #3 + switch (pllsource) + 800881e: 2a02 cmp r2, #2 + 8008820: d015 beq.n 800884e + 8008822: 490f ldr r1, [pc, #60] ; (8008860 ) + 8008824: 2a03 cmp r2, #3 + 8008826: bf08 it eq + 8008828: 4608 moveq r0, r1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 800882a: 68d9 ldr r1, [r3, #12] + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 800882c: 68da ldr r2, [r3, #12] + 800882e: f3c2 2206 ubfx r2, r2, #8, #7 + 8008832: 4342 muls r2, r0 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008834: 68d8 ldr r0, [r3, #12] + 8008836: f3c0 6041 ubfx r0, r0, #25, #2 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 800883a: f3c1 1103 ubfx r1, r1, #4, #4 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 800883e: 3001 adds r0, #1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008840: 3101 adds r1, #1 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008842: 0040 lsls r0, r0, #1 + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 8008844: fbb2 f2f1 udiv r2, r2, r1 + sysclockfreq = pllvco / pllr; + 8008848: fbb2 f0f0 udiv r0, r2, r0 + 800884c: 4770 bx lr + pllvco = HSI_VALUE; + 800884e: 4805 ldr r0, [pc, #20] ; (8008864 ) + 8008850: e7eb b.n 800882a + sysclockfreq = HSI_VALUE; + 8008852: 4804 ldr r0, [pc, #16] ; (8008864 ) +} + 8008854: 4770 bx lr + 8008856: bf00 nop + 8008858: 40021000 .word 0x40021000 + 800885c: 0800e9f4 .word 0x0800e9f4 + 8008860: 007a1200 .word 0x007a1200 + 8008864: 00f42400 .word 0x00f42400 + +08008868 : +{ + 8008868: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + if(RCC_OscInitStruct == NULL) + 800886c: 4605 mov r5, r0 + 800886e: b908 cbnz r0, 8008874 + return HAL_ERROR; + 8008870: 2001 movs r0, #1 + 8008872: e047 b.n 8008904 + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008874: 4c94 ldr r4, [pc, #592] ; (8008ac8 ) + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 8008876: 6803 ldr r3, [r0, #0] + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008878: 68a6 ldr r6, [r4, #8] + pll_config = __HAL_RCC_GET_PLL_OSCSOURCE(); + 800887a: 68e7 ldr r7, [r4, #12] + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 800887c: 06db lsls r3, r3, #27 + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 800887e: f006 060c and.w r6, r6, #12 + pll_config = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8008882: f007 0703 and.w r7, r7, #3 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 8008886: d575 bpl.n 8008974 + if((sysclk_source == RCC_CFGR_SWS_MSI) || + 8008888: b11e cbz r6, 8008892 + 800888a: 2e0c cmp r6, #12 + 800888c: d154 bne.n 8008938 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_MSI))) + 800888e: 2f01 cmp r7, #1 + 8008890: d152 bne.n 8008938 + if((READ_BIT(RCC->CR, RCC_CR_MSIRDY) != 0U) && (RCC_OscInitStruct->MSIState == RCC_MSI_OFF)) + 8008892: 6823 ldr r3, [r4, #0] + 8008894: 0798 lsls r0, r3, #30 + 8008896: d502 bpl.n 800889e + 8008898: 69ab ldr r3, [r5, #24] + 800889a: 2b00 cmp r3, #0 + 800889c: d0e8 beq.n 8008870 + if(RCC_OscInitStruct->MSIClockRange > __HAL_RCC_GET_MSI_RANGE()) + 800889e: 6823 ldr r3, [r4, #0] + 80088a0: 6a28 ldr r0, [r5, #32] + 80088a2: 0719 lsls r1, r3, #28 + 80088a4: bf56 itet pl + 80088a6: f8d4 3094 ldrpl.w r3, [r4, #148] ; 0x94 + 80088aa: 6823 ldrmi r3, [r4, #0] + 80088ac: 091b lsrpl r3, r3, #4 + 80088ae: f003 03f0 and.w r3, r3, #240 ; 0xf0 + 80088b2: 4298 cmp r0, r3 + 80088b4: d929 bls.n 800890a + if(RCC_SetFlashLatencyFromMSIRange(RCC_OscInitStruct->MSIClockRange) != HAL_OK) + 80088b6: f7ff feb3 bl 8008620 + 80088ba: 2800 cmp r0, #0 + 80088bc: d1d8 bne.n 8008870 + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 80088be: 6823 ldr r3, [r4, #0] + 80088c0: f043 0308 orr.w r3, r3, #8 + 80088c4: 6023 str r3, [r4, #0] + 80088c6: 6823 ldr r3, [r4, #0] + 80088c8: 6a2a ldr r2, [r5, #32] + 80088ca: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 80088ce: 4313 orrs r3, r2 + 80088d0: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 80088d2: 6863 ldr r3, [r4, #4] + 80088d4: 69ea ldr r2, [r5, #28] + 80088d6: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 80088da: ea43 2302 orr.w r3, r3, r2, lsl #8 + 80088de: 6063 str r3, [r4, #4] + SystemCoreClock = HAL_RCC_GetSysClockFreq() >> (AHBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos] & 0x1FU); + 80088e0: f7ff ff74 bl 80087cc + 80088e4: 68a3 ldr r3, [r4, #8] + 80088e6: 4a79 ldr r2, [pc, #484] ; (8008acc ) + 80088e8: f3c3 1303 ubfx r3, r3, #4, #4 + 80088ec: 5cd3 ldrb r3, [r2, r3] + 80088ee: f003 031f and.w r3, r3, #31 + 80088f2: 40d8 lsrs r0, r3 + 80088f4: 4b76 ldr r3, [pc, #472] ; (8008ad0 ) + 80088f6: 6018 str r0, [r3, #0] + status = HAL_InitTick(uwTickPrio); + 80088f8: 4b76 ldr r3, [pc, #472] ; (8008ad4 ) + 80088fa: 6818 ldr r0, [r3, #0] + 80088fc: f7fe fbf8 bl 80070f0 + if(status != HAL_OK) + 8008900: 2800 cmp r0, #0 + 8008902: d037 beq.n 8008974 +} + 8008904: b003 add sp, #12 + 8008906: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 800890a: 6823 ldr r3, [r4, #0] + 800890c: f043 0308 orr.w r3, r3, #8 + 8008910: 6023 str r3, [r4, #0] + 8008912: 6823 ldr r3, [r4, #0] + 8008914: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008918: 4303 orrs r3, r0 + 800891a: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 800891c: 6863 ldr r3, [r4, #4] + 800891e: 69ea ldr r2, [r5, #28] + 8008920: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 8008924: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008928: 6063 str r3, [r4, #4] + if(sysclk_source == RCC_CFGR_SWS_MSI) + 800892a: 2e00 cmp r6, #0 + 800892c: d1d8 bne.n 80088e0 + if(RCC_SetFlashLatencyFromMSIRange(RCC_OscInitStruct->MSIClockRange) != HAL_OK) + 800892e: f7ff fe77 bl 8008620 + 8008932: 2800 cmp r0, #0 + 8008934: d0d4 beq.n 80088e0 + 8008936: e79b b.n 8008870 + if(RCC_OscInitStruct->MSIState != RCC_MSI_OFF) + 8008938: 69ab ldr r3, [r5, #24] + 800893a: 2b00 cmp r3, #0 + 800893c: d03a beq.n 80089b4 + __HAL_RCC_MSI_ENABLE(); + 800893e: 6823 ldr r3, [r4, #0] + 8008940: f043 0301 orr.w r3, r3, #1 + 8008944: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008946: f7fe fbd1 bl 80070ec + 800894a: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 800894c: 6823 ldr r3, [r4, #0] + 800894e: 079a lsls r2, r3, #30 + 8008950: d528 bpl.n 80089a4 + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 8008952: 6823 ldr r3, [r4, #0] + 8008954: f043 0308 orr.w r3, r3, #8 + 8008958: 6023 str r3, [r4, #0] + 800895a: 6823 ldr r3, [r4, #0] + 800895c: 6a2a ldr r2, [r5, #32] + 800895e: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008962: 4313 orrs r3, r2 + 8008964: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 8008966: 6863 ldr r3, [r4, #4] + 8008968: 69ea ldr r2, [r5, #28] + 800896a: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 800896e: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008972: 6063 str r3, [r4, #4] + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE) + 8008974: 682b ldr r3, [r5, #0] + 8008976: 07d8 lsls r0, r3, #31 + 8008978: d42d bmi.n 80089d6 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI) == RCC_OSCILLATORTYPE_HSI) + 800897a: 682b ldr r3, [r5, #0] + 800897c: 0799 lsls r1, r3, #30 + 800897e: d46b bmi.n 8008a58 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSI) == RCC_OSCILLATORTYPE_LSI) + 8008980: 682b ldr r3, [r5, #0] + 8008982: 0718 lsls r0, r3, #28 + 8008984: f100 80a8 bmi.w 8008ad8 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE) + 8008988: 682b ldr r3, [r5, #0] + 800898a: 0759 lsls r1, r3, #29 + 800898c: f100 80ce bmi.w 8008b2c + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI48) == RCC_OSCILLATORTYPE_HSI48) + 8008990: 682b ldr r3, [r5, #0] + 8008992: 069f lsls r7, r3, #26 + 8008994: f100 8137 bmi.w 8008c06 + if(RCC_OscInitStruct->PLL.PLLState != RCC_PLL_NONE) + 8008998: 6aab ldr r3, [r5, #40] ; 0x28 + 800899a: 2b00 cmp r3, #0 + 800899c: f040 815d bne.w 8008c5a + return HAL_OK; + 80089a0: 2000 movs r0, #0 + 80089a2: e7af b.n 8008904 + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 80089a4: f7fe fba2 bl 80070ec + 80089a8: eba0 0008 sub.w r0, r0, r8 + 80089ac: 2802 cmp r0, #2 + 80089ae: d9cd bls.n 800894c + return HAL_TIMEOUT; + 80089b0: 2003 movs r0, #3 + 80089b2: e7a7 b.n 8008904 + __HAL_RCC_MSI_DISABLE(); + 80089b4: 6823 ldr r3, [r4, #0] + 80089b6: f023 0301 bic.w r3, r3, #1 + 80089ba: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80089bc: f7fe fb96 bl 80070ec + 80089c0: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) != 0U) + 80089c2: 6823 ldr r3, [r4, #0] + 80089c4: 079b lsls r3, r3, #30 + 80089c6: d5d5 bpl.n 8008974 + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 80089c8: f7fe fb90 bl 80070ec + 80089cc: eba0 0008 sub.w r0, r0, r8 + 80089d0: 2802 cmp r0, #2 + 80089d2: d9f6 bls.n 80089c2 + 80089d4: e7ec b.n 80089b0 + if((sysclk_source == RCC_CFGR_SWS_HSE) || + 80089d6: 2e08 cmp r6, #8 + 80089d8: d003 beq.n 80089e2 + 80089da: 2e0c cmp r6, #12 + 80089dc: d108 bne.n 80089f0 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_HSE))) + 80089de: 2f03 cmp r7, #3 + 80089e0: d106 bne.n 80089f0 + if((READ_BIT(RCC->CR, RCC_CR_HSERDY) != 0U) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF)) + 80089e2: 6823 ldr r3, [r4, #0] + 80089e4: 039a lsls r2, r3, #14 + 80089e6: d5c8 bpl.n 800897a + 80089e8: 686b ldr r3, [r5, #4] + 80089ea: 2b00 cmp r3, #0 + 80089ec: d1c5 bne.n 800897a + 80089ee: e73f b.n 8008870 + __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState); + 80089f0: 686b ldr r3, [r5, #4] + 80089f2: f5b3 3f80 cmp.w r3, #65536 ; 0x10000 + 80089f6: d110 bne.n 8008a1a + 80089f8: 6823 ldr r3, [r4, #0] + 80089fa: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 80089fe: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008a00: f7fe fb74 bl 80070ec + 8008a04: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U) + 8008a06: 6823 ldr r3, [r4, #0] + 8008a08: 039b lsls r3, r3, #14 + 8008a0a: d4b6 bmi.n 800897a + if((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE) + 8008a0c: f7fe fb6e bl 80070ec + 8008a10: eba0 0008 sub.w r0, r0, r8 + 8008a14: 2864 cmp r0, #100 ; 0x64 + 8008a16: d9f6 bls.n 8008a06 + 8008a18: e7ca b.n 80089b0 + __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState); + 8008a1a: f5b3 2fa0 cmp.w r3, #327680 ; 0x50000 + 8008a1e: d104 bne.n 8008a2a + 8008a20: 6823 ldr r3, [r4, #0] + 8008a22: f443 2380 orr.w r3, r3, #262144 ; 0x40000 + 8008a26: 6023 str r3, [r4, #0] + 8008a28: e7e6 b.n 80089f8 + 8008a2a: 6822 ldr r2, [r4, #0] + 8008a2c: f422 3280 bic.w r2, r2, #65536 ; 0x10000 + 8008a30: 6022 str r2, [r4, #0] + 8008a32: 6822 ldr r2, [r4, #0] + 8008a34: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 8008a38: 6022 str r2, [r4, #0] + if(RCC_OscInitStruct->HSEState != RCC_HSE_OFF) + 8008a3a: 2b00 cmp r3, #0 + 8008a3c: d1e0 bne.n 8008a00 + tickstart = HAL_GetTick(); + 8008a3e: f7fe fb55 bl 80070ec + 8008a42: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSERDY) != 0U) + 8008a44: 6823 ldr r3, [r4, #0] + 8008a46: 0398 lsls r0, r3, #14 + 8008a48: d597 bpl.n 800897a + if((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE) + 8008a4a: f7fe fb4f bl 80070ec + 8008a4e: eba0 0008 sub.w r0, r0, r8 + 8008a52: 2864 cmp r0, #100 ; 0x64 + 8008a54: d9f6 bls.n 8008a44 + 8008a56: e7ab b.n 80089b0 + if((sysclk_source == RCC_CFGR_SWS_HSI) || + 8008a58: 2e04 cmp r6, #4 + 8008a5a: d003 beq.n 8008a64 + 8008a5c: 2e0c cmp r6, #12 + 8008a5e: d110 bne.n 8008a82 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_HSI))) + 8008a60: 2f02 cmp r7, #2 + 8008a62: d10e bne.n 8008a82 + if((READ_BIT(RCC->CR, RCC_CR_HSIRDY) != 0U) && (RCC_OscInitStruct->HSIState == RCC_HSI_OFF)) + 8008a64: 6823 ldr r3, [r4, #0] + 8008a66: 0559 lsls r1, r3, #21 + 8008a68: d503 bpl.n 8008a72 + 8008a6a: 68eb ldr r3, [r5, #12] + 8008a6c: 2b00 cmp r3, #0 + 8008a6e: f43f aeff beq.w 8008870 + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue); + 8008a72: 6863 ldr r3, [r4, #4] + 8008a74: 692a ldr r2, [r5, #16] + 8008a76: f023 43fe bic.w r3, r3, #2130706432 ; 0x7f000000 + 8008a7a: ea43 6302 orr.w r3, r3, r2, lsl #24 + 8008a7e: 6063 str r3, [r4, #4] + 8008a80: e77e b.n 8008980 + if(RCC_OscInitStruct->HSIState != RCC_HSI_OFF) + 8008a82: 68eb ldr r3, [r5, #12] + 8008a84: b17b cbz r3, 8008aa6 + __HAL_RCC_HSI_ENABLE(); + 8008a86: 6823 ldr r3, [r4, #0] + 8008a88: f443 7380 orr.w r3, r3, #256 ; 0x100 + 8008a8c: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008a8e: f7fe fb2d bl 80070ec + 8008a92: 4607 mov r7, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 8008a94: 6823 ldr r3, [r4, #0] + 8008a96: 055a lsls r2, r3, #21 + 8008a98: d4eb bmi.n 8008a72 + if((HAL_GetTick() - tickstart) > HSI_TIMEOUT_VALUE) + 8008a9a: f7fe fb27 bl 80070ec + 8008a9e: 1bc0 subs r0, r0, r7 + 8008aa0: 2802 cmp r0, #2 + 8008aa2: d9f7 bls.n 8008a94 + 8008aa4: e784 b.n 80089b0 + __HAL_RCC_HSI_DISABLE(); + 8008aa6: 6823 ldr r3, [r4, #0] + 8008aa8: f423 7380 bic.w r3, r3, #256 ; 0x100 + 8008aac: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008aae: f7fe fb1d bl 80070ec + 8008ab2: 4607 mov r7, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSIRDY) != 0U) + 8008ab4: 6823 ldr r3, [r4, #0] + 8008ab6: 055b lsls r3, r3, #21 + 8008ab8: f57f af62 bpl.w 8008980 + if((HAL_GetTick() - tickstart) > HSI_TIMEOUT_VALUE) + 8008abc: f7fe fb16 bl 80070ec + 8008ac0: 1bc0 subs r0, r0, r7 + 8008ac2: 2802 cmp r0, #2 + 8008ac4: d9f6 bls.n 8008ab4 + 8008ac6: e773 b.n 80089b0 + 8008ac8: 40021000 .word 0x40021000 + 8008acc: 0800e9dc .word 0x0800e9dc + 8008ad0: 2009e2a8 .word 0x2009e2a8 + 8008ad4: 2009e2ac .word 0x2009e2ac + if(RCC_OscInitStruct->LSIState != RCC_LSI_OFF) + 8008ad8: 696b ldr r3, [r5, #20] + 8008ada: b19b cbz r3, 8008b04 + __HAL_RCC_LSI_ENABLE(); + 8008adc: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008ae0: f043 0301 orr.w r3, r3, #1 + 8008ae4: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + tickstart = HAL_GetTick(); + 8008ae8: f7fe fb00 bl 80070ec + 8008aec: 4607 mov r7, r0 + while(READ_BIT(RCC->CSR, RCC_CSR_LSIRDY) == 0U) + 8008aee: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008af2: 079a lsls r2, r3, #30 + 8008af4: f53f af48 bmi.w 8008988 + if((HAL_GetTick() - tickstart) > LSI_TIMEOUT_VALUE) + 8008af8: f7fe faf8 bl 80070ec + 8008afc: 1bc0 subs r0, r0, r7 + 8008afe: 2802 cmp r0, #2 + 8008b00: d9f5 bls.n 8008aee + 8008b02: e755 b.n 80089b0 + __HAL_RCC_LSI_DISABLE(); + 8008b04: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008b08: f023 0301 bic.w r3, r3, #1 + 8008b0c: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + tickstart = HAL_GetTick(); + 8008b10: f7fe faec bl 80070ec + 8008b14: 4607 mov r7, r0 + while(READ_BIT(RCC->CSR, RCC_CSR_LSIRDY) != 0U) + 8008b16: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008b1a: 079b lsls r3, r3, #30 + 8008b1c: f57f af34 bpl.w 8008988 + if((HAL_GetTick() - tickstart) > LSI_TIMEOUT_VALUE) + 8008b20: f7fe fae4 bl 80070ec + 8008b24: 1bc0 subs r0, r0, r7 + 8008b26: 2802 cmp r0, #2 + 8008b28: d9f5 bls.n 8008b16 + 8008b2a: e741 b.n 80089b0 + if(HAL_IS_BIT_CLR(RCC->APB1ENR1, RCC_APB1ENR1_PWREN)) + 8008b2c: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008b2e: 00df lsls r7, r3, #3 + 8008b30: d429 bmi.n 8008b86 + __HAL_RCC_PWR_CLK_ENABLE(); + 8008b32: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008b34: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 8008b38: 65a3 str r3, [r4, #88] ; 0x58 + 8008b3a: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008b3c: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 8008b40: 9301 str r3, [sp, #4] + 8008b42: 9b01 ldr r3, [sp, #4] + pwrclkchanged = SET; + 8008b44: f04f 0801 mov.w r8, #1 + if(HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP)) + 8008b48: 4f9c ldr r7, [pc, #624] ; (8008dbc ) + 8008b4a: 683b ldr r3, [r7, #0] + 8008b4c: 05d8 lsls r0, r3, #23 + 8008b4e: d51d bpl.n 8008b8c + __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState); + 8008b50: 68ab ldr r3, [r5, #8] + 8008b52: 2b01 cmp r3, #1 + 8008b54: d12b bne.n 8008bae + 8008b56: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008b5a: f043 0301 orr.w r3, r3, #1 + 8008b5e: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + tickstart = HAL_GetTick(); + 8008b62: f7fe fac3 bl 80070ec + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008b66: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8008b6a: 4607 mov r7, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0U) + 8008b6c: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008b70: 079a lsls r2, r3, #30 + 8008b72: d542 bpl.n 8008bfa + if(pwrclkchanged == SET) + 8008b74: f1b8 0f00 cmp.w r8, #0 + 8008b78: f43f af0a beq.w 8008990 + __HAL_RCC_PWR_CLK_DISABLE(); + 8008b7c: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008b7e: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8008b82: 65a3 str r3, [r4, #88] ; 0x58 + 8008b84: e704 b.n 8008990 + FlagStatus pwrclkchanged = RESET; + 8008b86: f04f 0800 mov.w r8, #0 + 8008b8a: e7dd b.n 8008b48 + SET_BIT(PWR->CR1, PWR_CR1_DBP); + 8008b8c: 683b ldr r3, [r7, #0] + 8008b8e: f443 7380 orr.w r3, r3, #256 ; 0x100 + 8008b92: 603b str r3, [r7, #0] + tickstart = HAL_GetTick(); + 8008b94: f7fe faaa bl 80070ec + 8008b98: 4681 mov r9, r0 + while(HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP)) + 8008b9a: 683b ldr r3, [r7, #0] + 8008b9c: 05d9 lsls r1, r3, #23 + 8008b9e: d4d7 bmi.n 8008b50 + if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) + 8008ba0: f7fe faa4 bl 80070ec + 8008ba4: eba0 0009 sub.w r0, r0, r9 + 8008ba8: 2802 cmp r0, #2 + 8008baa: d9f6 bls.n 8008b9a + 8008bac: e700 b.n 80089b0 + __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState); + 8008bae: 2b05 cmp r3, #5 + 8008bb0: d106 bne.n 8008bc0 + 8008bb2: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008bb6: f043 0304 orr.w r3, r3, #4 + 8008bba: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + 8008bbe: e7ca b.n 8008b56 + 8008bc0: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8008bc4: f022 0201 bic.w r2, r2, #1 + 8008bc8: f8c4 2090 str.w r2, [r4, #144] ; 0x90 + 8008bcc: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8008bd0: f022 0204 bic.w r2, r2, #4 + 8008bd4: f8c4 2090 str.w r2, [r4, #144] ; 0x90 + if(RCC_OscInitStruct->LSEState != RCC_LSE_OFF) + 8008bd8: 2b00 cmp r3, #0 + 8008bda: d1c2 bne.n 8008b62 + tickstart = HAL_GetTick(); + 8008bdc: f7fe fa86 bl 80070ec + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008be0: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8008be4: 4607 mov r7, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) != 0U) + 8008be6: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008bea: 079b lsls r3, r3, #30 + 8008bec: d5c2 bpl.n 8008b74 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008bee: f7fe fa7d bl 80070ec + 8008bf2: 1bc0 subs r0, r0, r7 + 8008bf4: 4548 cmp r0, r9 + 8008bf6: d9f6 bls.n 8008be6 + 8008bf8: e6da b.n 80089b0 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008bfa: f7fe fa77 bl 80070ec + 8008bfe: 1bc0 subs r0, r0, r7 + 8008c00: 4548 cmp r0, r9 + 8008c02: d9b3 bls.n 8008b6c + 8008c04: e6d4 b.n 80089b0 + if(RCC_OscInitStruct->HSI48State != RCC_HSI48_OFF) + 8008c06: 6a6b ldr r3, [r5, #36] ; 0x24 + 8008c08: b19b cbz r3, 8008c32 + __HAL_RCC_HSI48_ENABLE(); + 8008c0a: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008c0e: f043 0301 orr.w r3, r3, #1 + 8008c12: f8c4 3098 str.w r3, [r4, #152] ; 0x98 + tickstart = HAL_GetTick(); + 8008c16: f7fe fa69 bl 80070ec + 8008c1a: 4607 mov r7, r0 + while(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48RDY) == 0U) + 8008c1c: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008c20: 0798 lsls r0, r3, #30 + 8008c22: f53f aeb9 bmi.w 8008998 + if((HAL_GetTick() - tickstart) > HSI48_TIMEOUT_VALUE) + 8008c26: f7fe fa61 bl 80070ec + 8008c2a: 1bc0 subs r0, r0, r7 + 8008c2c: 2802 cmp r0, #2 + 8008c2e: d9f5 bls.n 8008c1c + 8008c30: e6be b.n 80089b0 + __HAL_RCC_HSI48_DISABLE(); + 8008c32: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008c36: f023 0301 bic.w r3, r3, #1 + 8008c3a: f8c4 3098 str.w r3, [r4, #152] ; 0x98 + tickstart = HAL_GetTick(); + 8008c3e: f7fe fa55 bl 80070ec + 8008c42: 4607 mov r7, r0 + while(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48RDY) != 0U) + 8008c44: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008c48: 0799 lsls r1, r3, #30 + 8008c4a: f57f aea5 bpl.w 8008998 + if((HAL_GetTick() - tickstart) > HSI48_TIMEOUT_VALUE) + 8008c4e: f7fe fa4d bl 80070ec + 8008c52: 1bc0 subs r0, r0, r7 + 8008c54: 2802 cmp r0, #2 + 8008c56: d9f5 bls.n 8008c44 + 8008c58: e6aa b.n 80089b0 + if(RCC_OscInitStruct->PLL.PLLState == RCC_PLL_ON) + 8008c5a: 2b02 cmp r3, #2 + 8008c5c: f040 808c bne.w 8008d78 + pll_config = RCC->PLLCFGR; + 8008c60: 68e3 ldr r3, [r4, #12] + if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) || + 8008c62: 6aea ldr r2, [r5, #44] ; 0x2c + 8008c64: f003 0103 and.w r1, r3, #3 + 8008c68: 4291 cmp r1, r2 + 8008c6a: d122 bne.n 8008cb2 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != ((RCC_OscInitStruct->PLL.PLLM - 1U) << RCC_PLLCFGR_PLLM_Pos)) || + 8008c6c: 6b29 ldr r1, [r5, #48] ; 0x30 + 8008c6e: f003 02f0 and.w r2, r3, #240 ; 0xf0 + 8008c72: 3901 subs r1, #1 + if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) || + 8008c74: ebb2 1f01 cmp.w r2, r1, lsl #4 + 8008c78: d11b bne.n 8008cb2 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)) || + 8008c7a: 6b69 ldr r1, [r5, #52] ; 0x34 + 8008c7c: f403 42fe and.w r2, r3, #32512 ; 0x7f00 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != ((RCC_OscInitStruct->PLL.PLLM - 1U) << RCC_PLLCFGR_PLLM_Pos)) || + 8008c80: ebb2 2f01 cmp.w r2, r1, lsl #8 + 8008c84: d115 bne.n 8008cb2 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLPDIV) != (RCC_OscInitStruct->PLL.PLLP << RCC_PLLCFGR_PLLPDIV_Pos)) || + 8008c86: 6ba9 ldr r1, [r5, #56] ; 0x38 + 8008c88: f003 4278 and.w r2, r3, #4160749568 ; 0xf8000000 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)) || + 8008c8c: ebb2 6fc1 cmp.w r2, r1, lsl #27 + 8008c90: d10f bne.n 8008cb2 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != ((((RCC_OscInitStruct->PLL.PLLQ) >> 1U) - 1U) << RCC_PLLCFGR_PLLQ_Pos)) || + 8008c92: 6bea ldr r2, [r5, #60] ; 0x3c + 8008c94: 0852 lsrs r2, r2, #1 + 8008c96: f403 01c0 and.w r1, r3, #6291456 ; 0x600000 + 8008c9a: 3a01 subs r2, #1 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLPDIV) != (RCC_OscInitStruct->PLL.PLLP << RCC_PLLCFGR_PLLPDIV_Pos)) || + 8008c9c: ebb1 5f42 cmp.w r1, r2, lsl #21 + 8008ca0: d107 bne.n 8008cb2 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLR) != ((((RCC_OscInitStruct->PLL.PLLR) >> 1U) - 1U) << RCC_PLLCFGR_PLLR_Pos))) + 8008ca2: 6c2a ldr r2, [r5, #64] ; 0x40 + 8008ca4: 0852 lsrs r2, r2, #1 + 8008ca6: f003 63c0 and.w r3, r3, #100663296 ; 0x6000000 + 8008caa: 3a01 subs r2, #1 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != ((((RCC_OscInitStruct->PLL.PLLQ) >> 1U) - 1U) << RCC_PLLCFGR_PLLQ_Pos)) || + 8008cac: ebb3 6f42 cmp.w r3, r2, lsl #25 + 8008cb0: d049 beq.n 8008d46 + if(sysclk_source != RCC_CFGR_SWS_PLL) + 8008cb2: 2e0c cmp r6, #12 + 8008cb4: f43f addc beq.w 8008870 + if((READ_BIT(RCC->CR, RCC_CR_PLLSAI1ON) != 0U) + 8008cb8: 6823 ldr r3, [r4, #0] + 8008cba: 015a lsls r2, r3, #5 + 8008cbc: f53f add8 bmi.w 8008870 + || (READ_BIT(RCC->CR, RCC_CR_PLLSAI2ON) != 0U) + 8008cc0: 6823 ldr r3, [r4, #0] + 8008cc2: 00db lsls r3, r3, #3 + 8008cc4: f53f add4 bmi.w 8008870 + __HAL_RCC_PLL_DISABLE(); + 8008cc8: 6823 ldr r3, [r4, #0] + 8008cca: f023 7380 bic.w r3, r3, #16777216 ; 0x1000000 + 8008cce: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008cd0: f7fe fa0c bl 80070ec + 8008cd4: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != 0U) + 8008cd6: 6823 ldr r3, [r4, #0] + 8008cd8: 019f lsls r7, r3, #6 + 8008cda: d42e bmi.n 8008d3a + __HAL_RCC_PLL_CONFIG(RCC_OscInitStruct->PLL.PLLSource, + 8008cdc: 68e2 ldr r2, [r4, #12] + 8008cde: 4b38 ldr r3, [pc, #224] ; (8008dc0 ) + 8008ce0: 4013 ands r3, r2 + 8008ce2: 6aea ldr r2, [r5, #44] ; 0x2c + 8008ce4: 4313 orrs r3, r2 + 8008ce6: 6b6a ldr r2, [r5, #52] ; 0x34 + 8008ce8: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008cec: 6baa ldr r2, [r5, #56] ; 0x38 + 8008cee: ea43 63c2 orr.w r3, r3, r2, lsl #27 + 8008cf2: 6b2a ldr r2, [r5, #48] ; 0x30 + 8008cf4: 3a01 subs r2, #1 + 8008cf6: ea43 1302 orr.w r3, r3, r2, lsl #4 + 8008cfa: 6bea ldr r2, [r5, #60] ; 0x3c + 8008cfc: 0852 lsrs r2, r2, #1 + 8008cfe: 3a01 subs r2, #1 + 8008d00: ea43 5342 orr.w r3, r3, r2, lsl #21 + 8008d04: 6c2a ldr r2, [r5, #64] ; 0x40 + 8008d06: 0852 lsrs r2, r2, #1 + 8008d08: 3a01 subs r2, #1 + 8008d0a: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8008d0e: 60e3 str r3, [r4, #12] + __HAL_RCC_PLL_ENABLE(); + 8008d10: 6823 ldr r3, [r4, #0] + 8008d12: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8008d16: 6023 str r3, [r4, #0] + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SYSCLK); + 8008d18: 68e3 ldr r3, [r4, #12] + 8008d1a: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8008d1e: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8008d20: f7fe f9e4 bl 80070ec + 8008d24: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 8008d26: 6823 ldr r3, [r4, #0] + 8008d28: 0198 lsls r0, r3, #6 + 8008d2a: f53f ae39 bmi.w 80089a0 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008d2e: f7fe f9dd bl 80070ec + 8008d32: 1b40 subs r0, r0, r5 + 8008d34: 2802 cmp r0, #2 + 8008d36: d9f6 bls.n 8008d26 + 8008d38: e63a b.n 80089b0 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008d3a: f7fe f9d7 bl 80070ec + 8008d3e: 1b80 subs r0, r0, r6 + 8008d40: 2802 cmp r0, #2 + 8008d42: d9c8 bls.n 8008cd6 + 8008d44: e634 b.n 80089b0 + if(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 8008d46: 6823 ldr r3, [r4, #0] + 8008d48: 0199 lsls r1, r3, #6 + 8008d4a: f53f ae29 bmi.w 80089a0 + __HAL_RCC_PLL_ENABLE(); + 8008d4e: 6823 ldr r3, [r4, #0] + 8008d50: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8008d54: 6023 str r3, [r4, #0] + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SYSCLK); + 8008d56: 68e3 ldr r3, [r4, #12] + 8008d58: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8008d5c: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8008d5e: f7fe f9c5 bl 80070ec + 8008d62: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 8008d64: 6823 ldr r3, [r4, #0] + 8008d66: 019a lsls r2, r3, #6 + 8008d68: f53f ae1a bmi.w 80089a0 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008d6c: f7fe f9be bl 80070ec + 8008d70: 1b40 subs r0, r0, r5 + 8008d72: 2802 cmp r0, #2 + 8008d74: d9f6 bls.n 8008d64 + 8008d76: e61b b.n 80089b0 + if(sysclk_source != RCC_CFGR_SWS_PLL) + 8008d78: 2e0c cmp r6, #12 + 8008d7a: f43f ad79 beq.w 8008870 + __HAL_RCC_PLL_DISABLE(); + 8008d7e: 6823 ldr r3, [r4, #0] + 8008d80: f023 7380 bic.w r3, r3, #16777216 ; 0x1000000 + 8008d84: 6023 str r3, [r4, #0] + if(READ_BIT(RCC->CR, (RCC_CR_PLLSAI1RDY | RCC_CR_PLLSAI2RDY)) == 0U) + 8008d86: 6823 ldr r3, [r4, #0] + 8008d88: f013 5f20 tst.w r3, #671088640 ; 0x28000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 8008d8c: bf02 ittt eq + 8008d8e: 68e3 ldreq r3, [r4, #12] + 8008d90: f023 0303 biceq.w r3, r3, #3 + 8008d94: 60e3 streq r3, [r4, #12] + __HAL_RCC_PLLCLKOUT_DISABLE(RCC_PLL_SYSCLK | RCC_PLL_48M1CLK | RCC_PLL_SAI3CLK); + 8008d96: 68e3 ldr r3, [r4, #12] + 8008d98: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 8008d9c: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 8008da0: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8008da2: f7fe f9a3 bl 80070ec + 8008da6: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != 0U) + 8008da8: 6823 ldr r3, [r4, #0] + 8008daa: 019b lsls r3, r3, #6 + 8008dac: f57f adf8 bpl.w 80089a0 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008db0: f7fe f99c bl 80070ec + 8008db4: 1b40 subs r0, r0, r5 + 8008db6: 2802 cmp r0, #2 + 8008db8: d9f6 bls.n 8008da8 + 8008dba: e5f9 b.n 80089b0 + 8008dbc: 40007000 .word 0x40007000 + 8008dc0: 019d800c .word 0x019d800c + +08008dc4 : +{ + 8008dc4: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 8008dc8: 460e mov r6, r1 + if(RCC_ClkInitStruct == NULL) + 8008dca: 4605 mov r5, r0 + 8008dcc: b910 cbnz r0, 8008dd4 + return HAL_ERROR; + 8008dce: 2001 movs r0, #1 +} + 8008dd0: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + if(FLatency > __HAL_FLASH_GET_LATENCY()) + 8008dd4: 4a6f ldr r2, [pc, #444] ; (8008f94 ) + 8008dd6: 6813 ldr r3, [r2, #0] + 8008dd8: f003 030f and.w r3, r3, #15 + 8008ddc: 428b cmp r3, r1 + 8008dde: d335 bcc.n 8008e4c + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK) + 8008de0: 6829 ldr r1, [r5, #0] + 8008de2: f011 0701 ands.w r7, r1, #1 + 8008de6: d13c bne.n 8008e62 + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK) + 8008de8: 682a ldr r2, [r5, #0] + 8008dea: 0791 lsls r1, r2, #30 + 8008dec: f140 80b7 bpl.w 8008f5e + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider); + 8008df0: 4969 ldr r1, [pc, #420] ; (8008f98 ) + 8008df2: 68a8 ldr r0, [r5, #8] + 8008df4: 688b ldr r3, [r1, #8] + 8008df6: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008dfa: 4303 orrs r3, r0 + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1); + 8008dfc: 608b str r3, [r1, #8] + if(FLatency < __HAL_FLASH_GET_LATENCY()) + 8008dfe: 4965 ldr r1, [pc, #404] ; (8008f94 ) + 8008e00: 680b ldr r3, [r1, #0] + 8008e02: f003 030f and.w r3, r3, #15 + 8008e06: 42b3 cmp r3, r6 + 8008e08: f200 80b1 bhi.w 8008f6e + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1) + 8008e0c: f012 0f04 tst.w r2, #4 + 8008e10: 4c61 ldr r4, [pc, #388] ; (8008f98 ) + 8008e12: f040 80b8 bne.w 8008f86 + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2) + 8008e16: 0713 lsls r3, r2, #28 + 8008e18: d506 bpl.n 8008e28 + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U)); + 8008e1a: 68a3 ldr r3, [r4, #8] + 8008e1c: 692a ldr r2, [r5, #16] + 8008e1e: f423 5360 bic.w r3, r3, #14336 ; 0x3800 + 8008e22: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 8008e26: 60a3 str r3, [r4, #8] + SystemCoreClock = HAL_RCC_GetSysClockFreq() >> (AHBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos] & 0x1FU); + 8008e28: f7ff fcd0 bl 80087cc + 8008e2c: 68a3 ldr r3, [r4, #8] + 8008e2e: 4a5b ldr r2, [pc, #364] ; (8008f9c ) + 8008e30: f3c3 1303 ubfx r3, r3, #4, #4 + 8008e34: 5cd3 ldrb r3, [r2, r3] + 8008e36: f003 031f and.w r3, r3, #31 + 8008e3a: 40d8 lsrs r0, r3 + 8008e3c: 4b58 ldr r3, [pc, #352] ; (8008fa0 ) + 8008e3e: 6018 str r0, [r3, #0] + status = HAL_InitTick(uwTickPrio); + 8008e40: 4b58 ldr r3, [pc, #352] ; (8008fa4 ) + 8008e42: 6818 ldr r0, [r3, #0] +} + 8008e44: e8bd 43f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + status = HAL_InitTick(uwTickPrio); + 8008e48: f7fe b952 b.w 80070f0 + __HAL_FLASH_SET_LATENCY(FLatency); + 8008e4c: 6813 ldr r3, [r2, #0] + 8008e4e: f023 030f bic.w r3, r3, #15 + 8008e52: 430b orrs r3, r1 + 8008e54: 6013 str r3, [r2, #0] + if(__HAL_FLASH_GET_LATENCY() != FLatency) + 8008e56: 6813 ldr r3, [r2, #0] + 8008e58: f003 030f and.w r3, r3, #15 + 8008e5c: 428b cmp r3, r1 + 8008e5e: d1b6 bne.n 8008dce + 8008e60: e7be b.n 8008de0 + if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) + 8008e62: 686b ldr r3, [r5, #4] + 8008e64: 4c4c ldr r4, [pc, #304] ; (8008f98 ) + 8008e66: 2b03 cmp r3, #3 + 8008e68: d163 bne.n 8008f32 + if(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 8008e6a: 6823 ldr r3, [r4, #0] + 8008e6c: 019b lsls r3, r3, #6 + 8008e6e: d5ae bpl.n 8008dce +static uint32_t RCC_GetSysClockFreqFromPLLSource(void) +{ + uint32_t msirange = 0U; + uint32_t pllvco, pllsource, pllr, pllm, sysclockfreq; /* no init needed */ + + if(__HAL_RCC_GET_PLL_OSCSOURCE() == RCC_PLLSOURCE_MSI) + 8008e70: 68e3 ldr r3, [r4, #12] + 8008e72: f003 0303 and.w r3, r3, #3 + 8008e76: 2b01 cmp r3, #1 + 8008e78: d145 bne.n 8008f06 + { + /* Get MSI range source */ + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 8008e7a: 6823 ldr r3, [r4, #0] + else + { /* MSIRANGE from RCC_CR applies */ + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + } + /*MSI frequency range in HZ*/ + msirange = MSIRangeTable[msirange]; + 8008e7c: 4a4a ldr r2, [pc, #296] ; (8008fa8 ) + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 8008e7e: 071f lsls r7, r3, #28 + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 8008e80: bf55 itete pl + 8008e82: f8d4 3094 ldrpl.w r3, [r4, #148] ; 0x94 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 8008e86: 6823 ldrmi r3, [r4, #0] + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 8008e88: f3c3 2303 ubfxpl r3, r3, #8, #4 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 8008e8c: f3c3 1303 ubfxmi r3, r3, #4, #4 + msirange = MSIRangeTable[msirange]; + 8008e90: f852 2023 ldr.w r2, [r2, r3, lsl #2] + } + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE) * PLLN / PLLM + SYSCLK = PLL_VCO / PLLR + */ + pllsource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8008e94: 68e3 ldr r3, [r4, #12] + 8008e96: f003 0303 and.w r3, r3, #3 + + switch (pllsource) + 8008e9a: 2b02 cmp r3, #2 + 8008e9c: d035 beq.n 8008f0a + 8008e9e: 4843 ldr r0, [pc, #268] ; (8008fac ) + 8008ea0: 2b03 cmp r3, #3 + 8008ea2: bf08 it eq + 8008ea4: 4602 moveq r2, r0 + case RCC_PLLSOURCE_MSI: /* MSI used as PLL clock source */ + default: + pllvco = msirange; + break; + } + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008ea6: 68e0 ldr r0, [r4, #12] + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 8008ea8: 68e3 ldr r3, [r4, #12] + 8008eaa: f3c3 2306 ubfx r3, r3, #8, #7 + 8008eae: 4353 muls r3, r2 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008eb0: 68e2 ldr r2, [r4, #12] + 8008eb2: f3c2 6241 ubfx r2, r2, #25, #2 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008eb6: f3c0 1003 ubfx r0, r0, #4, #4 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008eba: 3201 adds r2, #1 + 8008ebc: 0052 lsls r2, r2, #1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008ebe: 3001 adds r0, #1 + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 8008ec0: fbb3 f3f0 udiv r3, r3, r0 + sysclockfreq = pllvco / pllr; + 8008ec4: fbb3 f3f2 udiv r3, r3, r2 + if(RCC_GetSysClockFreqFromPLLSource() > 80000000U) + 8008ec8: 4a39 ldr r2, [pc, #228] ; (8008fb0 ) + 8008eca: 4293 cmp r3, r2 + 8008ecc: d81f bhi.n 8008f0e + uint32_t hpre = RCC_SYSCLK_DIV1; + 8008ece: 2700 movs r7, #0 + MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_ClkInitStruct->SYSCLKSource); + 8008ed0: 68a3 ldr r3, [r4, #8] + 8008ed2: 686a ldr r2, [r5, #4] + 8008ed4: f023 0303 bic.w r3, r3, #3 + 8008ed8: 4313 orrs r3, r2 + 8008eda: 60a3 str r3, [r4, #8] + tickstart = HAL_GetTick(); + 8008edc: f7fe f906 bl 80070ec + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 8008ee0: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8008ee4: 4680 mov r8, r0 + while(__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos)) + 8008ee6: 68a3 ldr r3, [r4, #8] + 8008ee8: 686a ldr r2, [r5, #4] + 8008eea: f003 030c and.w r3, r3, #12 + 8008eee: ebb3 0f82 cmp.w r3, r2, lsl #2 + 8008ef2: f43f af79 beq.w 8008de8 + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 8008ef6: f7fe f8f9 bl 80070ec + 8008efa: eba0 0008 sub.w r0, r0, r8 + 8008efe: 4548 cmp r0, r9 + 8008f00: d9f1 bls.n 8008ee6 + return HAL_TIMEOUT; + 8008f02: 2003 movs r0, #3 + 8008f04: e764 b.n 8008dd0 + uint32_t msirange = 0U; + 8008f06: 2200 movs r2, #0 + 8008f08: e7c4 b.n 8008e94 + pllvco = HSI_VALUE; + 8008f0a: 4a2a ldr r2, [pc, #168] ; (8008fb4 ) + 8008f0c: e7cb b.n 8008ea6 + if(READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) == RCC_SYSCLK_DIV1) + 8008f0e: 68a3 ldr r3, [r4, #8] + 8008f10: f013 0ff0 tst.w r3, #240 ; 0xf0 + 8008f14: d107 bne.n 8008f26 + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV2); + 8008f16: 68a3 ldr r3, [r4, #8] + 8008f18: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008f1c: f043 0380 orr.w r3, r3, #128 ; 0x80 + 8008f20: 60a3 str r3, [r4, #8] + hpre = RCC_SYSCLK_DIV2; + 8008f22: 2780 movs r7, #128 ; 0x80 + 8008f24: e7d4 b.n 8008ed0 + else if((((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK) && (RCC_ClkInitStruct->AHBCLKDivider == RCC_SYSCLK_DIV1)) + 8008f26: 0788 lsls r0, r1, #30 + 8008f28: d5d1 bpl.n 8008ece + 8008f2a: 68ab ldr r3, [r5, #8] + 8008f2c: 2b00 cmp r3, #0 + 8008f2e: d1ce bne.n 8008ece + 8008f30: e7f1 b.n 8008f16 + if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE) + 8008f32: 2b02 cmp r3, #2 + 8008f34: d10a bne.n 8008f4c + if(READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U) + 8008f36: 6823 ldr r3, [r4, #0] + 8008f38: f413 3f00 tst.w r3, #131072 ; 0x20000 + if(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 8008f3c: f43f af47 beq.w 8008dce + if(HAL_RCC_GetSysClockFreq() > 80000000U) + 8008f40: f7ff fc44 bl 80087cc + 8008f44: 4b1a ldr r3, [pc, #104] ; (8008fb0 ) + 8008f46: 4298 cmp r0, r3 + 8008f48: d9c1 bls.n 8008ece + 8008f4a: e7e4 b.n 8008f16 + else if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_MSI) + 8008f4c: b91b cbnz r3, 8008f56 + if(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 8008f4e: 6823 ldr r3, [r4, #0] + 8008f50: f013 0f02 tst.w r3, #2 + 8008f54: e7f2 b.n 8008f3c + if(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 8008f56: 6823 ldr r3, [r4, #0] + 8008f58: f413 6f80 tst.w r3, #1024 ; 0x400 + 8008f5c: e7ee b.n 8008f3c + if(hpre == RCC_SYSCLK_DIV2) + 8008f5e: 2f80 cmp r7, #128 ; 0x80 + 8008f60: f47f af4d bne.w 8008dfe + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1); + 8008f64: 490c ldr r1, [pc, #48] ; (8008f98 ) + 8008f66: 688b ldr r3, [r1, #8] + 8008f68: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008f6c: e746 b.n 8008dfc + __HAL_FLASH_SET_LATENCY(FLatency); + 8008f6e: 680b ldr r3, [r1, #0] + 8008f70: f023 030f bic.w r3, r3, #15 + 8008f74: 4333 orrs r3, r6 + 8008f76: 600b str r3, [r1, #0] + if(__HAL_FLASH_GET_LATENCY() != FLatency) + 8008f78: 680b ldr r3, [r1, #0] + 8008f7a: f003 030f and.w r3, r3, #15 + 8008f7e: 42b3 cmp r3, r6 + 8008f80: f47f af25 bne.w 8008dce + 8008f84: e742 b.n 8008e0c + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider); + 8008f86: 68a3 ldr r3, [r4, #8] + 8008f88: 68e9 ldr r1, [r5, #12] + 8008f8a: f423 63e0 bic.w r3, r3, #1792 ; 0x700 + 8008f8e: 430b orrs r3, r1 + 8008f90: 60a3 str r3, [r4, #8] + 8008f92: e740 b.n 8008e16 + 8008f94: 40022000 .word 0x40022000 + 8008f98: 40021000 .word 0x40021000 + 8008f9c: 0800e9dc .word 0x0800e9dc + 8008fa0: 2009e2a8 .word 0x2009e2a8 + 8008fa4: 2009e2ac .word 0x2009e2ac + 8008fa8: 0800e9f4 .word 0x0800e9f4 + 8008fac: 007a1200 .word 0x007a1200 + 8008fb0: 04c4b400 .word 0x04c4b400 + 8008fb4: 00f42400 .word 0x00f42400 + +08008fb8 : +} + 8008fb8: 4b01 ldr r3, [pc, #4] ; (8008fc0 ) + 8008fba: 6818 ldr r0, [r3, #0] + 8008fbc: 4770 bx lr + 8008fbe: bf00 nop + 8008fc0: 2009e2a8 .word 0x2009e2a8 + +08008fc4 : + return (HAL_RCC_GetHCLKFreq() >> (APBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos] & 0x1FU)); + 8008fc4: 4b05 ldr r3, [pc, #20] ; (8008fdc ) + 8008fc6: 4a06 ldr r2, [pc, #24] ; (8008fe0 ) + 8008fc8: 689b ldr r3, [r3, #8] + 8008fca: f3c3 2302 ubfx r3, r3, #8, #3 + 8008fce: 5cd3 ldrb r3, [r2, r3] + 8008fd0: 4a04 ldr r2, [pc, #16] ; (8008fe4 ) + 8008fd2: 6810 ldr r0, [r2, #0] + 8008fd4: f003 031f and.w r3, r3, #31 +} + 8008fd8: 40d8 lsrs r0, r3 + 8008fda: 4770 bx lr + 8008fdc: 40021000 .word 0x40021000 + 8008fe0: 0800e9ec .word 0x0800e9ec + 8008fe4: 2009e2a8 .word 0x2009e2a8 + +08008fe8 : + return (HAL_RCC_GetHCLKFreq()>> (APBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos] & 0x1FU)); + 8008fe8: 4b05 ldr r3, [pc, #20] ; (8009000 ) + 8008fea: 4a06 ldr r2, [pc, #24] ; (8009004 ) + 8008fec: 689b ldr r3, [r3, #8] + 8008fee: f3c3 23c2 ubfx r3, r3, #11, #3 + 8008ff2: 5cd3 ldrb r3, [r2, r3] + 8008ff4: 4a04 ldr r2, [pc, #16] ; (8009008 ) + 8008ff6: 6810 ldr r0, [r2, #0] + 8008ff8: f003 031f and.w r3, r3, #31 +} + 8008ffc: 40d8 lsrs r0, r3 + 8008ffe: 4770 bx lr + 8009000: 40021000 .word 0x40021000 + 8009004: 0800e9ec .word 0x0800e9ec + 8009008: 2009e2a8 .word 0x2009e2a8 + +0800900c : + RCC_OscInitStruct->OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_MSI | \ + 800900c: 233f movs r3, #63 ; 0x3f + 800900e: 6003 str r3, [r0, #0] + if(READ_BIT(RCC->CR, RCC_CR_HSEBYP) == RCC_CR_HSEBYP) + 8009010: 4b2e ldr r3, [pc, #184] ; (80090cc ) + 8009012: 681a ldr r2, [r3, #0] + 8009014: 0351 lsls r1, r2, #13 + 8009016: d54a bpl.n 80090ae + RCC_OscInitStruct->HSEState = RCC_HSE_BYPASS; + 8009018: f44f 22a0 mov.w r2, #327680 ; 0x50000 + RCC_OscInitStruct->HSEState = RCC_HSE_OFF; + 800901c: 6042 str r2, [r0, #4] + if(READ_BIT(RCC->CR, RCC_CR_MSION) == RCC_CR_MSION) + 800901e: 681a ldr r2, [r3, #0] + 8009020: f002 0201 and.w r2, r2, #1 + 8009024: 6182 str r2, [r0, #24] + RCC_OscInitStruct->MSICalibrationValue = READ_BIT(RCC->ICSCR, RCC_ICSCR_MSITRIM) >> RCC_ICSCR_MSITRIM_Pos; + 8009026: 685a ldr r2, [r3, #4] + 8009028: f3c2 2207 ubfx r2, r2, #8, #8 + 800902c: 61c2 str r2, [r0, #28] + RCC_OscInitStruct->MSIClockRange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE); + 800902e: 681a ldr r2, [r3, #0] + 8009030: f002 02f0 and.w r2, r2, #240 ; 0xf0 + 8009034: 6202 str r2, [r0, #32] + if(READ_BIT(RCC->CR, RCC_CR_HSION) == RCC_CR_HSION) + 8009036: 681a ldr r2, [r3, #0] + RCC_OscInitStruct->HSIState = RCC_HSI_ON; + 8009038: f402 7280 and.w r2, r2, #256 ; 0x100 + 800903c: 60c2 str r2, [r0, #12] + RCC_OscInitStruct->HSICalibrationValue = READ_BIT(RCC->ICSCR, RCC_ICSCR_HSITRIM) >> RCC_ICSCR_HSITRIM_Pos; + 800903e: 685a ldr r2, [r3, #4] + 8009040: f3c2 6206 ubfx r2, r2, #24, #7 + 8009044: 6102 str r2, [r0, #16] + if(READ_BIT(RCC->BDCR, RCC_BDCR_LSEBYP) == RCC_BDCR_LSEBYP) + 8009046: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800904a: 0752 lsls r2, r2, #29 + 800904c: d536 bpl.n 80090bc + RCC_OscInitStruct->LSEState = RCC_LSE_BYPASS; + 800904e: 2205 movs r2, #5 + RCC_OscInitStruct->LSEState = RCC_LSE_OFF; + 8009050: 6082 str r2, [r0, #8] + if(READ_BIT(RCC->CSR, RCC_CSR_LSION) == RCC_CSR_LSION) + 8009052: f8d3 2094 ldr.w r2, [r3, #148] ; 0x94 + 8009056: f002 0201 and.w r2, r2, #1 + 800905a: 6142 str r2, [r0, #20] + if(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48ON) == RCC_CRRCR_HSI48ON) + 800905c: f8d3 2098 ldr.w r2, [r3, #152] ; 0x98 + 8009060: f002 0201 and.w r2, r2, #1 + 8009064: 6242 str r2, [r0, #36] ; 0x24 + if(READ_BIT(RCC->CR, RCC_CR_PLLON) == RCC_CR_PLLON) + 8009066: 681a ldr r2, [r3, #0] + RCC_OscInitStruct->PLL.PLLState = RCC_PLL_OFF; + 8009068: f012 7f80 tst.w r2, #16777216 ; 0x1000000 + 800906c: bf14 ite ne + 800906e: 2202 movne r2, #2 + 8009070: 2201 moveq r2, #1 + 8009072: 6282 str r2, [r0, #40] ; 0x28 + RCC_OscInitStruct->PLL.PLLSource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8009074: 68da ldr r2, [r3, #12] + 8009076: f002 0203 and.w r2, r2, #3 + 800907a: 62c2 str r2, [r0, #44] ; 0x2c + RCC_OscInitStruct->PLL.PLLM = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U; + 800907c: 68da ldr r2, [r3, #12] + 800907e: f3c2 1203 ubfx r2, r2, #4, #4 + 8009082: 3201 adds r2, #1 + 8009084: 6302 str r2, [r0, #48] ; 0x30 + RCC_OscInitStruct->PLL.PLLN = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009086: 68da ldr r2, [r3, #12] + 8009088: f3c2 2206 ubfx r2, r2, #8, #7 + 800908c: 6342 str r2, [r0, #52] ; 0x34 + RCC_OscInitStruct->PLL.PLLQ = (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U); + 800908e: 68da ldr r2, [r3, #12] + 8009090: f3c2 5241 ubfx r2, r2, #21, #2 + 8009094: 3201 adds r2, #1 + 8009096: 0052 lsls r2, r2, #1 + 8009098: 63c2 str r2, [r0, #60] ; 0x3c + RCC_OscInitStruct->PLL.PLLR = (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U) << 1U); + 800909a: 68da ldr r2, [r3, #12] + 800909c: f3c2 6241 ubfx r2, r2, #25, #2 + 80090a0: 3201 adds r2, #1 + 80090a2: 0052 lsls r2, r2, #1 + 80090a4: 6402 str r2, [r0, #64] ; 0x40 + RCC_OscInitStruct->PLL.PLLP = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 80090a6: 68db ldr r3, [r3, #12] + 80090a8: 0edb lsrs r3, r3, #27 + 80090aa: 6383 str r3, [r0, #56] ; 0x38 +} + 80090ac: 4770 bx lr + else if(READ_BIT(RCC->CR, RCC_CR_HSEON) == RCC_CR_HSEON) + 80090ae: 681a ldr r2, [r3, #0] + 80090b0: f412 3280 ands.w r2, r2, #65536 ; 0x10000 + RCC_OscInitStruct->HSEState = RCC_HSE_ON; + 80090b4: bf18 it ne + 80090b6: f44f 3280 movne.w r2, #65536 ; 0x10000 + 80090ba: e7af b.n 800901c + else if(READ_BIT(RCC->BDCR, RCC_BDCR_LSEON) == RCC_BDCR_LSEON) + 80090bc: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 80090c0: f012 0201 ands.w r2, r2, #1 + RCC_OscInitStruct->LSEState = RCC_LSE_ON; + 80090c4: bf18 it ne + 80090c6: 2201 movne r2, #1 + 80090c8: e7c2 b.n 8009050 + 80090ca: bf00 nop + 80090cc: 40021000 .word 0x40021000 + +080090d0 : + RCC_ClkInitStruct->ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; + 80090d0: 230f movs r3, #15 + 80090d2: 6003 str r3, [r0, #0] + RCC_ClkInitStruct->SYSCLKSource = READ_BIT(RCC->CFGR, RCC_CFGR_SW); + 80090d4: 4b0b ldr r3, [pc, #44] ; (8009104 ) + 80090d6: 689a ldr r2, [r3, #8] + 80090d8: f002 0203 and.w r2, r2, #3 + 80090dc: 6042 str r2, [r0, #4] + RCC_ClkInitStruct->AHBCLKDivider = READ_BIT(RCC->CFGR, RCC_CFGR_HPRE); + 80090de: 689a ldr r2, [r3, #8] + 80090e0: f002 02f0 and.w r2, r2, #240 ; 0xf0 + 80090e4: 6082 str r2, [r0, #8] + RCC_ClkInitStruct->APB1CLKDivider = READ_BIT(RCC->CFGR, RCC_CFGR_PPRE1); + 80090e6: 689a ldr r2, [r3, #8] + 80090e8: f402 62e0 and.w r2, r2, #1792 ; 0x700 + 80090ec: 60c2 str r2, [r0, #12] + RCC_ClkInitStruct->APB2CLKDivider = (READ_BIT(RCC->CFGR, RCC_CFGR_PPRE2) >> 3U); + 80090ee: 689b ldr r3, [r3, #8] + 80090f0: 08db lsrs r3, r3, #3 + 80090f2: f403 63e0 and.w r3, r3, #1792 ; 0x700 + 80090f6: 6103 str r3, [r0, #16] + *pFLatency = __HAL_FLASH_GET_LATENCY(); + 80090f8: 4b03 ldr r3, [pc, #12] ; (8009108 ) + 80090fa: 681b ldr r3, [r3, #0] + 80090fc: f003 030f and.w r3, r3, #15 + 8009100: 600b str r3, [r1, #0] +} + 8009102: 4770 bx lr + 8009104: 40021000 .word 0x40021000 + 8009108: 40022000 .word 0x40022000 + +0800910c : + SET_BIT(RCC->CR, RCC_CR_CSSON) ; + 800910c: 4a02 ldr r2, [pc, #8] ; (8009118 ) + 800910e: 6813 ldr r3, [r2, #0] + 8009110: f443 2300 orr.w r3, r3, #524288 ; 0x80000 + 8009114: 6013 str r3, [r2, #0] +} + 8009116: 4770 bx lr + 8009118: 40021000 .word 0x40021000 + +0800911c : +} + 800911c: 4770 bx lr + ... + +08009120 : +{ + 8009120: b510 push {r4, lr} + if(__HAL_RCC_GET_IT(RCC_IT_CSS)) + 8009122: 4c05 ldr r4, [pc, #20] ; (8009138 ) + 8009124: 69e3 ldr r3, [r4, #28] + 8009126: 05db lsls r3, r3, #23 + 8009128: d504 bpl.n 8009134 + HAL_RCC_CSSCallback(); + 800912a: f7ff fff7 bl 800911c + __HAL_RCC_CLEAR_IT(RCC_IT_CSS); + 800912e: f44f 7380 mov.w r3, #256 ; 0x100 + 8009132: 6223 str r3, [r4, #32] +} + 8009134: bd10 pop {r4, pc} + 8009136: bf00 nop + 8009138: 40021000 .word 0x40021000 + +0800913c : +#if defined(RCC_PLLP_SUPPORT) + uint32_t pllp = 0U; +#endif /* RCC_PLLP_SUPPORT */ + + /* Handle SAIs */ + if(PeriphClk == RCC_PERIPHCLK_SAI1) + 800913c: f5b0 6f00 cmp.w r0, #2048 ; 0x800 + 8009140: 4a3d ldr r2, [pc, #244] ; (8009238 ) + 8009142: d108 bne.n 8009156 + { + srcclk = __HAL_RCC_GET_SAI1_SOURCE(); + 8009144: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009148: f003 03e0 and.w r3, r3, #224 ; 0xe0 + if(srcclk == RCC_SAI1CLKSOURCE_PIN) + 800914c: 2b60 cmp r3, #96 ; 0x60 + 800914e: d12d bne.n 80091ac + { + frequency = EXTERNAL_SAI1_CLOCK_VALUE; + 8009150: f64b 3080 movw r0, #48000 ; 0xbb80 + 8009154: 4770 bx lr + /* Else, PLL clock output to check below */ + } +#if defined(SAI2) + else + { + if(PeriphClk == RCC_PERIPHCLK_SAI2) + 8009156: f5b0 5f80 cmp.w r0, #4096 ; 0x1000 + 800915a: d12a bne.n 80091b2 + { + srcclk = __HAL_RCC_GET_SAI2_SOURCE(); + 800915c: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009160: f403 63e0 and.w r3, r3, #1792 ; 0x700 + if(srcclk == RCC_SAI2CLKSOURCE_PIN) + 8009164: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 8009168: d0f2 beq.n 8009150 + if(frequency == 0U) + { + pllvco = InputFrequency; + +#if defined(SAI2) + if((srcclk == RCC_SAI1CLKSOURCE_PLL) || (srcclk == RCC_SAI2CLKSOURCE_PLL)) + 800916a: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800916e: d15c bne.n 800922a + { + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY) && (__HAL_RCC_GET_PLLCLKOUT_CONFIG(RCC_PLL_SAI3CLK) != 0U)) + 8009170: 6810 ldr r0, [r2, #0] + 8009172: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009176: d05d beq.n 8009234 + 8009178: 68d0 ldr r0, [r2, #12] + 800917a: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 800917e: d059 beq.n 8009234 + { + /* f(PLL Source) / PLLM */ + pllvco = (pllvco / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009180: 68d0 ldr r0, [r2, #12] + 8009182: f3c0 1003 ubfx r0, r0, #4, #4 + 8009186: 3001 adds r0, #1 + 8009188: fbb1 f0f0 udiv r0, r1, r0 + /* f(PLLSAI3CLK) = f(VCO input) * PLLN / PLLP */ + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 800918c: 68d1 ldr r1, [r2, #12] +#if defined(RCC_PLLP_DIV_2_31_SUPPORT) + pllp = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 800918e: 68d3 ldr r3, [r2, #12] +#endif + if(pllp == 0U) + 8009190: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009192: f3c1 2106 ubfx r1, r1, #8, #7 + if(pllp == 0U) + 8009196: d105 bne.n 80091a4 + { + if(READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLP) != 0U) + 8009198: 68d3 ldr r3, [r2, #12] + { + pllp = 17U; + } + else + { + pllp = 7U; + 800919a: f413 3f00 tst.w r3, #131072 ; 0x20000 + 800919e: bf14 ite ne + 80091a0: 2311 movne r3, #17 + 80091a2: 2307 moveq r3, #7 + } + } + frequency = (pllvco * plln) / pllp; + 80091a4: 4348 muls r0, r1 + 80091a6: fbb0 f0f3 udiv r0, r0, r3 + 80091aa: 4770 bx lr + if((srcclk == RCC_SAI1CLKSOURCE_PLL) || (srcclk == RCC_SAI2CLKSOURCE_PLL)) + 80091ac: 2b40 cmp r3, #64 ; 0x40 + 80091ae: d0df beq.n 8009170 + else if(srcclk == 0U) /* RCC_SAI1CLKSOURCE_PLLSAI1 || RCC_SAI2CLKSOURCE_PLLSAI1 */ + 80091b0: b9ab cbnz r3, 80091de + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY) && (__HAL_RCC_GET_PLLSAI1CLKOUT_CONFIG(RCC_PLLSAI1_SAI1CLK) != 0U)) + 80091b2: 6810 ldr r0, [r2, #0] + 80091b4: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 80091b8: d03c beq.n 8009234 + 80091ba: 6910 ldr r0, [r2, #16] + 80091bc: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 80091c0: d038 beq.n 8009234 + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 80091c2: 6913 ldr r3, [r2, #16] + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 80091c4: 6910 ldr r0, [r2, #16] + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 80091c6: f3c3 1303 ubfx r3, r3, #4, #4 + 80091ca: 3301 adds r3, #1 + 80091cc: fbb1 f1f3 udiv r1, r1, r3 + pllp = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1PDIV) >> RCC_PLLSAI1CFGR_PLLSAI1PDIV_Pos; + 80091d0: 6913 ldr r3, [r2, #16] + if(pllp == 0U) + 80091d2: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 80091d4: f3c0 2006 ubfx r0, r0, #8, #7 + if(pllp == 0U) + 80091d8: d1e4 bne.n 80091a4 + if(READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1P) != 0U) + 80091da: 6913 ldr r3, [r2, #16] + 80091dc: e7dd b.n 800919a + else if((srcclk == RCC_SAI1CLKSOURCE_HSI) || (srcclk == RCC_SAI2CLKSOURCE_HSI)) + 80091de: 2b80 cmp r3, #128 ; 0x80 + 80091e0: d106 bne.n 80091f0 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 80091e2: 6810 ldr r0, [r2, #0] + frequency = HSI_VALUE; + 80091e4: 4b15 ldr r3, [pc, #84] ; (800923c ) + 80091e6: f410 6080 ands.w r0, r0, #1024 ; 0x400 + 80091ea: bf18 it ne + 80091ec: 4618 movne r0, r3 + 80091ee: 4770 bx lr + else if((srcclk == RCC_SAI1CLKSOURCE_PLLSAI2) || (srcclk == RCC_SAI2CLKSOURCE_PLLSAI2)) + 80091f0: 2b20 cmp r3, #32 + 80091f2: d002 beq.n 80091fa + 80091f4: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 80091f8: d115 bne.n 8009226 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI2RDY) && (__HAL_RCC_GET_PLLSAI2CLKOUT_CONFIG(RCC_PLLSAI2_SAI2CLK) != 0U)) + 80091fa: 6810 ldr r0, [r2, #0] + 80091fc: f010 5000 ands.w r0, r0, #536870912 ; 0x20000000 + 8009200: d018 beq.n 8009234 + 8009202: 6950 ldr r0, [r2, #20] + 8009204: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 8009208: d014 beq.n 8009234 + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2M) >> RCC_PLLSAI2CFGR_PLLSAI2M_Pos) + 1U)); + 800920a: 6953 ldr r3, [r2, #20] + 800920c: f3c3 1303 ubfx r3, r3, #4, #4 + 8009210: 3301 adds r3, #1 + 8009212: fbb1 f0f3 udiv r0, r1, r3 + plln = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 8009216: 6951 ldr r1, [r2, #20] + pllp = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2PDIV) >> RCC_PLLSAI2CFGR_PLLSAI2PDIV_Pos; + 8009218: 6953 ldr r3, [r2, #20] + if(pllp == 0U) + 800921a: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 800921c: f3c1 2106 ubfx r1, r1, #8, #7 + if(pllp == 0U) + 8009220: d1c0 bne.n 80091a4 + if(READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2P) != 0U) + 8009222: 6953 ldr r3, [r2, #20] + 8009224: e7b9 b.n 800919a + 8009226: 2000 movs r0, #0 + /* No clock source, frequency default init at 0 */ + } + } + + + return frequency; + 8009228: 4770 bx lr + else if(srcclk == 0U) /* RCC_SAI1CLKSOURCE_PLLSAI1 || RCC_SAI2CLKSOURCE_PLLSAI1 */ + 800922a: 2b00 cmp r3, #0 + 800922c: d0c1 beq.n 80091b2 + else if((srcclk == RCC_SAI1CLKSOURCE_HSI) || (srcclk == RCC_SAI2CLKSOURCE_HSI)) + 800922e: f5b3 6f80 cmp.w r3, #1024 ; 0x400 + 8009232: e7d5 b.n 80091e0 +} + 8009234: 4770 bx lr + 8009236: bf00 nop + 8009238: 40021000 .word 0x40021000 + 800923c: 00f42400 .word 0x00f42400 + +08009240 : +{ + 8009240: b5f8 push {r3, r4, r5, r6, r7, lr} + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009242: 4c3c ldr r4, [pc, #240] ; (8009334 ) + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai1->PLLSAI1Source) + 8009244: 6803 ldr r3, [r0, #0] + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009246: 68e2 ldr r2, [r4, #12] +{ + 8009248: 4605 mov r5, r0 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800924a: 0790 lsls r0, r2, #30 +{ + 800924c: 460f mov r7, r1 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800924e: d023 beq.n 8009298 + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai1->PLLSAI1Source) + 8009250: 68e2 ldr r2, [r4, #12] + 8009252: f002 0203 and.w r2, r2, #3 + 8009256: 429a cmp r2, r3 + 8009258: d16a bne.n 8009330 + || + 800925a: 2a00 cmp r2, #0 + 800925c: d068 beq.n 8009330 + __HAL_RCC_PLLSAI1_DISABLE(); + 800925e: 6823 ldr r3, [r4, #0] + 8009260: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 8009264: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8009266: f7fd ff41 bl 80070ec + 800926a: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 800926c: 6823 ldr r3, [r4, #0] + 800926e: 011a lsls r2, r3, #4 + 8009270: d42d bmi.n 80092ce + MODIFY_REG(RCC->PLLSAI1CFGR, + 8009272: 68ab ldr r3, [r5, #8] + 8009274: 021e lsls r6, r3, #8 + 8009276: 686b ldr r3, [r5, #4] + 8009278: 3b01 subs r3, #1 + 800927a: 0118 lsls r0, r3, #4 + if(Divider == DIVIDER_P_UPDATE) + 800927c: b377 cbz r7, 80092dc + else if(Divider == DIVIDER_Q_UPDATE) + 800927e: 2f01 cmp r7, #1 + 8009280: d145 bne.n 800930e + MODIFY_REG(RCC->PLLSAI1CFGR, + 8009282: 692b ldr r3, [r5, #16] + 8009284: 6927 ldr r7, [r4, #16] + 8009286: 085b lsrs r3, r3, #1 + 8009288: 1e59 subs r1, r3, #1 + 800928a: 4b2b ldr r3, [pc, #172] ; (8009338 ) + 800928c: 403b ands r3, r7 + 800928e: 4333 orrs r3, r6 + 8009290: 4303 orrs r3, r0 + 8009292: ea43 5341 orr.w r3, r3, r1, lsl #21 + 8009296: e029 b.n 80092ec + switch(PllSai1->PLLSAI1Source) + 8009298: 2b02 cmp r3, #2 + 800929a: d00d beq.n 80092b8 + 800929c: 2b03 cmp r3, #3 + 800929e: d00f beq.n 80092c0 + 80092a0: 2b01 cmp r3, #1 + 80092a2: d145 bne.n 8009330 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_MSIRDY)) + 80092a4: 6822 ldr r2, [r4, #0] + 80092a6: f012 0f02 tst.w r2, #2 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80092aa: d041 beq.n 8009330 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, PllSai1->PLLSAI1Source); + 80092ac: 68e0 ldr r0, [r4, #12] + 80092ae: f020 0003 bic.w r0, r0, #3 + 80092b2: 4318 orrs r0, r3 + 80092b4: 60e0 str r0, [r4, #12] + if(status == HAL_OK) + 80092b6: e7d2 b.n 800925e + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSIRDY)) + 80092b8: 6822 ldr r2, [r4, #0] + 80092ba: f412 6f80 tst.w r2, #1024 ; 0x400 + 80092be: e7f4 b.n 80092aa + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSERDY)) + 80092c0: 6822 ldr r2, [r4, #0] + 80092c2: 0391 lsls r1, r2, #14 + 80092c4: d4f2 bmi.n 80092ac + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80092c6: 6822 ldr r2, [r4, #0] + 80092c8: f412 2f80 tst.w r2, #262144 ; 0x40000 + 80092cc: e7ed b.n 80092aa + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 80092ce: f7fd ff0d bl 80070ec + 80092d2: 1b80 subs r0, r0, r6 + 80092d4: 2802 cmp r0, #2 + 80092d6: d9c9 bls.n 800926c + status = HAL_TIMEOUT; + 80092d8: 2003 movs r0, #3 +} + 80092da: bdf8 pop {r3, r4, r5, r6, r7, pc} + MODIFY_REG(RCC->PLLSAI1CFGR, + 80092dc: 68e9 ldr r1, [r5, #12] + 80092de: 6922 ldr r2, [r4, #16] + 80092e0: ea46 63c1 orr.w r3, r6, r1, lsl #27 + 80092e4: 4915 ldr r1, [pc, #84] ; (800933c ) + 80092e6: 4011 ands r1, r2 + 80092e8: 430b orrs r3, r1 + 80092ea: 4303 orrs r3, r0 + MODIFY_REG(RCC->PLLSAI1CFGR, + 80092ec: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1_ENABLE(); + 80092ee: 6823 ldr r3, [r4, #0] + 80092f0: f043 6380 orr.w r3, r3, #67108864 ; 0x4000000 + 80092f4: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80092f6: f7fd fef9 bl 80070ec + 80092fa: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 80092fc: 6823 ldr r3, [r4, #0] + 80092fe: 011b lsls r3, r3, #4 + 8009300: d510 bpl.n 8009324 + __HAL_RCC_PLLSAI1CLKOUT_ENABLE(PllSai1->PLLSAI1ClockOut); + 8009302: 6923 ldr r3, [r4, #16] + 8009304: 69aa ldr r2, [r5, #24] + 8009306: 4313 orrs r3, r2 + 8009308: 6123 str r3, [r4, #16] + 800930a: 2000 movs r0, #0 + return status; + 800930c: e7e5 b.n 80092da + MODIFY_REG(RCC->PLLSAI1CFGR, + 800930e: 696b ldr r3, [r5, #20] + 8009310: 6921 ldr r1, [r4, #16] + 8009312: 085b lsrs r3, r3, #1 + 8009314: 1e5a subs r2, r3, #1 + 8009316: 4b0a ldr r3, [pc, #40] ; (8009340 ) + 8009318: 400b ands r3, r1 + 800931a: 4333 orrs r3, r6 + 800931c: 4303 orrs r3, r0 + 800931e: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8009322: e7e3 b.n 80092ec + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 8009324: f7fd fee2 bl 80070ec + 8009328: 1b80 subs r0, r0, r6 + 800932a: 2802 cmp r0, #2 + 800932c: d9e6 bls.n 80092fc + 800932e: e7d3 b.n 80092d8 + status = HAL_ERROR; + 8009330: 2001 movs r0, #1 + 8009332: e7d2 b.n 80092da + 8009334: 40021000 .word 0x40021000 + 8009338: ff9f800f .word 0xff9f800f + 800933c: 07ff800f .word 0x07ff800f + 8009340: f9ff800f .word 0xf9ff800f + +08009344 : +static HAL_StatusTypeDef RCCEx_PLLSAI2_Config(RCC_PLLSAI2InitTypeDef *PllSai2, uint32_t Divider) + 8009344: b570 push {r4, r5, r6, lr} + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009346: 4c2f ldr r4, [pc, #188] ; (8009404 ) + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai2->PLLSAI2Source) + 8009348: 6803 ldr r3, [r0, #0] + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800934a: 68e2 ldr r2, [r4, #12] +static HAL_StatusTypeDef RCCEx_PLLSAI2_Config(RCC_PLLSAI2InitTypeDef *PllSai2, uint32_t Divider) + 800934c: 4605 mov r5, r0 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800934e: 0790 lsls r0, r2, #30 + 8009350: d026 beq.n 80093a0 + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai2->PLLSAI2Source) + 8009352: 68e2 ldr r2, [r4, #12] + 8009354: f002 0203 and.w r2, r2, #3 + 8009358: 429a cmp r2, r3 + 800935a: d151 bne.n 8009400 + || + 800935c: 2a00 cmp r2, #0 + 800935e: d04f beq.n 8009400 + __HAL_RCC_PLLSAI2_DISABLE(); + 8009360: 6823 ldr r3, [r4, #0] + 8009362: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8009366: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8009368: f7fd fec0 bl 80070ec + 800936c: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 800936e: 6823 ldr r3, [r4, #0] + 8009370: 009a lsls r2, r3, #2 + 8009372: d430 bmi.n 80093d6 + MODIFY_REG(RCC->PLLSAI2CFGR, + 8009374: e9d5 2302 ldrd r2, r3, [r5, #8] + 8009378: 06db lsls r3, r3, #27 + 800937a: 6961 ldr r1, [r4, #20] + 800937c: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8009380: 4a21 ldr r2, [pc, #132] ; (8009408 ) + 8009382: 400a ands r2, r1 + 8009384: 4313 orrs r3, r2 + 8009386: 686a ldr r2, [r5, #4] + 8009388: 3a01 subs r2, #1 + 800938a: ea43 1302 orr.w r3, r3, r2, lsl #4 + 800938e: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2_ENABLE(); + 8009390: 6823 ldr r3, [r4, #0] + 8009392: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 8009396: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8009398: f7fd fea8 bl 80070ec + 800939c: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 800939e: e026 b.n 80093ee + switch(PllSai2->PLLSAI2Source) + 80093a0: 2b02 cmp r3, #2 + 80093a2: d00d beq.n 80093c0 + 80093a4: 2b03 cmp r3, #3 + 80093a6: d00f beq.n 80093c8 + 80093a8: 2b01 cmp r3, #1 + 80093aa: d129 bne.n 8009400 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_MSIRDY)) + 80093ac: 6822 ldr r2, [r4, #0] + 80093ae: f012 0f02 tst.w r2, #2 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80093b2: d025 beq.n 8009400 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, PllSai2->PLLSAI2Source); + 80093b4: 68e0 ldr r0, [r4, #12] + 80093b6: f020 0003 bic.w r0, r0, #3 + 80093ba: 4318 orrs r0, r3 + 80093bc: 60e0 str r0, [r4, #12] + if(status == HAL_OK) + 80093be: e7cf b.n 8009360 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSIRDY)) + 80093c0: 6822 ldr r2, [r4, #0] + 80093c2: f412 6f80 tst.w r2, #1024 ; 0x400 + 80093c6: e7f4 b.n 80093b2 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSERDY)) + 80093c8: 6822 ldr r2, [r4, #0] + 80093ca: 0391 lsls r1, r2, #14 + 80093cc: d4f2 bmi.n 80093b4 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80093ce: 6822 ldr r2, [r4, #0] + 80093d0: f412 2f80 tst.w r2, #262144 ; 0x40000 + 80093d4: e7ed b.n 80093b2 + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 80093d6: f7fd fe89 bl 80070ec + 80093da: 1b80 subs r0, r0, r6 + 80093dc: 2802 cmp r0, #2 + 80093de: d9c6 bls.n 800936e + status = HAL_TIMEOUT; + 80093e0: 2003 movs r0, #3 +} + 80093e2: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 80093e4: f7fd fe82 bl 80070ec + 80093e8: 1b80 subs r0, r0, r6 + 80093ea: 2802 cmp r0, #2 + 80093ec: d8f8 bhi.n 80093e0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 80093ee: 6823 ldr r3, [r4, #0] + 80093f0: 009b lsls r3, r3, #2 + 80093f2: d5f7 bpl.n 80093e4 + __HAL_RCC_PLLSAI2CLKOUT_ENABLE(PllSai2->PLLSAI2ClockOut); + 80093f4: 6963 ldr r3, [r4, #20] + 80093f6: 69aa ldr r2, [r5, #24] + 80093f8: 4313 orrs r3, r2 + 80093fa: 6163 str r3, [r4, #20] + 80093fc: 2000 movs r0, #0 + return status; + 80093fe: e7f0 b.n 80093e2 + status = HAL_ERROR; + 8009400: 2001 movs r0, #1 + 8009402: e7ee b.n 80093e2 + 8009404: 40021000 .word 0x40021000 + 8009408: 07ff800f .word 0x07ff800f + +0800940c : +{ + 800940c: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI1) == RCC_PERIPHCLK_SAI1)) + 8009410: 6806 ldr r6, [r0, #0] + 8009412: f416 6600 ands.w r6, r6, #2048 ; 0x800 +{ + 8009416: 4604 mov r4, r0 + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI1) == RCC_PERIPHCLK_SAI1)) + 8009418: d007 beq.n 800942a + switch(PeriphClkInit->Sai1ClockSelection) + 800941a: 6ec1 ldr r1, [r0, #108] ; 0x6c + 800941c: 2940 cmp r1, #64 ; 0x40 + 800941e: d022 beq.n 8009466 + 8009420: d812 bhi.n 8009448 + 8009422: b331 cbz r1, 8009472 + 8009424: 2920 cmp r1, #32 + 8009426: d02b beq.n 8009480 + 8009428: 2601 movs r6, #1 + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI2) == RCC_PERIPHCLK_SAI2)) + 800942a: 6823 ldr r3, [r4, #0] + 800942c: 04db lsls r3, r3, #19 + 800942e: d509 bpl.n 8009444 + switch(PeriphClkInit->Sai2ClockSelection) + 8009430: 6f21 ldr r1, [r4, #112] ; 0x70 + 8009432: f5b1 7f00 cmp.w r1, #512 ; 0x200 + 8009436: d02f beq.n 8009498 + 8009438: d826 bhi.n 8009488 + 800943a: b399 cbz r1, 80094a4 + 800943c: f5b1 7f80 cmp.w r1, #256 ; 0x100 + 8009440: d073 beq.n 800952a + 8009442: 2601 movs r6, #1 + 8009444: 4635 mov r5, r6 + 8009446: e03c b.n 80094c2 + switch(PeriphClkInit->Sai1ClockSelection) + 8009448: 2960 cmp r1, #96 ; 0x60 + 800944a: d001 beq.n 8009450 + 800944c: 2980 cmp r1, #128 ; 0x80 + 800944e: d1eb bne.n 8009428 + __HAL_RCC_SAI1_CONFIG(PeriphClkInit->Sai1ClockSelection); + 8009450: 4a3b ldr r2, [pc, #236] ; (8009540 ) + 8009452: 6ee1 ldr r1, [r4, #108] ; 0x6c + 8009454: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009458: f023 03e0 bic.w r3, r3, #224 ; 0xe0 + 800945c: 430b orrs r3, r1 + 800945e: f8c2 309c str.w r3, [r2, #156] ; 0x9c + 8009462: 2600 movs r6, #0 + 8009464: e7e1 b.n 800942a + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 8009466: 4a36 ldr r2, [pc, #216] ; (8009540 ) + 8009468: 68d3 ldr r3, [r2, #12] + 800946a: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 800946e: 60d3 str r3, [r2, #12] + if(ret == HAL_OK) + 8009470: e7ee b.n 8009450 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_P_UPDATE); + 8009472: 3004 adds r0, #4 + 8009474: f7ff fee4 bl 8009240 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 8009478: 4606 mov r6, r0 + if(ret == HAL_OK) + 800947a: 2800 cmp r0, #0 + 800947c: d1d5 bne.n 800942a + 800947e: e7e7 b.n 8009450 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 8009480: 3020 adds r0, #32 + 8009482: f7ff ff5f bl 8009344 + 8009486: e7f7 b.n 8009478 + switch(PeriphClkInit->Sai2ClockSelection) + 8009488: f5b1 7f40 cmp.w r1, #768 ; 0x300 + 800948c: d002 beq.n 8009494 + 800948e: f5b1 6f80 cmp.w r1, #1024 ; 0x400 + 8009492: d1d6 bne.n 8009442 + 8009494: 4635 mov r5, r6 + 8009496: e009 b.n 80094ac + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 8009498: 4a29 ldr r2, [pc, #164] ; (8009540 ) + 800949a: 68d3 ldr r3, [r2, #12] + 800949c: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 80094a0: 60d3 str r3, [r2, #12] + break; + 80094a2: e7f7 b.n 8009494 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_P_UPDATE); + 80094a4: 1d20 adds r0, r4, #4 + 80094a6: f7ff fecb bl 8009240 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 80094aa: 4605 mov r5, r0 + if(ret == HAL_OK) + 80094ac: 2d00 cmp r5, #0 + 80094ae: d141 bne.n 8009534 + __HAL_RCC_SAI2_CONFIG(PeriphClkInit->Sai2ClockSelection); + 80094b0: 4a23 ldr r2, [pc, #140] ; (8009540 ) + 80094b2: 6f21 ldr r1, [r4, #112] ; 0x70 + 80094b4: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 80094b8: f423 63e0 bic.w r3, r3, #1792 ; 0x700 + 80094bc: 430b orrs r3, r1 + 80094be: f8c2 309c str.w r3, [r2, #156] ; 0x9c + if((PeriphClkInit->PeriphClockSelection & RCC_PERIPHCLK_RTC) == RCC_PERIPHCLK_RTC) + 80094c2: 6823 ldr r3, [r4, #0] + 80094c4: 039f lsls r7, r3, #14 + 80094c6: f140 817d bpl.w 80097c4 + if(__HAL_RCC_PWR_IS_CLK_DISABLED() != 0U) + 80094ca: 4f1d ldr r7, [pc, #116] ; (8009540 ) + 80094cc: 6dbb ldr r3, [r7, #88] ; 0x58 + 80094ce: 00d8 lsls r0, r3, #3 + 80094d0: d432 bmi.n 8009538 + __HAL_RCC_PWR_CLK_ENABLE(); + 80094d2: 6dbb ldr r3, [r7, #88] ; 0x58 + 80094d4: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 80094d8: 65bb str r3, [r7, #88] ; 0x58 + 80094da: 6dbb ldr r3, [r7, #88] ; 0x58 + 80094dc: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 80094e0: 9301 str r3, [sp, #4] + 80094e2: 9b01 ldr r3, [sp, #4] + pwrclkchanged = SET; + 80094e4: f04f 0801 mov.w r8, #1 + SET_BIT(PWR->CR1, PWR_CR1_DBP); + 80094e8: f8df 9058 ldr.w r9, [pc, #88] ; 8009544 + 80094ec: f8d9 3000 ldr.w r3, [r9] + 80094f0: f443 7380 orr.w r3, r3, #256 ; 0x100 + 80094f4: f8c9 3000 str.w r3, [r9] + tickstart = HAL_GetTick(); + 80094f8: f7fd fdf8 bl 80070ec + 80094fc: 4682 mov sl, r0 + while(READ_BIT(PWR->CR1, PWR_CR1_DBP) == 0U) + 80094fe: f8d9 3000 ldr.w r3, [r9] + 8009502: 05d9 lsls r1, r3, #23 + 8009504: d520 bpl.n 8009548 + if(ret == HAL_OK) + 8009506: bb35 cbnz r5, 8009556 + tmpregister = READ_BIT(RCC->BDCR, RCC_BDCR_RTCSEL); + 8009508: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + if((tmpregister != RCC_RTCCLKSOURCE_NONE) && (tmpregister != PeriphClkInit->RTCClockSelection)) + 800950c: f413 7340 ands.w r3, r3, #768 ; 0x300 + 8009510: f040 812e bne.w 8009770 + __HAL_RCC_RTC_CONFIG(PeriphClkInit->RTCClockSelection); + 8009514: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 8009518: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 800951c: f423 7340 bic.w r3, r3, #768 ; 0x300 + 8009520: 4313 orrs r3, r2 + 8009522: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + 8009526: 4635 mov r5, r6 + 8009528: e015 b.n 8009556 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 800952a: f104 0020 add.w r0, r4, #32 + 800952e: f7ff ff09 bl 8009344 + 8009532: e7ba b.n 80094aa + 8009534: 462e mov r6, r5 + 8009536: e7c4 b.n 80094c2 + FlagStatus pwrclkchanged = RESET; + 8009538: f04f 0800 mov.w r8, #0 + 800953c: e7d4 b.n 80094e8 + 800953e: bf00 nop + 8009540: 40021000 .word 0x40021000 + 8009544: 40007000 .word 0x40007000 + if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) + 8009548: f7fd fdd0 bl 80070ec + 800954c: eba0 000a sub.w r0, r0, sl + 8009550: 2802 cmp r0, #2 + 8009552: d9d4 bls.n 80094fe + ret = HAL_TIMEOUT; + 8009554: 2503 movs r5, #3 + if(pwrclkchanged == SET) + 8009556: f1b8 0f00 cmp.w r8, #0 + 800955a: d003 beq.n 8009564 + __HAL_RCC_PWR_CLK_DISABLE(); + 800955c: 6dbb ldr r3, [r7, #88] ; 0x58 + 800955e: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8009562: 65bb str r3, [r7, #88] ; 0x58 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART1) == RCC_PERIPHCLK_USART1) + 8009564: 6823 ldr r3, [r4, #0] + 8009566: 07d8 lsls r0, r3, #31 + 8009568: d508 bpl.n 800957c + __HAL_RCC_USART1_CONFIG(PeriphClkInit->Usart1ClockSelection); + 800956a: 49b2 ldr r1, [pc, #712] ; (8009834 ) + 800956c: 6be0 ldr r0, [r4, #60] ; 0x3c + 800956e: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009572: f022 0203 bic.w r2, r2, #3 + 8009576: 4302 orrs r2, r0 + 8009578: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART2) == RCC_PERIPHCLK_USART2) + 800957c: 0799 lsls r1, r3, #30 + 800957e: d508 bpl.n 8009592 + __HAL_RCC_USART2_CONFIG(PeriphClkInit->Usart2ClockSelection); + 8009580: 49ac ldr r1, [pc, #688] ; (8009834 ) + 8009582: 6c20 ldr r0, [r4, #64] ; 0x40 + 8009584: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009588: f022 020c bic.w r2, r2, #12 + 800958c: 4302 orrs r2, r0 + 800958e: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART3) == RCC_PERIPHCLK_USART3) + 8009592: 075a lsls r2, r3, #29 + 8009594: d508 bpl.n 80095a8 + __HAL_RCC_USART3_CONFIG(PeriphClkInit->Usart3ClockSelection); + 8009596: 49a7 ldr r1, [pc, #668] ; (8009834 ) + 8009598: 6c60 ldr r0, [r4, #68] ; 0x44 + 800959a: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 800959e: f022 0230 bic.w r2, r2, #48 ; 0x30 + 80095a2: 4302 orrs r2, r0 + 80095a4: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_UART4) == RCC_PERIPHCLK_UART4) + 80095a8: 071f lsls r7, r3, #28 + 80095aa: d508 bpl.n 80095be + __HAL_RCC_UART4_CONFIG(PeriphClkInit->Uart4ClockSelection); + 80095ac: 49a1 ldr r1, [pc, #644] ; (8009834 ) + 80095ae: 6ca0 ldr r0, [r4, #72] ; 0x48 + 80095b0: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80095b4: f022 02c0 bic.w r2, r2, #192 ; 0xc0 + 80095b8: 4302 orrs r2, r0 + 80095ba: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_UART5) == RCC_PERIPHCLK_UART5) + 80095be: 06de lsls r6, r3, #27 + 80095c0: d508 bpl.n 80095d4 + __HAL_RCC_UART5_CONFIG(PeriphClkInit->Uart5ClockSelection); + 80095c2: 499c ldr r1, [pc, #624] ; (8009834 ) + 80095c4: 6ce0 ldr r0, [r4, #76] ; 0x4c + 80095c6: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80095ca: f422 7240 bic.w r2, r2, #768 ; 0x300 + 80095ce: 4302 orrs r2, r0 + 80095d0: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPUART1) == RCC_PERIPHCLK_LPUART1) + 80095d4: 0698 lsls r0, r3, #26 + 80095d6: d508 bpl.n 80095ea + __HAL_RCC_LPUART1_CONFIG(PeriphClkInit->Lpuart1ClockSelection); + 80095d8: 4996 ldr r1, [pc, #600] ; (8009834 ) + 80095da: 6d20 ldr r0, [r4, #80] ; 0x50 + 80095dc: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80095e0: f422 6240 bic.w r2, r2, #3072 ; 0xc00 + 80095e4: 4302 orrs r2, r0 + 80095e6: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPTIM1) == (RCC_PERIPHCLK_LPTIM1)) + 80095ea: 0599 lsls r1, r3, #22 + 80095ec: d508 bpl.n 8009600 + __HAL_RCC_LPTIM1_CONFIG(PeriphClkInit->Lptim1ClockSelection); + 80095ee: 4991 ldr r1, [pc, #580] ; (8009834 ) + 80095f0: 6e60 ldr r0, [r4, #100] ; 0x64 + 80095f2: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80095f6: f422 2240 bic.w r2, r2, #786432 ; 0xc0000 + 80095fa: 4302 orrs r2, r0 + 80095fc: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPTIM2) == (RCC_PERIPHCLK_LPTIM2)) + 8009600: 055a lsls r2, r3, #21 + 8009602: d508 bpl.n 8009616 + __HAL_RCC_LPTIM2_CONFIG(PeriphClkInit->Lptim2ClockSelection); + 8009604: 498b ldr r1, [pc, #556] ; (8009834 ) + 8009606: 6ea0 ldr r0, [r4, #104] ; 0x68 + 8009608: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 800960c: f422 1240 bic.w r2, r2, #3145728 ; 0x300000 + 8009610: 4302 orrs r2, r0 + 8009612: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C1) == RCC_PERIPHCLK_I2C1) + 8009616: 065f lsls r7, r3, #25 + 8009618: d508 bpl.n 800962c + __HAL_RCC_I2C1_CONFIG(PeriphClkInit->I2c1ClockSelection); + 800961a: 4986 ldr r1, [pc, #536] ; (8009834 ) + 800961c: 6d60 ldr r0, [r4, #84] ; 0x54 + 800961e: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009622: f422 5240 bic.w r2, r2, #12288 ; 0x3000 + 8009626: 4302 orrs r2, r0 + 8009628: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C2) == RCC_PERIPHCLK_I2C2) + 800962c: 061e lsls r6, r3, #24 + 800962e: d508 bpl.n 8009642 + __HAL_RCC_I2C2_CONFIG(PeriphClkInit->I2c2ClockSelection); + 8009630: 4980 ldr r1, [pc, #512] ; (8009834 ) + 8009632: 6da0 ldr r0, [r4, #88] ; 0x58 + 8009634: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009638: f422 4240 bic.w r2, r2, #49152 ; 0xc000 + 800963c: 4302 orrs r2, r0 + 800963e: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C3) == RCC_PERIPHCLK_I2C3) + 8009642: 05d8 lsls r0, r3, #23 + 8009644: d508 bpl.n 8009658 + __HAL_RCC_I2C3_CONFIG(PeriphClkInit->I2c3ClockSelection); + 8009646: 497b ldr r1, [pc, #492] ; (8009834 ) + 8009648: 6de0 ldr r0, [r4, #92] ; 0x5c + 800964a: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 800964e: f422 3240 bic.w r2, r2, #196608 ; 0x30000 + 8009652: 4302 orrs r2, r0 + 8009654: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C4) == RCC_PERIPHCLK_I2C4) + 8009658: 02d9 lsls r1, r3, #11 + 800965a: d508 bpl.n 800966e + __HAL_RCC_I2C4_CONFIG(PeriphClkInit->I2c4ClockSelection); + 800965c: 4975 ldr r1, [pc, #468] ; (8009834 ) + 800965e: 6e20 ldr r0, [r4, #96] ; 0x60 + 8009660: f8d1 209c ldr.w r2, [r1, #156] ; 0x9c + 8009664: f022 0203 bic.w r2, r2, #3 + 8009668: 4302 orrs r2, r0 + 800966a: f8c1 209c str.w r2, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USB) == (RCC_PERIPHCLK_USB)) + 800966e: 049a lsls r2, r3, #18 + 8009670: d510 bpl.n 8009694 + __HAL_RCC_USB_CONFIG(PeriphClkInit->UsbClockSelection); + 8009672: 4a70 ldr r2, [pc, #448] ; (8009834 ) + 8009674: 6f61 ldr r1, [r4, #116] ; 0x74 + 8009676: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 800967a: f023 6340 bic.w r3, r3, #201326592 ; 0xc000000 + 800967e: 430b orrs r3, r1 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLL) + 8009680: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_USB_CONFIG(PeriphClkInit->UsbClockSelection); + 8009684: f8c2 3088 str.w r3, [r2, #136] ; 0x88 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLL) + 8009688: f040 809e bne.w 80097c8 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 800968c: 68d3 ldr r3, [r2, #12] + 800968e: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 8009692: 60d3 str r3, [r2, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SDMMC1) == (RCC_PERIPHCLK_SDMMC1)) + 8009694: 6823 ldr r3, [r4, #0] + 8009696: 031b lsls r3, r3, #12 + 8009698: d50f bpl.n 80096ba + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 800969a: 6fa1 ldr r1, [r4, #120] ; 0x78 + 800969c: 4b65 ldr r3, [pc, #404] ; (8009834 ) + 800969e: f5b1 4f80 cmp.w r1, #16384 ; 0x4000 + 80096a2: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 80096a6: f040 809b bne.w 80097e0 + 80096aa: f442 4280 orr.w r2, r2, #16384 ; 0x4000 + 80096ae: f8c3 209c str.w r2, [r3, #156] ; 0x9c + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 80096b2: 68da ldr r2, [r3, #12] + 80096b4: f442 3280 orr.w r2, r2, #65536 ; 0x10000 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 80096b8: 60da str r2, [r3, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_RNG) == (RCC_PERIPHCLK_RNG)) + 80096ba: 6823 ldr r3, [r4, #0] + 80096bc: 035f lsls r7, r3, #13 + 80096be: d510 bpl.n 80096e2 + __HAL_RCC_RNG_CONFIG(PeriphClkInit->RngClockSelection); + 80096c0: 4a5c ldr r2, [pc, #368] ; (8009834 ) + 80096c2: 6fe1 ldr r1, [r4, #124] ; 0x7c + 80096c4: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 80096c8: f023 6340 bic.w r3, r3, #201326592 ; 0xc000000 + 80096cc: 430b orrs r3, r1 + if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLL) + 80096ce: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_RNG_CONFIG(PeriphClkInit->RngClockSelection); + 80096d2: f8c2 3088 str.w r3, [r2, #136] ; 0x88 + if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLL) + 80096d6: f040 80a1 bne.w 800981c + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 80096da: 68d3 ldr r3, [r2, #12] + 80096dc: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 80096e0: 60d3 str r3, [r2, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_ADC) == RCC_PERIPHCLK_ADC) + 80096e2: 6823 ldr r3, [r4, #0] + 80096e4: 045e lsls r6, r3, #17 + 80096e6: d513 bpl.n 8009710 + __HAL_RCC_ADC_CONFIG(PeriphClkInit->AdcClockSelection); + 80096e8: 4952 ldr r1, [pc, #328] ; (8009834 ) + 80096ea: f8d4 2080 ldr.w r2, [r4, #128] ; 0x80 + 80096ee: f8d1 3088 ldr.w r3, [r1, #136] ; 0x88 + 80096f2: f023 5340 bic.w r3, r3, #805306368 ; 0x30000000 + 80096f6: 4313 orrs r3, r2 + if(PeriphClkInit->AdcClockSelection == RCC_ADCCLKSOURCE_PLLSAI1) + 80096f8: f1b2 5f80 cmp.w r2, #268435456 ; 0x10000000 + __HAL_RCC_ADC_CONFIG(PeriphClkInit->AdcClockSelection); + 80096fc: f8c1 3088 str.w r3, [r1, #136] ; 0x88 + if(PeriphClkInit->AdcClockSelection == RCC_ADCCLKSOURCE_PLLSAI1) + 8009700: d106 bne.n 8009710 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_R_UPDATE); + 8009702: 2102 movs r1, #2 + 8009704: 1d20 adds r0, r4, #4 + 8009706: f7ff fd9b bl 8009240 + if(ret != HAL_OK) + 800970a: 2800 cmp r0, #0 + 800970c: bf18 it ne + 800970e: 4605 movne r5, r0 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_DFSDM1) == RCC_PERIPHCLK_DFSDM1) + 8009710: 6822 ldr r2, [r4, #0] + 8009712: 03d0 lsls r0, r2, #15 + 8009714: d509 bpl.n 800972a + __HAL_RCC_DFSDM1_CONFIG(PeriphClkInit->Dfsdm1ClockSelection); + 8009716: 4947 ldr r1, [pc, #284] ; (8009834 ) + 8009718: f8d4 0084 ldr.w r0, [r4, #132] ; 0x84 + 800971c: f8d1 309c ldr.w r3, [r1, #156] ; 0x9c + 8009720: f023 0304 bic.w r3, r3, #4 + 8009724: 4303 orrs r3, r0 + 8009726: f8c1 309c str.w r3, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_DFSDM1AUDIO) == RCC_PERIPHCLK_DFSDM1AUDIO) + 800972a: 0291 lsls r1, r2, #10 + 800972c: d509 bpl.n 8009742 + __HAL_RCC_DFSDM1AUDIO_CONFIG(PeriphClkInit->Dfsdm1AudioClockSelection); + 800972e: 4941 ldr r1, [pc, #260] ; (8009834 ) + 8009730: f8d4 0088 ldr.w r0, [r4, #136] ; 0x88 + 8009734: f8d1 309c ldr.w r3, [r1, #156] ; 0x9c + 8009738: f023 0318 bic.w r3, r3, #24 + 800973c: 4303 orrs r3, r0 + 800973e: f8c1 309c str.w r3, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_OSPI) == RCC_PERIPHCLK_OSPI) + 8009742: 01d3 lsls r3, r2, #7 + 8009744: d510 bpl.n 8009768 + __HAL_RCC_OSPI_CONFIG(PeriphClkInit->OspiClockSelection); + 8009746: 4a3b ldr r2, [pc, #236] ; (8009834 ) + 8009748: f8d4 108c ldr.w r1, [r4, #140] ; 0x8c + 800974c: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009750: f423 1340 bic.w r3, r3, #3145728 ; 0x300000 + 8009754: 430b orrs r3, r1 + 8009756: f8c2 309c str.w r3, [r2, #156] ; 0x9c + if(PeriphClkInit->OspiClockSelection == RCC_OSPICLKSOURCE_PLL) + 800975a: f5b1 1f00 cmp.w r1, #2097152 ; 0x200000 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 800975e: bf02 ittt eq + 8009760: 68d3 ldreq r3, [r2, #12] + 8009762: f443 1380 orreq.w r3, r3, #1048576 ; 0x100000 + 8009766: 60d3 streq r3, [r2, #12] +} + 8009768: 4628 mov r0, r5 + 800976a: b002 add sp, #8 + 800976c: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if((tmpregister != RCC_RTCCLKSOURCE_NONE) && (tmpregister != PeriphClkInit->RTCClockSelection)) + 8009770: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8009774: 429a cmp r2, r3 + 8009776: f43f aecd beq.w 8009514 + tmpregister = READ_BIT(RCC->BDCR, ~(RCC_BDCR_RTCSEL)); + 800977a: f8d7 2090 ldr.w r2, [r7, #144] ; 0x90 + __HAL_RCC_BACKUPRESET_FORCE(); + 800977e: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 8009782: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8009786: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + __HAL_RCC_BACKUPRESET_RELEASE(); + 800978a: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + tmpregister = READ_BIT(RCC->BDCR, ~(RCC_BDCR_RTCSEL)); + 800978e: f422 7140 bic.w r1, r2, #768 ; 0x300 + __HAL_RCC_BACKUPRESET_RELEASE(); + 8009792: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + if (HAL_IS_BIT_SET(tmpregister, RCC_BDCR_LSEON)) + 8009796: 07d2 lsls r2, r2, #31 + __HAL_RCC_BACKUPRESET_RELEASE(); + 8009798: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + RCC->BDCR = tmpregister; + 800979c: f8c7 1090 str.w r1, [r7, #144] ; 0x90 + if (HAL_IS_BIT_SET(tmpregister, RCC_BDCR_LSEON)) + 80097a0: f57f aeb8 bpl.w 8009514 + tickstart = HAL_GetTick(); + 80097a4: f7fd fca2 bl 80070ec + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 80097a8: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 80097ac: 4605 mov r5, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0U) + 80097ae: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 80097b2: 079b lsls r3, r3, #30 + 80097b4: f53f aeae bmi.w 8009514 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 80097b8: f7fd fc98 bl 80070ec + 80097bc: 1b40 subs r0, r0, r5 + 80097be: 4548 cmp r0, r9 + 80097c0: d9f5 bls.n 80097ae + 80097c2: e6c7 b.n 8009554 + 80097c4: 4635 mov r5, r6 + 80097c6: e6cd b.n 8009564 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLLSAI1) + 80097c8: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 80097cc: f47f af62 bne.w 8009694 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 80097d0: 2101 movs r1, #1 + 80097d2: 1d20 adds r0, r4, #4 + 80097d4: f7ff fd34 bl 8009240 + if(ret != HAL_OK) + 80097d8: 2800 cmp r0, #0 + 80097da: bf18 it ne + 80097dc: 4605 movne r5, r0 + 80097de: e759 b.n 8009694 + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 80097e0: f422 4280 bic.w r2, r2, #16384 ; 0x4000 + 80097e4: f8c3 209c str.w r2, [r3, #156] ; 0x9c + 80097e8: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80097ec: f022 6240 bic.w r2, r2, #201326592 ; 0xc000000 + 80097f0: 430a orrs r2, r1 + if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLL) /* PLL "Q" ? */ + 80097f2: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 80097f6: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLL) /* PLL "Q" ? */ + 80097fa: d103 bne.n 8009804 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 80097fc: 68da ldr r2, [r3, #12] + 80097fe: f442 1280 orr.w r2, r2, #1048576 ; 0x100000 + 8009802: e759 b.n 80096b8 + else if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLLSAI1) + 8009804: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 8009808: f47f af57 bne.w 80096ba + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 800980c: 2101 movs r1, #1 + 800980e: 1d20 adds r0, r4, #4 + 8009810: f7ff fd16 bl 8009240 + if(ret != HAL_OK) + 8009814: 2800 cmp r0, #0 + 8009816: bf18 it ne + 8009818: 4605 movne r5, r0 + 800981a: e74e b.n 80096ba + else if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLLSAI1) + 800981c: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 8009820: f47f af5f bne.w 80096e2 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 8009824: 2101 movs r1, #1 + 8009826: 1d20 adds r0, r4, #4 + 8009828: f7ff fd0a bl 8009240 + if(ret != HAL_OK) + 800982c: 2800 cmp r0, #0 + 800982e: bf18 it ne + 8009830: 4605 movne r5, r0 + 8009832: e756 b.n 80096e2 + 8009834: 40021000 .word 0x40021000 + +08009838 : + PeriphClkInit->PeriphClockSelection = RCC_PERIPHCLK_USART1 | RCC_PERIPHCLK_USART2 | RCC_PERIPHCLK_USART3 | RCC_PERIPHCLK_UART4 | RCC_PERIPHCLK_UART5 | \ + 8009838: 4b5b ldr r3, [pc, #364] ; (80099a8 ) + 800983a: 6003 str r3, [r0, #0] + PeriphClkInit->PLLSAI1.PLLSAI1Source = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC) >> RCC_PLLCFGR_PLLSRC_Pos; + 800983c: 4b5b ldr r3, [pc, #364] ; (80099ac ) + 800983e: 68d9 ldr r1, [r3, #12] + 8009840: f001 0103 and.w r1, r1, #3 + 8009844: 6041 str r1, [r0, #4] + PeriphClkInit->PLLSAI1.PLLSAI1M = (READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U; + 8009846: 691a ldr r2, [r3, #16] + 8009848: f3c2 1203 ubfx r2, r2, #4, #4 + 800984c: 3201 adds r2, #1 + 800984e: 6082 str r2, [r0, #8] + PeriphClkInit->PLLSAI1.PLLSAI1N = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009850: 691a ldr r2, [r3, #16] + 8009852: f3c2 2206 ubfx r2, r2, #8, #7 + 8009856: 60c2 str r2, [r0, #12] + PeriphClkInit->PLLSAI1.PLLSAI1P = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1P) >> RCC_PLLSAI1CFGR_PLLSAI1P_Pos) << 4U) + 7U; + 8009858: 691a ldr r2, [r3, #16] + 800985a: 0b52 lsrs r2, r2, #13 + 800985c: f002 0210 and.w r2, r2, #16 + 8009860: 3207 adds r2, #7 + 8009862: 6102 str r2, [r0, #16] + PeriphClkInit->PLLSAI1.PLLSAI1Q = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) * 2U; + 8009864: 691a ldr r2, [r3, #16] + 8009866: f3c2 5241 ubfx r2, r2, #21, #2 + 800986a: 3201 adds r2, #1 + 800986c: 0052 lsls r2, r2, #1 + 800986e: 6142 str r2, [r0, #20] + PeriphClkInit->PLLSAI1.PLLSAI1R = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) * 2U; + 8009870: 691a ldr r2, [r3, #16] + PeriphClkInit->PLLSAI2.PLLSAI2Source = PeriphClkInit->PLLSAI1.PLLSAI1Source; + 8009872: 6201 str r1, [r0, #32] + PeriphClkInit->PLLSAI1.PLLSAI1R = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) * 2U; + 8009874: f3c2 6241 ubfx r2, r2, #25, #2 + 8009878: 3201 adds r2, #1 + 800987a: 0052 lsls r2, r2, #1 + 800987c: 6182 str r2, [r0, #24] + PeriphClkInit->PLLSAI2.PLLSAI2M = (READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2M) >> RCC_PLLSAI2CFGR_PLLSAI2M_Pos) + 1U; + 800987e: 695a ldr r2, [r3, #20] + 8009880: f3c2 1203 ubfx r2, r2, #4, #4 + 8009884: 3201 adds r2, #1 + 8009886: 6242 str r2, [r0, #36] ; 0x24 + PeriphClkInit->PLLSAI2.PLLSAI2N = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 8009888: 695a ldr r2, [r3, #20] + 800988a: f3c2 2206 ubfx r2, r2, #8, #7 + 800988e: 6282 str r2, [r0, #40] ; 0x28 + PeriphClkInit->PLLSAI2.PLLSAI2P = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2P) >> RCC_PLLSAI2CFGR_PLLSAI2P_Pos) << 4U) + 7U; + 8009890: 695a ldr r2, [r3, #20] + 8009892: 0b52 lsrs r2, r2, #13 + 8009894: f002 0210 and.w r2, r2, #16 + 8009898: 3207 adds r2, #7 + 800989a: 62c2 str r2, [r0, #44] ; 0x2c + PeriphClkInit->PLLSAI2.PLLSAI2Q = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2Q) >> RCC_PLLSAI2CFGR_PLLSAI2Q_Pos) + 1U) * 2U; + 800989c: 695a ldr r2, [r3, #20] + 800989e: f3c2 5241 ubfx r2, r2, #21, #2 + 80098a2: 3201 adds r2, #1 + 80098a4: 0052 lsls r2, r2, #1 + 80098a6: 6302 str r2, [r0, #48] ; 0x30 + PeriphClkInit->PLLSAI2.PLLSAI2R = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2R)>> RCC_PLLSAI2CFGR_PLLSAI2R_Pos) + 1U) * 2U; + 80098a8: 695a ldr r2, [r3, #20] + 80098aa: f3c2 6241 ubfx r2, r2, #25, #2 + 80098ae: 3201 adds r2, #1 + 80098b0: 0052 lsls r2, r2, #1 + 80098b2: 6342 str r2, [r0, #52] ; 0x34 + PeriphClkInit->Usart1ClockSelection = __HAL_RCC_GET_USART1_SOURCE(); + 80098b4: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098b8: f002 0203 and.w r2, r2, #3 + 80098bc: 63c2 str r2, [r0, #60] ; 0x3c + PeriphClkInit->Usart2ClockSelection = __HAL_RCC_GET_USART2_SOURCE(); + 80098be: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098c2: f002 020c and.w r2, r2, #12 + 80098c6: 6402 str r2, [r0, #64] ; 0x40 + PeriphClkInit->Usart3ClockSelection = __HAL_RCC_GET_USART3_SOURCE(); + 80098c8: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098cc: f002 0230 and.w r2, r2, #48 ; 0x30 + 80098d0: 6442 str r2, [r0, #68] ; 0x44 + PeriphClkInit->Uart4ClockSelection = __HAL_RCC_GET_UART4_SOURCE(); + 80098d2: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098d6: f002 02c0 and.w r2, r2, #192 ; 0xc0 + 80098da: 6482 str r2, [r0, #72] ; 0x48 + PeriphClkInit->Uart5ClockSelection = __HAL_RCC_GET_UART5_SOURCE(); + 80098dc: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098e0: f402 7240 and.w r2, r2, #768 ; 0x300 + 80098e4: 64c2 str r2, [r0, #76] ; 0x4c + PeriphClkInit->Lpuart1ClockSelection = __HAL_RCC_GET_LPUART1_SOURCE(); + 80098e6: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098ea: f402 6240 and.w r2, r2, #3072 ; 0xc00 + 80098ee: 6502 str r2, [r0, #80] ; 0x50 + PeriphClkInit->I2c1ClockSelection = __HAL_RCC_GET_I2C1_SOURCE(); + 80098f0: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098f4: f402 5240 and.w r2, r2, #12288 ; 0x3000 + 80098f8: 6542 str r2, [r0, #84] ; 0x54 + PeriphClkInit->I2c2ClockSelection = __HAL_RCC_GET_I2C2_SOURCE(); + 80098fa: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 80098fe: f402 4240 and.w r2, r2, #49152 ; 0xc000 + 8009902: 6582 str r2, [r0, #88] ; 0x58 + PeriphClkInit->I2c3ClockSelection = __HAL_RCC_GET_I2C3_SOURCE(); + 8009904: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009908: f402 3240 and.w r2, r2, #196608 ; 0x30000 + 800990c: 65c2 str r2, [r0, #92] ; 0x5c + PeriphClkInit->I2c4ClockSelection = __HAL_RCC_GET_I2C4_SOURCE(); + 800990e: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009912: f002 0203 and.w r2, r2, #3 + 8009916: 6602 str r2, [r0, #96] ; 0x60 + PeriphClkInit->Lptim1ClockSelection = __HAL_RCC_GET_LPTIM1_SOURCE(); + 8009918: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 800991c: f402 2240 and.w r2, r2, #786432 ; 0xc0000 + 8009920: 6642 str r2, [r0, #100] ; 0x64 + PeriphClkInit->Lptim2ClockSelection = __HAL_RCC_GET_LPTIM2_SOURCE(); + 8009922: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009926: f402 1240 and.w r2, r2, #3145728 ; 0x300000 + 800992a: 6682 str r2, [r0, #104] ; 0x68 + PeriphClkInit->Sai1ClockSelection = __HAL_RCC_GET_SAI1_SOURCE(); + 800992c: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009930: f002 02e0 and.w r2, r2, #224 ; 0xe0 + 8009934: 66c2 str r2, [r0, #108] ; 0x6c + PeriphClkInit->Sai2ClockSelection = __HAL_RCC_GET_SAI2_SOURCE(); + 8009936: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 800993a: f402 62e0 and.w r2, r2, #1792 ; 0x700 + 800993e: 6702 str r2, [r0, #112] ; 0x70 + PeriphClkInit->RTCClockSelection = __HAL_RCC_GET_RTC_SOURCE(); + 8009940: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 8009944: f402 7240 and.w r2, r2, #768 ; 0x300 + 8009948: f8c0 2090 str.w r2, [r0, #144] ; 0x90 + PeriphClkInit->UsbClockSelection = __HAL_RCC_GET_USB_SOURCE(); + 800994c: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009950: f002 6240 and.w r2, r2, #201326592 ; 0xc000000 + 8009954: 6742 str r2, [r0, #116] ; 0x74 + PeriphClkInit->Sdmmc1ClockSelection = __HAL_RCC_GET_SDMMC1_SOURCE(); + 8009956: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 800995a: 0452 lsls r2, r2, #17 + 800995c: bf56 itet pl + 800995e: f8d3 2088 ldrpl.w r2, [r3, #136] ; 0x88 + 8009962: f44f 4280 movmi.w r2, #16384 ; 0x4000 + 8009966: f002 6240 andpl.w r2, r2, #201326592 ; 0xc000000 + 800996a: 6782 str r2, [r0, #120] ; 0x78 + PeriphClkInit->RngClockSelection = __HAL_RCC_GET_RNG_SOURCE(); + 800996c: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009970: f002 6240 and.w r2, r2, #201326592 ; 0xc000000 + 8009974: 67c2 str r2, [r0, #124] ; 0x7c + PeriphClkInit->AdcClockSelection = __HAL_RCC_GET_ADC_SOURCE(); + 8009976: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 800997a: f002 5240 and.w r2, r2, #805306368 ; 0x30000000 + 800997e: f8c0 2080 str.w r2, [r0, #128] ; 0x80 + PeriphClkInit->Dfsdm1ClockSelection = __HAL_RCC_GET_DFSDM1_SOURCE(); + 8009982: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009986: f002 0204 and.w r2, r2, #4 + 800998a: f8c0 2084 str.w r2, [r0, #132] ; 0x84 + PeriphClkInit->Dfsdm1AudioClockSelection = __HAL_RCC_GET_DFSDM1AUDIO_SOURCE(); + 800998e: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009992: f002 0218 and.w r2, r2, #24 + 8009996: f8c0 2088 str.w r2, [r0, #136] ; 0x88 + PeriphClkInit->OspiClockSelection = __HAL_RCC_GET_OSPI_SOURCE(); + 800999a: f8d3 309c ldr.w r3, [r3, #156] ; 0x9c + 800999e: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + 80099a2: f8c0 308c str.w r3, [r0, #140] ; 0x8c +} + 80099a6: 4770 bx lr + 80099a8: 013f7fff .word 0x013f7fff + 80099ac: 40021000 .word 0x40021000 + +080099b0 : + if(PeriphClk == RCC_PERIPHCLK_RTC) + 80099b0: f5b0 3f00 cmp.w r0, #131072 ; 0x20000 +{ + 80099b4: b4f0 push {r4, r5, r6, r7} + 80099b6: 4d9a ldr r5, [pc, #616] ; (8009c20 ) + if(PeriphClk == RCC_PERIPHCLK_RTC) + 80099b8: d11c bne.n 80099f4 + srcclk = __HAL_RCC_GET_RTC_SOURCE(); + 80099ba: f8d5 3090 ldr.w r3, [r5, #144] ; 0x90 + 80099be: f403 7340 and.w r3, r3, #768 ; 0x300 + switch(srcclk) + 80099c2: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 80099c6: f000 8085 beq.w 8009ad4 + 80099ca: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 80099ce: d00a beq.n 80099e6 + 80099d0: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 80099d4: d154 bne.n 8009a80 + if(HAL_IS_BIT_SET(RCC->BDCR, RCC_BDCR_LSERDY)) + 80099d6: f8d5 0090 ldr.w r0, [r5, #144] ; 0x90 + frequency = LSE_VALUE; + 80099da: f010 0002 ands.w r0, r0, #2 + 80099de: bf18 it ne + 80099e0: f44f 4000 movne.w r0, #32768 ; 0x8000 + 80099e4: e11a b.n 8009c1c + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY)) + 80099e6: 6828 ldr r0, [r5, #0] + frequency = HSE_VALUE / 32U; + 80099e8: 4b8e ldr r3, [pc, #568] ; (8009c24 ) + 80099ea: f410 3000 ands.w r0, r0, #131072 ; 0x20000 + frequency = HSI_VALUE; + 80099ee: bf18 it ne + 80099f0: 4618 movne r0, r3 + 80099f2: e113 b.n 8009c1c + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 80099f4: 68eb ldr r3, [r5, #12] + 80099f6: f003 0303 and.w r3, r3, #3 + switch(pll_oscsource) + 80099fa: 2b02 cmp r3, #2 + 80099fc: d02f beq.n 8009a5e + 80099fe: 2b03 cmp r3, #3 + 8009a00: d034 beq.n 8009a6c + 8009a02: 2b01 cmp r3, #1 + 8009a04: d137 bne.n 8009a76 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_MSIRDY)) + 8009a06: 6829 ldr r1, [r5, #0] + 8009a08: f011 0102 ands.w r1, r1, #2 + 8009a0c: d00c beq.n 8009a28 + pllvco = MSIRangeTable[(__HAL_RCC_GET_MSI_RANGE() >> 4U)]; + 8009a0e: 682b ldr r3, [r5, #0] + 8009a10: 4a85 ldr r2, [pc, #532] ; (8009c28 ) + 8009a12: 0719 lsls r1, r3, #28 + 8009a14: bf4b itete mi + 8009a16: 682b ldrmi r3, [r5, #0] + 8009a18: f8d5 3094 ldrpl.w r3, [r5, #148] ; 0x94 + 8009a1c: f3c3 1303 ubfxmi r3, r3, #4, #4 + 8009a20: f3c3 2303 ubfxpl r3, r3, #8, #4 + 8009a24: f852 1023 ldr.w r1, [r2, r3, lsl #2] + switch(PeriphClk) + 8009a28: f5b0 6f80 cmp.w r0, #1024 ; 0x400 + 8009a2c: f000 8226 beq.w 8009e7c + 8009a30: d858 bhi.n 8009ae4 + 8009a32: 2820 cmp r0, #32 + 8009a34: f000 81be beq.w 8009db4 + 8009a38: d824 bhi.n 8009a84 + 8009a3a: 2808 cmp r0, #8 + 8009a3c: d81d bhi.n 8009a7a + 8009a3e: 2800 cmp r0, #0 + 8009a40: f000 80ec beq.w 8009c1c + 8009a44: 3801 subs r0, #1 + 8009a46: 2807 cmp r0, #7 + 8009a48: d81a bhi.n 8009a80 + 8009a4a: e8df f010 tbh [pc, r0, lsl #1] + 8009a4e: 0164 .short 0x0164 + 8009a50: 00190177 .word 0x00190177 + 8009a54: 00190189 .word 0x00190189 + 8009a58: 00190019 .word 0x00190019 + 8009a5c: 0196 .short 0x0196 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 8009a5e: 6829 ldr r1, [r5, #0] + pllvco = HSI_VALUE; + 8009a60: 4b72 ldr r3, [pc, #456] ; (8009c2c ) + 8009a62: f411 6180 ands.w r1, r1, #1024 ; 0x400 + pllvco = HSE_VALUE; + 8009a66: bf18 it ne + 8009a68: 4619 movne r1, r3 + 8009a6a: e7dd b.n 8009a28 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY)) + 8009a6c: 6829 ldr r1, [r5, #0] + pllvco = HSE_VALUE; + 8009a6e: 4b70 ldr r3, [pc, #448] ; (8009c30 ) + 8009a70: f411 3100 ands.w r1, r1, #131072 ; 0x20000 + 8009a74: e7f7 b.n 8009a66 + switch(pll_oscsource) + 8009a76: 2100 movs r1, #0 + 8009a78: e7d6 b.n 8009a28 + switch(PeriphClk) + 8009a7a: 2810 cmp r0, #16 + 8009a7c: f000 818a beq.w 8009d94 + 8009a80: 2000 movs r0, #0 + 8009a82: e0cb b.n 8009c1c + 8009a84: f5b0 7f80 cmp.w r0, #256 ; 0x100 + 8009a88: f000 81ea beq.w 8009e60 + 8009a8c: d80f bhi.n 8009aae + 8009a8e: 2840 cmp r0, #64 ; 0x40 + 8009a90: f000 81d5 beq.w 8009e3e + 8009a94: 2880 cmp r0, #128 ; 0x80 + 8009a96: d1f3 bne.n 8009a80 + srcclk = __HAL_RCC_GET_I2C2_SOURCE(); + 8009a98: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009a9c: f403 4340 and.w r3, r3, #49152 ; 0xc000 + switch(srcclk) + 8009aa0: f5b3 4f80 cmp.w r3, #16384 ; 0x4000 + 8009aa4: f000 8157 beq.w 8009d56 + 8009aa8: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + 8009aac: e1d0 b.n 8009e50 + switch(PeriphClk) + 8009aae: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8009ab2: d1e5 bne.n 8009a80 + srcclk = __HAL_RCC_GET_LPTIM1_SOURCE(); + 8009ab4: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009ab8: f403 2340 and.w r3, r3, #786432 ; 0xc0000 + switch(srcclk) + 8009abc: f5b3 2f00 cmp.w r3, #524288 ; 0x80000 + 8009ac0: f000 8137 beq.w 8009d32 + 8009ac4: f200 81d7 bhi.w 8009e76 + 8009ac8: 2b00 cmp r3, #0 + 8009aca: f000 81c6 beq.w 8009e5a + 8009ace: f5b3 2f80 cmp.w r3, #262144 ; 0x40000 + 8009ad2: d1d5 bne.n 8009a80 + if(HAL_IS_BIT_SET(RCC->CSR, RCC_CSR_LSIRDY)) + 8009ad4: f8d5 0094 ldr.w r0, [r5, #148] ; 0x94 + frequency = LSI_VALUE; + 8009ad8: f010 0002 ands.w r0, r0, #2 + 8009adc: bf18 it ne + 8009ade: f44f 40fa movne.w r0, #32000 ; 0x7d00 + 8009ae2: e09b b.n 8009c1c + switch(PeriphClk) + 8009ae4: f5b0 2f80 cmp.w r0, #262144 ; 0x40000 + 8009ae8: d040 beq.n 8009b6c + 8009aea: d819 bhi.n 8009b20 + 8009aec: f5b0 5f00 cmp.w r0, #8192 ; 0x2000 + 8009af0: d03c beq.n 8009b6c + 8009af2: d808 bhi.n 8009b06 + 8009af4: f5b0 6f00 cmp.w r0, #2048 ; 0x800 + 8009af8: d002 beq.n 8009b00 + 8009afa: f5b0 5f80 cmp.w r0, #4096 ; 0x1000 + 8009afe: d1bf bne.n 8009a80 +} + 8009b00: bcf0 pop {r4, r5, r6, r7} + frequency = RCCEx_GetSAIxPeriphCLKFreq(RCC_PERIPHCLK_SAI1, pllvco); + 8009b02: f7ff bb1b b.w 800913c + switch(PeriphClk) + 8009b06: f5b0 4f80 cmp.w r0, #16384 ; 0x4000 + 8009b0a: f000 8163 beq.w 8009dd4 + 8009b0e: f5b0 3f80 cmp.w r0, #65536 ; 0x10000 + 8009b12: d1b5 bne.n 8009a80 + srcclk = __HAL_RCC_GET_DFSDM1_SOURCE(); + 8009b14: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + if(srcclk == RCC_DFSDM1CLKSOURCE_PCLK2) + 8009b18: 075a lsls r2, r3, #29 + 8009b1a: f100 811c bmi.w 8009d56 + 8009b1e: e105 b.n 8009d2c + switch(PeriphClk) + 8009b20: f5b0 1f00 cmp.w r0, #2097152 ; 0x200000 + 8009b24: f000 817c beq.w 8009e20 + 8009b28: d80f bhi.n 8009b4a + 8009b2a: f5b0 2f00 cmp.w r0, #524288 ; 0x80000 + 8009b2e: f000 8081 beq.w 8009c34 + 8009b32: f5b0 1f80 cmp.w r0, #1048576 ; 0x100000 + 8009b36: d1a3 bne.n 8009a80 + srcclk = __HAL_RCC_GET_I2C4_SOURCE(); + 8009b38: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 8009b3c: f003 0303 and.w r3, r3, #3 + switch(srcclk) + 8009b40: 2b01 cmp r3, #1 + 8009b42: f000 8108 beq.w 8009d56 + 8009b46: 2b02 cmp r3, #2 + 8009b48: e182 b.n 8009e50 + switch(PeriphClk) + 8009b4a: f1b0 7f80 cmp.w r0, #16777216 ; 0x1000000 + 8009b4e: d197 bne.n 8009a80 + srcclk = __HAL_RCC_GET_OSPI_SOURCE(); + 8009b50: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 8009b54: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + switch(srcclk) + 8009b58: f5b3 1f80 cmp.w r3, #1048576 ; 0x100000 + 8009b5c: d033 beq.n 8009bc6 + 8009b5e: f5b3 1f00 cmp.w r3, #2097152 ; 0x200000 + 8009b62: f000 819c beq.w 8009e9e + 8009b66: 2b00 cmp r3, #0 + 8009b68: d18a bne.n 8009a80 + 8009b6a: e0f4 b.n 8009d56 + srcclk = READ_BIT(RCC->CCIPR, RCC_CCIPR_CLK48SEL); + 8009b6c: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009b70: f003 6340 and.w r3, r3, #201326592 ; 0xc000000 + switch(srcclk) + 8009b74: f1b3 6f00 cmp.w r3, #134217728 ; 0x8000000 + 8009b78: d037 beq.n 8009bea + 8009b7a: d820 bhi.n 8009bbe + 8009b7c: 2b00 cmp r3, #0 + 8009b7e: f000 80c4 beq.w 8009d0a + 8009b82: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + 8009b86: f47f af7b bne.w 8009a80 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY)) + 8009b8a: 6828 ldr r0, [r5, #0] + 8009b8c: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 8009b90: d044 beq.n 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1QEN)) + 8009b92: 6928 ldr r0, [r5, #16] + 8009b94: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009b98: d040 beq.n 8009c1c + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009b9a: 692f ldr r7, [r5, #16] + 8009b9c: f3c7 2706 ubfx r7, r7, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009ba0: 4379 muls r1, r7 + 8009ba2: 692f ldr r7, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009ba4: 6928 ldr r0, [r5, #16] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009ba6: f3c7 1703 ubfx r7, r7, #4, #4 + 8009baa: 3701 adds r7, #1 + 8009bac: fbb1 f1f7 udiv r1, r1, r7 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009bb0: f3c0 5041 ubfx r0, r0, #21, #2 + 8009bb4: 3001 adds r0, #1 + 8009bb6: 0040 lsls r0, r0, #1 + 8009bb8: fbb1 f0f0 udiv r0, r1, r0 + 8009bbc: e02e b.n 8009c1c + 8009bbe: f1b3 6f40 cmp.w r3, #201326592 ; 0xc000000 + 8009bc2: f47f af5d bne.w 8009a80 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_MSIRDY)) + 8009bc6: 6828 ldr r0, [r5, #0] + 8009bc8: f010 0002 ands.w r0, r0, #2 + 8009bcc: d026 beq.n 8009c1c + frequency = MSIRangeTable[(__HAL_RCC_GET_MSI_RANGE() >> 4U)]; + 8009bce: 682b ldr r3, [r5, #0] + 8009bd0: 4a15 ldr r2, [pc, #84] ; (8009c28 ) + 8009bd2: 071b lsls r3, r3, #28 + 8009bd4: bf4b itete mi + 8009bd6: 682b ldrmi r3, [r5, #0] + 8009bd8: f8d5 3094 ldrpl.w r3, [r5, #148] ; 0x94 + 8009bdc: f3c3 1303 ubfxmi r3, r3, #4, #4 + 8009be0: f3c3 2303 ubfxpl r3, r3, #8, #4 + 8009be4: f852 0023 ldr.w r0, [r2, r3, lsl #2] + 8009be8: e018 b.n 8009c1c + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009bea: 6828 ldr r0, [r5, #0] + 8009bec: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009bf0: d014 beq.n 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 8009bf2: 68e8 ldr r0, [r5, #12] + 8009bf4: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009bf8: d010 beq.n 8009c1c + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009bfa: 68e8 ldr r0, [r5, #12] + 8009bfc: f3c0 2006 ubfx r0, r0, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c00: 4348 muls r0, r1 + 8009c02: 68e9 ldr r1, [r5, #12] + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009c04: 68ed ldr r5, [r5, #12] + 8009c06: f3c5 5541 ubfx r5, r5, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c0a: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009c0e: 3501 adds r5, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c10: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009c12: 006d lsls r5, r5, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c14: fbb0 f0f1 udiv r0, r0, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009c18: fbb0 f0f5 udiv r0, r0, r5 +} + 8009c1c: bcf0 pop {r4, r5, r6, r7} + 8009c1e: 4770 bx lr + 8009c20: 40021000 .word 0x40021000 + 8009c24: 0003d090 .word 0x0003d090 + 8009c28: 0800e9f4 .word 0x0800e9f4 + 8009c2c: 00f42400 .word 0x00f42400 + 8009c30: 007a1200 .word 0x007a1200 + if(HAL_IS_BIT_SET(RCC->CCIPR2, RCC_CCIPR2_SDMMCSEL)) /* PLL "P" ? */ + 8009c34: f8d5 009c ldr.w r0, [r5, #156] ; 0x9c + 8009c38: f410 4080 ands.w r0, r0, #16384 ; 0x4000 + 8009c3c: d01f beq.n 8009c7e + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009c3e: 6828 ldr r0, [r5, #0] + 8009c40: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009c44: d0ea beq.n 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLPEN)) + 8009c46: 68e8 ldr r0, [r5, #12] + 8009c48: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 8009c4c: d0e6 beq.n 8009c1c + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009c4e: 68ee ldr r6, [r5, #12] + 8009c50: f3c6 2606 ubfx r6, r6, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c54: fb01 f006 mul.w r0, r1, r6 + 8009c58: 68ee ldr r6, [r5, #12] + pllp = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 8009c5a: 68eb ldr r3, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c5c: f3c6 1603 ubfx r6, r6, #4, #4 + if(pllp == 0U) + 8009c60: 0edb lsrs r3, r3, #27 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009c62: f106 0601 add.w r6, r6, #1 + 8009c66: fbb0 f0f6 udiv r0, r0, r6 + if(pllp == 0U) + 8009c6a: d105 bne.n 8009c78 + if(READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLP) != 0U) + 8009c6c: 68eb ldr r3, [r5, #12] + pllp = 7U; + 8009c6e: f413 3f00 tst.w r3, #131072 ; 0x20000 + 8009c72: bf14 ite ne + 8009c74: 2311 movne r3, #17 + 8009c76: 2307 moveq r3, #7 + frequency = (pllvco / pllp); + 8009c78: fbb0 f0f3 udiv r0, r0, r3 + 8009c7c: e7ce b.n 8009c1c + srcclk = READ_BIT(RCC->CCIPR, RCC_CCIPR_CLK48SEL); + 8009c7e: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009c82: f003 6340 and.w r3, r3, #201326592 ; 0xc000000 + switch(srcclk) + 8009c86: f1b3 6f00 cmp.w r3, #134217728 ; 0x8000000 + 8009c8a: d024 beq.n 8009cd6 + 8009c8c: d81e bhi.n 8009ccc + 8009c8e: 2b00 cmp r3, #0 + 8009c90: d03b beq.n 8009d0a + 8009c92: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + 8009c96: d1c1 bne.n 8009c1c + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY)) + 8009c98: 6828 ldr r0, [r5, #0] + 8009c9a: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 8009c9e: d0bd beq.n 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1QEN)) + 8009ca0: 6928 ldr r0, [r5, #16] + 8009ca2: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009ca6: d0b9 beq.n 8009c1c + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009ca8: 692a ldr r2, [r5, #16] + 8009caa: f3c2 2206 ubfx r2, r2, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009cae: 434a muls r2, r1 + 8009cb0: 6929 ldr r1, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009cb2: 6928 ldr r0, [r5, #16] + 8009cb4: f3c0 5041 ubfx r0, r0, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009cb8: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009cbc: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009cbe: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009cc0: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009cc2: fbb2 f2f1 udiv r2, r2, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009cc6: fbb2 f0f0 udiv r0, r2, r0 + 8009cca: e7a7 b.n 8009c1c + 8009ccc: f1b3 6f40 cmp.w r3, #201326592 ; 0xc000000 + 8009cd0: f43f af79 beq.w 8009bc6 + 8009cd4: e7a2 b.n 8009c1c + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009cd6: 6828 ldr r0, [r5, #0] + 8009cd8: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009cdc: d09e beq.n 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 8009cde: 68e8 ldr r0, [r5, #12] + 8009ce0: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009ce4: d09a beq.n 8009c1c + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009ce6: 68ec ldr r4, [r5, #12] + 8009ce8: f3c4 2406 ubfx r4, r4, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009cec: 434c muls r4, r1 + 8009cee: 68e9 ldr r1, [r5, #12] + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009cf0: 68e8 ldr r0, [r5, #12] + 8009cf2: f3c0 5041 ubfx r0, r0, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009cf6: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009cfa: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009cfc: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009cfe: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009d00: fbb4 f4f1 udiv r4, r4, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009d04: fbb4 f0f0 udiv r0, r4, r0 + 8009d08: e788 b.n 8009c1c + if(HAL_IS_BIT_SET(RCC->CRRCR, RCC_CRRCR_HSI48RDY)) /* HSI48 ? */ + 8009d0a: f8d5 0098 ldr.w r0, [r5, #152] ; 0x98 + frequency = HSI48_VALUE; + 8009d0e: 4b6f ldr r3, [pc, #444] ; (8009ecc ) + 8009d10: f010 0002 ands.w r0, r0, #2 + 8009d14: e66b b.n 80099ee + srcclk = __HAL_RCC_GET_USART1_SOURCE(); + 8009d16: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d1a: f003 0303 and.w r3, r3, #3 + switch(srcclk) + 8009d1e: 2b02 cmp r3, #2 + 8009d20: d007 beq.n 8009d32 + 8009d22: 2b03 cmp r3, #3 + 8009d24: f43f ae57 beq.w 80099d6 + 8009d28: 2b01 cmp r3, #1 + 8009d2a: d014 beq.n 8009d56 +} + 8009d2c: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetPCLK2Freq(); + 8009d2e: f7ff b95b b.w 8008fe8 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 8009d32: 6828 ldr r0, [r5, #0] + frequency = HSI_VALUE; + 8009d34: 4b66 ldr r3, [pc, #408] ; (8009ed0 ) + 8009d36: f410 6080 ands.w r0, r0, #1024 ; 0x400 + 8009d3a: e658 b.n 80099ee + srcclk = __HAL_RCC_GET_USART2_SOURCE(); + 8009d3c: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d40: f003 030c and.w r3, r3, #12 + switch(srcclk) + 8009d44: 2b08 cmp r3, #8 + 8009d46: d0f4 beq.n 8009d32 + 8009d48: d808 bhi.n 8009d5c + 8009d4a: 2b00 cmp r3, #0 + 8009d4c: f000 8085 beq.w 8009e5a + 8009d50: 2b04 cmp r3, #4 + 8009d52: f47f ae95 bne.w 8009a80 +} + 8009d56: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetSysClockFreq(); + 8009d58: f7fe bd38 b.w 80087cc + 8009d5c: 2b0c cmp r3, #12 + 8009d5e: e639 b.n 80099d4 + srcclk = __HAL_RCC_GET_USART3_SOURCE(); + 8009d60: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d64: f003 0330 and.w r3, r3, #48 ; 0x30 + switch(srcclk) + 8009d68: 2b20 cmp r3, #32 + 8009d6a: d0e2 beq.n 8009d32 + 8009d6c: d803 bhi.n 8009d76 + 8009d6e: 2b00 cmp r3, #0 + 8009d70: d073 beq.n 8009e5a + 8009d72: 2b10 cmp r3, #16 + 8009d74: e7ed b.n 8009d52 + 8009d76: 2b30 cmp r3, #48 ; 0x30 + 8009d78: e62c b.n 80099d4 + srcclk = __HAL_RCC_GET_UART4_SOURCE(); + 8009d7a: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d7e: f003 03c0 and.w r3, r3, #192 ; 0xc0 + switch(srcclk) + 8009d82: 2b80 cmp r3, #128 ; 0x80 + 8009d84: d0d5 beq.n 8009d32 + 8009d86: d803 bhi.n 8009d90 + 8009d88: 2b00 cmp r3, #0 + 8009d8a: d066 beq.n 8009e5a + 8009d8c: 2b40 cmp r3, #64 ; 0x40 + 8009d8e: e7e0 b.n 8009d52 + 8009d90: 2bc0 cmp r3, #192 ; 0xc0 + 8009d92: e61f b.n 80099d4 + srcclk = __HAL_RCC_GET_UART5_SOURCE(); + 8009d94: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d98: f403 7340 and.w r3, r3, #768 ; 0x300 + switch(srcclk) + 8009d9c: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 8009da0: d0c7 beq.n 8009d32 + 8009da2: d804 bhi.n 8009dae + 8009da4: 2b00 cmp r3, #0 + 8009da6: d058 beq.n 8009e5a + 8009da8: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 8009dac: e7d1 b.n 8009d52 + 8009dae: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 8009db2: e60f b.n 80099d4 + srcclk = __HAL_RCC_GET_LPUART1_SOURCE(); + 8009db4: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009db8: f403 6340 and.w r3, r3, #3072 ; 0xc00 + switch(srcclk) + 8009dbc: f5b3 6f00 cmp.w r3, #2048 ; 0x800 + 8009dc0: d0b7 beq.n 8009d32 + 8009dc2: d804 bhi.n 8009dce + 8009dc4: 2b00 cmp r3, #0 + 8009dc6: d048 beq.n 8009e5a + 8009dc8: f5b3 6f80 cmp.w r3, #1024 ; 0x400 + 8009dcc: e7c1 b.n 8009d52 + 8009dce: f5b3 6f40 cmp.w r3, #3072 ; 0xc00 + 8009dd2: e5ff b.n 80099d4 + srcclk = __HAL_RCC_GET_ADC_SOURCE(); + 8009dd4: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009dd8: f003 5340 and.w r3, r3, #805306368 ; 0x30000000 + switch(srcclk) + 8009ddc: f1b3 5f80 cmp.w r3, #268435456 ; 0x10000000 + 8009de0: d002 beq.n 8009de8 + 8009de2: f1b3 5f40 cmp.w r3, #805306368 ; 0x30000000 + 8009de6: e7b4 b.n 8009d52 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY) && (__HAL_RCC_GET_PLLSAI1CLKOUT_CONFIG(RCC_PLLSAI1_ADC1CLK) != 0U)) + 8009de8: 6828 ldr r0, [r5, #0] + 8009dea: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 8009dee: f43f af15 beq.w 8009c1c + 8009df2: 6928 ldr r0, [r5, #16] + 8009df4: f010 7080 ands.w r0, r0, #16777216 ; 0x1000000 + 8009df8: f43f af10 beq.w 8009c1c + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009dfc: 692b ldr r3, [r5, #16] + 8009dfe: f3c3 2306 ubfx r3, r3, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e02: 434b muls r3, r1 + 8009e04: 6929 ldr r1, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 8009e06: 6928 ldr r0, [r5, #16] + 8009e08: f3c0 6041 ubfx r0, r0, #25, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e0c: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 8009e10: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e12: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 8009e14: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e16: fbb3 f3f1 udiv r3, r3, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 8009e1a: fbb3 f0f0 udiv r0, r3, r0 + 8009e1e: e6fd b.n 8009c1c + srcclk = __HAL_RCC_GET_DFSDM1AUDIO_SOURCE(); + 8009e20: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 8009e24: f003 0318 and.w r3, r3, #24 + switch(srcclk) + 8009e28: 2b08 cmp r3, #8 + 8009e2a: d082 beq.n 8009d32 + 8009e2c: 2b10 cmp r3, #16 + 8009e2e: f43f aeca beq.w 8009bc6 + 8009e32: 2b00 cmp r3, #0 + 8009e34: f47f ae24 bne.w 8009a80 + frequency = RCCEx_GetSAIxPeriphCLKFreq(RCC_PERIPHCLK_SAI1, pllvco); + 8009e38: f44f 6000 mov.w r0, #2048 ; 0x800 + 8009e3c: e660 b.n 8009b00 + srcclk = __HAL_RCC_GET_I2C1_SOURCE(); + 8009e3e: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009e42: f403 5340 and.w r3, r3, #12288 ; 0x3000 + switch(srcclk) + 8009e46: f5b3 5f80 cmp.w r3, #4096 ; 0x1000 + 8009e4a: d084 beq.n 8009d56 + 8009e4c: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 8009e50: f43f af6f beq.w 8009d32 + 8009e54: 2b00 cmp r3, #0 + 8009e56: f47f ae13 bne.w 8009a80 +} + 8009e5a: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetPCLK1Freq(); + 8009e5c: f7ff b8b2 b.w 8008fc4 + srcclk = __HAL_RCC_GET_I2C3_SOURCE(); + 8009e60: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009e64: f403 3340 and.w r3, r3, #196608 ; 0x30000 + switch(srcclk) + 8009e68: f5b3 3f80 cmp.w r3, #65536 ; 0x10000 + 8009e6c: f43f af73 beq.w 8009d56 + 8009e70: f5b3 3f00 cmp.w r3, #131072 ; 0x20000 + 8009e74: e7ec b.n 8009e50 + 8009e76: f5b3 2f40 cmp.w r3, #786432 ; 0xc0000 + 8009e7a: e5ab b.n 80099d4 + srcclk = __HAL_RCC_GET_LPTIM2_SOURCE(); + 8009e7c: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009e80: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + switch(srcclk) + 8009e84: f5b3 1f00 cmp.w r3, #2097152 ; 0x200000 + 8009e88: f43f af53 beq.w 8009d32 + 8009e8c: d804 bhi.n 8009e98 + 8009e8e: 2b00 cmp r3, #0 + 8009e90: d0e3 beq.n 8009e5a + 8009e92: f5b3 1f80 cmp.w r3, #1048576 ; 0x100000 + 8009e96: e61c b.n 8009ad2 + 8009e98: f5b3 1f40 cmp.w r3, #3145728 ; 0x300000 + 8009e9c: e59a b.n 80099d4 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009e9e: 6828 ldr r0, [r5, #0] + 8009ea0: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009ea4: f43f aeba beq.w 8009c1c + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 8009ea8: 68e8 ldr r0, [r5, #12] + 8009eaa: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009eae: f43f aeb5 beq.w 8009c1c + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009eb2: 68e8 ldr r0, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009eb4: 68eb ldr r3, [r5, #12] + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009eb6: f3c0 2006 ubfx r0, r0, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009eba: f3c3 1303 ubfx r3, r3, #4, #4 + 8009ebe: 4341 muls r1, r0 + 8009ec0: 3301 adds r3, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009ec2: 68e8 ldr r0, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009ec4: fbb1 f1f3 udiv r1, r1, r3 + 8009ec8: e672 b.n 8009bb0 + 8009eca: bf00 nop + 8009ecc: 02dc6c00 .word 0x02dc6c00 + 8009ed0: 00f42400 .word 0x00f42400 + +08009ed4 : +{ + 8009ed4: b570 push {r4, r5, r6, lr} + __HAL_RCC_PLLSAI1_DISABLE(); + 8009ed6: 4c20 ldr r4, [pc, #128] ; (8009f58 ) + 8009ed8: 6823 ldr r3, [r4, #0] + 8009eda: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 8009ede: 6023 str r3, [r4, #0] +{ + 8009ee0: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 8009ee2: f7fd f903 bl 80070ec + 8009ee6: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 8009ee8: 6823 ldr r3, [r4, #0] + 8009eea: 011a lsls r2, r3, #4 + 8009eec: d423 bmi.n 8009f36 + __HAL_RCC_PLLSAI1_CONFIG(PLLSAI1Init->PLLSAI1M, PLLSAI1Init->PLLSAI1N, PLLSAI1Init->PLLSAI1P, PLLSAI1Init->PLLSAI1Q, PLLSAI1Init->PLLSAI1R); + 8009eee: e9d5 2302 ldrd r2, r3, [r5, #8] + 8009ef2: 06db lsls r3, r3, #27 + 8009ef4: 6921 ldr r1, [r4, #16] + 8009ef6: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8009efa: 4a18 ldr r2, [pc, #96] ; (8009f5c ) + 8009efc: 400a ands r2, r1 + 8009efe: 4313 orrs r3, r2 + 8009f00: 686a ldr r2, [r5, #4] + 8009f02: 3a01 subs r2, #1 + 8009f04: ea43 1302 orr.w r3, r3, r2, lsl #4 + 8009f08: 692a ldr r2, [r5, #16] + 8009f0a: 0852 lsrs r2, r2, #1 + 8009f0c: 3a01 subs r2, #1 + 8009f0e: ea43 5342 orr.w r3, r3, r2, lsl #21 + 8009f12: 696a ldr r2, [r5, #20] + 8009f14: 0852 lsrs r2, r2, #1 + 8009f16: 3a01 subs r2, #1 + 8009f18: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8009f1c: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1CLKOUT_ENABLE(PLLSAI1Init->PLLSAI1ClockOut); + 8009f1e: 6923 ldr r3, [r4, #16] + 8009f20: 69aa ldr r2, [r5, #24] + 8009f22: 4313 orrs r3, r2 + 8009f24: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1_ENABLE(); + 8009f26: 6823 ldr r3, [r4, #0] + 8009f28: f043 6380 orr.w r3, r3, #67108864 ; 0x4000000 + 8009f2c: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8009f2e: f7fd f8dd bl 80070ec + 8009f32: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 8009f34: e00b b.n 8009f4e + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 8009f36: f7fd f8d9 bl 80070ec + 8009f3a: 1b80 subs r0, r0, r6 + 8009f3c: 2802 cmp r0, #2 + 8009f3e: d9d3 bls.n 8009ee8 + status = HAL_TIMEOUT; + 8009f40: 2003 movs r0, #3 +} + 8009f42: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 8009f44: f7fd f8d2 bl 80070ec + 8009f48: 1b40 subs r0, r0, r5 + 8009f4a: 2802 cmp r0, #2 + 8009f4c: d8f8 bhi.n 8009f40 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 8009f4e: 6823 ldr r3, [r4, #0] + 8009f50: 011b lsls r3, r3, #4 + 8009f52: d5f7 bpl.n 8009f44 + 8009f54: 2000 movs r0, #0 + return status; + 8009f56: e7f4 b.n 8009f42 + 8009f58: 40021000 .word 0x40021000 + 8009f5c: 019d800f .word 0x019d800f + +08009f60 : +{ + 8009f60: b538 push {r3, r4, r5, lr} + __HAL_RCC_PLLSAI1_DISABLE(); + 8009f62: 4c11 ldr r4, [pc, #68] ; (8009fa8 ) + 8009f64: 6823 ldr r3, [r4, #0] + 8009f66: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 8009f6a: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8009f6c: f7fd f8be bl 80070ec + 8009f70: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 8009f72: 6823 ldr r3, [r4, #0] + 8009f74: f013 6300 ands.w r3, r3, #134217728 ; 0x8000000 + 8009f78: d10f bne.n 8009f9a + HAL_StatusTypeDef status = HAL_OK; + 8009f7a: 4618 mov r0, r3 + __HAL_RCC_PLLSAI1CLKOUT_DISABLE(RCC_PLLSAI1CFGR_PLLSAI1PEN|RCC_PLLSAI1CFGR_PLLSAI1QEN|RCC_PLLSAI1CFGR_PLLSAI1REN); + 8009f7c: 6923 ldr r3, [r4, #16] + 8009f7e: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 8009f82: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 8009f86: 6123 str r3, [r4, #16] + if(READ_BIT(RCC->CR, (RCC_CR_PLLRDY | RCC_CR_PLLSAI2RDY)) == 0U) + 8009f88: 6823 ldr r3, [r4, #0] + 8009f8a: f013 5f08 tst.w r3, #570425344 ; 0x22000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 8009f8e: bf02 ittt eq + 8009f90: 68e3 ldreq r3, [r4, #12] + 8009f92: f023 0303 biceq.w r3, r3, #3 + 8009f96: 60e3 streq r3, [r4, #12] +} + 8009f98: bd38 pop {r3, r4, r5, pc} + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 8009f9a: f7fd f8a7 bl 80070ec + 8009f9e: 1b40 subs r0, r0, r5 + 8009fa0: 2802 cmp r0, #2 + 8009fa2: d9e6 bls.n 8009f72 + status = HAL_TIMEOUT; + 8009fa4: 2003 movs r0, #3 + 8009fa6: e7e9 b.n 8009f7c + 8009fa8: 40021000 .word 0x40021000 + +08009fac : +{ + 8009fac: b570 push {r4, r5, r6, lr} + __HAL_RCC_PLLSAI2_DISABLE(); + 8009fae: 4c20 ldr r4, [pc, #128] ; (800a030 ) + 8009fb0: 6823 ldr r3, [r4, #0] + 8009fb2: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8009fb6: 6023 str r3, [r4, #0] +{ + 8009fb8: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 8009fba: f7fd f897 bl 80070ec + 8009fbe: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 8009fc0: 6823 ldr r3, [r4, #0] + 8009fc2: 009a lsls r2, r3, #2 + 8009fc4: d423 bmi.n 800a00e + __HAL_RCC_PLLSAI2_CONFIG(PLLSAI2Init->PLLSAI2M, PLLSAI2Init->PLLSAI2N, PLLSAI2Init->PLLSAI2P, PLLSAI2Init->PLLSAI2Q, PLLSAI2Init->PLLSAI2R); + 8009fc6: e9d5 2302 ldrd r2, r3, [r5, #8] + 8009fca: 06db lsls r3, r3, #27 + 8009fcc: 6961 ldr r1, [r4, #20] + 8009fce: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8009fd2: 4a18 ldr r2, [pc, #96] ; (800a034 ) + 8009fd4: 400a ands r2, r1 + 8009fd6: 4313 orrs r3, r2 + 8009fd8: 686a ldr r2, [r5, #4] + 8009fda: 3a01 subs r2, #1 + 8009fdc: ea43 1302 orr.w r3, r3, r2, lsl #4 + 8009fe0: 692a ldr r2, [r5, #16] + 8009fe2: 0852 lsrs r2, r2, #1 + 8009fe4: 3a01 subs r2, #1 + 8009fe6: ea43 5342 orr.w r3, r3, r2, lsl #21 + 8009fea: 696a ldr r2, [r5, #20] + 8009fec: 0852 lsrs r2, r2, #1 + 8009fee: 3a01 subs r2, #1 + 8009ff0: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8009ff4: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2CLKOUT_ENABLE(PLLSAI2Init->PLLSAI2ClockOut); + 8009ff6: 6963 ldr r3, [r4, #20] + 8009ff8: 69aa ldr r2, [r5, #24] + 8009ffa: 4313 orrs r3, r2 + 8009ffc: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2_ENABLE(); + 8009ffe: 6823 ldr r3, [r4, #0] + 800a000: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800a004: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a006: f7fd f871 bl 80070ec + 800a00a: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 800a00c: e00b b.n 800a026 + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a00e: f7fd f86d bl 80070ec + 800a012: 1b80 subs r0, r0, r6 + 800a014: 2802 cmp r0, #2 + 800a016: d9d3 bls.n 8009fc0 + status = HAL_TIMEOUT; + 800a018: 2003 movs r0, #3 +} + 800a01a: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a01c: f7fd f866 bl 80070ec + 800a020: 1b40 subs r0, r0, r5 + 800a022: 2802 cmp r0, #2 + 800a024: d8f8 bhi.n 800a018 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 800a026: 6823 ldr r3, [r4, #0] + 800a028: 009b lsls r3, r3, #2 + 800a02a: d5f7 bpl.n 800a01c + 800a02c: 2000 movs r0, #0 + return status; + 800a02e: e7f4 b.n 800a01a + 800a030: 40021000 .word 0x40021000 + 800a034: 019d800f .word 0x019d800f + +0800a038 : +{ + 800a038: b538 push {r3, r4, r5, lr} + __HAL_RCC_PLLSAI2_DISABLE(); + 800a03a: 4c11 ldr r4, [pc, #68] ; (800a080 ) + 800a03c: 6823 ldr r3, [r4, #0] + 800a03e: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800a042: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a044: f7fd f852 bl 80070ec + 800a048: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 800a04a: 6823 ldr r3, [r4, #0] + 800a04c: f013 5300 ands.w r3, r3, #536870912 ; 0x20000000 + 800a050: d10f bne.n 800a072 + HAL_StatusTypeDef status = HAL_OK; + 800a052: 4618 mov r0, r3 + __HAL_RCC_PLLSAI2CLKOUT_DISABLE(RCC_PLLSAI2CFGR_PLLSAI2PEN|RCC_PLLSAI2CFGR_PLLSAI2QEN|RCC_PLLSAI2CFGR_PLLSAI2REN); + 800a054: 6963 ldr r3, [r4, #20] + 800a056: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 800a05a: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 800a05e: 6163 str r3, [r4, #20] + if(READ_BIT(RCC->CR, (RCC_CR_PLLRDY | RCC_CR_PLLSAI1RDY)) == 0U) + 800a060: 6823 ldr r3, [r4, #0] + 800a062: f013 6f20 tst.w r3, #167772160 ; 0xa000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 800a066: bf02 ittt eq + 800a068: 68e3 ldreq r3, [r4, #12] + 800a06a: f023 0303 biceq.w r3, r3, #3 + 800a06e: 60e3 streq r3, [r4, #12] +} + 800a070: bd38 pop {r3, r4, r5, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a072: f7fd f83b bl 80070ec + 800a076: 1b40 subs r0, r0, r5 + 800a078: 2802 cmp r0, #2 + 800a07a: d9e6 bls.n 800a04a + status = HAL_TIMEOUT; + 800a07c: 2003 movs r0, #3 + 800a07e: e7e9 b.n 800a054 + 800a080: 40021000 .word 0x40021000 + +0800a084 : + __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(WakeUpClk); + 800a084: 4a03 ldr r2, [pc, #12] ; (800a094 ) + 800a086: 6893 ldr r3, [r2, #8] + 800a088: f423 4300 bic.w r3, r3, #32768 ; 0x8000 + 800a08c: 4318 orrs r0, r3 + 800a08e: 6090 str r0, [r2, #8] +} + 800a090: 4770 bx lr + 800a092: bf00 nop + 800a094: 40021000 .word 0x40021000 + +0800a098 : + __HAL_RCC_MSI_STANDBY_RANGE_CONFIG(MSIRange); + 800a098: 4a04 ldr r2, [pc, #16] ; (800a0ac ) + 800a09a: f8d2 3094 ldr.w r3, [r2, #148] ; 0x94 + 800a09e: f423 6370 bic.w r3, r3, #3840 ; 0xf00 + 800a0a2: ea43 1000 orr.w r0, r3, r0, lsl #4 + 800a0a6: f8c2 0094 str.w r0, [r2, #148] ; 0x94 +} + 800a0aa: 4770 bx lr + 800a0ac: 40021000 .word 0x40021000 + +0800a0b0 : + SET_BIT(RCC->BDCR, RCC_BDCR_LSECSSON); + 800a0b0: 4a03 ldr r2, [pc, #12] ; (800a0c0 ) + 800a0b2: f8d2 3090 ldr.w r3, [r2, #144] ; 0x90 + 800a0b6: f043 0320 orr.w r3, r3, #32 + 800a0ba: f8c2 3090 str.w r3, [r2, #144] ; 0x90 +} + 800a0be: 4770 bx lr + 800a0c0: 40021000 .word 0x40021000 + +0800a0c4 : + CLEAR_BIT(RCC->BDCR, RCC_BDCR_LSECSSON) ; + 800a0c4: 4b05 ldr r3, [pc, #20] ; (800a0dc ) + 800a0c6: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800a0ca: f022 0220 bic.w r2, r2, #32 + 800a0ce: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + __HAL_RCC_DISABLE_IT(RCC_IT_LSECSS); + 800a0d2: 699a ldr r2, [r3, #24] + 800a0d4: f422 7200 bic.w r2, r2, #512 ; 0x200 + 800a0d8: 619a str r2, [r3, #24] +} + 800a0da: 4770 bx lr + 800a0dc: 40021000 .word 0x40021000 + +0800a0e0 : + SET_BIT(RCC->BDCR, RCC_BDCR_LSECSSON) ; + 800a0e0: 4b0a ldr r3, [pc, #40] ; (800a10c ) + 800a0e2: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800a0e6: f042 0220 orr.w r2, r2, #32 + 800a0ea: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + __HAL_RCC_ENABLE_IT(RCC_IT_LSECSS); + 800a0ee: 699a ldr r2, [r3, #24] + 800a0f0: f442 7200 orr.w r2, r2, #512 ; 0x200 + 800a0f4: 619a str r2, [r3, #24] + __HAL_RCC_LSECSS_EXTI_ENABLE_IT(); + 800a0f6: f5a3 3386 sub.w r3, r3, #68608 ; 0x10c00 + 800a0fa: 681a ldr r2, [r3, #0] + 800a0fc: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 800a100: 601a str r2, [r3, #0] + __HAL_RCC_LSECSS_EXTI_ENABLE_RISING_EDGE(); + 800a102: 689a ldr r2, [r3, #8] + 800a104: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 800a108: 609a str r2, [r3, #8] +} + 800a10a: 4770 bx lr + 800a10c: 40021000 .word 0x40021000 + +0800a110 : +} + 800a110: 4770 bx lr + ... + +0800a114 : +{ + 800a114: b510 push {r4, lr} + if(__HAL_RCC_GET_IT(RCC_IT_LSECSS)) + 800a116: 4c05 ldr r4, [pc, #20] ; (800a12c ) + 800a118: 69e3 ldr r3, [r4, #28] + 800a11a: 059b lsls r3, r3, #22 + 800a11c: d504 bpl.n 800a128 + HAL_RCCEx_LSECSS_Callback(); + 800a11e: f7ff fff7 bl 800a110 + __HAL_RCC_CLEAR_IT(RCC_IT_LSECSS); + 800a122: f44f 7300 mov.w r3, #512 ; 0x200 + 800a126: 6223 str r3, [r4, #32] +} + 800a128: bd10 pop {r4, pc} + 800a12a: bf00 nop + 800a12c: 40021000 .word 0x40021000 + +0800a130 : + SET_BIT(RCC->CR, RCC_CR_MSIPLLEN) ; + 800a130: 4a02 ldr r2, [pc, #8] ; (800a13c ) + 800a132: 6813 ldr r3, [r2, #0] + 800a134: f043 0304 orr.w r3, r3, #4 + 800a138: 6013 str r3, [r2, #0] +} + 800a13a: 4770 bx lr + 800a13c: 40021000 .word 0x40021000 + +0800a140 : + CLEAR_BIT(RCC->CR, RCC_CR_MSIPLLEN) ; + 800a140: 4a02 ldr r2, [pc, #8] ; (800a14c ) + 800a142: 6813 ldr r3, [r2, #0] + 800a144: f023 0304 bic.w r3, r3, #4 + 800a148: 6013 str r3, [r2, #0] +} + 800a14a: 4770 bx lr + 800a14c: 40021000 .word 0x40021000 + +0800a150 : + MODIFY_REG(RCC->DLYCFGR, RCC_DLYCFGR_OCTOSPI1_DLY|RCC_DLYCFGR_OCTOSPI2_DLY, (Delay1 | (Delay2 << RCC_DLYCFGR_OCTOSPI2_DLY_Pos))) ; + 800a150: 4a05 ldr r2, [pc, #20] ; (800a168 ) + 800a152: f8d2 30a4 ldr.w r3, [r2, #164] ; 0xa4 + 800a156: f023 03ff bic.w r3, r3, #255 ; 0xff + 800a15a: 4318 orrs r0, r3 + 800a15c: ea40 1101 orr.w r1, r0, r1, lsl #4 + 800a160: f8c2 10a4 str.w r1, [r2, #164] ; 0xa4 +} + 800a164: 4770 bx lr + 800a166: bf00 nop + 800a168: 40021000 .word 0x40021000 + +0800a16c : + __HAL_RCC_CRS_FORCE_RESET(); + 800a16c: 4b10 ldr r3, [pc, #64] ; (800a1b0 ) + 800a16e: 6b9a ldr r2, [r3, #56] ; 0x38 + 800a170: f042 7280 orr.w r2, r2, #16777216 ; 0x1000000 + 800a174: 639a str r2, [r3, #56] ; 0x38 + __HAL_RCC_CRS_RELEASE_RESET(); + 800a176: 6b9a ldr r2, [r3, #56] ; 0x38 + 800a178: f022 7280 bic.w r2, r2, #16777216 ; 0x1000000 + 800a17c: 639a str r2, [r3, #56] ; 0x38 + value = (pInit->Prescaler | pInit->Source | pInit->Polarity); + 800a17e: e9d0 3200 ldrd r3, r2, [r0] + 800a182: 4313 orrs r3, r2 + 800a184: 6882 ldr r2, [r0, #8] + 800a186: 4313 orrs r3, r2 + value |= pInit->ReloadValue; + 800a188: 68c2 ldr r2, [r0, #12] + 800a18a: 4313 orrs r3, r2 + value |= (pInit->ErrorLimitValue << CRS_CFGR_FELIM_Pos); + 800a18c: 6902 ldr r2, [r0, #16] + 800a18e: ea43 4302 orr.w r3, r3, r2, lsl #16 + WRITE_REG(CRS->CFGR, value); + 800a192: 4a08 ldr r2, [pc, #32] ; (800a1b4 ) + 800a194: 6053 str r3, [r2, #4] + MODIFY_REG(CRS->CR, CRS_CR_TRIM, (pInit->HSI48CalibrationValue << CRS_CR_TRIM_Pos)); + 800a196: 6813 ldr r3, [r2, #0] + 800a198: 6941 ldr r1, [r0, #20] + 800a19a: f423 537c bic.w r3, r3, #16128 ; 0x3f00 + 800a19e: ea43 2301 orr.w r3, r3, r1, lsl #8 + 800a1a2: 6013 str r3, [r2, #0] + SET_BIT(CRS->CR, CRS_CR_AUTOTRIMEN | CRS_CR_CEN); + 800a1a4: 6813 ldr r3, [r2, #0] + 800a1a6: f043 0360 orr.w r3, r3, #96 ; 0x60 + 800a1aa: 6013 str r3, [r2, #0] +} + 800a1ac: 4770 bx lr + 800a1ae: bf00 nop + 800a1b0: 40021000 .word 0x40021000 + 800a1b4: 40006000 .word 0x40006000 + +0800a1b8 : + SET_BIT(CRS->CR, CRS_CR_SWSYNC); + 800a1b8: 4a02 ldr r2, [pc, #8] ; (800a1c4 ) + 800a1ba: 6813 ldr r3, [r2, #0] + 800a1bc: f043 0380 orr.w r3, r3, #128 ; 0x80 + 800a1c0: 6013 str r3, [r2, #0] +} + 800a1c2: 4770 bx lr + 800a1c4: 40006000 .word 0x40006000 + +0800a1c8 : + pSynchroInfo->ReloadValue = (READ_BIT(CRS->CFGR, CRS_CFGR_RELOAD)); + 800a1c8: 4b07 ldr r3, [pc, #28] ; (800a1e8 ) + 800a1ca: 685a ldr r2, [r3, #4] + 800a1cc: b292 uxth r2, r2 + 800a1ce: 6002 str r2, [r0, #0] + pSynchroInfo->HSI48CalibrationValue = (READ_BIT(CRS->CR, CRS_CR_TRIM) >> CRS_CR_TRIM_Pos); + 800a1d0: 681a ldr r2, [r3, #0] + 800a1d2: f3c2 2205 ubfx r2, r2, #8, #6 + 800a1d6: 6042 str r2, [r0, #4] + pSynchroInfo->FreqErrorCapture = (READ_BIT(CRS->ISR, CRS_ISR_FECAP) >> CRS_ISR_FECAP_Pos); + 800a1d8: 689a ldr r2, [r3, #8] + 800a1da: 0c12 lsrs r2, r2, #16 + 800a1dc: 6082 str r2, [r0, #8] + pSynchroInfo->FreqErrorDirection = (READ_BIT(CRS->ISR, CRS_ISR_FEDIR)); + 800a1de: 689b ldr r3, [r3, #8] + 800a1e0: f403 4300 and.w r3, r3, #32768 ; 0x8000 + 800a1e4: 60c3 str r3, [r0, #12] +} + 800a1e6: 4770 bx lr + 800a1e8: 40006000 .word 0x40006000 + +0800a1ec : +{ + 800a1ec: b5f8 push {r3, r4, r5, r6, r7, lr} + 800a1ee: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 800a1f0: f7fc ff7c bl 80070ec + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCOK)) + 800a1f4: 4c1e ldr r4, [pc, #120] ; (800a270 ) + tickstart = HAL_GetTick(); + 800a1f6: 4606 mov r6, r0 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCOK); + 800a1f8: 2701 movs r7, #1 + if(Timeout != HAL_MAX_DELAY) + 800a1fa: 1c68 adds r0, r5, #1 + 800a1fc: d12f bne.n 800a25e + crsstatus = RCC_CRS_TIMEOUT; + 800a1fe: 2000 movs r0, #0 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCOK)) + 800a200: 68a2 ldr r2, [r4, #8] + 800a202: 07d1 lsls r1, r2, #31 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCOK); + 800a204: bf48 it mi + 800a206: 60e7 strmi r7, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCWARN)) + 800a208: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCOK; + 800a20a: bf48 it mi + 800a20c: f040 0002 orrmi.w r0, r0, #2 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCWARN)) + 800a210: 0792 lsls r2, r2, #30 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCWARN); + 800a212: bf44 itt mi + 800a214: 2202 movmi r2, #2 + 800a216: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_TRIMOVF)) + 800a218: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCWARN; + 800a21a: bf48 it mi + 800a21c: f040 0004 orrmi.w r0, r0, #4 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_TRIMOVF)) + 800a220: 0553 lsls r3, r2, #21 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_TRIMOVF); + 800a222: bf44 itt mi + 800a224: 2204 movmi r2, #4 + 800a226: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCERR)) + 800a228: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_TRIMOVF; + 800a22a: bf48 it mi + 800a22c: f040 0020 orrmi.w r0, r0, #32 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCERR)) + 800a230: 05d1 lsls r1, r2, #23 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCERR); + 800a232: bf44 itt mi + 800a234: 2204 movmi r2, #4 + 800a236: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCMISS)) + 800a238: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCERR; + 800a23a: bf48 it mi + 800a23c: f040 0008 orrmi.w r0, r0, #8 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCMISS)) + 800a240: 0592 lsls r2, r2, #22 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCMISS); + 800a242: bf44 itt mi + 800a244: 2204 movmi r2, #4 + 800a246: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_ESYNC)) + 800a248: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCMISS; + 800a24a: bf48 it mi + 800a24c: f040 0010 orrmi.w r0, r0, #16 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_ESYNC)) + 800a250: 0713 lsls r3, r2, #28 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_ESYNC); + 800a252: bf44 itt mi + 800a254: 2208 movmi r2, #8 + 800a256: 60e2 strmi r2, [r4, #12] + } while(RCC_CRS_NONE == crsstatus); + 800a258: 2800 cmp r0, #0 + 800a25a: d0ce beq.n 800a1fa +} + 800a25c: bdf8 pop {r3, r4, r5, r6, r7, pc} + if(((HAL_GetTick() - tickstart) > Timeout) || (Timeout == 0U)) + 800a25e: f7fc ff45 bl 80070ec + 800a262: 1b80 subs r0, r0, r6 + 800a264: 42a8 cmp r0, r5 + 800a266: d801 bhi.n 800a26c + 800a268: 2d00 cmp r5, #0 + 800a26a: d1c8 bne.n 800a1fe + crsstatus = RCC_CRS_TIMEOUT; + 800a26c: 2001 movs r0, #1 + 800a26e: e7c7 b.n 800a200 + 800a270: 40006000 .word 0x40006000 + +0800a274 : + 800a274: 4770 bx lr + +0800a276 : + 800a276: 4770 bx lr + +0800a278 : + 800a278: 4770 bx lr + +0800a27a : +} + 800a27a: 4770 bx lr + +0800a27c : + uint32_t itflags = READ_REG(CRS->ISR); + 800a27c: 491b ldr r1, [pc, #108] ; (800a2ec ) +{ + 800a27e: b508 push {r3, lr} + uint32_t itflags = READ_REG(CRS->ISR); + 800a280: 688b ldr r3, [r1, #8] + uint32_t itsources = READ_REG(CRS->CR); + 800a282: 680a ldr r2, [r1, #0] + if(((itflags & RCC_CRS_FLAG_SYNCOK) != 0U) && ((itsources & RCC_CRS_IT_SYNCOK) != 0U)) + 800a284: 07d8 lsls r0, r3, #31 + 800a286: d506 bpl.n 800a296 + 800a288: 07d0 lsls r0, r2, #31 + 800a28a: d504 bpl.n 800a296 + WRITE_REG(CRS->ICR, CRS_ICR_SYNCOKC); + 800a28c: 2301 movs r3, #1 + 800a28e: 60cb str r3, [r1, #12] + HAL_RCCEx_CRS_SyncOkCallback(); + 800a290: f7ff fff0 bl 800a274 +} + 800a294: bd08 pop {r3, pc} + else if(((itflags & RCC_CRS_FLAG_SYNCWARN) != 0U) && ((itsources & RCC_CRS_IT_SYNCWARN) != 0U)) + 800a296: 0798 lsls r0, r3, #30 + 800a298: d507 bpl.n 800a2aa + 800a29a: 0791 lsls r1, r2, #30 + 800a29c: d505 bpl.n 800a2aa + WRITE_REG(CRS->ICR, CRS_ICR_SYNCWARNC); + 800a29e: 4b13 ldr r3, [pc, #76] ; (800a2ec ) + 800a2a0: 2202 movs r2, #2 + 800a2a2: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_SyncWarnCallback(); + 800a2a4: f7ff ffe7 bl 800a276 + 800a2a8: e7f4 b.n 800a294 + else if(((itflags & RCC_CRS_FLAG_ESYNC) != 0U) && ((itsources & RCC_CRS_IT_ESYNC) != 0U)) + 800a2aa: 0718 lsls r0, r3, #28 + 800a2ac: d507 bpl.n 800a2be + 800a2ae: 0711 lsls r1, r2, #28 + 800a2b0: d505 bpl.n 800a2be + WRITE_REG(CRS->ICR, CRS_ICR_ESYNCC); + 800a2b2: 4b0e ldr r3, [pc, #56] ; (800a2ec ) + 800a2b4: 2208 movs r2, #8 + 800a2b6: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_ExpectedSyncCallback(); + 800a2b8: f7ff ffde bl 800a278 + 800a2bc: e7ea b.n 800a294 + if(((itflags & RCC_CRS_FLAG_ERR) != 0U) && ((itsources & RCC_CRS_IT_ERR) != 0U)) + 800a2be: 0758 lsls r0, r3, #29 + 800a2c0: d5e8 bpl.n 800a294 + 800a2c2: 0751 lsls r1, r2, #29 + 800a2c4: d5e6 bpl.n 800a294 + crserror |= RCC_CRS_SYNCERR; + 800a2c6: f413 7080 ands.w r0, r3, #256 ; 0x100 + 800a2ca: bf18 it ne + 800a2cc: 2008 movne r0, #8 + if((itflags & RCC_CRS_FLAG_SYNCMISS) != 0U) + 800a2ce: 059a lsls r2, r3, #22 + crserror |= RCC_CRS_SYNCMISS; + 800a2d0: bf48 it mi + 800a2d2: f040 0010 orrmi.w r0, r0, #16 + if((itflags & RCC_CRS_FLAG_TRIMOVF) != 0U) + 800a2d6: 055b lsls r3, r3, #21 + WRITE_REG(CRS->ICR, CRS_ICR_ERRC); + 800a2d8: 4b04 ldr r3, [pc, #16] ; (800a2ec ) + 800a2da: f04f 0204 mov.w r2, #4 + crserror |= RCC_CRS_TRIMOVF; + 800a2de: bf48 it mi + 800a2e0: f040 0020 orrmi.w r0, r0, #32 + WRITE_REG(CRS->ICR, CRS_ICR_ERRC); + 800a2e4: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_ErrorCallback(crserror); + 800a2e6: f7ff ffc8 bl 800a27a +} + 800a2ea: e7d3 b.n 800a294 + 800a2ec: 40006000 .word 0x40006000 + +0800a2f0 : + * processing is suspended when possible and the Peripheral feeding point reached at + * suspension time is stored in the handle for resumption later on. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_WriteData(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size) +{ + 800a2f0: b573 push {r0, r1, r4, r5, r6, lr} + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + + for(buffercounter = 0U; buffercounter < Size; buffercounter+=4U) + { + /* Write input data 4 bytes at a time */ + HASH->DIN = *(uint32_t*)inputaddr; + 800a2f2: 4d1e ldr r5, [pc, #120] ; (800a36c ) + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800a2f4: 9101 str r1, [sp, #4] +{ + 800a2f6: 4604 mov r4, r0 + for(buffercounter = 0U; buffercounter < Size; buffercounter+=4U) + 800a2f8: 2100 movs r1, #0 + 800a2fa: 4291 cmp r1, r2 + 800a2fc: d221 bcs.n 800a342 + HASH->DIN = *(uint32_t*)inputaddr; + 800a2fe: 9b01 ldr r3, [sp, #4] + 800a300: 681b ldr r3, [r3, #0] + 800a302: 606b str r3, [r5, #4] + inputaddr+=4U; + 800a304: 9b01 ldr r3, [sp, #4] + + /* If the suspension flag has been raised and if the processing is not about + to end, suspend processing */ + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a306: f894 0036 ldrb.w r0, [r4, #54] ; 0x36 + inputaddr+=4U; + 800a30a: 3304 adds r3, #4 + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a30c: 2801 cmp r0, #1 + inputaddr+=4U; + 800a30e: 9301 str r3, [sp, #4] + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a310: f101 0304 add.w r3, r1, #4 + 800a314: d127 bne.n 800a366 + 800a316: 4293 cmp r3, r2 + 800a318: d225 bcs.n 800a366 + { + /* Wait for DINIS = 1, which occurs when 16 32-bit locations are free + in the input buffer */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800a31a: 6a6e ldr r6, [r5, #36] ; 0x24 + 800a31c: 07f6 lsls r6, r6, #31 + 800a31e: d522 bpl.n 800a366 + /* Reset SuspendRequest */ + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + + /* Depending whether the key or the input data were fed to the Peripheral, the feeding point + reached at suspension time is not saved in the same handle fields */ + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a320: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a324: 2500 movs r5, #0 + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a326: 2b02 cmp r3, #2 + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a328: f884 5036 strb.w r5, [r4, #54] ; 0x36 + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a32c: d001 beq.n 800a332 + 800a32e: 2b04 cmp r3, #4 + 800a330: d109 bne.n 800a346 + { + /* Save current reading and writing locations of Input and Output buffers */ + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + /* Save the number of bytes that remain to be processed at this point */ + hhash->HashInCount = Size - (buffercounter + 4U); + 800a332: 3a04 subs r2, #4 + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + 800a334: 9b01 ldr r3, [sp, #4] + 800a336: 60e3 str r3, [r4, #12] + hhash->HashInCount = Size - (buffercounter + 4U); + 800a338: 1a52 subs r2, r2, r1 + 800a33a: 6222 str r2, [r4, #32] + __HAL_UNLOCK(hhash); + return HAL_ERROR; + } + + /* Set the HASH state to Suspended and exit to stop entering data */ + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a33c: 2308 movs r3, #8 + 800a33e: f884 3035 strb.w r3, [r4, #53] ; 0x35 + } /* if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) */ + } /* if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4) < Size)) */ + } /* for(buffercounter = 0; buffercounter < Size; buffercounter+=4) */ + + /* At this point, all the data have been entered to the Peripheral: exit */ + return HAL_OK; + 800a342: 2000 movs r0, #0 + 800a344: e00d b.n 800a362 + else if ((hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_3)) + 800a346: 2b03 cmp r3, #3 + 800a348: d001 beq.n 800a34e + 800a34a: 2b05 cmp r3, #5 + 800a34c: d105 bne.n 800a35a + hhash->HashKeyCount = Size - (buffercounter + 4U); + 800a34e: 3a04 subs r2, #4 + hhash->pHashKeyBuffPtr = (uint8_t *)inputaddr; + 800a350: 9b01 ldr r3, [sp, #4] + 800a352: 6163 str r3, [r4, #20] + hhash->HashKeyCount = Size - (buffercounter + 4U); + 800a354: 1a52 subs r2, r2, r1 + 800a356: 62a2 str r2, [r4, #40] ; 0x28 + 800a358: e7f0 b.n 800a33c + hhash->State = HAL_HASH_STATE_READY; + 800a35a: f884 0035 strb.w r0, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800a35e: f884 5034 strb.w r5, [r4, #52] ; 0x34 +} + 800a362: b002 add sp, #8 + 800a364: bd70 pop {r4, r5, r6, pc} + 800a366: 4619 mov r1, r3 + 800a368: e7c7 b.n 800a2fa + 800a36a: bf00 nop + 800a36c: 50060400 .word 0x50060400 + +0800a370 : + */ +static void HASH_GetDigest(uint8_t *pMsgDigest, uint8_t Size) +{ + uint32_t msgdigest = (uint32_t)pMsgDigest; + + switch(Size) + 800a370: 291c cmp r1, #28 + 800a372: d027 beq.n 800a3c4 + 800a374: d804 bhi.n 800a380 + 800a376: 2910 cmp r1, #16 + 800a378: d005 beq.n 800a386 + 800a37a: 2914 cmp r1, #20 + 800a37c: d011 beq.n 800a3a2 + 800a37e: 4770 bx lr + 800a380: 2920 cmp r1, #32 + 800a382: d037 beq.n 800a3f4 + 800a384: 4770 bx lr + { + /* Read the message digest */ + case 16: /* MD5 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a386: 4b29 ldr r3, [pc, #164] ; (800a42c ) + 800a388: 68da ldr r2, [r3, #12] + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); + 800a38a: ba12 rev r2, r2 + 800a38c: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a38e: 691a ldr r2, [r3, #16] + 800a390: ba12 rev r2, r2 + 800a392: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a394: 695a ldr r2, [r3, #20] + 800a396: ba12 rev r2, r2 + 800a398: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a39a: 699b ldr r3, [r3, #24] + 800a39c: ba1b rev r3, r3 + 800a39e: 60c3 str r3, [r0, #12] + break; + 800a3a0: 4770 bx lr + case 20: /* SHA1 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a3a2: 4b22 ldr r3, [pc, #136] ; (800a42c ) + 800a3a4: 68da ldr r2, [r3, #12] + 800a3a6: ba12 rev r2, r2 + 800a3a8: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a3aa: 691a ldr r2, [r3, #16] + 800a3ac: ba12 rev r2, r2 + 800a3ae: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a3b0: 695a ldr r2, [r3, #20] + 800a3b2: ba12 rev r2, r2 + 800a3b4: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a3b6: 699a ldr r2, [r3, #24] + 800a3b8: ba12 rev r2, r2 + 800a3ba: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a3bc: 69db ldr r3, [r3, #28] + 800a3be: ba1b rev r3, r3 + 800a3c0: 6103 str r3, [r0, #16] + break; + 800a3c2: 4770 bx lr + case 28: /* SHA224 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a3c4: 4b19 ldr r3, [pc, #100] ; (800a42c ) + 800a3c6: 68da ldr r2, [r3, #12] + 800a3c8: ba12 rev r2, r2 + 800a3ca: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a3cc: 691a ldr r2, [r3, #16] + 800a3ce: ba12 rev r2, r2 + 800a3d0: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a3d2: 695a ldr r2, [r3, #20] + 800a3d4: ba12 rev r2, r2 + 800a3d6: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a3d8: 699a ldr r2, [r3, #24] + 800a3da: ba12 rev r2, r2 + 800a3dc: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a3de: 69db ldr r3, [r3, #28] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a3e0: 4a13 ldr r2, [pc, #76] ; (800a430 ) + 800a3e2: ba1b rev r3, r3 + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a3e4: 6103 str r3, [r0, #16] + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a3e6: 6a53 ldr r3, [r2, #36] ; 0x24 + 800a3e8: ba1b rev r3, r3 + 800a3ea: 6143 str r3, [r0, #20] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[6]); + 800a3ec: 6a93 ldr r3, [r2, #40] ; 0x28 + 800a3ee: ba1b rev r3, r3 + 800a3f0: 6183 str r3, [r0, #24] + break; + 800a3f2: 4770 bx lr + case 32: /* SHA256 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a3f4: 4b0d ldr r3, [pc, #52] ; (800a42c ) + 800a3f6: 68da ldr r2, [r3, #12] + 800a3f8: ba12 rev r2, r2 + 800a3fa: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a3fc: 691a ldr r2, [r3, #16] + 800a3fe: ba12 rev r2, r2 + 800a400: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a402: 695a ldr r2, [r3, #20] + 800a404: ba12 rev r2, r2 + 800a406: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a408: 699a ldr r2, [r3, #24] + 800a40a: ba12 rev r2, r2 + 800a40c: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a40e: 69db ldr r3, [r3, #28] + 800a410: ba1b rev r3, r3 + 800a412: 6103 str r3, [r0, #16] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a414: 4b06 ldr r3, [pc, #24] ; (800a430 ) + 800a416: 6a5a ldr r2, [r3, #36] ; 0x24 + 800a418: ba12 rev r2, r2 + 800a41a: 6142 str r2, [r0, #20] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[6]); + 800a41c: 6a9a ldr r2, [r3, #40] ; 0x28 + 800a41e: ba12 rev r2, r2 + 800a420: 6182 str r2, [r0, #24] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[7]); + 800a422: 6adb ldr r3, [r3, #44] ; 0x2c + 800a424: ba1b rev r3, r3 + 800a426: 61c3 str r3, [r0, #28] + break; + default: + break; + } +} + 800a428: 4770 bx lr + 800a42a: bf00 nop + 800a42c: 50060400 .word 0x50060400 + 800a430: 50060700 .word 0x50060700 + +0800a434 : + * @param Status the Flag status (SET or RESET). + * @param Timeout Timeout duration. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_WaitOnFlagUntilTimeout(HASH_HandleTypeDef *hhash, uint32_t Flag, FlagStatus Status, uint32_t Timeout) +{ + 800a434: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 800a438: 4604 mov r4, r0 + 800a43a: 460e mov r6, r1 + 800a43c: 4691 mov r9, r2 + 800a43e: 461d mov r5, r3 + uint32_t tickstart = HAL_GetTick(); + 800a440: f7fc fe54 bl 80070ec + 800a444: f8df 805c ldr.w r8, [pc, #92] ; 800a4a4 + 800a448: 4607 mov r7, r0 + + /* Wait until flag is set */ + if(Status == RESET) + 800a44a: f1b9 0f00 cmp.w r9, #0 + 800a44e: d021 beq.n 800a494 + } + } + } + else + { + while(__HAL_HASH_GET_FLAG(Flag) != RESET) + 800a450: f8d8 3024 ldr.w r3, [r8, #36] ; 0x24 + 800a454: ea36 0303 bics.w r3, r6, r3 + 800a458: d121 bne.n 800a49e + { + /* Check for the Timeout */ + if(Timeout != HAL_MAX_DELAY) + 800a45a: 1c6b adds r3, r5, #1 + 800a45c: d0f8 beq.n 800a450 + { + if(((HAL_GetTick()-tickstart) > Timeout) || (Timeout == 0U)) + 800a45e: f7fc fe45 bl 80070ec + 800a462: 1bc0 subs r0, r0, r7 + 800a464: 42a8 cmp r0, r5 + 800a466: d80a bhi.n 800a47e + 800a468: 2d00 cmp r5, #0 + 800a46a: d1f1 bne.n 800a450 + 800a46c: e007 b.n 800a47e + if(Timeout != HAL_MAX_DELAY) + 800a46e: 1c6a adds r2, r5, #1 + 800a470: d010 beq.n 800a494 + if(((HAL_GetTick()-tickstart) > Timeout) || (Timeout == 0U)) + 800a472: f7fc fe3b bl 80070ec + 800a476: 1bc0 subs r0, r0, r7 + 800a478: 42a8 cmp r0, r5 + 800a47a: d800 bhi.n 800a47e + 800a47c: b955 cbnz r5, 800a494 + { + /* Set State to Ready to be able to restart later on */ + hhash->State = HAL_HASH_STATE_READY; + 800a47e: 2301 movs r3, #1 + 800a480: f884 3035 strb.w r3, [r4, #53] ; 0x35 + /* Store time out issue in handle status */ + hhash->Status = HAL_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hhash); + 800a484: 2200 movs r2, #0 + hhash->Status = HAL_TIMEOUT; + 800a486: 2303 movs r3, #3 + 800a488: f884 302c strb.w r3, [r4, #44] ; 0x2c + __HAL_UNLOCK(hhash); + 800a48c: f884 2034 strb.w r2, [r4, #52] ; 0x34 + + return HAL_TIMEOUT; + 800a490: 4618 mov r0, r3 + 800a492: e005 b.n 800a4a0 + while(__HAL_HASH_GET_FLAG(Flag) == RESET) + 800a494: f8d8 3024 ldr.w r3, [r8, #36] ; 0x24 + 800a498: ea36 0303 bics.w r3, r6, r3 + 800a49c: d1e7 bne.n 800a46e + } + } + } + } + return HAL_OK; + 800a49e: 2000 movs r0, #0 +} + 800a4a0: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + 800a4a4: 50060400 .word 0x50060400 + +0800a4a8 : +} + 800a4a8: 4770 bx lr + ... + +0800a4ac : +{ + 800a4ac: b538 push {r3, r4, r5, lr} + if(hhash == NULL) + 800a4ae: 4604 mov r4, r0 + 800a4b0: b328 cbz r0, 800a4fe + if(hhash->State == HAL_HASH_STATE_RESET) + 800a4b2: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + 800a4b6: f003 02ff and.w r2, r3, #255 ; 0xff + 800a4ba: b91b cbnz r3, 800a4c4 + hhash->Lock = HAL_UNLOCKED; + 800a4bc: f880 2034 strb.w r2, [r0, #52] ; 0x34 + HAL_HASH_MspInit(hhash); + 800a4c0: f7ff fff2 bl 800a4a8 + hhash->HashInCount = 0; + 800a4c4: 2000 movs r0, #0 + MODIFY_REG(HASH->CR, HASH_CR_DATATYPE, hhash->Init.DataType); + 800a4c6: 4a0f ldr r2, [pc, #60] ; (800a504 ) + hhash->HashBuffSize = 0; + 800a4c8: 61e0 str r0, [r4, #28] + hhash->State = HAL_HASH_STATE_BUSY; + 800a4ca: 2302 movs r3, #2 + hhash->Phase = HAL_HASH_PHASE_READY; + 800a4cc: 2101 movs r1, #1 + hhash->State = HAL_HASH_STATE_BUSY; + 800a4ce: f884 3035 strb.w r3, [r4, #53] ; 0x35 + hhash->Phase = HAL_HASH_PHASE_READY; + 800a4d2: f884 102d strb.w r1, [r4, #45] ; 0x2d + hhash->HashInCount = 0; + 800a4d6: 6220 str r0, [r4, #32] + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a4d8: 86e0 strh r0, [r4, #54] ; 0x36 + hhash->HashITCounter = 0; + 800a4da: 6260 str r0, [r4, #36] ; 0x24 + hhash->NbWordsAlreadyPushed = 0; + 800a4dc: 63a0 str r0, [r4, #56] ; 0x38 + MODIFY_REG(HASH->CR, HASH_CR_DATATYPE, hhash->Init.DataType); + 800a4de: 6813 ldr r3, [r2, #0] + 800a4e0: 6825 ldr r5, [r4, #0] + 800a4e2: f023 0330 bic.w r3, r3, #48 ; 0x30 + 800a4e6: 432b orrs r3, r5 + 800a4e8: 6013 str r3, [r2, #0] +__HAL_HASH_RESET_MDMAT(); + 800a4ea: 6813 ldr r3, [r2, #0] + 800a4ec: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 800a4f0: 6013 str r3, [r2, #0] + hhash->State = HAL_HASH_STATE_READY; + 800a4f2: f884 1035 strb.w r1, [r4, #53] ; 0x35 + hhash->Status = HAL_OK; + 800a4f6: f884 002c strb.w r0, [r4, #44] ; 0x2c + hhash->ErrorCode = HAL_HASH_ERROR_NONE; + 800a4fa: 63e0 str r0, [r4, #60] ; 0x3c +} + 800a4fc: bd38 pop {r3, r4, r5, pc} + return HAL_ERROR; + 800a4fe: 2001 movs r0, #1 + 800a500: e7fc b.n 800a4fc + 800a502: bf00 nop + 800a504: 50060400 .word 0x50060400 + +0800a508 : + 800a508: 4770 bx lr + +0800a50a : + 800a50a: 4770 bx lr + +0800a50c : + 800a50c: 4770 bx lr + +0800a50e : + 800a50e: 4770 bx lr + +0800a510 : +{ + 800a510: b570 push {r4, r5, r6, lr} + * suspension time is stored in the handle for resumption later on. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_IT(HASH_HandleTypeDef *hhash) +{ + if (hhash->State == HAL_HASH_STATE_BUSY) + 800a512: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + 800a516: 2b02 cmp r3, #2 +{ + 800a518: 4604 mov r4, r0 + if (hhash->State == HAL_HASH_STATE_BUSY) + 800a51a: b2da uxtb r2, r3 + 800a51c: f040 80e7 bne.w 800a6ee + { + /* ITCounter must not be equal to 0 at this point. Report an error if this is the case. */ + if(hhash->HashITCounter == 0U) + 800a520: 6a43 ldr r3, [r0, #36] ; 0x24 + 800a522: 4d74 ldr r5, [pc, #464] ; (800a6f4 ) + 800a524: b94b cbnz r3, 800a53a + { + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a526: 6a2b ldr r3, [r5, #32] + 800a528: f023 0303 bic.w r3, r3, #3 + 800a52c: 622b str r3, [r5, #32] + /* HASH state set back to Ready to prevent any issue in user code + present in HAL_HASH_ErrorCallback() */ + hhash->State = HAL_HASH_STATE_READY; + 800a52e: 2301 movs r3, #1 + 800a530: f880 3035 strb.w r3, [r0, #53] ; 0x35 + hhash->Status = HASH_IT(hhash); + 800a534: f884 302c strb.w r3, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800a538: e099 b.n 800a66e + return HAL_ERROR; + } + else if (hhash->HashITCounter == 1U) + 800a53a: 6a43 ldr r3, [r0, #36] ; 0x24 + 800a53c: 2b01 cmp r3, #1 + } + else + { + /* Cruise speed reached, HashITCounter remains equal to 3 until the end of + the HASH processing or the end of the current step for HMAC processing. */ + hhash->HashITCounter = 3U; + 800a53e: bf16 itet ne + 800a540: 2303 movne r3, #3 + hhash->HashITCounter = 2U; + 800a542: 6242 streq r2, [r0, #36] ; 0x24 + hhash->HashITCounter = 3U; + 800a544: 6243 strne r3, [r0, #36] ; 0x24 + } + + /* If digest is ready */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DCIS)) + 800a546: 6a6b ldr r3, [r5, #36] ; 0x24 + 800a548: f013 0302 ands.w r3, r3, #2 + 800a54c: d022 beq.n 800a594 + { + /* Read the digest */ + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800a54e: 682a ldr r2, [r5, #0] + 800a550: 4b69 ldr r3, [pc, #420] ; (800a6f8 ) + 800a552: 6900 ldr r0, [r0, #16] + 800a554: 421a tst r2, r3 + 800a556: d019 beq.n 800a58c + 800a558: 682a ldr r2, [r5, #0] + 800a55a: 401a ands r2, r3 + 800a55c: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800a560: d016 beq.n 800a590 + 800a562: 682a ldr r2, [r5, #0] + 800a564: 4393 bics r3, r2 + 800a566: bf0c ite eq + 800a568: 2120 moveq r1, #32 + 800a56a: 2110 movne r1, #16 + 800a56c: f7ff ff00 bl 800a370 + + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a570: 6a2b ldr r3, [r5, #32] + 800a572: f023 0303 bic.w r3, r3, #3 + 800a576: 622b str r3, [r5, #32] + /* Change the HASH state */ + hhash->State = HAL_HASH_STATE_READY; + 800a578: 2301 movs r3, #1 + 800a57a: f884 3035 strb.w r3, [r4, #53] ; 0x35 + /* Reset HASH state machine */ + hhash->Phase = HAL_HASH_PHASE_READY; + 800a57e: f884 302d strb.w r3, [r4, #45] ; 0x2d + /* Call digest computation complete call back */ +#if (USE_HAL_HASH_REGISTER_CALLBACKS == 1) + hhash->DgstCpltCallback(hhash); +#else + HAL_HASH_DgstCpltCallback(hhash); + 800a582: 4620 mov r0, r4 + 800a584: f7ff ffc2 bl 800a50c + hhash->Status = HAL_OK; + 800a588: 2300 movs r3, #0 + 800a58a: e015 b.n 800a5b8 + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800a58c: 2114 movs r1, #20 + 800a58e: e7ed b.n 800a56c + 800a590: 211c movs r1, #28 + 800a592: e7eb b.n 800a56c + + return HAL_OK; + } + + /* If Peripheral ready to accept new data */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800a594: 6a6a ldr r2, [r5, #36] ; 0x24 + 800a596: 07d2 lsls r2, r2, #31 + 800a598: d5f6 bpl.n 800a588 + { + + /* If the suspension flag has been raised and if the processing is not about + to end, suspend processing */ + if ( (hhash->HashInCount != 0U) && (hhash->SuspendRequest == HAL_HASH_SUSPEND)) + 800a59a: 6a02 ldr r2, [r0, #32] + 800a59c: b17a cbz r2, 800a5be + 800a59e: f890 2036 ldrb.w r2, [r0, #54] ; 0x36 + 800a5a2: 2a01 cmp r2, #1 + 800a5a4: d10b bne.n 800a5be + { + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a5a6: 6a2a ldr r2, [r5, #32] + 800a5a8: f022 0203 bic.w r2, r2, #3 + 800a5ac: 622a str r2, [r5, #32] + + /* Reset SuspendRequest */ + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + + /* Change the HASH state */ + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a5ae: 2208 movs r2, #8 + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a5b0: f880 3036 strb.w r3, [r0, #54] ; 0x36 + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a5b4: f880 2035 strb.w r2, [r0, #53] ; 0x35 + hhash->Status = HAL_OK; + 800a5b8: f884 302c strb.w r3, [r4, #44] ; 0x2c +} + 800a5bc: e076 b.n 800a6ac + uint32_t buffercounter; + uint32_t inputcounter; + uint32_t ret = HASH_DIGEST_CALCULATION_NOT_STARTED; + + /* If there are more than 64 bytes remaining to be entered */ + if(hhash->HashInCount > 64U) + 800a5be: 6a21 ldr r1, [r4, #32] + { + inputaddr = (uint32_t)hhash->pHashInBuffPtr; + 800a5c0: 68e3 ldr r3, [r4, #12] + if(hhash->HashInCount > 64U) + 800a5c2: 2940 cmp r1, #64 ; 0x40 + inputaddr = (uint32_t)hhash->pHashInBuffPtr; + 800a5c4: 461a mov r2, r3 + if(hhash->HashInCount > 64U) + 800a5c6: d91c bls.n 800a602 + 800a5c8: f103 0140 add.w r1, r3, #64 ; 0x40 + /* Write the Input block in the Data IN register + (16 32-bit words, or 64 bytes are entered) */ + for(buffercounter = 0U; buffercounter < 64U; buffercounter+=4U) + { + HASH->DIN = *(uint32_t*)inputaddr; + 800a5cc: f853 0b04 ldr.w r0, [r3], #4 + 800a5d0: 6068 str r0, [r5, #4] + for(buffercounter = 0U; buffercounter < 64U; buffercounter+=4U) + 800a5d2: 4299 cmp r1, r3 + 800a5d4: d1fa bne.n 800a5cc + inputaddr+=4U; + } + /* If this is the start of input data entering, an additional word + must be entered to start up the HASH processing */ + if(hhash->HashITCounter == 2U) + 800a5d6: 6a63 ldr r3, [r4, #36] ; 0x24 + 800a5d8: 2b02 cmp r3, #2 + 800a5da: d10d bne.n 800a5f8 + { + HASH->DIN = *(uint32_t*)inputaddr; + 800a5dc: 680b ldr r3, [r1, #0] + 800a5de: 606b str r3, [r5, #4] + if(hhash->HashInCount >= 68U) + 800a5e0: 6a23 ldr r3, [r4, #32] + 800a5e2: 2b43 cmp r3, #67 ; 0x43 + 800a5e4: d905 bls.n 800a5f2 + { + /* There are still data waiting to be entered in the Peripheral. + Decrement buffer counter and set pointer to the proper + memory location for the next data entering round. */ + hhash->HashInCount -= 68U; + 800a5e6: 6a23 ldr r3, [r4, #32] + 800a5e8: 3b44 subs r3, #68 ; 0x44 + 800a5ea: 6223 str r3, [r4, #32] + hhash->pHashInBuffPtr+= 68U; + 800a5ec: 3244 adds r2, #68 ; 0x44 + { + /* 64 bytes have been entered and there are still some remaining: + Decrement buffer counter and set pointer to the proper + memory location for the next data entering round.*/ + hhash->HashInCount -= 64U; + hhash->pHashInBuffPtr+= 64U; + 800a5ee: 60e2 str r2, [r4, #12] + /* Reset buffer counter */ + hhash->HashInCount = 0; + } + + /* Return whether or digest calculation has started */ + return ret; + 800a5f0: e7ca b.n 800a588 + hhash->HashInCount = 0U; + 800a5f2: 2300 movs r3, #0 + 800a5f4: 6223 str r3, [r4, #32] + return ret; + 800a5f6: e7c7 b.n 800a588 + hhash->HashInCount -= 64U; + 800a5f8: 6a23 ldr r3, [r4, #32] + 800a5fa: 3b40 subs r3, #64 ; 0x40 + 800a5fc: 6223 str r3, [r4, #32] + hhash->pHashInBuffPtr+= 64U; + 800a5fe: 3240 adds r2, #64 ; 0x40 + 800a600: e7f5 b.n 800a5ee + inputcounter = hhash->HashInCount; + 800a602: 6a22 ldr r2, [r4, #32] + __HAL_HASH_DISABLE_IT(HASH_IT_DINI); + 800a604: 6a29 ldr r1, [r5, #32] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a606: 3203 adds r2, #3 + __HAL_HASH_DISABLE_IT(HASH_IT_DINI); + 800a608: f021 0101 bic.w r1, r1, #1 + 800a60c: f022 0203 bic.w r2, r2, #3 + 800a610: 6229 str r1, [r5, #32] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a612: 441a add r2, r3 + 800a614: 4293 cmp r3, r2 + 800a616: d10b bne.n 800a630 + if (hhash->Accumulation == 1U) + 800a618: 6c23 ldr r3, [r4, #64] ; 0x40 + 800a61a: 2b01 cmp r3, #1 + 800a61c: d10c bne.n 800a638 + hhash->Accumulation = 0U; + 800a61e: 2500 movs r5, #0 + 800a620: 6425 str r5, [r4, #64] ; 0x40 + HAL_HASH_InCpltCallback(hhash); + 800a622: 4620 mov r0, r4 + hhash->State = HAL_HASH_STATE_READY; + 800a624: f884 3035 strb.w r3, [r4, #53] ; 0x35 + HAL_HASH_InCpltCallback(hhash); + 800a628: f7ff ff6f bl 800a50a + hhash->HashInCount = 0; + 800a62c: 6225 str r5, [r4, #32] + return ret; + 800a62e: e7ab b.n 800a588 + HASH->DIN = *(uint32_t*)inputaddr; + 800a630: f853 1b04 ldr.w r1, [r3], #4 + 800a634: 6069 str r1, [r5, #4] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a636: e7ed b.n 800a614 + __HAL_HASH_START_DIGEST(); + 800a638: 68ab ldr r3, [r5, #8] + 800a63a: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800a63e: 60ab str r3, [r5, #8] + hhash->HashInCount = 0; + 800a640: 2300 movs r3, #0 + 800a642: 6223 str r3, [r4, #32] + HAL_HASH_InCpltCallback(hhash); + 800a644: 4620 mov r0, r4 + 800a646: f7ff ff60 bl 800a50a + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) + 800a64a: f894 602d ldrb.w r6, [r4, #45] ; 0x2d + 800a64e: 2e03 cmp r6, #3 + 800a650: d12d bne.n 800a6ae + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, HASH_TIMEOUTVALUE) != HAL_OK) + 800a652: f44f 737a mov.w r3, #1000 ; 0x3e8 + 800a656: 2201 movs r2, #1 + 800a658: 2108 movs r1, #8 + 800a65a: 4620 mov r0, r4 + 800a65c: f7ff feea bl 800a434 + 800a660: b168 cbz r0, 800a67e + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a662: 6a2b ldr r3, [r5, #32] + 800a664: f023 0303 bic.w r3, r3, #3 + 800a668: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a66a: f884 602c strb.w r6, [r4, #44] ; 0x2c + hhash->ErrorCode |= HAL_HASH_ERROR_IT; + 800a66e: 6be3 ldr r3, [r4, #60] ; 0x3c + 800a670: f043 0301 orr.w r3, r3, #1 + 800a674: 63e3 str r3, [r4, #60] ; 0x3c + HAL_HASH_ErrorCallback(hhash); + 800a676: 4620 mov r0, r4 + 800a678: f7ff ff49 bl 800a50e + 800a67c: e784 b.n 800a588 + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_2; /* Move phase from Step 1 to Step 2 */ + 800a67e: 2304 movs r3, #4 + 800a680: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); /* Set NBLW for the input message */ + 800a684: 68ab ldr r3, [r5, #8] + 800a686: 69e2 ldr r2, [r4, #28] + 800a688: f023 031f bic.w r3, r3, #31 + 800a68c: f002 0103 and.w r1, r2, #3 + 800a690: ea43 03c1 orr.w r3, r3, r1, lsl #3 + 800a694: 60ab str r3, [r5, #8] + hhash->pHashInBuffPtr = hhash->pHashMsgBuffPtr; /* Set the input data address */ + 800a696: 69a3 ldr r3, [r4, #24] + hhash->HashInCount = hhash->HashBuffSize; /* Set the input data size (in bytes) */ + 800a698: 6222 str r2, [r4, #32] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a69a: 60e3 str r3, [r4, #12] + hhash->HashITCounter = 1; /* Set ITCounter to 1 to indicate the start of a new phase */ + 800a69c: 2301 movs r3, #1 + 800a69e: 6263 str r3, [r4, #36] ; 0x24 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI); /* Enable IT (was disabled in HASH_Write_Block_Data) */ + 800a6a0: 6a2b ldr r3, [r5, #32] + 800a6a2: f043 0301 orr.w r3, r3, #1 + 800a6a6: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a6a8: f884 002c strb.w r0, [r4, #44] ; 0x2c +} + 800a6ac: bd70 pop {r4, r5, r6, pc} + else if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2) + 800a6ae: 2e04 cmp r6, #4 + 800a6b0: f47f af6a bne.w 800a588 + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, HASH_TIMEOUTVALUE) != HAL_OK) + 800a6b4: f44f 737a mov.w r3, #1000 ; 0x3e8 + 800a6b8: 2201 movs r2, #1 + 800a6ba: 2108 movs r1, #8 + 800a6bc: 4620 mov r0, r4 + 800a6be: f7ff feb9 bl 800a434 + 800a6c2: b128 cbz r0, 800a6d0 + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a6c4: 6a2b ldr r3, [r5, #32] + 800a6c6: f023 0303 bic.w r3, r3, #3 + 800a6ca: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a6cc: 2303 movs r3, #3 + 800a6ce: e731 b.n 800a534 + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_3; /* Move phase from Step 2 to Step 3 */ + 800a6d0: 2305 movs r3, #5 + 800a6d2: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); /* Set NBLW for the key */ + 800a6d6: 68ab ldr r3, [r5, #8] + 800a6d8: 6862 ldr r2, [r4, #4] + 800a6da: f023 031f bic.w r3, r3, #31 + 800a6de: f002 0103 and.w r1, r2, #3 + 800a6e2: ea43 03c1 orr.w r3, r3, r1, lsl #3 + 800a6e6: 60ab str r3, [r5, #8] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a6e8: 68a3 ldr r3, [r4, #8] + hhash->HashInCount = hhash->Init.KeySize; /* Set the key size (in bytes) */ + 800a6ea: 6222 str r2, [r4, #32] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a6ec: e7d5 b.n 800a69a + hhash->Status = HASH_IT(hhash); + 800a6ee: 2302 movs r3, #2 + 800a6f0: e720 b.n 800a534 + 800a6f2: bf00 nop + 800a6f4: 50060400 .word 0x50060400 + 800a6f8: 00040080 .word 0x00040080 + +0800a6fc : + return hhash->State; + 800a6fc: f890 0035 ldrb.w r0, [r0, #53] ; 0x35 +} + 800a700: 4770 bx lr + +0800a702 : +} + 800a702: f890 002c ldrb.w r0, [r0, #44] ; 0x2c + 800a706: 4770 bx lr + +0800a708 : + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->IMR,HASH_IT_DINI|HASH_IT_DCI); + 800a708: 4b0e ldr r3, [pc, #56] ; (800a744 ) + 800a70a: 6a1a ldr r2, [r3, #32] + 800a70c: f002 0203 and.w r2, r2, #3 + 800a710: 600a str r2, [r1, #0] + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->STR,HASH_STR_NBLW); + 800a712: 689a ldr r2, [r3, #8] + 800a714: f002 021f and.w r2, r2, #31 + 800a718: 604a str r2, [r1, #4] + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->CR,HASH_CR_DMAE|HASH_CR_DATATYPE|HASH_CR_MODE|HASH_CR_ALGO|HASH_CR_LKEY|HASH_CR_MDMAT); + 800a71a: 681b ldr r3, [r3, #0] + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800a71c: 4a0a ldr r2, [pc, #40] ; (800a748 ) + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->CR,HASH_CR_DMAE|HASH_CR_DATATYPE|HASH_CR_MODE|HASH_CR_ALGO|HASH_CR_LKEY|HASH_CR_MDMAT); + 800a71e: f023 437f bic.w r3, r3, #4278190080 ; 0xff000000 + 800a722: f423 037a bic.w r3, r3, #16384000 ; 0xfa0000 + 800a726: f423 435f bic.w r3, r3, #57088 ; 0xdf00 + 800a72a: f023 0307 bic.w r3, r3, #7 + 800a72e: 608b str r3, [r1, #8] + uint32_t csr_ptr = (uint32_t)HASH->CSR; + 800a730: 4b06 ldr r3, [pc, #24] ; (800a74c ) + mem_ptr+=4U; + 800a732: 310c adds r1, #12 + *(uint32_t*)(mem_ptr) = *(uint32_t*)(csr_ptr); + 800a734: f853 0b04 ldr.w r0, [r3], #4 + 800a738: f841 0b04 str.w r0, [r1], #4 + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800a73c: 4293 cmp r3, r2 + 800a73e: d1f9 bne.n 800a734 +} + 800a740: 4770 bx lr + 800a742: bf00 nop + 800a744: 50060400 .word 0x50060400 + 800a748: 500605d0 .word 0x500605d0 + 800a74c: 500604f8 .word 0x500604f8 + +0800a750 : + WRITE_REG(HASH->IMR, (*(uint32_t*)(mem_ptr))); + 800a750: 4b0a ldr r3, [pc, #40] ; (800a77c ) + 800a752: 680a ldr r2, [r1, #0] + 800a754: 621a str r2, [r3, #32] + WRITE_REG(HASH->STR, (*(uint32_t*)(mem_ptr))); + 800a756: 684a ldr r2, [r1, #4] + 800a758: 609a str r2, [r3, #8] + WRITE_REG(HASH->CR, (*(uint32_t*)(mem_ptr))); + 800a75a: 688a ldr r2, [r1, #8] + 800a75c: 601a str r2, [r3, #0] + __HAL_HASH_INIT(); + 800a75e: 681a ldr r2, [r3, #0] + 800a760: f042 0204 orr.w r2, r2, #4 + 800a764: 601a str r2, [r3, #0] + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800a766: 4a06 ldr r2, [pc, #24] ; (800a780 ) + mem_ptr+=4U; + 800a768: 310c adds r1, #12 + uint32_t csr_ptr = (uint32_t)HASH->CSR; + 800a76a: 33f8 adds r3, #248 ; 0xf8 + WRITE_REG((*(uint32_t*)(csr_ptr)), (*(uint32_t*)(mem_ptr))); + 800a76c: f851 0b04 ldr.w r0, [r1], #4 + 800a770: f843 0b04 str.w r0, [r3], #4 + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800a774: 4293 cmp r3, r2 + 800a776: d1f9 bne.n 800a76c +} + 800a778: 4770 bx lr + 800a77a: bf00 nop + 800a77c: 50060400 .word 0x50060400 + 800a780: 500605d0 .word 0x500605d0 + +0800a784 : + hhash->SuspendRequest = HAL_HASH_SUSPEND; + 800a784: 2301 movs r3, #1 + 800a786: f880 3036 strb.w r3, [r0, #54] ; 0x36 +} + 800a78a: 4770 bx lr + +0800a78c : + return hhash->ErrorCode; + 800a78c: 6bc0 ldr r0, [r0, #60] ; 0x3c +} + 800a78e: 4770 bx lr + +0800a790 : + * @param Timeout Timeout value. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout, uint32_t Algorithm) +{ + 800a790: b5f8 push {r3, r4, r5, r6, r7, lr} + 800a792: 461e mov r6, r3 + uint8_t *pInBuffer_tmp; /* input data address, input parameter of HASH_WriteData() */ + uint32_t Size_tmp; /* input data size (in bytes), input parameter of HASH_WriteData() */ + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a794: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + + + /* Initiate HASH processing in case of start or resumption */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800a798: 2b01 cmp r3, #1 +{ + 800a79a: 4604 mov r4, r0 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a79c: b2d8 uxtb r0, r3 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800a79e: d001 beq.n 800a7a4 + 800a7a0: 2808 cmp r0, #8 + 800a7a2: d17a bne.n 800a89a + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (pOutBuffer == NULL)) + 800a7a4: b101 cbz r1, 800a7a8 + 800a7a6: b926 cbnz r6, 800a7b2 + { + hhash->State = HAL_HASH_STATE_READY; + 800a7a8: 2501 movs r5, #1 + 800a7aa: f884 5035 strb.w r5, [r4, #53] ; 0x35 + } + else + { + return HAL_BUSY; + } +} + 800a7ae: 4628 mov r0, r5 + 800a7b0: bdf8 pop {r3, r4, r5, r6, r7, pc} + __HAL_LOCK(hhash); + 800a7b2: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800a7b6: 2b01 cmp r3, #1 + 800a7b8: d06f beq.n 800a89a + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800a7ba: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + __HAL_LOCK(hhash); + 800a7be: 2501 movs r5, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800a7c0: 42ab cmp r3, r5 + __HAL_LOCK(hhash); + 800a7c2: f884 5034 strb.w r5, [r4, #52] ; 0x34 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800a7c6: d148 bne.n 800a85a + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800a7c8: 4f36 ldr r7, [pc, #216] ; (800a8a4 ) + 800a7ca: 9b07 ldr r3, [sp, #28] + hhash->State = HAL_HASH_STATE_BUSY; + 800a7cc: f04f 0c02 mov.w ip, #2 + 800a7d0: f884 c035 strb.w ip, [r4, #53] ; 0x35 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800a7d4: 683d ldr r5, [r7, #0] + 800a7d6: f425 25a0 bic.w r5, r5, #327680 ; 0x50000 + 800a7da: f025 05c4 bic.w r5, r5, #196 ; 0xc4 + 800a7de: 431d orrs r5, r3 + 800a7e0: f045 0504 orr.w r5, r5, #4 + 800a7e4: 603d str r5, [r7, #0] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800a7e6: 68b8 ldr r0, [r7, #8] + 800a7e8: f002 0303 and.w r3, r2, #3 + 800a7ec: f020 001f bic.w r0, r0, #31 + 800a7f0: ea40 03c3 orr.w r3, r0, r3, lsl #3 + 800a7f4: 60bb str r3, [r7, #8] + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800a7f6: f884 c02d strb.w ip, [r4, #45] ; 0x2d + hhash->Status = HASH_WriteData(hhash, pInBuffer_tmp, Size_tmp); + 800a7fa: 4620 mov r0, r4 + 800a7fc: f7ff fd78 bl 800a2f0 + 800a800: 4605 mov r5, r0 + 800a802: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800a806: 2800 cmp r0, #0 + 800a808: d1d1 bne.n 800a7ae + if (hhash->State != HAL_HASH_STATE_SUSPENDED) + 800a80a: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800a80e: 2b08 cmp r3, #8 + 800a810: d03b beq.n 800a88a + __HAL_HASH_START_DIGEST(); + 800a812: 4f24 ldr r7, [pc, #144] ; (800a8a4 ) + 800a814: 68bb ldr r3, [r7, #8] + 800a816: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800a81a: 60bb str r3, [r7, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800a81c: 4602 mov r2, r0 + 800a81e: 9b06 ldr r3, [sp, #24] + 800a820: 2102 movs r1, #2 + 800a822: 4620 mov r0, r4 + 800a824: f7ff fe06 bl 800a434 + 800a828: 2800 cmp r0, #0 + 800a82a: d138 bne.n 800a89e + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800a82c: 683a ldr r2, [r7, #0] + 800a82e: 4b1e ldr r3, [pc, #120] ; (800a8a8 ) + 800a830: 421a tst r2, r3 + 800a832: d02e beq.n 800a892 + 800a834: 683a ldr r2, [r7, #0] + 800a836: 401a ands r2, r3 + 800a838: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800a83c: d02b beq.n 800a896 + 800a83e: 683a ldr r2, [r7, #0] + 800a840: 4393 bics r3, r2 + 800a842: bf0c ite eq + 800a844: 2120 moveq r1, #32 + 800a846: 2110 movne r1, #16 + 800a848: 4630 mov r0, r6 + 800a84a: f7ff fd91 bl 800a370 + hhash->State = HAL_HASH_STATE_READY; + 800a84e: 2301 movs r3, #1 + 800a850: f884 3035 strb.w r3, [r4, #53] ; 0x35 + hhash->Phase = HAL_HASH_PHASE_READY; + 800a854: f884 302d strb.w r3, [r4, #45] ; 0x2d + 800a858: e017 b.n 800a88a + else if (hhash->Phase == HAL_HASH_PHASE_PROCESS) + 800a85a: 2b02 cmp r3, #2 + 800a85c: d113 bne.n 800a886 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800a85e: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800a862: 2b08 cmp r3, #8 + __HAL_HASH_SET_NBVALIDBITS(Size); + 800a864: bf15 itete ne + 800a866: 4d0f ldrne r5, [pc, #60] ; (800a8a4 ) + Size_tmp = hhash->HashInCount; + 800a868: 6a22 ldreq r2, [r4, #32] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800a86a: 68a8 ldrne r0, [r5, #8] + pInBuffer_tmp = hhash->pHashInBuffPtr; + 800a86c: 68e1 ldreq r1, [r4, #12] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800a86e: bf1f itttt ne + 800a870: f002 0303 andne.w r3, r2, #3 + 800a874: f020 001f bicne.w r0, r0, #31 + 800a878: ea40 03c3 orrne.w r3, r0, r3, lsl #3 + 800a87c: 60ab strne r3, [r5, #8] + hhash->State = HAL_HASH_STATE_BUSY; + 800a87e: 2302 movs r3, #2 + 800a880: f884 3035 strb.w r3, [r4, #53] ; 0x35 + 800a884: e7b9 b.n 800a7fa + hhash->State = HAL_HASH_STATE_READY; + 800a886: f884 5035 strb.w r5, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800a88a: 2300 movs r3, #0 + 800a88c: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800a890: e78d b.n 800a7ae + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800a892: 2114 movs r1, #20 + 800a894: e7d8 b.n 800a848 + 800a896: 211c movs r1, #28 + 800a898: e7d6 b.n 800a848 + return HAL_BUSY; + 800a89a: 2502 movs r5, #2 + 800a89c: e787 b.n 800a7ae + return HAL_TIMEOUT; + 800a89e: 2503 movs r5, #3 + 800a8a0: e785 b.n 800a7ae + 800a8a2: bf00 nop + 800a8a4: 50060400 .word 0x50060400 + 800a8a8: 00040080 .word 0x00040080 + +0800a8ac : +{ + 800a8ac: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_MD5); + 800a8ae: 2480 movs r4, #128 ; 0x80 + 800a8b0: 9401 str r4, [sp, #4] + 800a8b2: 9c04 ldr r4, [sp, #16] + 800a8b4: 9400 str r4, [sp, #0] + 800a8b6: f7ff ff6b bl 800a790 +} + 800a8ba: b002 add sp, #8 + 800a8bc: bd10 pop {r4, pc} + +0800a8be : + 800a8be: f7ff bff5 b.w 800a8ac + +0800a8c2 : +{ + 800a8c2: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA1); + 800a8c4: 2400 movs r4, #0 + 800a8c6: 9401 str r4, [sp, #4] + 800a8c8: 9c04 ldr r4, [sp, #16] + 800a8ca: 9400 str r4, [sp, #0] + 800a8cc: f7ff ff60 bl 800a790 +} + 800a8d0: b002 add sp, #8 + 800a8d2: bd10 pop {r4, pc} + +0800a8d4 : + 800a8d4: f7ff bff5 b.w 800a8c2 + +0800a8d8 : + * @param Size length of the input buffer in bytes, must be a multiple of 4. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Accumulate(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint32_t Algorithm) +{ + 800a8d8: b570 push {r4, r5, r6, lr} + uint8_t *pInBuffer_tmp; /* input data address, input parameter of HASH_WriteData() */ + uint32_t Size_tmp; /* input data size (in bytes), input parameter of HASH_WriteData() */ + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a8da: f890 5035 ldrb.w r5, [r0, #53] ; 0x35 +{ + 800a8de: 4604 mov r4, r0 + + /* Make sure the input buffer size (in bytes) is a multiple of 4 */ + if ((Size % 4U) != 0U) + 800a8e0: 0790 lsls r0, r2, #30 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a8e2: b2ed uxtb r5, r5 + if ((Size % 4U) != 0U) + 800a8e4: d13e bne.n 800a964 + { + return HAL_ERROR; + } + + /* Initiate HASH processing in case of start or resumption */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800a8e6: 2d01 cmp r5, #1 + 800a8e8: d001 beq.n 800a8ee + 800a8ea: 2d08 cmp r5, #8 + 800a8ec: d13c bne.n 800a968 + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U)) + 800a8ee: b101 cbz r1, 800a8f2 + 800a8f0: b91a cbnz r2, 800a8fa + { + hhash->State = HAL_HASH_STATE_READY; + 800a8f2: 2001 movs r0, #1 + 800a8f4: f884 0035 strb.w r0, [r4, #53] ; 0x35 + { + return HAL_BUSY; + } + + +} + 800a8f8: bd70 pop {r4, r5, r6, pc} + __HAL_LOCK(hhash); + 800a8fa: f894 0034 ldrb.w r0, [r4, #52] ; 0x34 + 800a8fe: 2801 cmp r0, #1 + 800a900: d032 beq.n 800a968 + 800a902: 2001 movs r0, #1 + 800a904: f884 0034 strb.w r0, [r4, #52] ; 0x34 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800a908: f894 0035 ldrb.w r0, [r4, #53] ; 0x35 + 800a90c: 2808 cmp r0, #8 + 800a90e: f04f 0002 mov.w r0, #2 + hhash->State = HAL_HASH_STATE_BUSY; + 800a912: f884 0035 strb.w r0, [r4, #53] ; 0x35 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800a916: d113 bne.n 800a940 + pInBuffer_tmp = hhash->pHashInBuffPtr; /* pInBuffer_tmp is set to the input data address */ + 800a918: 68e1 ldr r1, [r4, #12] + Size_tmp = hhash->HashInCount; /* Size_tmp contains the input data size in bytes */ + 800a91a: 6a22 ldr r2, [r4, #32] + hhash->Status = HASH_WriteData(hhash, pInBuffer_tmp, Size_tmp); + 800a91c: 4620 mov r0, r4 + 800a91e: f7ff fce7 bl 800a2f0 + 800a922: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800a926: 2800 cmp r0, #0 + 800a928: d1e6 bne.n 800a8f8 + if (hhash->State != HAL_HASH_STATE_SUSPENDED) + 800a92a: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800a92e: 2b08 cmp r3, #8 + hhash->State = HAL_HASH_STATE_READY; + 800a930: bf1c itt ne + 800a932: 2301 movne r3, #1 + 800a934: f884 3035 strbne.w r3, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800a938: 2300 movs r3, #0 + 800a93a: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800a93e: e7db b.n 800a8f8 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800a940: f894 002d ldrb.w r0, [r4, #45] ; 0x2d + 800a944: 2801 cmp r0, #1 + 800a946: d109 bne.n 800a95c + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800a948: 4e08 ldr r6, [pc, #32] ; (800a96c ) + 800a94a: 6830 ldr r0, [r6, #0] + 800a94c: f420 20a0 bic.w r0, r0, #327680 ; 0x50000 + 800a950: f020 00c4 bic.w r0, r0, #196 ; 0xc4 + 800a954: 4318 orrs r0, r3 + 800a956: f040 0004 orr.w r0, r0, #4 + 800a95a: 6030 str r0, [r6, #0] + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800a95c: 2302 movs r3, #2 + 800a95e: f884 302d strb.w r3, [r4, #45] ; 0x2d + 800a962: e7db b.n 800a91c + return HAL_ERROR; + 800a964: 2001 movs r0, #1 + 800a966: e7c7 b.n 800a8f8 + return HAL_BUSY; + 800a968: 2002 movs r0, #2 + 800a96a: e7c5 b.n 800a8f8 + 800a96c: 50060400 .word 0x50060400 + +0800a970 : + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_MD5); + 800a970: 2380 movs r3, #128 ; 0x80 + 800a972: f7ff bfb1 b.w 800a8d8 + +0800a976 : + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA1); + 800a976: 2300 movs r3, #0 + 800a978: f7ff bfae b.w 800a8d8 + +0800a97c : + * @param Size length of the input buffer in bytes, must be a multiple of 4. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Accumulate_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint32_t Algorithm) +{ + 800a97c: b567 push {r0, r1, r2, r5, r6, lr} + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a97e: f890 5035 ldrb.w r5, [r0, #53] ; 0x35 + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800a982: 9101 str r1, [sp, #4] + uint32_t SizeVar = Size; + + /* Make sure the input buffer size (in bytes) is a multiple of 4 */ + if ((Size % 4U) != 0U) + 800a984: 0796 lsls r6, r2, #30 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800a986: b2ed uxtb r5, r5 + if ((Size % 4U) != 0U) + 800a988: d154 bne.n 800aa34 + { + return HAL_ERROR; + } + + /* Initiate HASH processing in case of start or resumption */ + if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800a98a: 2d01 cmp r5, #1 + 800a98c: d001 beq.n 800a992 + 800a98e: 2d08 cmp r5, #8 + 800a990: d152 bne.n 800aa38 + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U)) + 800a992: b101 cbz r1, 800a996 + 800a994: b92a cbnz r2, 800a9a2 + { + hhash->State = HAL_HASH_STATE_READY; + 800a996: 2301 movs r3, #1 + 800a998: f880 3035 strb.w r3, [r0, #53] ; 0x35 + else + { + return HAL_BUSY; + } + +} + 800a99c: 4618 mov r0, r3 + 800a99e: b003 add sp, #12 + 800a9a0: bd60 pop {r5, r6, pc} + __HAL_LOCK(hhash); + 800a9a2: f890 1034 ldrb.w r1, [r0, #52] ; 0x34 + 800a9a6: 2901 cmp r1, #1 + 800a9a8: d046 beq.n 800aa38 + 800a9aa: 2101 movs r1, #1 + 800a9ac: f880 1034 strb.w r1, [r0, #52] ; 0x34 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800a9b0: f890 1035 ldrb.w r1, [r0, #53] ; 0x35 + 800a9b4: 4d21 ldr r5, [pc, #132] ; (800aa3c ) + 800a9b6: 2908 cmp r1, #8 + 800a9b8: f04f 0102 mov.w r1, #2 + hhash->State = HAL_HASH_STATE_BUSY; + 800a9bc: f880 1035 strb.w r1, [r0, #53] ; 0x35 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800a9c0: d109 bne.n 800a9d6 + hhash->Accumulation = 1U; + 800a9c2: 2301 movs r3, #1 + 800a9c4: 6403 str r3, [r0, #64] ; 0x40 + __HAL_UNLOCK(hhash); + 800a9c6: 2300 movs r3, #0 + 800a9c8: f880 3034 strb.w r3, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI); + 800a9cc: 6a2a ldr r2, [r5, #32] + 800a9ce: f042 0201 orr.w r2, r2, #1 + 800a9d2: 622a str r2, [r5, #32] + return HAL_OK; + 800a9d4: e7e2 b.n 800a99c + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800a9d6: f890 602d ldrb.w r6, [r0, #45] ; 0x2d + 800a9da: 2e01 cmp r6, #1 + 800a9dc: d11b bne.n 800aa16 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800a9de: 6829 ldr r1, [r5, #0] + 800a9e0: f421 21a0 bic.w r1, r1, #327680 ; 0x50000 + 800a9e4: f021 01c4 bic.w r1, r1, #196 ; 0xc4 + 800a9e8: 4319 orrs r1, r3 + 800a9ea: f041 0104 orr.w r1, r1, #4 + 800a9ee: 6029 str r1, [r5, #0] + hhash->HashITCounter = 1; + 800a9f0: 6246 str r6, [r0, #36] ; 0x24 + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800a9f2: 2302 movs r3, #2 + 800a9f4: f880 302d strb.w r3, [r0, #45] ; 0x2d + while((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) && (SizeVar > 0U)) + 800a9f8: 6a6b ldr r3, [r5, #36] ; 0x24 + 800a9fa: 07d9 lsls r1, r3, #31 + 800a9fc: d400 bmi.n 800aa00 + 800a9fe: b96a cbnz r2, 800aa1c + if ((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) || (SizeVar == 0U)) + 800aa00: 6a6b ldr r3, [r5, #36] ; 0x24 + 800aa02: 07db lsls r3, r3, #31 + 800aa04: d500 bpl.n 800aa08 + 800aa06: b98a cbnz r2, 800aa2c + hhash->State = HAL_HASH_STATE_READY; + 800aa08: 2301 movs r3, #1 + 800aa0a: f880 3035 strb.w r3, [r0, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800aa0e: 2300 movs r3, #0 + 800aa10: f880 3034 strb.w r3, [r0, #52] ; 0x34 + return HAL_OK; + 800aa14: e7c2 b.n 800a99c + hhash->HashITCounter = 3; /* 'cruise-speed' reached during a previous buffer processing */ + 800aa16: 2303 movs r3, #3 + 800aa18: 6243 str r3, [r0, #36] ; 0x24 + 800aa1a: e7ea b.n 800a9f2 + HASH->DIN = *(uint32_t*)inputaddr; + 800aa1c: 9b01 ldr r3, [sp, #4] + 800aa1e: 681b ldr r3, [r3, #0] + 800aa20: 606b str r3, [r5, #4] + inputaddr+=4U; + 800aa22: 9b01 ldr r3, [sp, #4] + 800aa24: 3304 adds r3, #4 + 800aa26: 9301 str r3, [sp, #4] + SizeVar-=4U; + 800aa28: 3a04 subs r2, #4 + 800aa2a: e7e5 b.n 800a9f8 + hhash->HashInCount = SizeVar; /* Counter used to keep track of number of data + 800aa2c: 6202 str r2, [r0, #32] + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; /* Points at data which will be fed to the Peripheral at + 800aa2e: 9b01 ldr r3, [sp, #4] + 800aa30: 60c3 str r3, [r0, #12] + 800aa32: e7c6 b.n 800a9c2 + return HAL_ERROR; + 800aa34: 2301 movs r3, #1 + 800aa36: e7b1 b.n 800a99c + return HAL_BUSY; + 800aa38: 2302 movs r3, #2 + 800aa3a: e7af b.n 800a99c + 800aa3c: 50060400 .word 0x50060400 + +0800aa40 : + return HASH_Accumulate_IT(hhash, pInBuffer, Size,HASH_ALGOSELECTION_MD5); + 800aa40: 2380 movs r3, #128 ; 0x80 + 800aa42: f7ff bf9b b.w 800a97c + +0800aa46 : + return HASH_Accumulate_IT(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA1); + 800aa46: 2300 movs r3, #0 + 800aa48: f7ff bf98 b.w 800a97c + +0800aa4c : + * @param pOutBuffer pointer to the computed digest. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Algorithm) +{ + 800aa4c: b573 push {r0, r1, r4, r5, r6, lr} + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800aa4e: f890 4035 ldrb.w r4, [r0, #53] ; 0x35 + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800aa52: 9101 str r1, [sp, #4] + uint32_t polling_step = 0U; + uint32_t initialization_skipped = 0U; + uint32_t SizeVar = Size; + + /* If State is ready or suspended, start or resume IT-based HASH processing */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800aa54: 2c01 cmp r4, #1 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800aa56: b2e5 uxtb r5, r4 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800aa58: d001 beq.n 800aa5e + 800aa5a: 2d08 cmp r5, #8 + 800aa5c: d17f bne.n 800ab5e + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U) || (pOutBuffer == NULL)) + 800aa5e: b109 cbz r1, 800aa64 + 800aa60: b102 cbz r2, 800aa64 + 800aa62: b92b cbnz r3, 800aa70 + { + hhash->State = HAL_HASH_STATE_READY; + 800aa64: 2201 movs r2, #1 + 800aa66: f880 2035 strb.w r2, [r0, #53] ; 0x35 + else + { + return HAL_BUSY; + } + +} + 800aa6a: 4610 mov r0, r2 + 800aa6c: b002 add sp, #8 + 800aa6e: bd70 pop {r4, r5, r6, pc} + __HAL_LOCK(hhash); + 800aa70: f890 4034 ldrb.w r4, [r0, #52] ; 0x34 + 800aa74: 2c01 cmp r4, #1 + 800aa76: f04f 0402 mov.w r4, #2 + 800aa7a: d072 beq.n 800ab62 + hhash->State = HAL_HASH_STATE_BUSY; + 800aa7c: f880 4035 strb.w r4, [r0, #53] ; 0x35 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aa80: f890 402d ldrb.w r4, [r0, #45] ; 0x2d + __HAL_LOCK(hhash); + 800aa84: 2601 movs r6, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aa86: 42b4 cmp r4, r6 + __HAL_LOCK(hhash); + 800aa88: f880 6034 strb.w r6, [r0, #52] ; 0x34 + hhash->HashITCounter = 1; + 800aa8c: 4c36 ldr r4, [pc, #216] ; (800ab68 ) + 800aa8e: 6246 str r6, [r0, #36] ; 0x24 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aa90: d115 bne.n 800aabe + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800aa92: 6825 ldr r5, [r4, #0] + 800aa94: 9e06 ldr r6, [sp, #24] + 800aa96: f425 25a0 bic.w r5, r5, #327680 ; 0x50000 + 800aa9a: f025 05c4 bic.w r5, r5, #196 ; 0xc4 + 800aa9e: 4335 orrs r5, r6 + 800aaa0: f045 0504 orr.w r5, r5, #4 + 800aaa4: 6025 str r5, [r4, #0] + __HAL_HASH_SET_NBVALIDBITS(SizeVar); + 800aaa6: 68a6 ldr r6, [r4, #8] + 800aaa8: f002 0503 and.w r5, r2, #3 + 800aaac: f026 061f bic.w r6, r6, #31 + 800aab0: ea46 05c5 orr.w r5, r6, r5, lsl #3 + 800aab4: 60a5 str r5, [r4, #8] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800aab6: e9c0 1303 strd r1, r3, [r0, #12] + hhash->HashInCount = SizeVar; /* Counter used to keep track of number of data + 800aaba: 6202 str r2, [r0, #32] + uint32_t initialization_skipped = 0U; + 800aabc: 2600 movs r6, #0 + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800aabe: 2102 movs r1, #2 + 800aac0: f880 102d strb.w r1, [r0, #45] ; 0x2d + uint32_t polling_step = 0U; + 800aac4: 2100 movs r1, #0 + while((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) && (SizeVar > 3U)) + 800aac6: 6a65 ldr r5, [r4, #36] ; 0x24 + 800aac8: 07ed lsls r5, r5, #31 + 800aaca: d401 bmi.n 800aad0 + 800aacc: 2a03 cmp r2, #3 + 800aace: d80d bhi.n 800aaec + if (polling_step == 1U) + 800aad0: b349 cbz r1, 800ab26 + if (SizeVar == 0U) + 800aad2: b9a2 cbnz r2, 800aafe + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800aad4: 6103 str r3, [r0, #16] + __HAL_HASH_START_DIGEST(); + 800aad6: 68a3 ldr r3, [r4, #8] + 800aad8: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800aadc: 60a3 str r3, [r4, #8] + __HAL_UNLOCK(hhash); + 800aade: f880 2034 strb.w r2, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DCI); + 800aae2: 6a23 ldr r3, [r4, #32] + 800aae4: f043 0302 orr.w r3, r3, #2 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800aae8: 6223 str r3, [r4, #32] + return HAL_OK; + 800aaea: e7be b.n 800aa6a + HASH->DIN = *(uint32_t*)inputaddr; + 800aaec: 9901 ldr r1, [sp, #4] + 800aaee: 6809 ldr r1, [r1, #0] + 800aaf0: 6061 str r1, [r4, #4] + inputaddr+=4U; + 800aaf2: 9901 ldr r1, [sp, #4] + 800aaf4: 3104 adds r1, #4 + 800aaf6: 9101 str r1, [sp, #4] + SizeVar-=4U; + 800aaf8: 3a04 subs r2, #4 + polling_step = 1U; /* note that some words are entered before enabling the interrupt */ + 800aafa: 2101 movs r1, #1 + 800aafc: e7e3 b.n 800aac6 + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800aafe: 6a61 ldr r1, [r4, #36] ; 0x24 + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800ab00: f002 0503 and.w r5, r2, #3 + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800ab04: f011 0101 ands.w r1, r1, #1 + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800ab08: ea4f 05c5 mov.w r5, r5, lsl #3 + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800ab0c: d012 beq.n 800ab34 + hhash->HashInCount = SizeVar; + 800ab0e: 6202 str r2, [r0, #32] + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + 800ab10: 9a01 ldr r2, [sp, #4] + 800ab12: 60c2 str r2, [r0, #12] + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800ab14: 68a2 ldr r2, [r4, #8] + 800ab16: f022 021f bic.w r2, r2, #31 + 800ab1a: 432a orrs r2, r5 + 800ab1c: 60a2 str r2, [r4, #8] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800ab1e: 6103 str r3, [r0, #16] + if (initialization_skipped == 1U) + 800ab20: b10e cbz r6, 800ab26 + hhash->HashITCounter = 3; /* 'cruise-speed' reached during a previous buffer processing */ + 800ab22: 2303 movs r3, #3 + 800ab24: 6243 str r3, [r0, #36] ; 0x24 + __HAL_UNLOCK(hhash); + 800ab26: 2200 movs r2, #0 + 800ab28: f880 2034 strb.w r2, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800ab2c: 6a23 ldr r3, [r4, #32] + 800ab2e: f043 0303 orr.w r3, r3, #3 + 800ab32: e7d9 b.n 800aae8 + __HAL_HASH_SET_NBVALIDBITS(SizeVar); + 800ab34: 68a2 ldr r2, [r4, #8] + 800ab36: f022 021f bic.w r2, r2, #31 + 800ab3a: 432a orrs r2, r5 + 800ab3c: 60a2 str r2, [r4, #8] + HASH->DIN = *(uint32_t*)inputaddr; + 800ab3e: 9a01 ldr r2, [sp, #4] + 800ab40: 6812 ldr r2, [r2, #0] + 800ab42: 6062 str r2, [r4, #4] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800ab44: 6103 str r3, [r0, #16] + __HAL_HASH_START_DIGEST(); + 800ab46: 68a3 ldr r3, [r4, #8] + 800ab48: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800ab4c: 60a3 str r3, [r4, #8] + __HAL_UNLOCK(hhash); + 800ab4e: f880 1034 strb.w r1, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DCI); + 800ab52: 6a23 ldr r3, [r4, #32] + 800ab54: f043 0302 orr.w r3, r3, #2 + 800ab58: 6223 str r3, [r4, #32] + return HAL_OK; + 800ab5a: 460a mov r2, r1 + 800ab5c: e785 b.n 800aa6a + return HAL_BUSY; + 800ab5e: 2202 movs r2, #2 + 800ab60: e783 b.n 800aa6a + 800ab62: 4622 mov r2, r4 + 800ab64: e781 b.n 800aa6a + 800ab66: bf00 nop + 800ab68: 50060400 .word 0x50060400 + +0800ab6c : +{ + 800ab6c: b513 push {r0, r1, r4, lr} + return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_MD5); + 800ab6e: 2480 movs r4, #128 ; 0x80 + 800ab70: 9400 str r4, [sp, #0] + 800ab72: f7ff ff6b bl 800aa4c +} + 800ab76: b002 add sp, #8 + 800ab78: bd10 pop {r4, pc} + +0800ab7a : + 800ab7a: f7ff bff7 b.w 800ab6c + +0800ab7e : +{ + 800ab7e: b513 push {r0, r1, r4, lr} + return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_SHA1); + 800ab80: 2400 movs r4, #0 + 800ab82: 9400 str r4, [sp, #0] + 800ab84: f7ff ff62 bl 800aa4c +} + 800ab88: b002 add sp, #8 + 800ab8a: bd10 pop {r4, pc} + +0800ab8c : + 800ab8c: f7ff bff7 b.w 800ab7e + +0800ab90 : + * @param pOutBuffer pointer to the computed digest. + * @param Timeout Timeout value. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Finish(HASH_HandleTypeDef *hhash, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 800ab90: b570 push {r4, r5, r6, lr} + 800ab92: 4613 mov r3, r2 + + if(hhash->State == HAL_HASH_STATE_READY) + 800ab94: f890 2035 ldrb.w r2, [r0, #53] ; 0x35 + 800ab98: 2a01 cmp r2, #1 +{ + 800ab9a: 4605 mov r5, r0 + 800ab9c: 460e mov r6, r1 + if(hhash->State == HAL_HASH_STATE_READY) + 800ab9e: b2d4 uxtb r4, r2 + 800aba0: d12f bne.n 800ac02 + { + /* Check parameter */ + if (pOutBuffer == NULL) + 800aba2: b341 cbz r1, 800abf6 + { + return HAL_ERROR; + } + + /* Process Locked */ + __HAL_LOCK(hhash); + 800aba4: f890 2034 ldrb.w r2, [r0, #52] ; 0x34 + 800aba8: 2a01 cmp r2, #1 + 800abaa: f04f 0102 mov.w r1, #2 + 800abae: d028 beq.n 800ac02 + 800abb0: f880 4034 strb.w r4, [r0, #52] ; 0x34 + + /* Change the HASH state to busy */ + hhash->State = HAL_HASH_STATE_BUSY; + 800abb4: f880 1035 strb.w r1, [r0, #53] ; 0x35 + + /* Wait for DCIS flag to be set */ + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800abb8: 2200 movs r2, #0 + 800abba: f7ff fc3b bl 800a434 + 800abbe: 4604 mov r4, r0 + 800abc0: bb08 cbnz r0, 800ac06 + { + return HAL_TIMEOUT; + } + + /* Read the message digest */ + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800abc2: 4a12 ldr r2, [pc, #72] ; (800ac0c ) + 800abc4: 4b12 ldr r3, [pc, #72] ; (800ac10 ) + 800abc6: 6811 ldr r1, [r2, #0] + 800abc8: 4219 tst r1, r3 + 800abca: d016 beq.n 800abfa + 800abcc: 6811 ldr r1, [r2, #0] + 800abce: 4019 ands r1, r3 + 800abd0: f5b1 2f80 cmp.w r1, #262144 ; 0x40000 + 800abd4: d013 beq.n 800abfe + 800abd6: 6812 ldr r2, [r2, #0] + 800abd8: 4393 bics r3, r2 + 800abda: bf0c ite eq + 800abdc: 2120 moveq r1, #32 + 800abde: 2110 movne r1, #16 + 800abe0: 4630 mov r0, r6 + 800abe2: f7ff fbc5 bl 800a370 + + /* Change the HASH state to ready */ + hhash->State = HAL_HASH_STATE_READY; + 800abe6: 2301 movs r3, #1 + 800abe8: f885 3035 strb.w r3, [r5, #53] ; 0x35 + + /* Reset HASH state machine */ + hhash->Phase = HAL_HASH_PHASE_READY; + 800abec: f885 302d strb.w r3, [r5, #45] ; 0x2d + + /* Process UnLock */ + __HAL_UNLOCK(hhash); + 800abf0: 2300 movs r3, #0 + 800abf2: f885 3034 strb.w r3, [r5, #52] ; 0x34 + else + { + return HAL_BUSY; + } + +} + 800abf6: 4620 mov r0, r4 + 800abf8: bd70 pop {r4, r5, r6, pc} + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800abfa: 2114 movs r1, #20 + 800abfc: e7f0 b.n 800abe0 + 800abfe: 211c movs r1, #28 + 800ac00: e7ee b.n 800abe0 + return HAL_BUSY; + 800ac02: 2402 movs r4, #2 + 800ac04: e7f7 b.n 800abf6 + return HAL_TIMEOUT; + 800ac06: 2403 movs r4, #3 + 800ac08: e7f5 b.n 800abf6 + 800ac0a: bf00 nop + 800ac0c: 50060400 .word 0x50060400 + 800ac10: 00040080 .word 0x00040080 + +0800ac14 : + * @param Timeout Timeout value. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HMAC_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout, uint32_t Algorithm) +{ + 800ac14: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800ac18: 4604 mov r4, r0 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ac1a: f890 0035 ldrb.w r0, [r0, #53] ; 0x35 + + /* If State is ready or suspended, start or resume polling-based HASH processing */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800ac1e: 2801 cmp r0, #1 +{ + 800ac20: e9dd 7e06 ldrd r7, lr, [sp, #24] + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ac24: b2c5 uxtb r5, r0 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800ac26: d002 beq.n 800ac2e + 800ac28: 2d08 cmp r5, #8 + 800ac2a: f040 80df bne.w 800adec + { + /* Check input parameters */ + if ((pInBuffer == NULL) || /*(Size == 0U) ||*/ (hhash->Init.pKey == NULL) || (hhash->Init.KeySize == 0U) || (pOutBuffer == NULL)) + 800ac2e: b139 cbz r1, 800ac40 + 800ac30: f8d4 c008 ldr.w ip, [r4, #8] + 800ac34: f1bc 0f00 cmp.w ip, #0 + 800ac38: d002 beq.n 800ac40 + 800ac3a: 6865 ldr r5, [r4, #4] + 800ac3c: b105 cbz r5, 800ac40 + 800ac3e: b923 cbnz r3, 800ac4a + { + hhash->State = HAL_HASH_STATE_READY; + 800ac40: 2001 movs r0, #1 + 800ac42: f884 0035 strb.w r0, [r4, #53] ; 0x35 + return HMAC_Processing(hhash, Timeout); + + } + else + { + return HAL_BUSY; + 800ac46: 4605 mov r5, r0 + 800ac48: e05b b.n 800ad02 + __HAL_LOCK(hhash); + 800ac4a: f894 0034 ldrb.w r0, [r4, #52] ; 0x34 + 800ac4e: 2801 cmp r0, #1 + 800ac50: f04f 0002 mov.w r0, #2 + 800ac54: d0f7 beq.n 800ac46 + hhash->State = HAL_HASH_STATE_BUSY; + 800ac56: f884 0035 strb.w r0, [r4, #53] ; 0x35 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ac5a: f894 002d ldrb.w r0, [r4, #45] ; 0x2d + __HAL_LOCK(hhash); + 800ac5e: 2601 movs r6, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ac60: 42b0 cmp r0, r6 + __HAL_LOCK(hhash); + 800ac62: f884 6034 strb.w r6, [r4, #52] ; 0x34 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ac66: d118 bne.n 800ac9a + if(hhash->Init.KeySize > 64U) + 800ac68: 4e61 ldr r6, [pc, #388] ; (800adf0 ) + 800ac6a: f8df 818c ldr.w r8, [pc, #396] ; 800adf8 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_HMAC_KEYTYPE_LONGKEY | HASH_CR_INIT); + 800ac6e: 6830 ldr r0, [r6, #0] + 800ac70: ea00 0008 and.w r0, r0, r8 + 800ac74: ea40 000e orr.w r0, r0, lr + if(hhash->Init.KeySize > 64U) + 800ac78: 2d40 cmp r5, #64 ; 0x40 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_HMAC_KEYTYPE_LONGKEY | HASH_CR_INIT); + 800ac7a: bf88 it hi + 800ac7c: f440 3080 orrhi.w r0, r0, #65536 ; 0x10000 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_CR_INIT); + 800ac80: f040 0044 orr.w r0, r0, #68 ; 0x44 + 800ac84: 6030 str r0, [r6, #0] + hhash->pHashInBuffPtr = pInBuffer; /* Input data address, HMAC_Processing input parameter for Step 2 */ + 800ac86: e9c4 1303 strd r1, r3, [r4, #12] + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_1; + 800ac8a: 2003 movs r0, #3 + hhash->HashInCount = Size; /* Input data size, HMAC_Processing input parameter for Step 2 */ + 800ac8c: 6222 str r2, [r4, #32] + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_1; + 800ac8e: f884 002d strb.w r0, [r4, #45] ; 0x2d + hhash->HashBuffSize = Size; /* Store the input buffer size for the whole HMAC process */ + 800ac92: 61e2 str r2, [r4, #28] + hhash->pHashKeyBuffPtr = hhash->Init.pKey; /* Key address, HMAC_Processing input parameter for Step 1 and Step 3 */ + 800ac94: f8c4 c014 str.w ip, [r4, #20] + hhash->HashKeyCount = hhash->Init.KeySize; /* Key size, HMAC_Processing input parameter for Step 1 and Step 3 */ + 800ac98: 62a5 str r5, [r4, #40] ; 0x28 + if ((hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_1) && (hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_2) && (hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_3)) + 800ac9a: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + 800ac9e: 1eda subs r2, r3, #3 + 800aca0: 2a02 cmp r2, #2 + 800aca2: d906 bls.n 800acb2 + hhash->State = HAL_HASH_STATE_READY; + 800aca4: 2001 movs r0, #1 + __HAL_UNLOCK(hhash); + 800aca6: 2300 movs r3, #0 + hhash->State = HAL_HASH_STATE_READY; + 800aca8: f884 0035 strb.w r0, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800acac: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_ERROR; + 800acb0: e7c9 b.n 800ac46 + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) + 800acb2: 2b03 cmp r3, #3 + 800acb4: 4e4e ldr r6, [pc, #312] ; (800adf0 ) + 800acb6: d155 bne.n 800ad64 + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800acb8: 68b3 ldr r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800acba: 6961 ldr r1, [r4, #20] + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800acbc: f023 031f bic.w r3, r3, #31 + 800acc0: f005 0503 and.w r5, r5, #3 + 800acc4: ea43 05c5 orr.w r5, r3, r5, lsl #3 + 800acc8: 60b5 str r5, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800acca: 6aa2 ldr r2, [r4, #40] ; 0x28 + 800accc: 4620 mov r0, r4 + 800acce: f7ff fb0f bl 800a2f0 + 800acd2: 4605 mov r5, r0 + 800acd4: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800acd8: b998 cbnz r0, 800ad02 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800acda: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800acde: 2b08 cmp r3, #8 + 800ace0: d103 bne.n 800acea + __HAL_UNLOCK(hhash); + 800ace2: 2000 movs r0, #0 + 800ace4: f884 0034 strb.w r0, [r4, #52] ; 0x34 + return HAL_OK; + 800ace8: e7ad b.n 800ac46 + __HAL_HASH_START_DIGEST(); + 800acea: 68b3 ldr r3, [r6, #8] + 800acec: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800acf0: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, Timeout) != HAL_OK) + 800acf2: 2201 movs r2, #1 + 800acf4: 463b mov r3, r7 + 800acf6: 2108 movs r1, #8 + 800acf8: 4620 mov r0, r4 + 800acfa: f7ff fb9b bl 800a434 + 800acfe: b118 cbz r0, 800ad08 + return HAL_TIMEOUT; + 800ad00: 2503 movs r5, #3 + } +} + 800ad02: 4628 mov r0, r5 + 800ad04: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_2; + 800ad08: 2304 movs r3, #4 + 800ad0a: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); + 800ad0e: 68b3 ldr r3, [r6, #8] + 800ad10: 69e2 ldr r2, [r4, #28] + hhash->Status = HASH_WriteData(hhash, hhash->pHashInBuffPtr, hhash->HashInCount); + 800ad12: 68e1 ldr r1, [r4, #12] + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); + 800ad14: f002 0203 and.w r2, r2, #3 + 800ad18: f023 031f bic.w r3, r3, #31 + 800ad1c: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 800ad20: 60b3 str r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashInBuffPtr, hhash->HashInCount); + 800ad22: 6a22 ldr r2, [r4, #32] + 800ad24: 4620 mov r0, r4 + 800ad26: f7ff fae3 bl 800a2f0 + 800ad2a: 4605 mov r5, r0 + 800ad2c: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800ad30: 2800 cmp r0, #0 + 800ad32: d1e6 bne.n 800ad02 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800ad34: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800ad38: 2b08 cmp r3, #8 + 800ad3a: d0d2 beq.n 800ace2 + __HAL_HASH_START_DIGEST(); + 800ad3c: 68b3 ldr r3, [r6, #8] + 800ad3e: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800ad42: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, Timeout) != HAL_OK) + 800ad44: 2201 movs r2, #1 + 800ad46: 463b mov r3, r7 + 800ad48: 2108 movs r1, #8 + 800ad4a: 4620 mov r0, r4 + 800ad4c: f7ff fb72 bl 800a434 + 800ad50: 2800 cmp r0, #0 + 800ad52: d1d5 bne.n 800ad00 + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_3; + 800ad54: 2305 movs r3, #5 + 800ad56: f884 302d strb.w r3, [r4, #45] ; 0x2d + hhash->pHashKeyBuffPtr = hhash->Init.pKey; + 800ad5a: 68a3 ldr r3, [r4, #8] + 800ad5c: 6163 str r3, [r4, #20] + hhash->HashKeyCount = hhash->Init.KeySize; + 800ad5e: 6863 ldr r3, [r4, #4] + 800ad60: 62a3 str r3, [r4, #40] ; 0x28 + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_3) + 800ad62: e001 b.n 800ad68 + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2) + 800ad64: 2b04 cmp r3, #4 + 800ad66: d0d2 beq.n 800ad0e + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800ad68: 68b3 ldr r3, [r6, #8] + 800ad6a: 6862 ldr r2, [r4, #4] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800ad6c: 6961 ldr r1, [r4, #20] + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800ad6e: f002 0203 and.w r2, r2, #3 + 800ad72: f023 031f bic.w r3, r3, #31 + 800ad76: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 800ad7a: 60b3 str r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800ad7c: 6aa2 ldr r2, [r4, #40] ; 0x28 + 800ad7e: 4620 mov r0, r4 + 800ad80: f7ff fab6 bl 800a2f0 + 800ad84: 4605 mov r5, r0 + 800ad86: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800ad8a: 2800 cmp r0, #0 + 800ad8c: d1b9 bne.n 800ad02 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800ad8e: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800ad92: 2b08 cmp r3, #8 + 800ad94: d0a5 beq.n 800ace2 + __HAL_HASH_START_DIGEST(); + 800ad96: 68b3 ldr r3, [r6, #8] + 800ad98: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800ad9c: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800ad9e: 4602 mov r2, r0 + 800ada0: 463b mov r3, r7 + 800ada2: 2102 movs r1, #2 + 800ada4: 4620 mov r0, r4 + 800ada6: f7ff fb45 bl 800a434 + 800adaa: 4605 mov r5, r0 + 800adac: 2800 cmp r0, #0 + 800adae: d1a7 bne.n 800ad00 + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800adb0: 6832 ldr r2, [r6, #0] + 800adb2: 4b10 ldr r3, [pc, #64] ; (800adf4 ) + 800adb4: 6920 ldr r0, [r4, #16] + 800adb6: 421a tst r2, r3 + 800adb8: d014 beq.n 800ade4 + 800adba: 6832 ldr r2, [r6, #0] + 800adbc: 401a ands r2, r3 + 800adbe: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800adc2: d011 beq.n 800ade8 + 800adc4: 6832 ldr r2, [r6, #0] + 800adc6: 4393 bics r3, r2 + 800adc8: bf0c ite eq + 800adca: 2120 moveq r1, #32 + 800adcc: 2110 movne r1, #16 + 800adce: f7ff facf bl 800a370 + hhash->Phase = HAL_HASH_PHASE_READY; + 800add2: 2301 movs r3, #1 + 800add4: f884 302d strb.w r3, [r4, #45] ; 0x2d + hhash->State = HAL_HASH_STATE_READY; + 800add8: f884 3035 strb.w r3, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800addc: 2300 movs r3, #0 + 800adde: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800ade2: e78e b.n 800ad02 + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800ade4: 2114 movs r1, #20 + 800ade6: e7f2 b.n 800adce + 800ade8: 211c movs r1, #28 + 800adea: e7f0 b.n 800adce + return HAL_BUSY; + 800adec: 2502 movs r5, #2 + 800adee: e788 b.n 800ad02 + 800adf0: 50060400 .word 0x50060400 + 800adf4: 00040080 .word 0x00040080 + 800adf8: fffaff3b .word 0xfffaff3b + +0800adfc : + * @param Tickstart : Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef OSPI_WaitFlagStateUntilTimeout(OSPI_HandleTypeDef *hospi, uint32_t Flag, + FlagStatus State, uint32_t Tickstart, uint32_t Timeout) +{ + 800adfc: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800ae00: f8dd 8018 ldr.w r8, [sp, #24] + 800ae04: 4604 mov r4, r0 + 800ae06: 460e mov r6, r1 + 800ae08: 4615 mov r5, r2 + 800ae0a: 461f mov r7, r3 + /* Wait until flag is in expected state */ + while((__HAL_OSPI_GET_FLAG(hospi, Flag)) != State) + 800ae0c: 6822 ldr r2, [r4, #0] + 800ae0e: 6a13 ldr r3, [r2, #32] + 800ae10: 4233 tst r3, r6 + 800ae12: bf14 ite ne + 800ae14: 2301 movne r3, #1 + 800ae16: 2300 moveq r3, #0 + 800ae18: 42ab cmp r3, r5 + 800ae1a: d101 bne.n 800ae20 + + return HAL_ERROR; + } + } + } + return HAL_OK; + 800ae1c: 2000 movs r0, #0 + 800ae1e: e012 b.n 800ae46 + if (Timeout != HAL_MAX_DELAY) + 800ae20: f1b8 3fff cmp.w r8, #4294967295 ; 0xffffffff + 800ae24: d0f3 beq.n 800ae0e + if(((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800ae26: f7fc f961 bl 80070ec + 800ae2a: 1bc0 subs r0, r0, r7 + 800ae2c: 4540 cmp r0, r8 + 800ae2e: d802 bhi.n 800ae36 + 800ae30: f1b8 0f00 cmp.w r8, #0 + 800ae34: d1ea bne.n 800ae0c + hospi->State = HAL_OSPI_STATE_ERROR; + 800ae36: f44f 7300 mov.w r3, #512 ; 0x200 + 800ae3a: 6463 str r3, [r4, #68] ; 0x44 + hospi->ErrorCode |= HAL_OSPI_ERROR_TIMEOUT; + 800ae3c: 6ca3 ldr r3, [r4, #72] ; 0x48 + 800ae3e: f043 0301 orr.w r3, r3, #1 + 800ae42: 64a3 str r3, [r4, #72] ; 0x48 + 800ae44: 2001 movs r0, #1 +} + 800ae46: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +0800ae4a : +} + 800ae4a: 4770 bx lr + +0800ae4c : +{ + 800ae4c: b5f0 push {r4, r5, r6, r7, lr} + 800ae4e: b085 sub sp, #20 + 800ae50: 4604 mov r4, r0 + uint32_t tickstart = HAL_GetTick(); + 800ae52: f7fc f94b bl 80070ec + 800ae56: 4603 mov r3, r0 + if (hospi == NULL) + 800ae58: 2c00 cmp r4, #0 + 800ae5a: d05d beq.n 800af18 + hospi->ErrorCode = HAL_OSPI_ERROR_NONE; + 800ae5c: 2000 movs r0, #0 + 800ae5e: 64a0 str r0, [r4, #72] ; 0x48 + if (hospi->State == HAL_OSPI_STATE_RESET) + 800ae60: 6c66 ldr r6, [r4, #68] ; 0x44 + 800ae62: 2e00 cmp r6, #0 + 800ae64: d156 bne.n 800af14 + HAL_OSPI_MspInit(hospi); + 800ae66: 4620 mov r0, r4 + 800ae68: 9303 str r3, [sp, #12] + 800ae6a: f7ff ffee bl 800ae4a + MODIFY_REG(hospi->Instance->DCR1, + 800ae6e: 6b20 ldr r0, [r4, #48] ; 0x30 + 800ae70: 68e1 ldr r1, [r4, #12] + 800ae72: 6825 ldr r5, [r4, #0] + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800ae74: 9b03 ldr r3, [sp, #12] + MODIFY_REG(hospi->Instance->DCR1, + 800ae76: 68af ldr r7, [r5, #8] + 800ae78: 4301 orrs r1, r0 + 800ae7a: 69e0 ldr r0, [r4, #28] + 800ae7c: 4301 orrs r1, r0 + 800ae7e: 4827 ldr r0, [pc, #156] ; (800af1c ) + 800ae80: 4038 ands r0, r7 + 800ae82: 4301 orrs r1, r0 + 800ae84: 6920 ldr r0, [r4, #16] + 800ae86: 3801 subs r0, #1 + 800ae88: ea41 4100 orr.w r1, r1, r0, lsl #16 + 800ae8c: 6960 ldr r0, [r4, #20] + 800ae8e: 3801 subs r0, #1 + hospi->Timeout = Timeout; + 800ae90: f241 3288 movw r2, #5000 ; 0x1388 + MODIFY_REG(hospi->Instance->DCR1, + 800ae94: ea41 2100 orr.w r1, r1, r0, lsl #8 + hospi->Timeout = Timeout; + 800ae98: 64e2 str r2, [r4, #76] ; 0x4c + MODIFY_REG(hospi->Instance->DCR1, + 800ae9a: 60a9 str r1, [r5, #8] + hospi->Instance->DCR3 = (hospi->Init.ChipSelectBoundary << OCTOSPI_DCR3_CSBOUND_Pos); + 800ae9c: 6ae1 ldr r1, [r4, #44] ; 0x2c + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold - 1U) << OCTOSPI_CR_FTHRES_Pos)); + 800ae9e: 6860 ldr r0, [r4, #4] + hospi->Instance->DCR3 = (hospi->Init.ChipSelectBoundary << OCTOSPI_DCR3_CSBOUND_Pos); + 800aea0: 0409 lsls r1, r1, #16 + 800aea2: 6129 str r1, [r5, #16] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold - 1U) << OCTOSPI_CR_FTHRES_Pos)); + 800aea4: 6829 ldr r1, [r5, #0] + 800aea6: 3801 subs r0, #1 + 800aea8: f421 51f8 bic.w r1, r1, #7936 ; 0x1f00 + 800aeac: ea41 2100 orr.w r1, r1, r0, lsl #8 + 800aeb0: 6029 str r1, [r5, #0] + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800aeb2: 4620 mov r0, r4 + 800aeb4: 9200 str r2, [sp, #0] + 800aeb6: 2120 movs r1, #32 + 800aeb8: 4632 mov r2, r6 + 800aeba: f7ff ff9f bl 800adfc + if (status == HAL_OK) + 800aebe: bb48 cbnz r0, 800af14 + MODIFY_REG(hospi->Instance->DCR2, OCTOSPI_DCR2_PRESCALER, ((hospi->Init.ClockPrescaler - 1U) << OCTOSPI_DCR2_PRESCALER_Pos)); + 800aec0: 6823 ldr r3, [r4, #0] + 800aec2: 6a22 ldr r2, [r4, #32] + 800aec4: 68d9 ldr r1, [r3, #12] + 800aec6: 3a01 subs r2, #1 + 800aec8: f021 01ff bic.w r1, r1, #255 ; 0xff + 800aecc: 430a orrs r2, r1 + 800aece: 60da str r2, [r3, #12] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_DQM, hospi->Init.DualQuad); + 800aed0: 681a ldr r2, [r3, #0] + 800aed2: 68a1 ldr r1, [r4, #8] + 800aed4: f022 0240 bic.w r2, r2, #64 ; 0x40 + 800aed8: 430a orrs r2, r1 + 800aeda: 601a str r2, [r3, #0] + MODIFY_REG(hospi->Instance->TCR, (OCTOSPI_TCR_SSHIFT | OCTOSPI_TCR_DHQC), (hospi->Init.SampleShifting | hospi->Init.DelayHoldQuarterCycle)); + 800aedc: e9d4 2509 ldrd r2, r5, [r4, #36] ; 0x24 + 800aee0: f8d3 1108 ldr.w r1, [r3, #264] ; 0x108 + 800aee4: 432a orrs r2, r5 + 800aee6: f021 41a0 bic.w r1, r1, #1342177280 ; 0x50000000 + 800aeea: 430a orrs r2, r1 + 800aeec: f8c3 2108 str.w r2, [r3, #264] ; 0x108 + __HAL_OSPI_ENABLE(hospi); + 800aef0: 681a ldr r2, [r3, #0] + 800aef2: f042 0201 orr.w r2, r2, #1 + 800aef6: 601a str r2, [r3, #0] + if (hospi->Init.FreeRunningClock == HAL_OSPI_FREERUNCLK_ENABLE) + 800aef8: 69a2 ldr r2, [r4, #24] + 800aefa: 2a02 cmp r2, #2 + SET_BIT(hospi->Instance->DCR1, OCTOSPI_DCR1_FRCK); + 800aefc: bf02 ittt eq + 800aefe: 689a ldreq r2, [r3, #8] + 800af00: f042 0202 orreq.w r2, r2, #2 + 800af04: 609a streq r2, [r3, #8] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800af06: 68e3 ldr r3, [r4, #12] + 800af08: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + hospi->State = HAL_OSPI_STATE_HYPERBUS_INIT; + 800af0c: bf0c ite eq + 800af0e: 2301 moveq r3, #1 + hospi->State = HAL_OSPI_STATE_READY; + 800af10: 2302 movne r3, #2 + 800af12: 6463 str r3, [r4, #68] ; 0x44 +} + 800af14: b005 add sp, #20 + 800af16: bdf0 pop {r4, r5, r6, r7, pc} + status = HAL_ERROR; + 800af18: 2001 movs r0, #1 + 800af1a: e7fb b.n 800af14 + 800af1c: f8e0f8f4 .word 0xf8e0f8f4 + +0800af20 : +{ + 800af20: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800af24: 4605 mov r5, r0 + 800af26: b085 sub sp, #20 + 800af28: 460c mov r4, r1 + 800af2a: 9202 str r2, [sp, #8] + uint32_t tickstart = HAL_GetTick(); + 800af2c: f7fc f8de bl 80070ec + state = hospi->State; + 800af30: 6c6a ldr r2, [r5, #68] ; 0x44 + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800af32: 2a02 cmp r2, #2 + uint32_t tickstart = HAL_GetTick(); + 800af34: ee07 0a90 vmov s15, r0 + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800af38: d105 bne.n 800af46 + 800af3a: 68ea ldr r2, [r5, #12] + 800af3c: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + 800af40: d107 bne.n 800af52 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800af42: 2310 movs r3, #16 + 800af44: e109 b.n 800b15a + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800af46: 2a14 cmp r2, #20 + 800af48: f040 8084 bne.w 800b054 + ((state == HAL_OSPI_STATE_READ_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG)) || + 800af4c: 6822 ldr r2, [r4, #0] + 800af4e: 2a02 cmp r2, #2 + ((state == HAL_OSPI_STATE_WRITE_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG))) + 800af50: d1f7 bne.n 800af42 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, Timeout); + 800af52: 9a02 ldr r2, [sp, #8] + 800af54: 9200 str r2, [sp, #0] + 800af56: ee17 3a90 vmov r3, s15 + 800af5a: 2200 movs r2, #0 + 800af5c: 2120 movs r1, #32 + 800af5e: 4628 mov r0, r5 + 800af60: edcd 7a03 vstr s15, [sp, #12] + 800af64: f7ff ff4a bl 800adfc + if (status == HAL_OK) + 800af68: eddd 7a03 vldr s15, [sp, #12] + 800af6c: 2800 cmp r0, #0 + 800af6e: f040 80b9 bne.w 800b0e4 +{ + HAL_StatusTypeDef status = HAL_OK; + __IO uint32_t *ccr_reg, *tcr_reg, *ir_reg, *abr_reg; + + /* Re-initialize the value of the functional mode */ + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, 0U); + 800af72: 6829 ldr r1, [r5, #0] + hospi->ErrorCode = HAL_OSPI_ERROR_NONE; + 800af74: 64a8 str r0, [r5, #72] ; 0x48 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, 0U); + 800af76: 680a ldr r2, [r1, #0] + 800af78: f022 5240 bic.w r2, r2, #805306368 ; 0x30000000 + 800af7c: 600a str r2, [r1, #0] + + /* Configure the flash ID */ + if (hospi->Init.DualQuad == HAL_OSPI_DUALQUAD_DISABLE) + 800af7e: 68aa ldr r2, [r5, #8] + 800af80: b92a cbnz r2, 800af8e + { + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FSEL, cmd->FlashId); + 800af82: 680a ldr r2, [r1, #0] + 800af84: 6866 ldr r6, [r4, #4] + 800af86: f022 0280 bic.w r2, r2, #128 ; 0x80 + 800af8a: 4332 orrs r2, r6 + 800af8c: 600a str r2, [r1, #0] + } + + if (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG) + 800af8e: 6822 ldr r2, [r4, #0] + ir_reg = &(hospi->Instance->IR); + abr_reg = &(hospi->Instance->ABR); + } + + /* Configure the CCR register with DQS and SIOO modes */ + *ccr_reg = (cmd->DQSMode | cmd->SIOOMode); + 800af90: e9d4 6712 ldrd r6, r7, [r4, #72] ; 0x48 + if (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG) + 800af94: 2a02 cmp r2, #2 + ccr_reg = &(hospi->Instance->WCCR); + 800af96: bf0c ite eq + 800af98: f501 72c0 addeq.w r2, r1, #384 ; 0x180 + ccr_reg = &(hospi->Instance->CCR); + 800af9c: f501 7280 addne.w r2, r1, #256 ; 0x100 + *ccr_reg = (cmd->DQSMode | cmd->SIOOMode); + 800afa0: ea46 0607 orr.w r6, r6, r7 + 800afa4: 6016 str r6, [r2, #0] + + if (cmd->AlternateBytesMode != HAL_OSPI_ALTERNATE_BYTES_NONE) + 800afa6: 6ae6 ldr r6, [r4, #44] ; 0x2c + tcr_reg = &(hospi->Instance->WTCR); + 800afa8: bf03 ittte eq + 800afaa: f501 7cc4 addeq.w ip, r1, #392 ; 0x188 + ir_reg = &(hospi->Instance->WIR); + 800afae: f501 7ec8 addeq.w lr, r1, #400 ; 0x190 + abr_reg = &(hospi->Instance->WABR); + 800afb2: f501 78d0 addeq.w r8, r1, #416 ; 0x1a0 + tcr_reg = &(hospi->Instance->TCR); + 800afb6: f501 7c84 addne.w ip, r1, #264 ; 0x108 + ir_reg = &(hospi->Instance->IR); + 800afba: bf1c itt ne + 800afbc: f501 7e88 addne.w lr, r1, #272 ; 0x110 + abr_reg = &(hospi->Instance->ABR); + 800afc0: f501 7890 addne.w r8, r1, #288 ; 0x120 + if (cmd->AlternateBytesMode != HAL_OSPI_ALTERNATE_BYTES_NONE) + 800afc4: b16e cbz r6, 800afe2 + { + /* Configure the ABR register with alternate bytes value */ + *abr_reg = cmd->AlternateBytes; + 800afc6: 6aa6 ldr r6, [r4, #40] ; 0x28 + 800afc8: f8c8 6000 str.w r6, [r8] + + /* Configure the CCR register with alternate bytes communication parameters */ + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ABMODE | OCTOSPI_CCR_ABDTR | OCTOSPI_CCR_ABSIZE), + 800afcc: 6ae3 ldr r3, [r4, #44] ; 0x2c + 800afce: 6b67 ldr r7, [r4, #52] ; 0x34 + 800afd0: 6816 ldr r6, [r2, #0] + 800afd2: 431f orrs r7, r3 + 800afd4: 6b23 ldr r3, [r4, #48] ; 0x30 + 800afd6: f426 187c bic.w r8, r6, #4128768 ; 0x3f0000 + 800afda: 431f orrs r7, r3 + 800afdc: ea47 0708 orr.w r7, r7, r8 + 800afe0: 6017 str r7, [r2, #0] + (cmd->AlternateBytesMode | cmd->AlternateBytesDtrMode | cmd->AlternateBytesSize)); + } + + /* Configure the TCR register with the number of dummy cycles */ + MODIFY_REG((*tcr_reg), OCTOSPI_TCR_DCYC, cmd->DummyCycles); + 800afe2: f8dc 7000 ldr.w r7, [ip] + 800afe6: 6c66 ldr r6, [r4, #68] ; 0x44 + 800afe8: f027 071f bic.w r7, r7, #31 + 800afec: 433e orrs r6, r7 + 800afee: f8cc 6000 str.w r6, [ip] + + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800aff2: f8d4 c038 ldr.w ip, [r4, #56] ; 0x38 + 800aff6: f1bc 0f00 cmp.w ip, #0 + 800affa: d004 beq.n 800b006 + { + if (cmd->OperationType == HAL_OSPI_OPTYPE_COMMON_CFG) + 800affc: 6827 ldr r7, [r4, #0] + 800affe: b917 cbnz r7, 800b006 + { + /* Configure the DLR register with the number of data */ + hospi->Instance->DLR = (cmd->NbData - 1U); + 800b000: 6be7 ldr r7, [r4, #60] ; 0x3c + 800b002: 3f01 subs r7, #1 + 800b004: 640f str r7, [r1, #64] ; 0x40 + } + } + + if (cmd->InstructionMode != HAL_OSPI_INSTRUCTION_NONE) + 800b006: 68e6 ldr r6, [r4, #12] + { + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b008: 69e7 ldr r7, [r4, #28] + if (cmd->InstructionMode != HAL_OSPI_INSTRUCTION_NONE) + 800b00a: 2e00 cmp r6, #0 + 800b00c: f000 8082 beq.w 800b114 + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + { + /* ---- Command with instruction, address and data ---- */ + + /* Configure the CCR register with all communication parameters */ + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b010: e9d4 8904 ldrd r8, r9, [r4, #16] + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b014: 2f00 cmp r7, #0 + 800b016: d040 beq.n 800b09a + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b018: e9d4 ab08 ldrd sl, fp, [r4, #32] + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b01c: f1bc 0f00 cmp.w ip, #0 + 800b020: d01e beq.n 800b060 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b022: ea4c 0606 orr.w r6, ip, r6 + 800b026: 433e orrs r6, r7 + 800b028: ea46 0909 orr.w r9, r6, r9 + 800b02c: ea49 0808 orr.w r8, r9, r8 + 800b030: 6813 ldr r3, [r2, #0] + 800b032: 6c26 ldr r6, [r4, #64] ; 0x40 + 800b034: 4f52 ldr r7, [pc, #328] ; (800b180 ) + 800b036: ea48 0b0b orr.w fp, r8, fp + 800b03a: ea4b 0b0a orr.w fp, fp, sl + 800b03e: ea4b 0606 orr.w r6, fp, r6 + 800b042: 401f ands r7, r3 + 800b044: 433e orrs r6, r7 + + /* The DHQC bit is linked with DDTR bit which should be activated */ + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + (cmd->InstructionDtrMode == HAL_OSPI_INSTRUCTION_DTR_ENABLE)) + { + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b046: 6016 str r6, [r2, #0] + } + } + + /* Configure the IR register with the instruction value */ + *ir_reg = cmd->Instruction; + 800b048: 68a2 ldr r2, [r4, #8] + 800b04a: f8ce 2000 str.w r2, [lr] + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + (cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize)); + } + + /* Configure the AR register with the instruction value */ + hospi->Instance->AR = cmd->Address; + 800b04e: 69a2 ldr r2, [r4, #24] + 800b050: 648a str r2, [r1, #72] ; 0x48 + if (status == HAL_OK) + 800b052: e038 b.n 800b0c6 + ((state == HAL_OSPI_STATE_READ_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG)) || + 800b054: 2a24 cmp r2, #36 ; 0x24 + 800b056: f47f af74 bne.w 800af42 + ((state == HAL_OSPI_STATE_WRITE_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG))) + 800b05a: 6822 ldr r2, [r4, #0] + 800b05c: 2a01 cmp r2, #1 + 800b05e: e777 b.n 800af50 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b060: 433e orrs r6, r7 + 800b062: f8d2 c000 ldr.w ip, [r2] + 800b066: ea46 0609 orr.w r6, r6, r9 + 800b06a: ea46 0608 orr.w r6, r6, r8 + 800b06e: ea46 060b orr.w r6, r6, fp + 800b072: f42c 5c7c bic.w ip, ip, #16128 ; 0x3f00 + 800b076: ea46 060a orr.w r6, r6, sl + 800b07a: f02c 0c3f bic.w ip, ip, #63 ; 0x3f + 800b07e: ea46 060c orr.w r6, r6, ip + 800b082: 6016 str r6, [r2, #0] + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + 800b084: 6aae ldr r6, [r5, #40] ; 0x28 + 800b086: f1b6 5f80 cmp.w r6, #268435456 ; 0x10000000 + 800b08a: d1dd bne.n 800b048 + 800b08c: 6966 ldr r6, [r4, #20] + 800b08e: 2e08 cmp r6, #8 + 800b090: d1da bne.n 800b048 + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b092: 6816 ldr r6, [r2, #0] + 800b094: f046 6600 orr.w r6, r6, #134217728 ; 0x8000000 + 800b098: e7d5 b.n 800b046 + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b09a: f1bc 0f00 cmp.w ip, #0 + 800b09e: d024 beq.n 800b0ea + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b0a0: ea4c 0106 orr.w r1, ip, r6 + 800b0a4: 6817 ldr r7, [r2, #0] + 800b0a6: 6c26 ldr r6, [r4, #64] ; 0x40 + 800b0a8: ea41 0109 orr.w r1, r1, r9 + 800b0ac: ea41 0108 orr.w r1, r1, r8 + 800b0b0: f027 6a70 bic.w sl, r7, #251658240 ; 0xf000000 + 800b0b4: 4331 orrs r1, r6 + 800b0b6: f02a 0a3f bic.w sl, sl, #63 ; 0x3f + 800b0ba: ea41 010a orr.w r1, r1, sl + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b0be: 6011 str r1, [r2, #0] + *ir_reg = cmd->Instruction; + 800b0c0: 68a2 ldr r2, [r4, #8] + 800b0c2: f8ce 2000 str.w r2, [lr] + if (cmd->DataMode == HAL_OSPI_DATA_NONE) + 800b0c6: 6ba2 ldr r2, [r4, #56] ; 0x38 + 800b0c8: 2a00 cmp r2, #0 + 800b0ca: d149 bne.n 800b160 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_TC, SET, tickstart, Timeout); + 800b0cc: 9b02 ldr r3, [sp, #8] + 800b0ce: 9300 str r3, [sp, #0] + 800b0d0: 2201 movs r2, #1 + 800b0d2: ee17 3a90 vmov r3, s15 + 800b0d6: 2102 movs r1, #2 + 800b0d8: 4628 mov r0, r5 + 800b0da: f7ff fe8f bl 800adfc + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TC); + 800b0de: 682b ldr r3, [r5, #0] + 800b0e0: 2202 movs r2, #2 + 800b0e2: 625a str r2, [r3, #36] ; 0x24 +} + 800b0e4: b005 add sp, #20 + 800b0e6: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE), + 800b0ea: 6811 ldr r1, [r2, #0] + 800b0ec: ea46 0609 orr.w r6, r6, r9 + 800b0f0: ea46 0808 orr.w r8, r6, r8 + 800b0f4: f021 063f bic.w r6, r1, #63 ; 0x3f + 800b0f8: ea48 0606 orr.w r6, r8, r6 + 800b0fc: 6016 str r6, [r2, #0] + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + 800b0fe: 6aa9 ldr r1, [r5, #40] ; 0x28 + 800b100: f1b1 5f80 cmp.w r1, #268435456 ; 0x10000000 + 800b104: d1dc bne.n 800b0c0 + 800b106: 6961 ldr r1, [r4, #20] + 800b108: 2908 cmp r1, #8 + 800b10a: d1d9 bne.n 800b0c0 + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b10c: 6811 ldr r1, [r2, #0] + 800b10e: f041 6100 orr.w r1, r1, #134217728 ; 0x8000000 + 800b112: e7d4 b.n 800b0be + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b114: b307 cbz r7, 800b158 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b116: e9d4 9808 ldrd r9, r8, [r4, #32] + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b11a: f1bc 0f00 cmp.w ip, #0 + 800b11e: d011 beq.n 800b144 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE | + 800b120: f8d2 e000 ldr.w lr, [r2] + 800b124: 6c23 ldr r3, [r4, #64] ; 0x40 + 800b126: ea4c 0607 orr.w r6, ip, r7 + 800b12a: ea46 0608 orr.w r6, r6, r8 + 800b12e: ea46 0609 orr.w r6, r6, r9 + 800b132: f02e 6e70 bic.w lr, lr, #251658240 ; 0xf000000 + 800b136: 431e orrs r6, r3 + 800b138: f42e 5e7c bic.w lr, lr, #16128 ; 0x3f00 + 800b13c: ea46 060e orr.w r6, r6, lr + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + 800b140: 6016 str r6, [r2, #0] + 800b142: e784 b.n 800b04e + 800b144: f8d2 c000 ldr.w ip, [r2] + 800b148: ea48 0607 orr.w r6, r8, r7 + 800b14c: ea46 0609 orr.w r6, r6, r9 + 800b150: f42c 577c bic.w r7, ip, #16128 ; 0x3f00 + 800b154: 433e orrs r6, r7 + 800b156: e7f3 b.n 800b140 + } + else + { + /* ---- Invalid command configuration (no instruction, no address) ---- */ + status = HAL_ERROR; + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_PARAM; + 800b158: 2308 movs r3, #8 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b15a: 64ab str r3, [r5, #72] ; 0x48 + status = HAL_ERROR; + 800b15c: 2001 movs r0, #1 + 800b15e: e7c1 b.n 800b0e4 + if (cmd->OperationType == HAL_OSPI_OPTYPE_COMMON_CFG) + 800b160: 6823 ldr r3, [r4, #0] + 800b162: b90b cbnz r3, 800b168 + hospi->State = HAL_OSPI_STATE_CMD_CFG; + 800b164: 2304 movs r3, #4 + 800b166: e005 b.n 800b174 + else if (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG) + 800b168: 2b01 cmp r3, #1 + if (hospi->State == HAL_OSPI_STATE_WRITE_CMD_CFG) + 800b16a: 6c6b ldr r3, [r5, #68] ; 0x44 + else if (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG) + 800b16c: d104 bne.n 800b178 + if (hospi->State == HAL_OSPI_STATE_WRITE_CMD_CFG) + 800b16e: 2b24 cmp r3, #36 ; 0x24 + 800b170: d0f8 beq.n 800b164 + hospi->State = HAL_OSPI_STATE_READ_CMD_CFG; + 800b172: 2314 movs r3, #20 + hospi->State = HAL_OSPI_STATE_WRITE_CMD_CFG; + 800b174: 646b str r3, [r5, #68] ; 0x44 + 800b176: e7b5 b.n 800b0e4 + if (hospi->State == HAL_OSPI_STATE_READ_CMD_CFG) + 800b178: 2b14 cmp r3, #20 + 800b17a: d0f3 beq.n 800b164 + hospi->State = HAL_OSPI_STATE_WRITE_CMD_CFG; + 800b17c: 2324 movs r3, #36 ; 0x24 + 800b17e: e7f9 b.n 800b174 + 800b180: f0ffc0c0 .word 0xf0ffc0c0 + +0800b184 : +{ + 800b184: b5f0 push {r4, r5, r6, r7, lr} + 800b186: 4604 mov r4, r0 + 800b188: b085 sub sp, #20 + 800b18a: 460f mov r7, r1 + 800b18c: 4616 mov r6, r2 + uint32_t tickstart = HAL_GetTick(); + 800b18e: f7fb ffad bl 80070ec + __IO uint32_t *data_reg = &hospi->Instance->DR; + 800b192: 6825 ldr r5, [r4, #0] + uint32_t tickstart = HAL_GetTick(); + 800b194: 4603 mov r3, r0 + uint32_t addr_reg = hospi->Instance->AR; + 800b196: 6ca8 ldr r0, [r5, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b198: f8d5 c110 ldr.w ip, [r5, #272] ; 0x110 + if (pData == NULL) + 800b19c: b91f cbnz r7, 800b1a6 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_PARAM; + 800b19e: 2308 movs r3, #8 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b1a0: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b1a2: 2001 movs r0, #1 + 800b1a4: e034 b.n 800b210 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b1a6: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b1a8: 2a04 cmp r2, #4 + 800b1aa: d13b bne.n 800b224 + hospi->XferCount = READ_REG(hospi->Instance->DLR) + 1U; + 800b1ac: 6c2a ldr r2, [r5, #64] ; 0x40 + hospi->pBuffPtr = pData; + 800b1ae: 6367 str r7, [r4, #52] ; 0x34 + hospi->XferCount = READ_REG(hospi->Instance->DLR) + 1U; + 800b1b0: 3201 adds r2, #1 + 800b1b2: 63e2 str r2, [r4, #60] ; 0x3c + hospi->XferSize = hospi->XferCount; + 800b1b4: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b1b6: 63a2 str r2, [r4, #56] ; 0x38 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b1b8: 6829 ldr r1, [r5, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b1ba: 68e2 ldr r2, [r4, #12] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b1bc: f021 5140 bic.w r1, r1, #805306368 ; 0x30000000 + 800b1c0: f041 5180 orr.w r1, r1, #268435456 ; 0x10000000 + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b1c4: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b1c8: 6029 str r1, [r5, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b1ca: d123 bne.n 800b214 + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b1cc: 64a8 str r0, [r5, #72] ; 0x48 + status = OSPI_WaitFlagStateUntilTimeout(hospi, (HAL_OSPI_FLAG_FT | HAL_OSPI_FLAG_TC), SET, tickstart, Timeout); + 800b1ce: 9600 str r6, [sp, #0] + 800b1d0: 2201 movs r2, #1 + 800b1d2: 2106 movs r1, #6 + 800b1d4: 4620 mov r0, r4 + 800b1d6: 9303 str r3, [sp, #12] + 800b1d8: f7ff fe10 bl 800adfc + if (status != HAL_OK) + 800b1dc: b9c0 cbnz r0, 800b210 + *hospi->pBuffPtr = *((__IO uint8_t *)data_reg); + 800b1de: 6b62 ldr r2, [r4, #52] ; 0x34 + 800b1e0: f895 1050 ldrb.w r1, [r5, #80] ; 0x50 + 800b1e4: 7011 strb r1, [r2, #0] + hospi->pBuffPtr++; + 800b1e6: 6b62 ldr r2, [r4, #52] ; 0x34 + } while(hospi->XferCount > 0U); + 800b1e8: 9b03 ldr r3, [sp, #12] + hospi->pBuffPtr++; + 800b1ea: 3201 adds r2, #1 + 800b1ec: 6362 str r2, [r4, #52] ; 0x34 + hospi->XferCount--; + 800b1ee: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b1f0: 3a01 subs r2, #1 + 800b1f2: 63e2 str r2, [r4, #60] ; 0x3c + } while(hospi->XferCount > 0U); + 800b1f4: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b1f6: 2a00 cmp r2, #0 + 800b1f8: d1e9 bne.n 800b1ce + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_TC, SET, tickstart, Timeout); + 800b1fa: 9600 str r6, [sp, #0] + 800b1fc: 2201 movs r2, #1 + 800b1fe: 2102 movs r1, #2 + 800b200: 4620 mov r0, r4 + 800b202: f7ff fdfb bl 800adfc + if (status == HAL_OK) + 800b206: b918 cbnz r0, 800b210 + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TC); + 800b208: 6822 ldr r2, [r4, #0] + 800b20a: 2302 movs r3, #2 + 800b20c: 6253 str r3, [r2, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_READY; + 800b20e: 6463 str r3, [r4, #68] ; 0x44 +} + 800b210: b005 add sp, #20 + 800b212: bdf0 pop {r4, r5, r6, r7, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b214: f8d5 2100 ldr.w r2, [r5, #256] ; 0x100 + 800b218: f412 6fe0 tst.w r2, #1792 ; 0x700 + 800b21c: d1d6 bne.n 800b1cc + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b21e: f8c5 c110 str.w ip, [r5, #272] ; 0x110 + 800b222: e7d4 b.n 800b1ce + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b224: 2310 movs r3, #16 + 800b226: e7bb b.n 800b1a0 + +0800b228 : +{ + 800b228: e92d 41ff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, lr} + 800b22c: 4604 mov r4, r0 + 800b22e: 4616 mov r6, r2 + 800b230: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800b232: f7fb ff5b bl 80070ec + uint32_t addr_reg = hospi->Instance->AR; + 800b236: 6822 ldr r2, [r4, #0] + 800b238: 6c97 ldr r7, [r2, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b23a: f8d2 8110 ldr.w r8, [r2, #272] ; 0x110 + if ((hospi->State == HAL_OSPI_STATE_CMD_CFG) && (cfg->AutomaticStop == HAL_OSPI_AUTOMATIC_STOP_ENABLE)) + 800b23e: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b240: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b242: 4603 mov r3, r0 + if ((hospi->State == HAL_OSPI_STATE_CMD_CFG) && (cfg->AutomaticStop == HAL_OSPI_AUTOMATIC_STOP_ENABLE)) + 800b244: d13c bne.n 800b2c0 + 800b246: 68ea ldr r2, [r5, #12] + 800b248: f5b2 0f80 cmp.w r2, #4194304 ; 0x400000 + 800b24c: d138 bne.n 800b2c0 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, Timeout); + 800b24e: 9003 str r0, [sp, #12] + 800b250: 9600 str r6, [sp, #0] + 800b252: 2200 movs r2, #0 + 800b254: 2120 movs r1, #32 + 800b256: 4620 mov r0, r4 + 800b258: f7ff fdd0 bl 800adfc + if (status == HAL_OK) + 800b25c: bb28 cbnz r0, 800b2aa + WRITE_REG (hospi->Instance->PSMAR, cfg->Match); + 800b25e: 6822 ldr r2, [r4, #0] + 800b260: 6829 ldr r1, [r5, #0] + 800b262: f8c2 1088 str.w r1, [r2, #136] ; 0x88 + WRITE_REG (hospi->Instance->PSMKR, cfg->Mask); + 800b266: 6869 ldr r1, [r5, #4] + 800b268: f8c2 1080 str.w r1, [r2, #128] ; 0x80 + WRITE_REG (hospi->Instance->PIR, cfg->Interval); + 800b26c: 6929 ldr r1, [r5, #16] + 800b26e: f8c2 1090 str.w r1, [r2, #144] ; 0x90 + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b272: e9d5 1502 ldrd r1, r5, [r5, #8] + 800b276: 6810 ldr r0, [r2, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b278: 9b03 ldr r3, [sp, #12] + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b27a: 4329 orrs r1, r5 + 800b27c: f020 5043 bic.w r0, r0, #817889280 ; 0x30c00000 + 800b280: 4301 orrs r1, r0 + 800b282: f041 5100 orr.w r1, r1, #536870912 ; 0x20000000 + 800b286: 6011 str r1, [r2, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b288: 68e1 ldr r1, [r4, #12] + 800b28a: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 800b28e: d10f bne.n 800b2b0 + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b290: 6497 str r7, [r2, #72] ; 0x48 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_SM, SET, tickstart, Timeout); + 800b292: 9600 str r6, [sp, #0] + 800b294: 2201 movs r2, #1 + 800b296: 2108 movs r1, #8 + 800b298: 4620 mov r0, r4 + 800b29a: f7ff fdaf bl 800adfc + if (status == HAL_OK) + 800b29e: b920 cbnz r0, 800b2aa + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_SM); + 800b2a0: 6823 ldr r3, [r4, #0] + 800b2a2: 2208 movs r2, #8 + 800b2a4: 625a str r2, [r3, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_READY; + 800b2a6: 2302 movs r3, #2 + 800b2a8: 6463 str r3, [r4, #68] ; 0x44 +} + 800b2aa: b004 add sp, #16 + 800b2ac: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b2b0: f8d2 1100 ldr.w r1, [r2, #256] ; 0x100 + 800b2b4: f411 6fe0 tst.w r1, #1792 ; 0x700 + 800b2b8: d1ea bne.n 800b290 + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b2ba: f8c2 8110 str.w r8, [r2, #272] ; 0x110 + 800b2be: e7e8 b.n 800b292 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b2c0: 2310 movs r3, #16 + 800b2c2: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b2c4: 2001 movs r0, #1 + 800b2c6: e7f0 b.n 800b2aa + +0800b2c8 : +{ + 800b2c8: b5f7 push {r0, r1, r2, r4, r5, r6, r7, lr} + 800b2ca: 4604 mov r4, r0 + 800b2cc: 460f mov r7, r1 + uint32_t tickstart = HAL_GetTick(); + 800b2ce: f7fb ff0d bl 80070ec + uint32_t addr_reg = hospi->Instance->AR; + 800b2d2: 6822 ldr r2, [r4, #0] + 800b2d4: 6c95 ldr r5, [r2, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b2d6: f8d2 6110 ldr.w r6, [r2, #272] ; 0x110 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b2da: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b2dc: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b2de: 4603 mov r3, r0 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b2e0: d132 bne.n 800b348 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b2e2: 6ce2 ldr r2, [r4, #76] ; 0x4c + 800b2e4: 9200 str r2, [sp, #0] + 800b2e6: 2120 movs r1, #32 + 800b2e8: 2200 movs r2, #0 + 800b2ea: 4620 mov r0, r4 + 800b2ec: f7ff fd86 bl 800adfc + if (status == HAL_OK) + 800b2f0: bb00 cbnz r0, 800b334 + WRITE_REG (hospi->Instance->PSMAR, cfg->Match); + 800b2f2: 6823 ldr r3, [r4, #0] + 800b2f4: 683a ldr r2, [r7, #0] + 800b2f6: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + WRITE_REG (hospi->Instance->PSMKR, cfg->Mask); + 800b2fa: 687a ldr r2, [r7, #4] + 800b2fc: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + WRITE_REG (hospi->Instance->PIR, cfg->Interval); + 800b300: 693a ldr r2, [r7, #16] + 800b302: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b306: e9d7 2702 ldrd r2, r7, [r7, #8] + 800b30a: 6819 ldr r1, [r3, #0] + 800b30c: 433a orrs r2, r7 + 800b30e: f021 5143 bic.w r1, r1, #817889280 ; 0x30c00000 + 800b312: 430a orrs r2, r1 + 800b314: f042 5200 orr.w r2, r2, #536870912 ; 0x20000000 + 800b318: 601a str r2, [r3, #0] + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TE | HAL_OSPI_FLAG_SM); + 800b31a: 2209 movs r2, #9 + 800b31c: 625a str r2, [r3, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_BUSY_AUTO_POLLING; + 800b31e: 2248 movs r2, #72 ; 0x48 + 800b320: 6462 str r2, [r4, #68] ; 0x44 + __HAL_OSPI_ENABLE_IT(hospi, HAL_OSPI_IT_SM | HAL_OSPI_IT_TE); + 800b322: 681a ldr r2, [r3, #0] + 800b324: f442 2210 orr.w r2, r2, #589824 ; 0x90000 + 800b328: 601a str r2, [r3, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b32a: 68e2 ldr r2, [r4, #12] + 800b32c: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + 800b330: d102 bne.n 800b338 + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b332: 649d str r5, [r3, #72] ; 0x48 +} + 800b334: b003 add sp, #12 + 800b336: bdf0 pop {r4, r5, r6, r7, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b338: f8d3 2100 ldr.w r2, [r3, #256] ; 0x100 + 800b33c: f412 6fe0 tst.w r2, #1792 ; 0x700 + 800b340: d1f7 bne.n 800b332 + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b342: f8c3 6110 str.w r6, [r3, #272] ; 0x110 + 800b346: e7f5 b.n 800b334 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b348: 2310 movs r3, #16 + 800b34a: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b34c: 2001 movs r0, #1 + 800b34e: e7f1 b.n 800b334 + +0800b350 : +{ + 800b350: b573 push {r0, r1, r4, r5, r6, lr} + 800b352: 4604 mov r4, r0 + 800b354: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800b356: f7fb fec9 bl 80070ec + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b35a: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b35c: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b35e: 4603 mov r3, r0 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b360: d121 bne.n 800b3a6 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b362: 6ce2 ldr r2, [r4, #76] ; 0x4c + 800b364: 9200 str r2, [sp, #0] + 800b366: 2120 movs r1, #32 + 800b368: 2200 movs r2, #0 + 800b36a: 4620 mov r0, r4 + 800b36c: f7ff fd46 bl 800adfc + if (status == HAL_OK) + 800b370: b9b8 cbnz r0, 800b3a2 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b372: 682e ldr r6, [r5, #0] + WRITE_REG(hospi->Instance->LPTR, cfg->TimeOutPeriod); + 800b374: 6822 ldr r2, [r4, #0] + hospi->State = HAL_OSPI_STATE_BUSY_MEM_MAPPED; + 800b376: 2388 movs r3, #136 ; 0x88 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b378: 2e08 cmp r6, #8 + hospi->State = HAL_OSPI_STATE_BUSY_MEM_MAPPED; + 800b37a: 6463 str r3, [r4, #68] ; 0x44 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b37c: d108 bne.n 800b390 + WRITE_REG(hospi->Instance->LPTR, cfg->TimeOutPeriod); + 800b37e: 686b ldr r3, [r5, #4] + 800b380: f8c2 3130 str.w r3, [r2, #304] ; 0x130 + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TO); + 800b384: 2310 movs r3, #16 + 800b386: 6253 str r3, [r2, #36] ; 0x24 + __HAL_OSPI_ENABLE_IT(hospi, HAL_OSPI_IT_TO); + 800b388: 6811 ldr r1, [r2, #0] + 800b38a: f441 1180 orr.w r1, r1, #1048576 ; 0x100000 + 800b38e: 6011 str r1, [r2, #0] + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_TCEN | OCTOSPI_CR_FMODE), + 800b390: 6813 ldr r3, [r2, #0] + 800b392: f023 5340 bic.w r3, r3, #805306368 ; 0x30000000 + 800b396: f023 0308 bic.w r3, r3, #8 + 800b39a: 4333 orrs r3, r6 + 800b39c: f043 5340 orr.w r3, r3, #805306368 ; 0x30000000 + 800b3a0: 6013 str r3, [r2, #0] +} + 800b3a2: b002 add sp, #8 + 800b3a4: bd70 pop {r4, r5, r6, pc} + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b3a6: 2310 movs r3, #16 + 800b3a8: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b3aa: 2001 movs r0, #1 + 800b3ac: e7f9 b.n 800b3a2 + +0800b3ae : + if ((hospi->State & OSPI_BUSY_STATE_MASK) == 0U) + 800b3ae: 6c43 ldr r3, [r0, #68] ; 0x44 + 800b3b0: f013 0308 ands.w r3, r3, #8 + 800b3b4: d10a bne.n 800b3cc + hospi->Init.FifoThreshold = Threshold; + 800b3b6: 6041 str r1, [r0, #4] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold-1U) << OCTOSPI_CR_FTHRES_Pos)); + 800b3b8: 6800 ldr r0, [r0, #0] + 800b3ba: 6802 ldr r2, [r0, #0] + 800b3bc: 3901 subs r1, #1 + 800b3be: f422 52f8 bic.w r2, r2, #7936 ; 0x1f00 + 800b3c2: ea42 2101 orr.w r1, r2, r1, lsl #8 + 800b3c6: 6001 str r1, [r0, #0] + HAL_StatusTypeDef status = HAL_OK; + 800b3c8: 4618 mov r0, r3 + 800b3ca: 4770 bx lr + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b3cc: 2310 movs r3, #16 + 800b3ce: 6483 str r3, [r0, #72] ; 0x48 + status = HAL_ERROR; + 800b3d0: 2001 movs r0, #1 +} + 800b3d2: 4770 bx lr + +0800b3d4 : + return ((READ_BIT(hospi->Instance->CR, OCTOSPI_CR_FTHRES) >> OCTOSPI_CR_FTHRES_Pos) + 1U); + 800b3d4: 6803 ldr r3, [r0, #0] + 800b3d6: 6818 ldr r0, [r3, #0] + 800b3d8: f3c0 2004 ubfx r0, r0, #8, #5 +} + 800b3dc: 3001 adds r0, #1 + 800b3de: 4770 bx lr + +0800b3e0 : + hospi->Timeout = Timeout; + 800b3e0: 64c1 str r1, [r0, #76] ; 0x4c +} + 800b3e2: 2000 movs r0, #0 + 800b3e4: 4770 bx lr + +0800b3e6 : + return hospi->ErrorCode; + 800b3e6: 6c80 ldr r0, [r0, #72] ; 0x48 +} + 800b3e8: 4770 bx lr + +0800b3ea : + return hospi->State; + 800b3ea: 6c40 ldr r0, [r0, #68] ; 0x44 +} + 800b3ec: 4770 bx lr + ... + +0800b3f0 : +{ + 800b3f0: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + if (hospi->Instance == OCTOSPI1) + 800b3f4: 6802 ldr r2, [r0, #0] + other_instance = 0U; + 800b3f6: 4bbf ldr r3, [pc, #764] ; (800b6f4 ) + * @retval HAL status + */ +static HAL_StatusTypeDef OSPIM_GetConfig(uint8_t instance_nb, OSPIM_CfgTypeDef *cfg) +{ + HAL_StatusTypeDef status = HAL_OK; + uint32_t reg, value = 0U; + 800b3f8: f8df 8304 ldr.w r8, [pc, #772] ; 800b700 + other_instance = 0U; + 800b3fc: 429a cmp r2, r3 +{ + 800b3fe: b08b sub sp, #44 ; 0x2c + } + + /* Get the information about the instance */ + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + { + reg = OCTOSPIM->PCR[index]; + 800b400: 4bbd ldr r3, [pc, #756] ; (800b6f8 ) + other_instance = 0U; + 800b402: bf0b itete eq + 800b404: f04f 0a01 moveq.w sl, #1 + 800b408: f04f 0a00 movne.w sl, #0 + 800b40c: 2000 moveq r0, #0 + 800b40e: 2001 movne r0, #1 + for (index = 0U; index < OSPI_NB_INSTANCE; index++) + 800b410: 466a mov r2, sp + instance = 1U; + 800b412: 2501 movs r5, #1 + cfg->ClkPort = 0U; + 800b414: 2700 movs r7, #0 + cfg->DQSPort = 0U; + 800b416: e9c2 7700 strd r7, r7, [r2] + cfg->IOLowPort = 0U; + 800b41a: e9c2 7702 strd r7, r7, [r2, #8] + uint32_t reg, value = 0U; + 800b41e: 2d02 cmp r5, #2 + 800b420: bf0c ite eq + 800b422: 46c4 moveq ip, r8 + 800b424: f04f 0c00 movne.w ip, #0 + cfg->IOHighPort = 0U; + 800b428: 6117 str r7, [r2, #16] + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + 800b42a: f04f 0e00 mov.w lr, #0 + reg = OCTOSPIM->PCR[index]; + 800b42e: eb03 048e add.w r4, r3, lr, lsl #2 + { + /* The clock is enabled on this port */ + if ((reg & OCTOSPIM_PCR_CLKSRC) == (value & OCTOSPIM_PCR_CLKSRC)) + { + /* The clock correspond to the instance passed as parameter */ + cfg->ClkPort = index+1U; + 800b432: f10e 0601 add.w r6, lr, #1 + reg = OCTOSPIM->PCR[index]; + 800b436: 6864 ldr r4, [r4, #4] + if ((reg & OCTOSPIM_PCR_CLKEN) != 0U) + 800b438: f014 0f01 tst.w r4, #1 + 800b43c: d005 beq.n 800b44a + if ((reg & OCTOSPIM_PCR_CLKSRC) == (value & OCTOSPIM_PCR_CLKSRC)) + 800b43e: ea84 0e0c eor.w lr, r4, ip + 800b442: f01e 0f02 tst.w lr, #2 + cfg->ClkPort = index+1U; + 800b446: bf08 it eq + 800b448: 6016 streq r6, [r2, #0] + } + } + + if ((reg & OCTOSPIM_PCR_DQSEN) != 0U) + 800b44a: f014 0f10 tst.w r4, #16 + 800b44e: d005 beq.n 800b45c + { + /* The DQS is enabled on this port */ + if ((reg & OCTOSPIM_PCR_DQSSRC) == (value & OCTOSPIM_PCR_DQSSRC)) + 800b450: ea84 0e0c eor.w lr, r4, ip + 800b454: f01e 0f20 tst.w lr, #32 + { + /* The DQS correspond to the instance passed as parameter */ + cfg->DQSPort = index+1U; + 800b458: bf08 it eq + 800b45a: 6056 streq r6, [r2, #4] + } + } + + if ((reg & OCTOSPIM_PCR_NCSEN) != 0U) + 800b45c: f414 7f80 tst.w r4, #256 ; 0x100 + 800b460: d005 beq.n 800b46e + { + /* The nCS is enabled on this port */ + if ((reg & OCTOSPIM_PCR_NCSSRC) == (value & OCTOSPIM_PCR_NCSSRC)) + 800b462: ea84 0e0c eor.w lr, r4, ip + 800b466: f41e 7f00 tst.w lr, #512 ; 0x200 + { + /* The nCS correspond to the instance passed as parameter */ + cfg->NCSPort = index+1U; + 800b46a: bf08 it eq + 800b46c: 6096 streq r6, [r2, #8] + } + } + + if ((reg & OCTOSPIM_PCR_IOLEN) != 0U) + 800b46e: f414 3f80 tst.w r4, #65536 ; 0x10000 + 800b472: d00d beq.n 800b490 + { + /* The IO Low is enabled on this port */ + if ((reg & OCTOSPIM_PCR_IOLSRC_1) == (value & OCTOSPIM_PCR_IOLSRC_1)) + 800b474: ea84 0e0c eor.w lr, r4, ip + 800b478: f41e 2f80 tst.w lr, #262144 ; 0x40000 + 800b47c: d108 bne.n 800b490 + { + /* The IO Low correspond to the instance passed as parameter */ + if ((reg & OCTOSPIM_PCR_IOLSRC_0) == 0U) + 800b47e: f414 3f00 tst.w r4, #131072 ; 0x20000 + { + cfg->IOLowPort = (OCTOSPIM_PCR_IOLEN | (index+1U)); + 800b482: bf0c ite eq + 800b484: f446 3e80 orreq.w lr, r6, #65536 ; 0x10000 + } + else + { + cfg->IOLowPort = (OCTOSPIM_PCR_IOHEN | (index+1U)); + 800b488: f046 7e80 orrne.w lr, r6, #16777216 ; 0x1000000 + 800b48c: f8c2 e00c str.w lr, [r2, #12] + } + } + } + + if ((reg & OCTOSPIM_PCR_IOHEN) != 0U) + 800b490: f014 7f80 tst.w r4, #16777216 ; 0x1000000 + 800b494: d00b beq.n 800b4ae + { + /* The IO High is enabled on this port */ + if ((reg & OCTOSPIM_PCR_IOHSRC_1) == (value & OCTOSPIM_PCR_IOHSRC_1)) + 800b496: ea84 0e0c eor.w lr, r4, ip + 800b49a: f01e 6f80 tst.w lr, #67108864 ; 0x4000000 + 800b49e: d106 bne.n 800b4ae + { + /* The IO High correspond to the instance passed as parameter */ + if ((reg & OCTOSPIM_PCR_IOHSRC_0) == 0U) + 800b4a0: 01a4 lsls r4, r4, #6 + { + cfg->IOHighPort = (OCTOSPIM_PCR_IOLEN | (index+1U)); + 800b4a2: bf54 ite pl + 800b4a4: f446 3480 orrpl.w r4, r6, #65536 ; 0x10000 + } + else + { + cfg->IOHighPort = (OCTOSPIM_PCR_IOHEN | (index+1U)); + 800b4a8: f046 7480 orrmi.w r4, r6, #16777216 ; 0x1000000 + 800b4ac: 6114 str r4, [r2, #16] + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + 800b4ae: 2e02 cmp r6, #2 + 800b4b0: f04f 0e01 mov.w lr, #1 + 800b4b4: d1bb bne.n 800b42e + for (index = 0U; index < OSPI_NB_INSTANCE; index++) + 800b4b6: 2d02 cmp r5, #2 + 800b4b8: f102 0214 add.w r2, r2, #20 + 800b4bc: f040 8117 bne.w 800b6ee + if ((OCTOSPI1->CR & OCTOSPI_CR_EN) != 0U) + 800b4c0: 4c8c ldr r4, [pc, #560] ; (800b6f4 ) + 800b4c2: 6825 ldr r5, [r4, #0] + 800b4c4: ea15 050e ands.w r5, r5, lr + CLEAR_BIT(OCTOSPI1->CR, OCTOSPI_CR_EN); + 800b4c8: bf1e ittt ne + 800b4ca: 6822 ldrne r2, [r4, #0] + 800b4cc: f022 0201 bicne.w r2, r2, #1 + 800b4d0: 6022 strne r2, [r4, #0] + if ((OCTOSPI2->CR & OCTOSPI_CR_EN) != 0U) + 800b4d2: 4a8a ldr r2, [pc, #552] ; (800b6fc ) + 800b4d4: 6814 ldr r4, [r2, #0] + ospi_enabled |= 0x1U; + 800b4d6: bf18 it ne + 800b4d8: 4675 movne r5, lr + if ((OCTOSPI2->CR & OCTOSPI_CR_EN) != 0U) + 800b4da: 07e6 lsls r6, r4, #31 + CLEAR_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b4dc: bf42 ittt mi + 800b4de: 6814 ldrmi r4, [r2, #0] + 800b4e0: f024 0401 bicmi.w r4, r4, #1 + 800b4e4: 6014 strmi r4, [r2, #0] + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b4e6: aa0a add r2, sp, #40 ; 0x28 + 800b4e8: f04f 0414 mov.w r4, #20 + 800b4ec: fb04 2400 mla r4, r4, r0, r2 + ospi_enabled |= 0x2U; + 800b4f0: bf48 it mi + 800b4f2: f045 0b02 orrmi.w fp, r5, #2 + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b4f6: f854 2c20 ldr.w r2, [r4, #-32] + 800b4fa: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 800b4fe: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b502: bf58 it pl + 800b504: 46ab movpl fp, r5 + 800b506: 6856 ldr r6, [r2, #4] + 800b508: f426 7680 bic.w r6, r6, #256 ; 0x100 + 800b50c: 6056 str r6, [r2, #4] + if (IOM_cfg[instance].ClkPort != 0U) + 800b50e: f854 2c28 ldr.w r2, [r4, #-40] + 800b512: b382 cbz r2, 800b576 + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].ClkPort-1U)], OCTOSPIM_PCR_CLKEN); + 800b514: 3a01 subs r2, #1 + 800b516: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b51a: 6856 ldr r6, [r2, #4] + 800b51c: f026 0601 bic.w r6, r6, #1 + 800b520: 6056 str r6, [r2, #4] + if (IOM_cfg[instance].DQSPort != 0U) + 800b522: f854 2c24 ldr.w r2, [r4, #-36] + 800b526: b132 cbz r2, 800b536 + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].DQSPort-1U)], OCTOSPIM_PCR_DQSEN); + 800b528: 3a01 subs r2, #1 + 800b52a: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b52e: 6854 ldr r4, [r2, #4] + 800b530: f024 0410 bic.w r4, r4, #16 + 800b534: 6054 str r4, [r2, #4] + if (IOM_cfg[instance].IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b536: 2214 movs r2, #20 + 800b538: ac0a add r4, sp, #40 ; 0x28 + 800b53a: fb02 4200 mla r2, r2, r0, r4 + 800b53e: f852 2c1c ldr.w r2, [r2, #-28] + 800b542: b142 cbz r2, 800b556 + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[instance].IOLowPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOLEN); + 800b544: 3a01 subs r2, #1 + 800b546: f002 0201 and.w r2, r2, #1 + 800b54a: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b54e: 6854 ldr r4, [r2, #4] + 800b550: f424 3480 bic.w r4, r4, #65536 ; 0x10000 + 800b554: 6054 str r4, [r2, #4] + if (IOM_cfg[instance].IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800b556: 2214 movs r2, #20 + 800b558: ac0a add r4, sp, #40 ; 0x28 + 800b55a: fb02 4200 mla r2, r2, r0, r4 + 800b55e: f852 2c18 ldr.w r2, [r2, #-24] + 800b562: b142 cbz r2, 800b576 + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[instance].IOHighPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOHEN); + 800b564: 3a01 subs r2, #1 + 800b566: f002 0201 and.w r2, r2, #1 + 800b56a: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b56e: 6854 ldr r4, [r2, #4] + 800b570: f024 7480 bic.w r4, r4, #16777216 ; 0x1000000 + 800b574: 6054 str r4, [r2, #4] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b576: aa0a add r2, sp, #40 ; 0x28 + 800b578: f04f 0914 mov.w r9, #20 + 800b57c: fb09 290a mla r9, r9, sl, r2 + 800b580: f8d1 c000 ldr.w ip, [r1] + 800b584: f859 8c28 ldr.w r8, [r9, #-40] + (cfg->IOHighPort == IOM_cfg[other_instance].IOHighPort)) + 800b588: f859 4c18 ldr.w r4, [r9, #-24] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b58c: 45c4 cmp ip, r8 + (cfg->NCSPort == IOM_cfg[other_instance].NCSPort) || (cfg->IOLowPort == IOM_cfg[other_instance].IOLowPort) || + 800b58e: e9d1 6e01 ldrd r6, lr, [r1, #4] + (cfg->IOHighPort == IOM_cfg[other_instance].IOHighPort)) + 800b592: e9d1 2103 ldrd r2, r1, [r1, #12] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b596: d00d beq.n 800b5b4 + 800b598: f859 7c24 ldr.w r7, [r9, #-36] + 800b59c: 42b7 cmp r7, r6 + 800b59e: d009 beq.n 800b5b4 + 800b5a0: f859 7c20 ldr.w r7, [r9, #-32] + 800b5a4: 45be cmp lr, r7 + 800b5a6: d005 beq.n 800b5b4 + (cfg->NCSPort == IOM_cfg[other_instance].NCSPort) || (cfg->IOLowPort == IOM_cfg[other_instance].IOLowPort) || + 800b5a8: f859 7c1c ldr.w r7, [r9, #-28] + 800b5ac: 4297 cmp r7, r2 + 800b5ae: d001 beq.n 800b5b4 + 800b5b0: 428c cmp r4, r1 + 800b5b2: d142 bne.n 800b63a + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].ClkPort-1U)], OCTOSPIM_PCR_CLKEN); + 800b5b4: f108 38ff add.w r8, r8, #4294967295 ; 0xffffffff + 800b5b8: eb03 0888 add.w r8, r3, r8, lsl #2 + 800b5bc: f8d8 7004 ldr.w r7, [r8, #4] + 800b5c0: f027 0701 bic.w r7, r7, #1 + 800b5c4: f8c8 7004 str.w r7, [r8, #4] + if (IOM_cfg[other_instance].DQSPort != 0U) + 800b5c8: 2714 movs r7, #20 + 800b5ca: f10d 0828 add.w r8, sp, #40 ; 0x28 + 800b5ce: fb07 870a mla r7, r7, sl, r8 + 800b5d2: f857 7c24 ldr.w r7, [r7, #-36] + 800b5d6: b147 cbz r7, 800b5ea + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].DQSPort-1U)], OCTOSPIM_PCR_DQSEN); + 800b5d8: 3f01 subs r7, #1 + 800b5da: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b5de: f8d7 8004 ldr.w r8, [r7, #4] + 800b5e2: f028 0810 bic.w r8, r8, #16 + 800b5e6: f8c7 8004 str.w r8, [r7, #4] + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b5ea: 2714 movs r7, #20 + 800b5ec: f10d 0828 add.w r8, sp, #40 ; 0x28 + 800b5f0: fb07 8a0a mla sl, r7, sl, r8 + 800b5f4: f85a 7c20 ldr.w r7, [sl, #-32] + 800b5f8: 3f01 subs r7, #1 + 800b5fa: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b5fe: f8d7 8004 ldr.w r8, [r7, #4] + 800b602: f428 7880 bic.w r8, r8, #256 ; 0x100 + 800b606: f8c7 8004 str.w r8, [r7, #4] + if (IOM_cfg[other_instance].IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b60a: f85a 7c1c ldr.w r7, [sl, #-28] + 800b60e: b157 cbz r7, 800b626 + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[other_instance].IOLowPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOLEN); + 800b610: 3f01 subs r7, #1 + 800b612: f007 0701 and.w r7, r7, #1 + 800b616: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b61a: f8d7 8004 ldr.w r8, [r7, #4] + 800b61e: f428 3880 bic.w r8, r8, #65536 ; 0x10000 + 800b622: f8c7 8004 str.w r8, [r7, #4] + if (IOM_cfg[other_instance].IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800b626: b144 cbz r4, 800b63a + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[other_instance].IOHighPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOHEN); + 800b628: 3c01 subs r4, #1 + 800b62a: f004 0401 and.w r4, r4, #1 + 800b62e: eb03 0484 add.w r4, r3, r4, lsl #2 + 800b632: 6867 ldr r7, [r4, #4] + 800b634: f027 7780 bic.w r7, r7, #16777216 ; 0x1000000 + 800b638: 6067 str r7, [r4, #4] + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b63a: f10e 3eff add.w lr, lr, #4294967295 ; 0xffffffff + 800b63e: eb03 0e8e add.w lr, r3, lr, lsl #2 + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b642: f10c 3cff add.w ip, ip, #4294967295 ; 0xffffffff + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b646: f8de 4004 ldr.w r4, [lr, #4] + 800b64a: f424 7440 bic.w r4, r4, #768 ; 0x300 + 800b64e: ea44 2440 orr.w r4, r4, r0, lsl #9 + 800b652: f444 7480 orr.w r4, r4, #256 ; 0x100 + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b656: eb03 0c8c add.w ip, r3, ip, lsl #2 + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b65a: f8ce 4004 str.w r4, [lr, #4] + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b65e: f8dc 4004 ldr.w r4, [ip, #4] + 800b662: f024 0403 bic.w r4, r4, #3 + 800b666: ea44 0440 orr.w r4, r4, r0, lsl #1 + 800b66a: f044 0401 orr.w r4, r4, #1 + 800b66e: f8cc 4004 str.w r4, [ip, #4] + if (cfg->DQSPort != 0U) + 800b672: b156 cbz r6, 800b68a + MODIFY_REG(OCTOSPIM->PCR[(cfg->DQSPort-1U)], (OCTOSPIM_PCR_DQSEN | OCTOSPIM_PCR_DQSSRC), (OCTOSPIM_PCR_DQSEN | (instance << OCTOSPIM_PCR_DQSSRC_Pos))); + 800b674: 3e01 subs r6, #1 + 800b676: eb03 0686 add.w r6, r3, r6, lsl #2 + 800b67a: 6874 ldr r4, [r6, #4] + 800b67c: f024 0430 bic.w r4, r4, #48 ; 0x30 + 800b680: ea44 1440 orr.w r4, r4, r0, lsl #5 + 800b684: f044 0410 orr.w r4, r4, #16 + 800b688: 6074 str r4, [r6, #4] + if ((cfg->IOLowPort & OCTOSPIM_PCR_IOLEN) != 0U) + 800b68a: 03d4 lsls r4, r2, #15 + 800b68c: d53a bpl.n 800b704 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOLEN | OCTOSPIM_PCR_IOLSRC), + 800b68e: 3a01 subs r2, #1 + 800b690: f002 0201 and.w r2, r2, #1 + 800b694: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b698: 6854 ldr r4, [r2, #4] + 800b69a: f424 24e0 bic.w r4, r4, #458752 ; 0x70000 + 800b69e: ea44 4480 orr.w r4, r4, r0, lsl #18 + 800b6a2: f444 3480 orr.w r4, r4, #65536 ; 0x10000 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b6a6: 6054 str r4, [r2, #4] + if ((cfg->IOHighPort & OCTOSPIM_PCR_IOLEN) != 0U) + 800b6a8: 03ca lsls r2, r1, #15 + 800b6aa: d53a bpl.n 800b722 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOLEN | OCTOSPIM_PCR_IOLSRC), + 800b6ac: 3901 subs r1, #1 + 800b6ae: f001 0101 and.w r1, r1, #1 + 800b6b2: eb03 0381 add.w r3, r3, r1, lsl #2 + 800b6b6: 685a ldr r2, [r3, #4] + 800b6b8: f422 22e0 bic.w r2, r2, #458752 ; 0x70000 + 800b6bc: ea42 4080 orr.w r0, r2, r0, lsl #18 + 800b6c0: f440 3040 orr.w r0, r0, #196608 ; 0x30000 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b6c4: 6058 str r0, [r3, #4] + if ((ospi_enabled & 0x1U) != 0U) + 800b6c6: b125 cbz r5, 800b6d2 + SET_BIT(OCTOSPI1->CR, OCTOSPI_CR_EN); + 800b6c8: 4a0a ldr r2, [pc, #40] ; (800b6f4 ) + 800b6ca: 6813 ldr r3, [r2, #0] + 800b6cc: f043 0301 orr.w r3, r3, #1 + 800b6d0: 6013 str r3, [r2, #0] + if ((ospi_enabled & 0x2U) != 0U) + 800b6d2: f01b 0f02 tst.w fp, #2 + SET_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b6d6: bf1c itt ne + 800b6d8: 4a08 ldrne r2, [pc, #32] ; (800b6fc ) + 800b6da: 6813 ldrne r3, [r2, #0] +} + 800b6dc: f04f 0000 mov.w r0, #0 + SET_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b6e0: bf1c itt ne + 800b6e2: f043 0301 orrne.w r3, r3, #1 + 800b6e6: 6013 strne r3, [r2, #0] +} + 800b6e8: b00b add sp, #44 ; 0x2c + 800b6ea: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + 800b6ee: 4635 mov r5, r6 + 800b6f0: e691 b.n 800b416 + 800b6f2: bf00 nop + 800b6f4: a0001000 .word 0xa0001000 + 800b6f8: 50061c00 .word 0x50061c00 + 800b6fc: a0001400 .word 0xa0001400 + 800b700: 04040222 .word 0x04040222 + else if (cfg->IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b704: 2a00 cmp r2, #0 + 800b706: d0cf beq.n 800b6a8 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b708: 3a01 subs r2, #1 + 800b70a: f002 0201 and.w r2, r2, #1 + 800b70e: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b712: 6854 ldr r4, [r2, #4] + 800b714: f024 64e0 bic.w r4, r4, #117440512 ; 0x7000000 + 800b718: ea44 6480 orr.w r4, r4, r0, lsl #26 + 800b71c: f044 7480 orr.w r4, r4, #16777216 ; 0x1000000 + 800b720: e7c1 b.n 800b6a6 + else if (cfg->IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800b722: 2900 cmp r1, #0 + 800b724: d0cf beq.n 800b6c6 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b726: 3901 subs r1, #1 + 800b728: f001 0101 and.w r1, r1, #1 + 800b72c: eb03 0381 add.w r3, r3, r1, lsl #2 + 800b730: 685a ldr r2, [r3, #4] + 800b732: f022 62e0 bic.w r2, r2, #117440512 ; 0x7000000 + 800b736: ea42 6080 orr.w r0, r2, r0, lsl #26 + 800b73a: f040 7040 orr.w r0, r0, #50331648 ; 0x3000000 + 800b73e: e7c1 b.n 800b6c4 + +0800b740 : + * @arg @ref I2C_GENERATE_START_WRITE Generate Restart for write request. + * @retval None + */ +static void I2C_TransferConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t Size, uint32_t Mode, + uint32_t Request) +{ + 800b740: b530 push {r4, r5, lr} + 800b742: 9d03 ldr r5, [sp, #12] + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + assert_param(IS_TRANSFER_MODE(Mode)); + assert_param(IS_TRANSFER_REQUEST(Request)); + + /* update CR2 register */ + MODIFY_REG(hi2c->Instance->CR2, + 800b744: 6804 ldr r4, [r0, #0] + 800b746: ea45 4202 orr.w r2, r5, r2, lsl #16 + 800b74a: 431a orrs r2, r3 + 800b74c: 4b05 ldr r3, [pc, #20] ; (800b764 ) + 800b74e: 6860 ldr r0, [r4, #4] + 800b750: f3c1 0109 ubfx r1, r1, #0, #10 + 800b754: ea43 5355 orr.w r3, r3, r5, lsr #21 + 800b758: 430a orrs r2, r1 + 800b75a: ea20 0003 bic.w r0, r0, r3 + 800b75e: 4302 orrs r2, r0 + 800b760: 6062 str r2, [r4, #4] + ((I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | \ + (I2C_CR2_RD_WRN & (uint32_t)(Request >> (31U - I2C_CR2_RD_WRN_Pos))) | I2C_CR2_START | I2C_CR2_STOP)), \ + (uint32_t)(((uint32_t)DevAddress & I2C_CR2_SADD) | + (((uint32_t)Size << I2C_CR2_NBYTES_Pos) & I2C_CR2_NBYTES) | (uint32_t)Mode | (uint32_t)Request)); +} + 800b762: bd30 pop {r4, r5, pc} + 800b764: 03ff63ff .word 0x03ff63ff + +0800b768 : + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800b768: 6803 ldr r3, [r0, #0] +{ + 800b76a: b570 push {r4, r5, r6, lr} + 800b76c: 4604 mov r4, r0 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800b76e: 6998 ldr r0, [r3, #24] + 800b770: f010 0010 ands.w r0, r0, #16 +{ + 800b774: 460d mov r5, r1 + 800b776: 4616 mov r6, r2 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800b778: d116 bne.n 800b7a8 +} + 800b77a: bd70 pop {r4, r5, r6, pc} + if (Timeout != HAL_MAX_DELAY) + 800b77c: 1c6a adds r2, r5, #1 + 800b77e: d014 beq.n 800b7aa + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800b780: f7fb fcb4 bl 80070ec + 800b784: 1b80 subs r0, r0, r6 + 800b786: 4285 cmp r5, r0 + 800b788: d300 bcc.n 800b78c + 800b78a: b96d cbnz r5, 800b7a8 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800b78c: 6c63 ldr r3, [r4, #68] ; 0x44 + 800b78e: f043 0320 orr.w r3, r3, #32 + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + 800b792: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800b794: 2320 movs r3, #32 + 800b796: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800b79a: 2300 movs r3, #0 + 800b79c: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800b7a0: f884 3040 strb.w r3, [r4, #64] ; 0x40 + return HAL_ERROR; + 800b7a4: 2001 movs r0, #1 + 800b7a6: e7e8 b.n 800b77a + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET) + 800b7a8: 6823 ldr r3, [r4, #0] + 800b7aa: 699a ldr r2, [r3, #24] + 800b7ac: 0690 lsls r0, r2, #26 + 800b7ae: d5e5 bpl.n 800b77c + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); + 800b7b0: 2210 movs r2, #16 + 800b7b2: 61da str r2, [r3, #28] + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800b7b4: 2220 movs r2, #32 + 800b7b6: 61da str r2, [r3, #28] + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) != RESET) + 800b7b8: 699a ldr r2, [r3, #24] + 800b7ba: 0791 lsls r1, r2, #30 + hi2c->Instance->TXDR = 0x00U; + 800b7bc: bf44 itt mi + 800b7be: 2200 movmi r2, #0 + 800b7c0: 629a strmi r2, [r3, #40] ; 0x28 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXE) == RESET) + 800b7c2: 699a ldr r2, [r3, #24] + 800b7c4: 07d2 lsls r2, r2, #31 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_TXE); + 800b7c6: bf5e ittt pl + 800b7c8: 699a ldrpl r2, [r3, #24] + 800b7ca: f042 0201 orrpl.w r2, r2, #1 + 800b7ce: 619a strpl r2, [r3, #24] + I2C_RESET_CR2(hi2c); + 800b7d0: 685a ldr r2, [r3, #4] + 800b7d2: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800b7d6: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800b7da: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800b7de: f022 0201 bic.w r2, r2, #1 + 800b7e2: 605a str r2, [r3, #4] + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + 800b7e4: 6c63 ldr r3, [r4, #68] ; 0x44 + 800b7e6: f043 0304 orr.w r3, r3, #4 + 800b7ea: e7d2 b.n 800b792 + +0800b7ec : +{ + 800b7ec: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800b7f0: 9f06 ldr r7, [sp, #24] + 800b7f2: 4604 mov r4, r0 + 800b7f4: 4688 mov r8, r1 + 800b7f6: 4616 mov r6, r2 + 800b7f8: 461d mov r5, r3 + while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status) + 800b7fa: 6822 ldr r2, [r4, #0] + 800b7fc: 6993 ldr r3, [r2, #24] + 800b7fe: ea38 0303 bics.w r3, r8, r3 + 800b802: bf0c ite eq + 800b804: 2301 moveq r3, #1 + 800b806: 2300 movne r3, #0 + 800b808: 42b3 cmp r3, r6 + 800b80a: d001 beq.n 800b810 + return HAL_OK; + 800b80c: 2000 movs r0, #0 + 800b80e: e015 b.n 800b83c + if (Timeout != HAL_MAX_DELAY) + 800b810: 1c6b adds r3, r5, #1 + 800b812: d0f3 beq.n 800b7fc + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800b814: f7fb fc6a bl 80070ec + 800b818: 1bc0 subs r0, r0, r7 + 800b81a: 42a8 cmp r0, r5 + 800b81c: d801 bhi.n 800b822 + 800b81e: 2d00 cmp r5, #0 + 800b820: d1eb bne.n 800b7fa + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800b822: 6c63 ldr r3, [r4, #68] ; 0x44 + 800b824: f043 0320 orr.w r3, r3, #32 + 800b828: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800b82a: 2320 movs r3, #32 + 800b82c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800b830: 2300 movs r3, #0 + 800b832: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800b836: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800b83a: 2001 movs r0, #1 +} + 800b83c: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +0800b840 : +{ + 800b840: b570 push {r4, r5, r6, lr} + 800b842: 4604 mov r4, r0 + 800b844: 460d mov r5, r1 + 800b846: 4616 mov r6, r2 + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET) + 800b848: 6823 ldr r3, [r4, #0] + 800b84a: 699b ldr r3, [r3, #24] + 800b84c: 069b lsls r3, r3, #26 + 800b84e: d501 bpl.n 800b854 + return HAL_OK; + 800b850: 2000 movs r0, #0 +} + 800b852: bd70 pop {r4, r5, r6, pc} + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800b854: 4632 mov r2, r6 + 800b856: 4629 mov r1, r5 + 800b858: 4620 mov r0, r4 + 800b85a: f7ff ff85 bl 800b768 + 800b85e: b990 cbnz r0, 800b886 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800b860: f7fb fc44 bl 80070ec + 800b864: 1b80 subs r0, r0, r6 + 800b866: 42a8 cmp r0, r5 + 800b868: d801 bhi.n 800b86e + 800b86a: 2d00 cmp r5, #0 + 800b86c: d1ec bne.n 800b848 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800b86e: 6c63 ldr r3, [r4, #68] ; 0x44 + 800b870: f043 0320 orr.w r3, r3, #32 + 800b874: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800b876: 2320 movs r3, #32 + 800b878: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800b87c: 2300 movs r3, #0 + 800b87e: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800b882: f884 3040 strb.w r3, [r4, #64] ; 0x40 + return HAL_ERROR; + 800b886: 2001 movs r0, #1 + 800b888: e7e3 b.n 800b852 + +0800b88a : +} + 800b88a: 4770 bx lr + +0800b88c : +{ + 800b88c: b510 push {r4, lr} + if (hi2c == NULL) + 800b88e: 4604 mov r4, r0 + 800b890: 2800 cmp r0, #0 + 800b892: d04a beq.n 800b92a + if (hi2c->State == HAL_I2C_STATE_RESET) + 800b894: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 + 800b898: f003 02ff and.w r2, r3, #255 ; 0xff + 800b89c: b91b cbnz r3, 800b8a6 + hi2c->Lock = HAL_UNLOCKED; + 800b89e: f880 2040 strb.w r2, [r0, #64] ; 0x40 + HAL_I2C_MspInit(hi2c); + 800b8a2: f7ff fff2 bl 800b88a + hi2c->State = HAL_I2C_STATE_BUSY; + 800b8a6: 2324 movs r3, #36 ; 0x24 + 800b8a8: f884 3041 strb.w r3, [r4, #65] ; 0x41 + __HAL_I2C_DISABLE(hi2c); + 800b8ac: 6823 ldr r3, [r4, #0] + 800b8ae: 681a ldr r2, [r3, #0] + 800b8b0: f022 0201 bic.w r2, r2, #1 + 800b8b4: 601a str r2, [r3, #0] + hi2c->Instance->TIMINGR = hi2c->Init.Timing & TIMING_CLEAR_MASK; + 800b8b6: 6862 ldr r2, [r4, #4] + 800b8b8: f022 6270 bic.w r2, r2, #251658240 ; 0xf000000 + 800b8bc: 611a str r2, [r3, #16] + hi2c->Instance->OAR1 &= ~I2C_OAR1_OA1EN; + 800b8be: 689a ldr r2, [r3, #8] + 800b8c0: f422 4200 bic.w r2, r2, #32768 ; 0x8000 + 800b8c4: 609a str r2, [r3, #8] + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | hi2c->Init.OwnAddress1); + 800b8c6: e9d4 2102 ldrd r2, r1, [r4, #8] + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) + 800b8ca: 2901 cmp r1, #1 + 800b8cc: d124 bne.n 800b918 + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | hi2c->Init.OwnAddress1); + 800b8ce: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 800b8d2: 609a str r2, [r3, #8] + hi2c->Instance->CR2 |= (I2C_CR2_AUTOEND | I2C_CR2_NACK); + 800b8d4: 685a ldr r2, [r3, #4] + 800b8d6: f042 7200 orr.w r2, r2, #33554432 ; 0x2000000 + 800b8da: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 800b8de: 605a str r2, [r3, #4] + hi2c->Instance->OAR2 &= ~I2C_DUALADDRESS_ENABLE; + 800b8e0: 68da ldr r2, [r3, #12] + 800b8e2: f422 4200 bic.w r2, r2, #32768 ; 0x8000 + 800b8e6: 60da str r2, [r3, #12] + hi2c->Instance->OAR2 = (hi2c->Init.DualAddressMode | hi2c->Init.OwnAddress2 | (hi2c->Init.OwnAddress2Masks << 8)); + 800b8e8: e9d4 2104 ldrd r2, r1, [r4, #16] + 800b8ec: 430a orrs r2, r1 + 800b8ee: 69a1 ldr r1, [r4, #24] + 800b8f0: ea42 2201 orr.w r2, r2, r1, lsl #8 + 800b8f4: 60da str r2, [r3, #12] + hi2c->Instance->CR1 = (hi2c->Init.GeneralCallMode | hi2c->Init.NoStretchMode); + 800b8f6: e9d4 2107 ldrd r2, r1, [r4, #28] + 800b8fa: 430a orrs r2, r1 + 800b8fc: 601a str r2, [r3, #0] + __HAL_I2C_ENABLE(hi2c); + 800b8fe: 681a ldr r2, [r3, #0] + 800b900: f042 0201 orr.w r2, r2, #1 + 800b904: 601a str r2, [r3, #0] + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800b906: 2000 movs r0, #0 + hi2c->State = HAL_I2C_STATE_READY; + 800b908: 2320 movs r3, #32 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800b90a: 6460 str r0, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800b90c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->PreviousState = I2C_STATE_NONE; + 800b910: 6320 str r0, [r4, #48] ; 0x30 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800b912: f884 0042 strb.w r0, [r4, #66] ; 0x42 +} + 800b916: bd10 pop {r4, pc} + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE | hi2c->Init.OwnAddress1); + 800b918: f442 4204 orr.w r2, r2, #33792 ; 0x8400 + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_10BIT) + 800b91c: 2902 cmp r1, #2 + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE | hi2c->Init.OwnAddress1); + 800b91e: 609a str r2, [r3, #8] + hi2c->Instance->CR2 = (I2C_CR2_ADD10); + 800b920: bf04 itt eq + 800b922: f44f 6200 moveq.w r2, #2048 ; 0x800 + 800b926: 605a streq r2, [r3, #4] + 800b928: e7d4 b.n 800b8d4 + return HAL_ERROR; + 800b92a: 2001 movs r0, #1 + 800b92c: e7f3 b.n 800b916 + +0800b92e : + 800b92e: 4770 bx lr + +0800b930 : +{ + 800b930: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + 800b934: 4698 mov r8, r3 + if (hi2c->State == HAL_I2C_STATE_READY) + 800b936: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 +{ + 800b93a: 9f0a ldr r7, [sp, #40] ; 0x28 + if (hi2c->State == HAL_I2C_STATE_READY) + 800b93c: 2b20 cmp r3, #32 +{ + 800b93e: 4604 mov r4, r0 + 800b940: 460e mov r6, r1 + 800b942: 4691 mov r9, r2 + if (hi2c->State == HAL_I2C_STATE_READY) + 800b944: f040 80a3 bne.w 800ba8e + __HAL_LOCK(hi2c); + 800b948: f890 3040 ldrb.w r3, [r0, #64] ; 0x40 + 800b94c: 2b01 cmp r3, #1 + 800b94e: f000 809e beq.w 800ba8e + 800b952: f04f 0a01 mov.w sl, #1 + 800b956: f880 a040 strb.w sl, [r0, #64] ; 0x40 + tickstart = HAL_GetTick(); + 800b95a: f7fb fbc7 bl 80070ec + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800b95e: 2319 movs r3, #25 + tickstart = HAL_GetTick(); + 800b960: 4605 mov r5, r0 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800b962: 9000 str r0, [sp, #0] + 800b964: 4652 mov r2, sl + 800b966: f44f 4100 mov.w r1, #32768 ; 0x8000 + 800b96a: 4620 mov r0, r4 + 800b96c: f7ff ff3e bl 800b7ec + 800b970: b118 cbz r0, 800b97a + return HAL_ERROR; + 800b972: 2001 movs r0, #1 +} + 800b974: b002 add sp, #8 + 800b976: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hi2c->State = HAL_I2C_STATE_BUSY_TX; + 800b97a: 2321 movs r3, #33 ; 0x21 + 800b97c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_MASTER; + 800b980: 2310 movs r3, #16 + 800b982: f884 3042 strb.w r3, [r4, #66] ; 0x42 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800b986: 6460 str r0, [r4, #68] ; 0x44 + hi2c->XferCount = Size; + 800b988: f8a4 802a strh.w r8, [r4, #42] ; 0x2a + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800b98c: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->pBuffPtr = pData; + 800b98e: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800b992: b29b uxth r3, r3 + 800b994: 2bff cmp r3, #255 ; 0xff + hi2c->XferISR = NULL; + 800b996: 6360 str r0, [r4, #52] ; 0x34 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800b998: 4b3e ldr r3, [pc, #248] ; (800ba94 ) + 800b99a: d927 bls.n 800b9ec + hi2c->XferSize = MAX_NBYTE_SIZE; + 800b99c: 22ff movs r2, #255 ; 0xff + 800b99e: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE); + 800b9a0: 9300 str r3, [sp, #0] + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800b9a2: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800b9a6: 4631 mov r1, r6 + 800b9a8: 4620 mov r0, r4 + 800b9aa: f7ff fec9 bl 800b740 + while (hi2c->XferCount > 0U) + 800b9ae: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800b9b0: b29b uxth r3, r3 + 800b9b2: 2b00 cmp r3, #0 + 800b9b4: d13e bne.n 800ba34 + if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + 800b9b6: 462a mov r2, r5 + 800b9b8: 4639 mov r1, r7 + 800b9ba: 4620 mov r0, r4 + 800b9bc: f7ff ff40 bl 800b840 + 800b9c0: 2800 cmp r0, #0 + 800b9c2: d1d6 bne.n 800b972 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800b9c4: 6823 ldr r3, [r4, #0] + 800b9c6: 2120 movs r1, #32 + 800b9c8: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800b9ca: 685a ldr r2, [r3, #4] + 800b9cc: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800b9d0: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800b9d4: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800b9d8: f022 0201 bic.w r2, r2, #1 + 800b9dc: 605a str r2, [r3, #4] + hi2c->State = HAL_I2C_STATE_READY; + 800b9de: f884 1041 strb.w r1, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800b9e2: f884 0040 strb.w r0, [r4, #64] ; 0x40 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800b9e6: f884 0042 strb.w r0, [r4, #66] ; 0x42 + return HAL_OK; + 800b9ea: e7c3 b.n 800b974 + hi2c->XferSize = hi2c->XferCount; + 800b9ec: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_WRITE); + 800b9ee: 9300 str r3, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800b9f0: b292 uxth r2, r2 + 800b9f2: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800b9f4: f04f 7300 mov.w r3, #33554432 ; 0x2000000 + 800b9f8: b2d2 uxtb r2, r2 + 800b9fa: e7d4 b.n 800b9a6 + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800b9fc: 462a mov r2, r5 + 800b9fe: 4639 mov r1, r7 + 800ba00: 4620 mov r0, r4 + 800ba02: f7ff feb1 bl 800b768 + 800ba06: 2800 cmp r0, #0 + 800ba08: d1b3 bne.n 800b972 + if (Timeout != HAL_MAX_DELAY) + 800ba0a: 1c7a adds r2, r7, #1 + 800ba0c: d012 beq.n 800ba34 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800ba0e: f7fb fb6d bl 80070ec + 800ba12: 1b40 subs r0, r0, r5 + 800ba14: 4287 cmp r7, r0 + 800ba16: d300 bcc.n 800ba1a + 800ba18: b967 cbnz r7, 800ba34 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800ba1a: 6c63 ldr r3, [r4, #68] ; 0x44 + 800ba1c: f043 0320 orr.w r3, r3, #32 + 800ba20: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800ba22: 2320 movs r3, #32 + 800ba24: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800ba28: 2300 movs r3, #0 + 800ba2a: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800ba2e: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800ba32: e79e b.n 800b972 + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) == RESET) + 800ba34: 6822 ldr r2, [r4, #0] + 800ba36: 6993 ldr r3, [r2, #24] + 800ba38: 079b lsls r3, r3, #30 + 800ba3a: d5df bpl.n 800b9fc + hi2c->Instance->TXDR = *hi2c->pBuffPtr; + 800ba3c: 6a63 ldr r3, [r4, #36] ; 0x24 + 800ba3e: f813 1b01 ldrb.w r1, [r3], #1 + 800ba42: 6291 str r1, [r2, #40] ; 0x28 + hi2c->pBuffPtr++; + 800ba44: 6263 str r3, [r4, #36] ; 0x24 + hi2c->XferCount--; + 800ba46: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800ba48: 8d22 ldrh r2, [r4, #40] ; 0x28 + hi2c->XferCount--; + 800ba4a: 3b01 subs r3, #1 + 800ba4c: b29b uxth r3, r3 + 800ba4e: 8563 strh r3, [r4, #42] ; 0x2a + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800ba50: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800ba52: 3a01 subs r2, #1 + 800ba54: b292 uxth r2, r2 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800ba56: b29b uxth r3, r3 + hi2c->XferSize--; + 800ba58: 8522 strh r2, [r4, #40] ; 0x28 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800ba5a: 2b00 cmp r3, #0 + 800ba5c: d0a7 beq.n 800b9ae + 800ba5e: 2a00 cmp r2, #0 + 800ba60: d1a5 bne.n 800b9ae + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK) + 800ba62: 9500 str r5, [sp, #0] + 800ba64: 463b mov r3, r7 + 800ba66: 2180 movs r1, #128 ; 0x80 + 800ba68: 4620 mov r0, r4 + 800ba6a: f7ff febf bl 800b7ec + 800ba6e: 2800 cmp r0, #0 + 800ba70: f47f af7f bne.w 800b972 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800ba74: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800ba76: b29b uxth r3, r3 + 800ba78: 2bff cmp r3, #255 ; 0xff + 800ba7a: d903 bls.n 800ba84 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800ba7c: 22ff movs r2, #255 ; 0xff + 800ba7e: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800ba80: 9000 str r0, [sp, #0] + 800ba82: e78e b.n 800b9a2 + hi2c->XferSize = hi2c->XferCount; + 800ba84: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800ba86: 9000 str r0, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800ba88: b292 uxth r2, r2 + 800ba8a: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800ba8c: e7b2 b.n 800b9f4 + return HAL_BUSY; + 800ba8e: 2002 movs r0, #2 + 800ba90: e770 b.n 800b974 + 800ba92: bf00 nop + 800ba94: 80002000 .word 0x80002000 + +0800ba98 : +{ + 800ba98: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + 800ba9c: 4698 mov r8, r3 + if (hi2c->State == HAL_I2C_STATE_READY) + 800ba9e: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 +{ + 800baa2: 9f0a ldr r7, [sp, #40] ; 0x28 + if (hi2c->State == HAL_I2C_STATE_READY) + 800baa4: 2b20 cmp r3, #32 +{ + 800baa6: 4604 mov r4, r0 + 800baa8: 460e mov r6, r1 + 800baaa: 4691 mov r9, r2 + if (hi2c->State == HAL_I2C_STATE_READY) + 800baac: f040 80be bne.w 800bc2c + __HAL_LOCK(hi2c); + 800bab0: f890 3040 ldrb.w r3, [r0, #64] ; 0x40 + 800bab4: 2b01 cmp r3, #1 + 800bab6: f000 80b9 beq.w 800bc2c + 800baba: f04f 0a01 mov.w sl, #1 + 800babe: f880 a040 strb.w sl, [r0, #64] ; 0x40 + tickstart = HAL_GetTick(); + 800bac2: f7fb fb13 bl 80070ec + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800bac6: 2319 movs r3, #25 + tickstart = HAL_GetTick(); + 800bac8: 4605 mov r5, r0 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800baca: 9000 str r0, [sp, #0] + 800bacc: 4652 mov r2, sl + 800bace: f44f 4100 mov.w r1, #32768 ; 0x8000 + 800bad2: 4620 mov r0, r4 + 800bad4: f7ff fe8a bl 800b7ec + 800bad8: b118 cbz r0, 800bae2 + return HAL_ERROR; + 800bada: 2001 movs r0, #1 +} + 800badc: b002 add sp, #8 + 800bade: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hi2c->State = HAL_I2C_STATE_BUSY_RX; + 800bae2: 2322 movs r3, #34 ; 0x22 + 800bae4: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_MASTER; + 800bae8: 2310 movs r3, #16 + 800baea: f884 3042 strb.w r3, [r4, #66] ; 0x42 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800baee: 6460 str r0, [r4, #68] ; 0x44 + hi2c->XferCount = Size; + 800baf0: f8a4 802a strh.w r8, [r4, #42] ; 0x2a + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800baf4: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->pBuffPtr = pData; + 800baf6: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bafa: b29b uxth r3, r3 + 800bafc: 2bff cmp r3, #255 ; 0xff + hi2c->XferISR = NULL; + 800bafe: 6360 str r0, [r4, #52] ; 0x34 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bb00: 4b4b ldr r3, [pc, #300] ; (800bc30 ) + 800bb02: d909 bls.n 800bb18 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800bb04: 22ff movs r2, #255 ; 0xff + 800bb06: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_READ); + 800bb08: 9300 str r3, [sp, #0] + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bb0a: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bb0e: 4631 mov r1, r6 + 800bb10: 4620 mov r0, r4 + 800bb12: f7ff fe15 bl 800b740 + 800bb16: e052 b.n 800bbbe + hi2c->XferSize = hi2c->XferCount; + 800bb18: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_READ); + 800bb1a: 9300 str r3, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800bb1c: b292 uxth r2, r2 + 800bb1e: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bb20: f04f 7300 mov.w r3, #33554432 ; 0x2000000 + 800bb24: b2d2 uxtb r2, r2 + 800bb26: e7f2 b.n 800bb0e + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800bb28: 2120 movs r1, #32 + 800bb2a: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800bb2c: 685a ldr r2, [r3, #4] + 800bb2e: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800bb32: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800bb36: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800bb3a: f022 0201 bic.w r2, r2, #1 + 800bb3e: 605a str r2, [r3, #4] + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800bb40: 2300 movs r3, #0 + 800bb42: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bb44: f884 1041 strb.w r1, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bb48: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800bb4c: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800bb50: e7c3 b.n 800bada + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800bb52: f7fb facb bl 80070ec + 800bb56: 1b40 subs r0, r0, r5 + 800bb58: 4287 cmp r7, r0 + 800bb5a: d300 bcc.n 800bb5e + 800bb5c: b947 cbnz r7, 800bb70 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800bb5e: 6c63 ldr r3, [r4, #68] ; 0x44 + 800bb60: f043 0320 orr.w r3, r3, #32 + 800bb64: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bb66: 2320 movs r3, #32 + 800bb68: f884 3041 strb.w r3, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800bb6c: 2300 movs r3, #0 + 800bb6e: e7ed b.n 800bb4c + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == RESET) + 800bb70: 6823 ldr r3, [r4, #0] + 800bb72: 699b ldr r3, [r3, #24] + 800bb74: 075b lsls r3, r3, #29 + 800bb76: d410 bmi.n 800bb9a + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800bb78: 462a mov r2, r5 + 800bb7a: 4639 mov r1, r7 + 800bb7c: 4620 mov r0, r4 + 800bb7e: f7ff fdf3 bl 800b768 + 800bb82: 2800 cmp r0, #0 + 800bb84: d1a9 bne.n 800bada + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == SET) + 800bb86: 6823 ldr r3, [r4, #0] + 800bb88: 699a ldr r2, [r3, #24] + 800bb8a: 0691 lsls r1, r2, #26 + 800bb8c: d5e1 bpl.n 800bb52 + if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == SET) && (hi2c->XferSize > 0U)) + 800bb8e: 699a ldr r2, [r3, #24] + 800bb90: 0752 lsls r2, r2, #29 + 800bb92: d5c9 bpl.n 800bb28 + 800bb94: 8d22 ldrh r2, [r4, #40] ; 0x28 + 800bb96: 2a00 cmp r2, #0 + 800bb98: d0c6 beq.n 800bb28 + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->RXDR; + 800bb9a: 6823 ldr r3, [r4, #0] + 800bb9c: 6a5a ldr r2, [r3, #36] ; 0x24 + 800bb9e: 6a63 ldr r3, [r4, #36] ; 0x24 + 800bba0: 701a strb r2, [r3, #0] + hi2c->pBuffPtr++; + 800bba2: 6a63 ldr r3, [r4, #36] ; 0x24 + hi2c->XferSize--; + 800bba4: 8d22 ldrh r2, [r4, #40] ; 0x28 + hi2c->pBuffPtr++; + 800bba6: 3301 adds r3, #1 + 800bba8: 6263 str r3, [r4, #36] ; 0x24 + hi2c->XferCount--; + 800bbaa: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bbac: 3b01 subs r3, #1 + 800bbae: b29b uxth r3, r3 + 800bbb0: 8563 strh r3, [r4, #42] ; 0x2a + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bbb2: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800bbb4: 3a01 subs r2, #1 + 800bbb6: b292 uxth r2, r2 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bbb8: b29b uxth r3, r3 + hi2c->XferSize--; + 800bbba: 8522 strh r2, [r4, #40] ; 0x28 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bbbc: b9f3 cbnz r3, 800bbfc + while (hi2c->XferCount > 0U) + 800bbbe: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bbc0: b29b uxth r3, r3 + 800bbc2: 2b00 cmp r3, #0 + 800bbc4: d1d4 bne.n 800bb70 + if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + 800bbc6: 462a mov r2, r5 + 800bbc8: 4639 mov r1, r7 + 800bbca: 4620 mov r0, r4 + 800bbcc: f7ff fe38 bl 800b840 + 800bbd0: 2800 cmp r0, #0 + 800bbd2: d182 bne.n 800bada + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800bbd4: 6823 ldr r3, [r4, #0] + 800bbd6: 2120 movs r1, #32 + 800bbd8: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800bbda: 685a ldr r2, [r3, #4] + 800bbdc: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800bbe0: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800bbe4: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800bbe8: f022 0201 bic.w r2, r2, #1 + 800bbec: 605a str r2, [r3, #4] + hi2c->State = HAL_I2C_STATE_READY; + 800bbee: f884 1041 strb.w r1, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800bbf2: f884 0040 strb.w r0, [r4, #64] ; 0x40 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bbf6: f884 0042 strb.w r0, [r4, #66] ; 0x42 + return HAL_OK; + 800bbfa: e76f b.n 800badc + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bbfc: 2a00 cmp r2, #0 + 800bbfe: d1de bne.n 800bbbe + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK) + 800bc00: 9500 str r5, [sp, #0] + 800bc02: 463b mov r3, r7 + 800bc04: 2180 movs r1, #128 ; 0x80 + 800bc06: 4620 mov r0, r4 + 800bc08: f7ff fdf0 bl 800b7ec + 800bc0c: 2800 cmp r0, #0 + 800bc0e: f47f af64 bne.w 800bada + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bc12: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bc14: b29b uxth r3, r3 + 800bc16: 2bff cmp r3, #255 ; 0xff + 800bc18: d903 bls.n 800bc22 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800bc1a: 22ff movs r2, #255 ; 0xff + 800bc1c: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bc1e: 9000 str r0, [sp, #0] + 800bc20: e773 b.n 800bb0a + hi2c->XferSize = hi2c->XferCount; + 800bc22: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bc24: 9000 str r0, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800bc26: b292 uxth r2, r2 + 800bc28: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bc2a: e779 b.n 800bb20 + return HAL_BUSY; + 800bc2c: 2002 movs r0, #2 + 800bc2e: e755 b.n 800badc + 800bc30: 80002400 .word 0x80002400 + +0800bc34 : + * @param hsd Pointer to SD handle + * @param pSCR pointer to the buffer that will contain the SCR value + * @retval error state + */ +static uint32_t SD_FindSCR(SD_HandleTypeDef *hsd, uint32_t *pSCR) +{ + 800bc34: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 800bc38: b086 sub sp, #24 + 800bc3a: 4605 mov r5, r0 + 800bc3c: 4688 mov r8, r1 + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t tickstart = HAL_GetTick(); + 800bc3e: f7fb fa55 bl 80070ec + uint32_t index = 0U; + uint32_t tempscr[2U] = {0UL, 0UL}; + uint32_t *scr = pSCR; + + /* Set Block Size To 8 Bytes */ + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 8U); + 800bc42: 2108 movs r1, #8 + uint32_t tickstart = HAL_GetTick(); + 800bc44: 4681 mov r9, r0 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 8U); + 800bc46: 6828 ldr r0, [r5, #0] + 800bc48: f001 f996 bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800bc4c: 4604 mov r4, r0 + 800bc4e: bb48 cbnz r0, 800bca4 + { + return errorstate; + } + + /* Send CMD55 APP_CMD with argument as card's RCA */ + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)((hsd->SdCard.RelCardAdd) << 16U)); + 800bc50: 6ca9 ldr r1, [r5, #72] ; 0x48 + 800bc52: 6828 ldr r0, [r5, #0] + 800bc54: 0409 lsls r1, r1, #16 + 800bc56: f001 fac8 bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800bc5a: 4604 mov r4, r0 + 800bc5c: bb10 cbnz r0, 800bca4 + { + return errorstate; + } + + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = 8U; + 800bc5e: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 800bc62: 2308 movs r3, #8 + 800bc64: e9cd 0300 strd r0, r3, [sp] + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_8B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800bc68: 2630 movs r6, #48 ; 0x30 + 800bc6a: 2302 movs r3, #2 + 800bc6c: e9cd 6302 strd r6, r3, [sp, #8] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_ENABLE; + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bc70: 4669 mov r1, sp + config.DPSM = SDMMC_DPSM_ENABLE; + 800bc72: 2301 movs r3, #1 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bc74: 6828 ldr r0, [r5, #0] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800bc76: 9404 str r4, [sp, #16] + config.DPSM = SDMMC_DPSM_ENABLE; + 800bc78: 9305 str r3, [sp, #20] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bc7a: f001 f8a1 bl 800cdc0 + + /* Send ACMD51 SD_APP_SEND_SCR with argument as 0 */ + errorstate = SDMMC_CmdSendSCR(hsd->Instance); + 800bc7e: 6828 ldr r0, [r5, #0] + 800bc80: f001 fae7 bl 800d252 + if(errorstate != HAL_SD_ERROR_NONE) + 800bc84: 4604 mov r4, r0 + 800bc86: b968 cbnz r0, 800bca4 + uint32_t tempscr[2U] = {0UL, 0UL}; + 800bc88: 4607 mov r7, r0 + 800bc8a: 4606 mov r6, r0 + { + return errorstate; + } + +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND | SDMMC_FLAG_DATAEND)) + 800bc8c: f240 5a2a movw sl, #1322 ; 0x52a + 800bc90: 6828 ldr r0, [r5, #0] + 800bc92: 6b42 ldr r2, [r0, #52] ; 0x34 + 800bc94: ea12 0f0a tst.w r2, sl + { + if((!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOE)) && (index == 0U)) + 800bc98: 6b42 ldr r2, [r0, #52] ; 0x34 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND | SDMMC_FLAG_DATAEND)) + 800bc9a: d007 beq.n 800bcac + return HAL_SD_ERROR_TIMEOUT; + } + } +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800bc9c: 0712 lsls r2, r2, #28 + 800bc9e: d519 bpl.n 800bcd4 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800bca0: 2408 movs r4, #8 + + return HAL_SD_ERROR_DATA_CRC_FAIL; + } + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bca2: 6384 str r4, [r0, #56] ; 0x38 + ((tempscr[0] & SDMMC_16TO23BITS) >> 8) | ((tempscr[0] & SDMMC_24TO31BITS) >> 24)); + + } + + return HAL_SD_ERROR_NONE; +} + 800bca4: 4620 mov r0, r4 + 800bca6: b006 add sp, #24 + 800bca8: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if((!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOE)) && (index == 0U)) + 800bcac: 0311 lsls r1, r2, #12 + 800bcae: d408 bmi.n 800bcc2 + 800bcb0: b93c cbnz r4, 800bcc2 + tempscr[0] = SDMMC_ReadFIFO(hsd->Instance); + 800bcb2: f001 f849 bl 800cd48 + 800bcb6: 4606 mov r6, r0 + tempscr[1] = SDMMC_ReadFIFO(hsd->Instance); + 800bcb8: 6828 ldr r0, [r5, #0] + 800bcba: f001 f845 bl 800cd48 + index++; + 800bcbe: 2401 movs r4, #1 + tempscr[1] = SDMMC_ReadFIFO(hsd->Instance); + 800bcc0: 4607 mov r7, r0 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800bcc2: f7fb fa13 bl 80070ec + 800bcc6: eba0 0009 sub.w r0, r0, r9 + 800bcca: 3001 adds r0, #1 + 800bccc: d1e0 bne.n 800bc90 + return HAL_SD_ERROR_TIMEOUT; + 800bcce: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + 800bcd2: e7e7 b.n 800bca4 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800bcd4: 6b42 ldr r2, [r0, #52] ; 0x34 + 800bcd6: 0793 lsls r3, r2, #30 + 800bcd8: d501 bpl.n 800bcde + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800bcda: 2402 movs r4, #2 + 800bcdc: e7e1 b.n 800bca2 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800bcde: 6b44 ldr r4, [r0, #52] ; 0x34 + 800bce0: f014 0420 ands.w r4, r4, #32 + 800bce4: d001 beq.n 800bcea + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bce6: 2420 movs r4, #32 + 800bce8: e7db b.n 800bca2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800bcea: 4a04 ldr r2, [pc, #16] ; (800bcfc ) + 800bcec: 6382 str r2, [r0, #56] ; 0x38 + *scr = (((tempscr[1] & SDMMC_0TO7BITS) << 24) | ((tempscr[1] & SDMMC_8TO15BITS) << 8) |\ + 800bcee: ba3f rev r7, r7 + 800bcf0: ba36 rev r6, r6 + 800bcf2: f8c8 7000 str.w r7, [r8] + *scr = (((tempscr[0] & SDMMC_0TO7BITS) << 24) | ((tempscr[0] & SDMMC_8TO15BITS) << 8) |\ + 800bcf6: f8c8 6004 str.w r6, [r8, #4] + return HAL_SD_ERROR_NONE; + 800bcfa: e7d3 b.n 800bca4 + 800bcfc: 18000f3a .word 0x18000f3a + +0800bd00 : + * of PLL to have SDMMCCK clock between 50 and 120 MHz + * @param hsd SD handle + * @retval SD Card error state + */ +static uint32_t SD_UltraHighSpeed(SD_HandleTypeDef *hsd) +{ + 800bd00: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + uint32_t errorstate = HAL_SD_ERROR_NONE; + SDMMC_DataInitTypeDef sdmmc_datainitstructure; + uint32_t SD_hs[16] = {0}; + 800bd04: 2640 movs r6, #64 ; 0x40 +{ + 800bd06: b096 sub sp, #88 ; 0x58 + 800bd08: 4605 mov r5, r0 + uint32_t SD_hs[16] = {0}; + 800bd0a: 4632 mov r2, r6 + 800bd0c: 2100 movs r1, #0 + 800bd0e: a806 add r0, sp, #24 + 800bd10: f001 fcb0 bl 800d674 + uint32_t count, loop = 0 ; + uint32_t Timeout = HAL_GetTick(); + 800bd14: f7fb f9ea bl 80070ec + + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800bd18: 6deb ldr r3, [r5, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800bd1a: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800bd1c: 2b00 cmp r3, #0 + 800bd1e: d067 beq.n 800bdf0 + { + /* Standard Speed Card <= 12.5Mhz */ + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + } + + if((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) && + 800bd20: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800bd24: d167 bne.n 800bdf6 + 800bd26: 69af ldr r7, [r5, #24] + 800bd28: 2f01 cmp r7, #1 + 800bd2a: d164 bne.n 800bdf6 + (hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE)) + { + /* Initialize the Data control register */ + hsd->Instance->DCTRL = 0; + 800bd2c: 6828 ldr r0, [r5, #0] + 800bd2e: 2300 movs r3, #0 + 800bd30: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800bd32: 4631 mov r1, r6 + 800bd34: f001 f920 bl 800cf78 + + if (errorstate != HAL_SD_ERROR_NONE) + 800bd38: 4604 mov r4, r0 + 800bd3a: 2800 cmp r0, #0 + 800bd3c: d13e bne.n 800bdbc + { + return errorstate; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800bd3e: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800bd42: e9cd 3600 strd r3, r6, [sp] + sdmmc_datainitstructure.DataBlockSize = SDMMC_DATABLOCK_SIZE_64B ; + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800bd46: e9cd 0704 strd r0, r7, [sp, #16] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800bd4a: 2660 movs r6, #96 ; 0x60 + 800bd4c: 2302 movs r3, #2 + + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800bd4e: 6828 ldr r0, [r5, #0] + 800bd50: 4669 mov r1, sp + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800bd52: e9cd 6302 strd r6, r3, [sp, #8] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800bd56: f001 f833 bl 800cdc0 + 800bd5a: 2800 cmp r0, #0 + 800bd5c: d14d bne.n 800bdfa + { + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + } + + errorstate = SDMMC_CmdSwitch(hsd->Instance, SDMMC_SDR104_SWITCH_PATTERN); + 800bd5e: 492a ldr r1, [pc, #168] ; (800be08 ) + 800bd60: 6828 ldr r0, [r5, #0] + 800bd62: f001 fa74 bl 800d24e + if(errorstate != HAL_SD_ERROR_NONE) + 800bd66: 4604 mov r4, r0 + 800bd68: bb40 cbnz r0, 800bdbc + uint32_t count, loop = 0 ; + 800bd6a: 4607 mov r7, r0 + { + return errorstate; + } + + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800bd6c: f240 592a movw r9, #1322 ; 0x52a + 800bd70: 682b ldr r3, [r5, #0] + 800bd72: 6b5e ldr r6, [r3, #52] ; 0x34 + 800bd74: ea16 0609 ands.w r6, r6, r9 + 800bd78: d005 beq.n 800bd86 + hsd->State= HAL_SD_STATE_READY; + return HAL_SD_ERROR_TIMEOUT; + } + } + + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800bd7a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800bd7c: 0711 lsls r1, r2, #28 + 800bd7e: d521 bpl.n 800bdc4 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800bd80: 2208 movs r2, #8 + 800bd82: 639a str r2, [r3, #56] ; 0x38 + + return errorstate; + 800bd84: e01a b.n 800bdbc + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800bd86: 6b5b ldr r3, [r3, #52] ; 0x34 + 800bd88: 0418 lsls r0, r3, #16 + 800bd8a: d50b bpl.n 800bda4 + 800bd8c: ab06 add r3, sp, #24 + 800bd8e: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800bd92: 6828 ldr r0, [r5, #0] + 800bd94: f000 ffd8 bl 800cd48 + for (count = 0U; count < 8U; count++) + 800bd98: 3601 adds r6, #1 + 800bd9a: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800bd9c: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800bda0: d1f7 bne.n 800bd92 + loop ++; + 800bda2: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800bda4: f7fb f9a2 bl 80070ec + 800bda8: eba0 0008 sub.w r0, r0, r8 + 800bdac: 3001 adds r0, #1 + 800bdae: d1df bne.n 800bd70 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800bdb0: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + hsd->State= HAL_SD_STATE_READY; + 800bdb4: 2301 movs r3, #1 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800bdb6: 63ac str r4, [r5, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800bdb8: f885 3034 strb.w r3, [r5, #52] ; 0x34 +#endif /* (DLYB_SDMMC1) || (DLYB_SDMMC2) */ + } + } + + return errorstate; +} + 800bdbc: 4620 mov r0, r4 + 800bdbe: b016 add sp, #88 ; 0x58 + 800bdc0: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800bdc4: 6b5a ldr r2, [r3, #52] ; 0x34 + 800bdc6: 0792 lsls r2, r2, #30 + 800bdc8: d502 bpl.n 800bdd0 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800bdca: 2402 movs r4, #2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bdcc: 639c str r4, [r3, #56] ; 0x38 + return errorstate; + 800bdce: e7f5 b.n 800bdbc + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800bdd0: 6b5c ldr r4, [r3, #52] ; 0x34 + 800bdd2: f014 0420 ands.w r4, r4, #32 + 800bdd6: d001 beq.n 800bddc + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bdd8: 2420 movs r4, #32 + 800bdda: e7f7 b.n 800bdcc + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800bddc: 4a0b ldr r2, [pc, #44] ; (800be0c ) + 800bdde: 639a str r2, [r3, #56] ; 0x38 + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800bde0: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800bde4: 079b lsls r3, r3, #30 + 800bde6: d50b bpl.n 800be00 + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800bde8: 2001 movs r0, #1 + 800bdea: f7fb f9ef bl 80071cc + 800bdee: e7e5 b.n 800bdbc + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800bdf0: f04f 6480 mov.w r4, #67108864 ; 0x4000000 + 800bdf4: e7e2 b.n 800bdbc + uint32_t errorstate = HAL_SD_ERROR_NONE; + 800bdf6: 2400 movs r4, #0 + 800bdf8: e7e0 b.n 800bdbc + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + 800bdfa: f44f 3480 mov.w r4, #65536 ; 0x10000 + 800bdfe: e7dd b.n 800bdbc + errorstate = SDMMC_ERROR_UNSUPPORTED_FEATURE; + 800be00: f04f 5480 mov.w r4, #268435456 ; 0x10000000 + 800be04: e7da b.n 800bdbc + 800be06: bf00 nop + 800be08: 80ff1f03 .word 0x80ff1f03 + 800be0c: 18000f3a .word 0x18000f3a + +0800be10 : +} + 800be10: 4770 bx lr + +0800be12 : + 800be12: 4770 bx lr + +0800be14 : +{ + 800be14: b510 push {r4, lr} + if(hsd == NULL) + 800be16: 4604 mov r4, r0 + 800be18: b198 cbz r0, 800be42 + hsd->State = HAL_SD_STATE_BUSY; + 800be1a: 2303 movs r3, #3 + 800be1c: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800be20: 6983 ldr r3, [r0, #24] + 800be22: 2b01 cmp r3, #1 + 800be24: d102 bne.n 800be2c + HAL_SDEx_DriveTransceiver_1_8V_Callback(RESET); + 800be26: 2000 movs r0, #0 + 800be28: f7fb f9d0 bl 80071cc + (void)SDMMC_PowerState_OFF(hsd->Instance); + 800be2c: 6820 ldr r0, [r4, #0] + 800be2e: f000 ffa3 bl 800cd78 + HAL_SD_MspDeInit(hsd); + 800be32: 4620 mov r0, r4 + 800be34: f7ff ffed bl 800be12 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800be38: 2000 movs r0, #0 + 800be3a: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_RESET; + 800be3c: f884 0034 strb.w r0, [r4, #52] ; 0x34 +} + 800be40: bd10 pop {r4, pc} + return HAL_ERROR; + 800be42: 2001 movs r0, #1 + 800be44: e7fc b.n 800be40 + ... + +0800be48 : +{ + 800be48: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800be4c: b087 sub sp, #28 + 800be4e: 4604 mov r4, r0 + 800be50: 460e mov r6, r1 + 800be52: 4692 mov sl, r2 + 800be54: 461f mov r7, r3 + uint32_t tickstart = HAL_GetTick(); + 800be56: f7fb f949 bl 80070ec + 800be5a: 4681 mov r9, r0 + if(NULL == pData) + 800be5c: b936 cbnz r6, 800be6c + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800be5e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800be60: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800be64: 63a3 str r3, [r4, #56] ; 0x38 + return HAL_ERROR; + 800be66: f04f 0801 mov.w r8, #1 + 800be6a: e011 b.n 800be90 + if(hsd->State == HAL_SD_STATE_READY) + 800be6c: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800be70: 2b01 cmp r3, #1 + 800be72: fa5f f883 uxtb.w r8, r3 + 800be76: f040 80c3 bne.w 800c000 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800be7a: 6d62 ldr r2, [r4, #84] ; 0x54 + 800be7c: eb0a 0307 add.w r3, sl, r7 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800be80: 2100 movs r1, #0 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800be82: 4293 cmp r3, r2 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800be84: 63a1 str r1, [r4, #56] ; 0x38 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800be86: d907 bls.n 800be98 + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + 800be88: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800be8a: f043 7300 orr.w r3, r3, #33554432 ; 0x2000000 + 800be8e: 63a3 str r3, [r4, #56] ; 0x38 +} + 800be90: 4640 mov r0, r8 + 800be92: b007 add sp, #28 + 800be94: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + hsd->State = HAL_SD_STATE_BUSY; + 800be98: 2303 movs r3, #3 + 800be9a: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800be9e: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->Instance->DCTRL = 0U; + 800bea0: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800bea2: 2b01 cmp r3, #1 + config.DataTimeOut = SDMMC_DATATIMEOUT; + 800bea4: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800bea8: 9300 str r3, [sp, #0] + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800beaa: ea4f 2347 mov.w r3, r7, lsl #9 + 800beae: 9301 str r3, [sp, #4] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800beb0: f04f 0502 mov.w r5, #2 + 800beb4: f04f 0390 mov.w r3, #144 ; 0x90 + 800beb8: e9cd 3502 strd r3, r5, [sp, #8] + hsd->Instance->DCTRL = 0U; + 800bebc: 62c1 str r1, [r0, #44] ; 0x2c + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800bebe: f04f 0300 mov.w r3, #0 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bec2: 4669 mov r1, sp + config.DPSM = SDMMC_DPSM_DISABLE; + 800bec4: e9cd 3304 strd r3, r3, [sp, #16] + add *= 512U; + 800bec8: bf18 it ne + 800beca: ea4f 2a4a movne.w sl, sl, lsl #9 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bece: f000 ff77 bl 800cdc0 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800bed2: 6820 ldr r0, [r4, #0] + 800bed4: 68c3 ldr r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800bed6: 2f01 cmp r7, #1 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800bed8: f043 0340 orr.w r3, r3, #64 ; 0x40 + 800bedc: 60c3 str r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800bede: d910 bls.n 800bf02 + hsd->Context = SD_CONTEXT_READ_MULTIPLE_BLOCK; + 800bee0: 6325 str r5, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add); + 800bee2: 4651 mov r1, sl + 800bee4: f001 f87a bl 800cfdc + if(errorstate != HAL_SD_ERROR_NONE) + 800bee8: b188 cbz r0, 800bf0e + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800beea: 6823 ldr r3, [r4, #0] + 800beec: 4a46 ldr r2, [pc, #280] ; (800c008 ) + 800beee: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800bef0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800bef2: 4318 orrs r0, r3 + 800bef4: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800bef6: 2301 movs r3, #1 + 800bef8: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800befc: 2300 movs r3, #0 + 800befe: 6323 str r3, [r4, #48] ; 0x30 + return HAL_ERROR; + 800bf00: e7c6 b.n 800be90 + hsd->Context = SD_CONTEXT_READ_SINGLE_BLOCK; + 800bf02: 2301 movs r3, #1 + 800bf04: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, add); + 800bf06: 4651 mov r1, sl + 800bf08: f001 f84f bl 800cfaa + 800bf0c: e7ec b.n 800bee8 + dataremaining = config.DataLength; + 800bf0e: 9d01 ldr r5, [sp, #4] + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800bf10: 6820 ldr r0, [r4, #0] + 800bf12: 6b43 ldr r3, [r0, #52] ; 0x34 + 800bf14: f413 7f95 tst.w r3, #298 ; 0x12a + 800bf18: d01b beq.n 800bf52 + __SDMMC_CMDTRANS_DISABLE( hsd->Instance); + 800bf1a: 68c3 ldr r3, [r0, #12] + 800bf1c: f023 0340 bic.w r3, r3, #64 ; 0x40 + 800bf20: 60c3 str r3, [r0, #12] + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U)) + 800bf22: 6b43 ldr r3, [r0, #52] ; 0x34 + 800bf24: 05db lsls r3, r3, #23 + 800bf26: d508 bpl.n 800bf3a + 800bf28: 2f01 cmp r7, #1 + 800bf2a: d906 bls.n 800bf3a + if(hsd->SdCard.CardType != CARD_SECURED) + 800bf2c: 6be3 ldr r3, [r4, #60] ; 0x3c + 800bf2e: 2b03 cmp r3, #3 + 800bf30: d003 beq.n 800bf3a + errorstate = SDMMC_CmdStopTransfer(hsd->Instance); + 800bf32: f001 f91b bl 800d16c + if(errorstate != HAL_SD_ERROR_NONE) + 800bf36: 2800 cmp r0, #0 + 800bf38: d1d7 bne.n 800beea + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800bf3a: 6823 ldr r3, [r4, #0] + 800bf3c: 6b58 ldr r0, [r3, #52] ; 0x34 + 800bf3e: f010 0008 ands.w r0, r0, #8 + 800bf42: d038 beq.n 800bfb6 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800bf44: 4a30 ldr r2, [pc, #192] ; (800c008 ) + 800bf46: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT; + 800bf48: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800bf4a: f043 0308 orr.w r3, r3, #8 + 800bf4e: 63a3 str r3, [r4, #56] ; 0x38 + 800bf50: e7d1 b.n 800bef6 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF) && (dataremaining > 0U)) + 800bf52: 6b43 ldr r3, [r0, #52] ; 0x34 + 800bf54: 041a lsls r2, r3, #16 + 800bf56: d518 bpl.n 800bf8a + 800bf58: b1bd cbz r5, 800bf8a + 800bf5a: f106 0a04 add.w sl, r6, #4 + 800bf5e: f106 0b24 add.w fp, r6, #36 ; 0x24 + data = SDMMC_ReadFIFO(hsd->Instance); + 800bf62: 6820 ldr r0, [r4, #0] + 800bf64: f000 fef0 bl 800cd48 + *tempbuff = (uint8_t)((data >> 8U) & 0xFFU); + 800bf68: 0a02 lsrs r2, r0, #8 + 800bf6a: f80a 2c03 strb.w r2, [sl, #-3] + *tempbuff = (uint8_t)((data >> 16U) & 0xFFU); + 800bf6e: 0c02 lsrs r2, r0, #16 + 800bf70: f80a 2c02 strb.w r2, [sl, #-2] + *tempbuff = (uint8_t)((data >> 24U) & 0xFFU); + 800bf74: 0e02 lsrs r2, r0, #24 + *tempbuff = (uint8_t)(data & 0xFFU); + 800bf76: f80a 0c04 strb.w r0, [sl, #-4] + *tempbuff = (uint8_t)((data >> 24U) & 0xFFU); + 800bf7a: f80a 2c01 strb.w r2, [sl, #-1] + for(count = 0U; count < 8U; count++) + 800bf7e: f10a 0a04 add.w sl, sl, #4 + 800bf82: 45d3 cmp fp, sl + 800bf84: d1ed bne.n 800bf62 + tempbuff++; + 800bf86: 3620 adds r6, #32 + dataremaining--; + 800bf88: 3d20 subs r5, #32 + if(((HAL_GetTick()-tickstart) >= Timeout) || (Timeout == 0U)) + 800bf8a: f7fb f8af bl 80070ec + 800bf8e: 9b10 ldr r3, [sp, #64] ; 0x40 + 800bf90: eba0 0009 sub.w r0, r0, r9 + 800bf94: 4298 cmp r0, r3 + 800bf96: d3bb bcc.n 800bf10 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800bf98: 6823 ldr r3, [r4, #0] + 800bf9a: 4a1b ldr r2, [pc, #108] ; (800c008 ) + 800bf9c: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT; + 800bf9e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800bfa0: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 800bfa4: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800bfa6: 2301 movs r3, #1 + 800bfa8: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800bfac: 2300 movs r3, #0 + 800bfae: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800bfb0: f04f 0803 mov.w r8, #3 + 800bfb4: e76c b.n 800be90 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800bfb6: 6b59 ldr r1, [r3, #52] ; 0x34 + 800bfb8: f011 0102 ands.w r1, r1, #2 + 800bfbc: d00a beq.n 800bfd4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800bfbe: 4a12 ldr r2, [pc, #72] ; (800c008 ) + 800bfc0: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL; + 800bfc2: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800bfc4: f043 0302 orr.w r3, r3, #2 + 800bfc8: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800bfca: 2301 movs r3, #1 + 800bfcc: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800bfd0: 6320 str r0, [r4, #48] ; 0x30 + return HAL_ERROR; + 800bfd2: e75d b.n 800be90 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800bfd4: 6b5a ldr r2, [r3, #52] ; 0x34 + 800bfd6: f012 0220 ands.w r2, r2, #32 + 800bfda: d00a beq.n 800bff2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800bfdc: 4a0a ldr r2, [pc, #40] ; (800c008 ) + 800bfde: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN; + 800bfe0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800bfe2: f043 0320 orr.w r3, r3, #32 + 800bfe6: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800bfe8: 2301 movs r3, #1 + 800bfea: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800bfee: 6321 str r1, [r4, #48] ; 0x30 + return HAL_ERROR; + 800bff0: e74e b.n 800be90 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800bff2: 4906 ldr r1, [pc, #24] ; (800c00c ) + 800bff4: 6399 str r1, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800bff6: 2301 movs r3, #1 + 800bff8: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800bffc: 4690 mov r8, r2 + 800bffe: e747 b.n 800be90 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c000: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c002: f043 5300 orr.w r3, r3, #536870912 ; 0x20000000 + 800c006: e72d b.n 800be64 + 800c008: 1fe00fff .word 0x1fe00fff + 800c00c: 18000f3a .word 0x18000f3a + +0800c010 : +{ + 800c010: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800c014: b089 sub sp, #36 ; 0x24 + 800c016: 4604 mov r4, r0 + 800c018: 460d mov r5, r1 + 800c01a: 4692 mov sl, r2 + 800c01c: 461f mov r7, r3 + uint32_t tickstart = HAL_GetTick(); + 800c01e: f7fb f865 bl 80070ec + 800c022: 4681 mov r9, r0 + if(NULL == pData) + 800c024: b935 cbnz r5, 800c034 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800c026: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c028: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c02c: 63a3 str r3, [r4, #56] ; 0x38 + return HAL_ERROR; + 800c02e: f04f 0801 mov.w r8, #1 + 800c032: e011 b.n 800c058 + if(hsd->State == HAL_SD_STATE_READY) + 800c034: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800c038: 2b01 cmp r3, #1 + 800c03a: fa5f f883 uxtb.w r8, r3 + 800c03e: f040 80b4 bne.w 800c1aa + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c042: 6d62 ldr r2, [r4, #84] ; 0x54 + 800c044: eb0a 0307 add.w r3, sl, r7 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c048: 2100 movs r1, #0 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c04a: 4293 cmp r3, r2 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c04c: 63a1 str r1, [r4, #56] ; 0x38 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c04e: d907 bls.n 800c060 + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + 800c050: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c052: f043 7300 orr.w r3, r3, #33554432 ; 0x2000000 + 800c056: 63a3 str r3, [r4, #56] ; 0x38 +} + 800c058: 4640 mov r0, r8 + 800c05a: b009 add sp, #36 ; 0x24 + 800c05c: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + hsd->State = HAL_SD_STATE_BUSY; + 800c060: 2303 movs r3, #3 + 800c062: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c066: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->Instance->DCTRL = 0U; + 800c068: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c06a: 2b01 cmp r3, #1 + config.DataTimeOut = SDMMC_DATATIMEOUT; + 800c06c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800c070: 9302 str r3, [sp, #8] + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800c072: ea4f 2347 mov.w r3, r7, lsl #9 + hsd->Instance->DCTRL = 0U; + 800c076: 62c1 str r1, [r0, #44] ; 0x2c + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800c078: 9303 str r3, [sp, #12] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + 800c07a: f04f 0190 mov.w r1, #144 ; 0x90 + 800c07e: f04f 0300 mov.w r3, #0 + 800c082: e9cd 1304 strd r1, r3, [sp, #16] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c086: a902 add r1, sp, #8 + config.DPSM = SDMMC_DPSM_DISABLE; + 800c088: e9cd 3306 strd r3, r3, [sp, #24] + add *= 512U; + 800c08c: bf18 it ne + 800c08e: ea4f 2a4a movne.w sl, sl, lsl #9 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c092: f000 fe95 bl 800cdc0 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c096: 6820 ldr r0, [r4, #0] + 800c098: 68c3 ldr r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c09a: 2f01 cmp r7, #1 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c09c: f043 0340 orr.w r3, r3, #64 ; 0x40 + 800c0a0: 60c3 str r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c0a2: d911 bls.n 800c0c8 + hsd->Context = SD_CONTEXT_WRITE_MULTIPLE_BLOCK; + 800c0a4: 2320 movs r3, #32 + 800c0a6: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add); + 800c0a8: 4651 mov r1, sl + 800c0aa: f000 ffc9 bl 800d040 + if(errorstate != HAL_SD_ERROR_NONE) + 800c0ae: b188 cbz r0, 800c0d4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c0b0: 6823 ldr r3, [r4, #0] + 800c0b2: 4a40 ldr r2, [pc, #256] ; (800c1b4 ) + 800c0b4: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c0b6: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c0b8: 4318 orrs r0, r3 + 800c0ba: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c0bc: 2301 movs r3, #1 + 800c0be: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c0c2: 2300 movs r3, #0 + 800c0c4: 6323 str r3, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c0c6: e7c7 b.n 800c058 + hsd->Context = SD_CONTEXT_WRITE_SINGLE_BLOCK; + 800c0c8: 2310 movs r3, #16 + 800c0ca: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, add); + 800c0cc: 4651 mov r1, sl + 800c0ce: f000 ff9e bl 800d00e + 800c0d2: e7ec b.n 800c0ae + dataremaining = config.DataLength; + 800c0d4: 9e03 ldr r6, [sp, #12] + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800c0d6: 6820 ldr r0, [r4, #0] + 800c0d8: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c0da: f413 7f8d tst.w r3, #282 ; 0x11a + 800c0de: d01b beq.n 800c118 + __SDMMC_CMDTRANS_DISABLE( hsd->Instance); + 800c0e0: 68c3 ldr r3, [r0, #12] + 800c0e2: f023 0340 bic.w r3, r3, #64 ; 0x40 + 800c0e6: 60c3 str r3, [r0, #12] + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U)) + 800c0e8: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c0ea: 05db lsls r3, r3, #23 + 800c0ec: d508 bpl.n 800c100 + 800c0ee: 2f01 cmp r7, #1 + 800c0f0: d906 bls.n 800c100 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c0f2: 6be3 ldr r3, [r4, #60] ; 0x3c + 800c0f4: 2b03 cmp r3, #3 + 800c0f6: d003 beq.n 800c100 + errorstate = SDMMC_CmdStopTransfer(hsd->Instance); + 800c0f8: f001 f838 bl 800d16c + if(errorstate != HAL_SD_ERROR_NONE) + 800c0fc: 2800 cmp r0, #0 + 800c0fe: d1d7 bne.n 800c0b0 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c100: 6823 ldr r3, [r4, #0] + 800c102: 6b58 ldr r0, [r3, #52] ; 0x34 + 800c104: f010 0008 ands.w r0, r0, #8 + 800c108: d02a beq.n 800c160 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c10a: 4a2a ldr r2, [pc, #168] ; (800c1b4 ) + 800c10c: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT; + 800c10e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c110: f043 0308 orr.w r3, r3, #8 + 800c114: 63a3 str r3, [r4, #56] ; 0x38 + 800c116: e7d1 b.n 800c0bc + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXFIFOHE) && (dataremaining > 0U)) + 800c118: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c11a: 045a lsls r2, r3, #17 + 800c11c: d50c bpl.n 800c138 + 800c11e: b15e cbz r6, 800c138 + 800c120: f105 0b20 add.w fp, r5, #32 + data |= ((uint32_t)(*tempbuff) << 24U); + 800c124: f855 3b04 ldr.w r3, [r5], #4 + (void)SDMMC_WriteFIFO(hsd->Instance, &data); + 800c128: 6820 ldr r0, [r4, #0] + data |= ((uint32_t)(*tempbuff) << 24U); + 800c12a: 9301 str r3, [sp, #4] + (void)SDMMC_WriteFIFO(hsd->Instance, &data); + 800c12c: a901 add r1, sp, #4 + 800c12e: f000 fe0e bl 800cd4e + for(count = 0U; count < 8U; count++) + 800c132: 45ab cmp fp, r5 + 800c134: d1f6 bne.n 800c124 + dataremaining--; + 800c136: 3e20 subs r6, #32 + if(((HAL_GetTick()-tickstart) >= Timeout) || (Timeout == 0U)) + 800c138: f7fa ffd8 bl 80070ec + 800c13c: 9b12 ldr r3, [sp, #72] ; 0x48 + 800c13e: eba0 0009 sub.w r0, r0, r9 + 800c142: 4298 cmp r0, r3 + 800c144: d3c7 bcc.n 800c0d6 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c146: 6823 ldr r3, [r4, #0] + 800c148: 4a1a ldr r2, [pc, #104] ; (800c1b4 ) + 800c14a: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c14c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c14e: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c150: 2301 movs r3, #1 + 800c152: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c156: 2300 movs r3, #0 + 800c158: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800c15a: f04f 0803 mov.w r8, #3 + 800c15e: e77b b.n 800c058 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c160: 6b59 ldr r1, [r3, #52] ; 0x34 + 800c162: f011 0102 ands.w r1, r1, #2 + 800c166: d00a beq.n 800c17e + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c168: 4a12 ldr r2, [pc, #72] ; (800c1b4 ) + 800c16a: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL; + 800c16c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c16e: f043 0302 orr.w r3, r3, #2 + 800c172: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c174: 2301 movs r3, #1 + 800c176: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c17a: 6320 str r0, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c17c: e76c b.n 800c058 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR)) + 800c17e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c180: f012 0210 ands.w r2, r2, #16 + 800c184: d00a beq.n 800c19c + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c186: 4a0b ldr r2, [pc, #44] ; (800c1b4 ) + 800c188: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_TX_UNDERRUN; + 800c18a: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c18c: f043 0310 orr.w r3, r3, #16 + 800c190: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c192: 2301 movs r3, #1 + 800c194: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c198: 6321 str r1, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c19a: e75d b.n 800c058 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c19c: 4906 ldr r1, [pc, #24] ; (800c1b8 ) + 800c19e: 6399 str r1, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c1a0: 2301 movs r3, #1 + 800c1a2: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800c1a6: 4690 mov r8, r2 + 800c1a8: e756 b.n 800c058 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c1aa: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c1ac: f043 5300 orr.w r3, r3, #536870912 ; 0x20000000 + 800c1b0: e73c b.n 800c02c + 800c1b2: bf00 nop + 800c1b4: 1fe00fff .word 0x1fe00fff + 800c1b8: 18000f3a .word 0x18000f3a + +0800c1bc : + return hsd->State; + 800c1bc: f890 0034 ldrb.w r0, [r0, #52] ; 0x34 +} + 800c1c0: 4770 bx lr + +0800c1c2 : + return hsd->ErrorCode; + 800c1c2: 6b80 ldr r0, [r0, #56] ; 0x38 +} + 800c1c4: 4770 bx lr + +0800c1c6 : + 800c1c6: 4770 bx lr + +0800c1c8 : + 800c1c8: 4770 bx lr + +0800c1ca : + 800c1ca: 4770 bx lr + +0800c1cc : + 800c1cc: 4770 bx lr + ... + +0800c1d0 : + pCSD->CSDStruct = (uint8_t)((hsd->CSD[0] & 0xC0000000U) >> 30U); + 800c1d0: 6e03 ldr r3, [r0, #96] ; 0x60 + 800c1d2: 0f9a lsrs r2, r3, #30 + 800c1d4: 700a strb r2, [r1, #0] + pCSD->SysSpecVersion = (uint8_t)((hsd->CSD[0] & 0x3C000000U) >> 26U); + 800c1d6: f3c3 6283 ubfx r2, r3, #26, #4 + 800c1da: 704a strb r2, [r1, #1] + pCSD->Reserved1 = (uint8_t)((hsd->CSD[0] & 0x03000000U) >> 24U); + 800c1dc: f3c3 6201 ubfx r2, r3, #24, #2 + 800c1e0: 708a strb r2, [r1, #2] + pCSD->TAAC = (uint8_t)((hsd->CSD[0] & 0x00FF0000U) >> 16U); + 800c1e2: f3c3 4207 ubfx r2, r3, #16, #8 + 800c1e6: 70ca strb r2, [r1, #3] + pCSD->NSAC = (uint8_t)((hsd->CSD[0] & 0x0000FF00U) >> 8U); + 800c1e8: f3c3 2207 ubfx r2, r3, #8, #8 + pCSD->MaxBusClkFrec = (uint8_t)(hsd->CSD[0] & 0x000000FFU); + 800c1ec: b2db uxtb r3, r3 + pCSD->NSAC = (uint8_t)((hsd->CSD[0] & 0x0000FF00U) >> 8U); + 800c1ee: 710a strb r2, [r1, #4] + pCSD->MaxBusClkFrec = (uint8_t)(hsd->CSD[0] & 0x000000FFU); + 800c1f0: 714b strb r3, [r1, #5] + pCSD->CardComdClasses = (uint16_t)((hsd->CSD[1] & 0xFFF00000U) >> 20U); + 800c1f2: 6e43 ldr r3, [r0, #100] ; 0x64 + 800c1f4: 0d1a lsrs r2, r3, #20 + 800c1f6: 80ca strh r2, [r1, #6] + pCSD->RdBlockLen = (uint8_t)((hsd->CSD[1] & 0x000F0000U) >> 16U); + 800c1f8: f3c3 4203 ubfx r2, r3, #16, #4 + 800c1fc: 720a strb r2, [r1, #8] + pCSD->PartBlockRead = (uint8_t)((hsd->CSD[1] & 0x00008000U) >> 15U); + 800c1fe: f3c3 32c0 ubfx r2, r3, #15, #1 + 800c202: 724a strb r2, [r1, #9] + pCSD->WrBlockMisalign = (uint8_t)((hsd->CSD[1] & 0x00004000U) >> 14U); + 800c204: f3c3 3280 ubfx r2, r3, #14, #1 + 800c208: 728a strb r2, [r1, #10] + pCSD->RdBlockMisalign = (uint8_t)((hsd->CSD[1] & 0x00002000U) >> 13U); + 800c20a: f3c3 3240 ubfx r2, r3, #13, #1 + 800c20e: 72ca strb r2, [r1, #11] + pCSD->DSRImpl = (uint8_t)((hsd->CSD[1] & 0x00001000U) >> 12U); + 800c210: f3c3 3200 ubfx r2, r3, #12, #1 + 800c214: 730a strb r2, [r1, #12] + pCSD->Reserved2 = 0U; /*!< Reserved */ + 800c216: 2200 movs r2, #0 + 800c218: 734a strb r2, [r1, #13] + if(hsd->SdCard.CardType == CARD_SDSC) + 800c21a: 6bc2 ldr r2, [r0, #60] ; 0x3c +{ + 800c21c: b510 push {r4, lr} + if(hsd->SdCard.CardType == CARD_SDSC) + 800c21e: 2a00 cmp r2, #0 + 800c220: d16c bne.n 800c2fc + pCSD->DeviceSize = (((hsd->CSD[1] & 0x000003FFU) << 2U) | ((hsd->CSD[2] & 0xC0000000U) >> 30U)); + 800c222: 6e82 ldr r2, [r0, #104] ; 0x68 + 800c224: f640 74fc movw r4, #4092 ; 0xffc + 800c228: ea04 0383 and.w r3, r4, r3, lsl #2 + 800c22c: ea43 7392 orr.w r3, r3, r2, lsr #30 + 800c230: 610b str r3, [r1, #16] + pCSD->MaxRdCurrentVDDMin = (uint8_t)((hsd->CSD[2] & 0x38000000U) >> 27U); + 800c232: f3c2 63c2 ubfx r3, r2, #27, #3 + 800c236: 750b strb r3, [r1, #20] + pCSD->MaxRdCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x07000000U) >> 24U); + 800c238: f3c2 6302 ubfx r3, r2, #24, #3 + 800c23c: 754b strb r3, [r1, #21] + pCSD->MaxWrCurrentVDDMin = (uint8_t)((hsd->CSD[2] & 0x00E00000U) >> 21U); + 800c23e: f3c2 5342 ubfx r3, r2, #21, #3 + 800c242: 758b strb r3, [r1, #22] + pCSD->MaxWrCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x001C0000U) >> 18U); + 800c244: f3c2 4382 ubfx r3, r2, #18, #3 + pCSD->DeviceSizeMul = (uint8_t)((hsd->CSD[2] & 0x00038000U) >> 15U); + 800c248: f3c2 32c2 ubfx r2, r2, #15, #3 + pCSD->MaxWrCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x001C0000U) >> 18U); + 800c24c: 75cb strb r3, [r1, #23] + pCSD->DeviceSizeMul = (uint8_t)((hsd->CSD[2] & 0x00038000U) >> 15U); + 800c24e: 760a strb r2, [r1, #24] + hsd->SdCard.BlockNbr = (pCSD->DeviceSize + 1U) ; + 800c250: 690b ldr r3, [r1, #16] + hsd->SdCard.BlockNbr *= (1UL << ((pCSD->DeviceSizeMul & 0x07U) + 2U)); + 800c252: 7e0a ldrb r2, [r1, #24] + 800c254: f002 0207 and.w r2, r2, #7 + hsd->SdCard.BlockNbr = (pCSD->DeviceSize + 1U) ; + 800c258: 3301 adds r3, #1 + hsd->SdCard.BlockNbr *= (1UL << ((pCSD->DeviceSizeMul & 0x07U) + 2U)); + 800c25a: 3202 adds r2, #2 + 800c25c: fa03 f202 lsl.w r2, r3, r2 + 800c260: 64c2 str r2, [r0, #76] ; 0x4c + hsd->SdCard.BlockSize = (1UL << (pCSD->RdBlockLen & 0x0FU)); + 800c262: 7a0b ldrb r3, [r1, #8] + 800c264: f003 040f and.w r4, r3, #15 + 800c268: 2301 movs r3, #1 + 800c26a: 40a3 lsls r3, r4 + 800c26c: 6503 str r3, [r0, #80] ; 0x50 + hsd->SdCard.LogBlockNbr = (hsd->SdCard.BlockNbr) * ((hsd->SdCard.BlockSize) / 512U); + 800c26e: 0a5b lsrs r3, r3, #9 + 800c270: 4353 muls r3, r2 + 800c272: 6543 str r3, [r0, #84] ; 0x54 + hsd->SdCard.LogBlockSize = 512U; + 800c274: f44f 7300 mov.w r3, #512 ; 0x200 + hsd->SdCard.LogBlockSize = hsd->SdCard.BlockSize; + 800c278: 6583 str r3, [r0, #88] ; 0x58 + pCSD->EraseGrSize = (uint8_t)((hsd->CSD[2] & 0x00004000U) >> 14U); + 800c27a: 6e83 ldr r3, [r0, #104] ; 0x68 + 800c27c: f3c3 3280 ubfx r2, r3, #14, #1 + 800c280: 764a strb r2, [r1, #25] + pCSD->EraseGrMul = (uint8_t)((hsd->CSD[2] & 0x00003F80U) >> 7U); + 800c282: f3c3 12c6 ubfx r2, r3, #7, #7 + pCSD->WrProtectGrSize = (uint8_t)(hsd->CSD[2] & 0x0000007FU); + 800c286: f003 037f and.w r3, r3, #127 ; 0x7f + pCSD->EraseGrMul = (uint8_t)((hsd->CSD[2] & 0x00003F80U) >> 7U); + 800c28a: 768a strb r2, [r1, #26] + pCSD->WrProtectGrSize = (uint8_t)(hsd->CSD[2] & 0x0000007FU); + 800c28c: 76cb strb r3, [r1, #27] + pCSD->WrProtectGrEnable = (uint8_t)((hsd->CSD[3] & 0x80000000U) >> 31U); + 800c28e: 6ec3 ldr r3, [r0, #108] ; 0x6c + 800c290: 0fda lsrs r2, r3, #31 + 800c292: 770a strb r2, [r1, #28] + pCSD->ManDeflECC = (uint8_t)((hsd->CSD[3] & 0x60000000U) >> 29U); + 800c294: f3c3 7241 ubfx r2, r3, #29, #2 + 800c298: 774a strb r2, [r1, #29] + pCSD->WrSpeedFact = (uint8_t)((hsd->CSD[3] & 0x1C000000U) >> 26U); + 800c29a: f3c3 6282 ubfx r2, r3, #26, #3 + 800c29e: 778a strb r2, [r1, #30] + pCSD->MaxWrBlockLen= (uint8_t)((hsd->CSD[3] & 0x03C00000U) >> 22U); + 800c2a0: f3c3 5283 ubfx r2, r3, #22, #4 + 800c2a4: 77ca strb r2, [r1, #31] + pCSD->WriteBlockPaPartial = (uint8_t)((hsd->CSD[3] & 0x00200000U) >> 21U); + 800c2a6: f3c3 5240 ubfx r2, r3, #21, #1 + 800c2aa: f881 2020 strb.w r2, [r1, #32] + pCSD->Reserved3 = 0; + 800c2ae: 2000 movs r0, #0 + pCSD->ContentProtectAppli = (uint8_t)((hsd->CSD[3] & 0x00010000U) >> 16U); + 800c2b0: f3c3 4200 ubfx r2, r3, #16, #1 + pCSD->Reserved3 = 0; + 800c2b4: f881 0021 strb.w r0, [r1, #33] ; 0x21 + pCSD->ContentProtectAppli = (uint8_t)((hsd->CSD[3] & 0x00010000U) >> 16U); + 800c2b8: f881 2022 strb.w r2, [r1, #34] ; 0x22 + pCSD->FileFormatGroup = (uint8_t)((hsd->CSD[3] & 0x00008000U) >> 15U); + 800c2bc: f3c3 32c0 ubfx r2, r3, #15, #1 + 800c2c0: f881 2023 strb.w r2, [r1, #35] ; 0x23 + pCSD->CopyFlag = (uint8_t)((hsd->CSD[3] & 0x00004000U) >> 14U); + 800c2c4: f3c3 3280 ubfx r2, r3, #14, #1 + 800c2c8: f881 2024 strb.w r2, [r1, #36] ; 0x24 + pCSD->PermWrProtect = (uint8_t)((hsd->CSD[3] & 0x00002000U) >> 13U); + 800c2cc: f3c3 3240 ubfx r2, r3, #13, #1 + 800c2d0: f881 2025 strb.w r2, [r1, #37] ; 0x25 + pCSD->TempWrProtect = (uint8_t)((hsd->CSD[3] & 0x00001000U) >> 12U); + 800c2d4: f3c3 3200 ubfx r2, r3, #12, #1 + 800c2d8: f881 2026 strb.w r2, [r1, #38] ; 0x26 + pCSD->FileFormat = (uint8_t)((hsd->CSD[3] & 0x00000C00U) >> 10U); + 800c2dc: f3c3 2281 ubfx r2, r3, #10, #2 + 800c2e0: f881 2027 strb.w r2, [r1, #39] ; 0x27 + pCSD->ECC= (uint8_t)((hsd->CSD[3] & 0x00000300U) >> 8U); + 800c2e4: f3c3 2201 ubfx r2, r3, #8, #2 + pCSD->CSD_CRC = (uint8_t)((hsd->CSD[3] & 0x000000FEU) >> 1U); + 800c2e8: f3c3 0346 ubfx r3, r3, #1, #7 + pCSD->ECC= (uint8_t)((hsd->CSD[3] & 0x00000300U) >> 8U); + 800c2ec: f881 2028 strb.w r2, [r1, #40] ; 0x28 + pCSD->CSD_CRC = (uint8_t)((hsd->CSD[3] & 0x000000FEU) >> 1U); + 800c2f0: f881 3029 strb.w r3, [r1, #41] ; 0x29 + pCSD->Reserved4 = 1; + 800c2f4: 2301 movs r3, #1 + 800c2f6: f881 302a strb.w r3, [r1, #42] ; 0x2a +} + 800c2fa: bd10 pop {r4, pc} + else if(hsd->SdCard.CardType == CARD_SDHC_SDXC) + 800c2fc: 2a01 cmp r2, #1 + 800c2fe: d10f bne.n 800c320 + pCSD->DeviceSize = (((hsd->CSD[1] & 0x0000003FU) << 16U) | ((hsd->CSD[2] & 0xFFFF0000U) >> 16U)); + 800c300: f8b0 206a ldrh.w r2, [r0, #106] ; 0x6a + 800c304: 041b lsls r3, r3, #16 + 800c306: f403 137c and.w r3, r3, #4128768 ; 0x3f0000 + 800c30a: 4313 orrs r3, r2 + 800c30c: 610b str r3, [r1, #16] + hsd->SdCard.BlockNbr = ((pCSD->DeviceSize + 1U) * 1024U); + 800c30e: 690b ldr r3, [r1, #16] + 800c310: 3301 adds r3, #1 + 800c312: 029b lsls r3, r3, #10 + 800c314: 64c3 str r3, [r0, #76] ; 0x4c + hsd->SdCard.LogBlockNbr = hsd->SdCard.BlockNbr; + 800c316: 6543 str r3, [r0, #84] ; 0x54 + hsd->SdCard.BlockSize = 512U; + 800c318: f44f 7300 mov.w r3, #512 ; 0x200 + 800c31c: 6503 str r3, [r0, #80] ; 0x50 + 800c31e: e7ab b.n 800c278 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c320: 6803 ldr r3, [r0, #0] + 800c322: 4a05 ldr r2, [pc, #20] ; (800c338 ) + 800c324: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c326: 6b83 ldr r3, [r0, #56] ; 0x38 + 800c328: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800c32c: 6383 str r3, [r0, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c32e: 2301 movs r3, #1 + 800c330: f880 3034 strb.w r3, [r0, #52] ; 0x34 + return HAL_ERROR; + 800c334: 4618 mov r0, r3 + 800c336: e7e0 b.n 800c2fa + 800c338: 1fe00fff .word 0x1fe00fff + +0800c33c : +{ + 800c33c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + 800c340: 2300 movs r3, #0 +{ + 800c342: b099 sub sp, #100 ; 0x64 + 800c344: 4604 mov r4, r0 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800c346: f44f 2000 mov.w r0, #524288 ; 0x80000 + Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + 800c34a: e9cd 3307 strd r3, r3, [sp, #28] + Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + 800c34e: e9cd 3309 strd r3, r3, [sp, #36] ; 0x24 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800c352: f7fd fb2d bl 80099b0 + if (sdmmc_clk == 0U) + 800c356: 4605 mov r5, r0 + 800c358: b948 cbnz r0, 800c36e + hsd->State = HAL_SD_STATE_READY; + 800c35a: 2501 movs r5, #1 + hsd->ErrorCode = SDMMC_ERROR_INVALID_PARAMETER; + 800c35c: f04f 6300 mov.w r3, #134217728 ; 0x8000000 + hsd->State = HAL_SD_STATE_READY; + 800c360: f884 5034 strb.w r5, [r4, #52] ; 0x34 + hsd->ErrorCode = SDMMC_ERROR_INVALID_PARAMETER; + 800c364: 63a3 str r3, [r4, #56] ; 0x38 +} + 800c366: 4628 mov r0, r5 + 800c368: b019 add sp, #100 ; 0x64 + 800c36a: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + Init.Transceiver = hsd->Init.Transceiver; + 800c36e: 69a3 ldr r3, [r4, #24] + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c370: 6827 ldr r7, [r4, #0] + Init.Transceiver = hsd->Init.Transceiver; + 800c372: 930c str r3, [sp, #48] ; 0x30 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c374: 2b01 cmp r3, #1 + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c376: bf08 it eq + 800c378: 683b ldreq r3, [r7, #0] + Init.ClockDiv = sdmmc_clk / (2U * SD_INIT_FREQ); + 800c37a: 4e99 ldr r6, [pc, #612] ; (800c5e0 ) + 800c37c: fbb0 f6f6 udiv r6, r0, r6 + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c380: bf04 itt eq + 800c382: f043 0310 orreq.w r3, r3, #16 + 800c386: 603b streq r3, [r7, #0] + status = SDMMC_Init(hsd->Instance, Init); + 800c388: 960b str r6, [sp, #44] ; 0x2c + 800c38a: ab0a add r3, sp, #40 ; 0x28 + 800c38c: e893 0007 ldmia.w r3, {r0, r1, r2} + 800c390: e88d 0007 stmia.w sp, {r0, r1, r2} + 800c394: ab07 add r3, sp, #28 + 800c396: cb0e ldmia r3, {r1, r2, r3} + 800c398: 4638 mov r0, r7 + 800c39a: f000 fcbb bl 800cd14 + if(status != HAL_OK) + 800c39e: b108 cbz r0, 800c3a4 + return HAL_ERROR; + 800c3a0: 2501 movs r5, #1 + 800c3a2: e7e0 b.n 800c366 + status = SDMMC_PowerState_ON(hsd->Instance); + 800c3a4: 6820 ldr r0, [r4, #0] + 800c3a6: f000 fcd7 bl 800cd58 + if(status != HAL_OK) + 800c3aa: 4607 mov r7, r0 + 800c3ac: 2800 cmp r0, #0 + 800c3ae: d1f7 bne.n 800c3a0 + sdmmc_clk = sdmmc_clk/(2U*Init.ClockDiv); + 800c3b0: 0076 lsls r6, r6, #1 + HAL_Delay(1U+ (74U*1000U/(sdmmc_clk))); + 800c3b2: 488c ldr r0, [pc, #560] ; (800c5e4 ) + sdmmc_clk = sdmmc_clk/(2U*Init.ClockDiv); + 800c3b4: fbb5 f5f6 udiv r5, r5, r6 + HAL_Delay(1U+ (74U*1000U/(sdmmc_clk))); + 800c3b8: fbb0 f0f5 udiv r0, r0, r5 + 800c3bc: 3001 adds r0, #1 + 800c3be: f7f7 faa8 bl 8003912 + __IO uint32_t count = 0U; + 800c3c2: 9706 str r7, [sp, #24] + uint32_t tickstart = HAL_GetTick(); + 800c3c4: f7fa fe92 bl 80070ec + 800c3c8: 4606 mov r6, r0 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c3ca: 6820 ldr r0, [r4, #0] + 800c3cc: f000 fd18 bl 800ce00 + if(errorstate != HAL_SD_ERROR_NONE) + 800c3d0: 4605 mov r5, r0 + 800c3d2: b940 cbnz r0, 800c3e6 + errorstate = SDMMC_CmdOperCond(hsd->Instance); + 800c3d4: 6820 ldr r0, [r4, #0] + 800c3d6: f001 f8fd bl 800d5d4 + if(errorstate != HAL_SD_ERROR_NONE) + 800c3da: b158 cbz r0, 800c3f4 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c3dc: 6820 ldr r0, [r4, #0] + hsd->SdCard.CardVersion = CARD_V1_X; + 800c3de: 6425 str r5, [r4, #64] ; 0x40 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c3e0: f000 fd0e bl 800ce00 + if(errorstate != HAL_SD_ERROR_NONE) + 800c3e4: b180 cbz r0, 800c408 + hsd->State = HAL_SD_STATE_READY; + 800c3e6: 2501 movs r5, #1 + 800c3e8: f884 5034 strb.w r5, [r4, #52] ; 0x34 + hsd->ErrorCode |= errorstate; + 800c3ec: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c3ee: 4318 orrs r0, r3 + 800c3f0: 63a0 str r0, [r4, #56] ; 0x38 + return HAL_ERROR; + 800c3f2: e7b8 b.n 800c366 + hsd->SdCard.CardVersion = CARD_V2_X; + 800c3f4: 2301 movs r3, #1 + 800c3f6: 6423 str r3, [r4, #64] ; 0x40 + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c3f8: 6820 ldr r0, [r4, #0] + 800c3fa: 2100 movs r1, #0 + 800c3fc: f000 fef5 bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c400: b128 cbz r0, 800c40e + return HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c402: f04f 5080 mov.w r0, #268435456 ; 0x10000000 + 800c406: e7ee b.n 800c3e6 + if( hsd->SdCard.CardVersion == CARD_V2_X) + 800c408: 6c23 ldr r3, [r4, #64] ; 0x40 + 800c40a: 2b01 cmp r3, #1 + 800c40c: d0f4 beq.n 800c3f8 + errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_VOLTAGE_WINDOW_SD | SDMMC_HIGH_CAPACITY | SD_SWITCH_1_8V_CAPACITY); + 800c40e: f8df 91dc ldr.w r9, [pc, #476] ; 800c5ec +{ + 800c412: 2700 movs r7, #0 + while((count < SDMMC_MAX_VOLT_TRIAL) && (validvoltage == 0U)) + 800c414: f64f 78fe movw r8, #65534 ; 0xfffe + 800c418: 9b06 ldr r3, [sp, #24] + 800c41a: 4543 cmp r3, r8 + 800c41c: d800 bhi.n 800c420 + 800c41e: b12f cbz r7, 800c42c + if(count >= SDMMC_MAX_VOLT_TRIAL) + 800c420: 9b06 ldr r3, [sp, #24] + 800c422: 4543 cmp r3, r8 + 800c424: d918 bls.n 800c458 + return HAL_SD_ERROR_INVALID_VOLTRANGE; + 800c426: f04f 7080 mov.w r0, #16777216 ; 0x1000000 + 800c42a: e7dc b.n 800c3e6 + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c42c: 6820 ldr r0, [r4, #0] + 800c42e: 4639 mov r1, r7 + 800c430: f000 fedb bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c434: 2800 cmp r0, #0 + 800c436: d1d6 bne.n 800c3e6 + errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_VOLTAGE_WINDOW_SD | SDMMC_HIGH_CAPACITY | SD_SWITCH_1_8V_CAPACITY); + 800c438: 6820 ldr r0, [r4, #0] + 800c43a: 4649 mov r1, r9 + 800c43c: f001 f816 bl 800d46c + if(errorstate != HAL_SD_ERROR_NONE) + 800c440: 2800 cmp r0, #0 + 800c442: d1de bne.n 800c402 + response = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c444: 4639 mov r1, r7 + 800c446: 6820 ldr r0, [r4, #0] + 800c448: f000 fcb7 bl 800cdba + count++; + 800c44c: 9b06 ldr r3, [sp, #24] + 800c44e: 3301 adds r3, #1 + response = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c450: 4605 mov r5, r0 + validvoltage = (((response >> 31U) == 1U) ? 1U : 0U); + 800c452: 0fc7 lsrs r7, r0, #31 + count++; + 800c454: 9306 str r3, [sp, #24] + 800c456: e7df b.n 800c418 + if((response & SDMMC_HIGH_CAPACITY) == SDMMC_HIGH_CAPACITY) /* (response &= SD_HIGH_CAPACITY) */ + 800c458: f015 4380 ands.w r3, r5, #1073741824 ; 0x40000000 + 800c45c: d04b beq.n 800c4f6 + hsd->SdCard.CardType = CARD_SDHC_SDXC; + 800c45e: 2301 movs r3, #1 + 800c460: 63e3 str r3, [r4, #60] ; 0x3c + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c462: 69a3 ldr r3, [r4, #24] + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c464: 6820 ldr r0, [r4, #0] + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c466: 2b01 cmp r3, #1 + 800c468: d12d bne.n 800c4c6 + if((response & SD_SWITCH_1_8V_CAPACITY) == SD_SWITCH_1_8V_CAPACITY) + 800c46a: 01ef lsls r7, r5, #7 + 800c46c: d52b bpl.n 800c4c6 + hsd->SdCard.CardSpeed = CARD_ULTRA_HIGH_SPEED; + 800c46e: f44f 7300 mov.w r3, #512 ; 0x200 + 800c472: 65e3 str r3, [r4, #92] ; 0x5c + hsd->Instance->POWER |= SDMMC_POWER_VSWITCHEN; + 800c474: 6803 ldr r3, [r0, #0] + 800c476: f043 0308 orr.w r3, r3, #8 + 800c47a: 6003 str r3, [r0, #0] + errorstate = SDMMC_CmdVoltageSwitch(hsd->Instance); + 800c47c: f000 ff4e bl 800d31c + if(errorstate != HAL_SD_ERROR_NONE) + 800c480: 2800 cmp r0, #0 + 800c482: d1b0 bne.n 800c3e6 + while(( hsd->Instance->STA & SDMMC_FLAG_CKSTOP) != SDMMC_FLAG_CKSTOP) + 800c484: 6823 ldr r3, [r4, #0] + 800c486: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c488: 0155 lsls r5, r2, #5 + 800c48a: d526 bpl.n 800c4da + hsd->Instance->ICR = SDMMC_FLAG_CKSTOP; + 800c48c: f04f 6280 mov.w r2, #67108864 ; 0x4000000 + 800c490: 639a str r2, [r3, #56] ; 0x38 + if(( hsd->Instance->STA & SDMMC_FLAG_BUSYD0) != SDMMC_FLAG_BUSYD0) + 800c492: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c494: 02d8 lsls r0, r3, #11 + 800c496: d5b4 bpl.n 800c402 + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800c498: 2001 movs r0, #1 + 800c49a: f7fa fe97 bl 80071cc + hsd->Instance->POWER |= SDMMC_POWER_VSWITCH; + 800c49e: 6822 ldr r2, [r4, #0] + 800c4a0: 6813 ldr r3, [r2, #0] + 800c4a2: f043 0304 orr.w r3, r3, #4 + 800c4a6: 6013 str r3, [r2, #0] + while(( hsd->Instance->STA & SDMMC_FLAG_VSWEND) != SDMMC_FLAG_VSWEND) + 800c4a8: 6823 ldr r3, [r4, #0] + 800c4aa: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c4ac: 0191 lsls r1, r2, #6 + 800c4ae: d51c bpl.n 800c4ea + hsd->Instance->ICR = SDMMC_FLAG_VSWEND; + 800c4b0: f04f 7200 mov.w r2, #33554432 ; 0x2000000 + 800c4b4: 639a str r2, [r3, #56] ; 0x38 + if(( hsd->Instance->STA & SDMMC_FLAG_BUSYD0) == SDMMC_FLAG_BUSYD0) + 800c4b6: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c4b8: 02d2 lsls r2, r2, #11 + 800c4ba: d4b4 bmi.n 800c426 + hsd->Instance->POWER = 0x13U; + 800c4bc: 2213 movs r2, #19 + 800c4be: 601a str r2, [r3, #0] + hsd->Instance->ICR = 0xFFFFFFFFU; + 800c4c0: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800c4c4: 639a str r2, [r3, #56] ; 0x38 + uint16_t sd_rca = 1U; + 800c4c6: 2301 movs r3, #1 + if(SDMMC_GetPowerState(hsd->Instance) == 0U) + 800c4c8: 6820 ldr r0, [r4, #0] + uint16_t sd_rca = 1U; + 800c4ca: f8ad 3016 strh.w r3, [sp, #22] + if(SDMMC_GetPowerState(hsd->Instance) == 0U) + 800c4ce: f000 fc59 bl 800cd84 + 800c4d2: b990 cbnz r0, 800c4fa + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800c4d4: f04f 6080 mov.w r0, #67108864 ; 0x4000000 + 800c4d8: e785 b.n 800c3e6 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c4da: f7fa fe07 bl 80070ec + 800c4de: 1b80 subs r0, r0, r6 + 800c4e0: 3001 adds r0, #1 + 800c4e2: d1cf bne.n 800c484 + return HAL_SD_ERROR_TIMEOUT; + 800c4e4: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800c4e8: e77d b.n 800c3e6 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c4ea: f7fa fdff bl 80070ec + 800c4ee: 1b80 subs r0, r0, r6 + 800c4f0: 3001 adds r0, #1 + 800c4f2: d1d9 bne.n 800c4a8 + 800c4f4: e7f6 b.n 800c4e4 + hsd->SdCard.CardType = CARD_SDSC; + 800c4f6: 63e3 str r3, [r4, #60] ; 0x3c + if(errorstate != HAL_SD_ERROR_NONE) + 800c4f8: e7e5 b.n 800c4c6 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c4fa: 6be3 ldr r3, [r4, #60] ; 0x3c + 800c4fc: 2b03 cmp r3, #3 + 800c4fe: d045 beq.n 800c58c + errorstate = SDMMC_CmdSendCID(hsd->Instance); + 800c500: 6820 ldr r0, [r4, #0] + 800c502: f000 ff65 bl 800d3d0 + if(errorstate != HAL_SD_ERROR_NONE) + 800c506: 2800 cmp r0, #0 + 800c508: f47f af6d bne.w 800c3e6 + hsd->CID[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c50c: 4601 mov r1, r0 + 800c50e: 6820 ldr r0, [r4, #0] + 800c510: f000 fc53 bl 800cdba + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c514: 2104 movs r1, #4 + hsd->CID[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c516: 6720 str r0, [r4, #112] ; 0x70 + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c518: 6820 ldr r0, [r4, #0] + 800c51a: f000 fc4e bl 800cdba + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c51e: 2108 movs r1, #8 + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c520: 6760 str r0, [r4, #116] ; 0x74 + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c522: 6820 ldr r0, [r4, #0] + 800c524: f000 fc49 bl 800cdba + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c528: 210c movs r1, #12 + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c52a: 67a0 str r0, [r4, #120] ; 0x78 + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c52c: 6820 ldr r0, [r4, #0] + 800c52e: f000 fc44 bl 800cdba + if(hsd->SdCard.CardType != CARD_SECURED) + 800c532: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c534: 67e0 str r0, [r4, #124] ; 0x7c + if(hsd->SdCard.CardType != CARD_SECURED) + 800c536: 2b03 cmp r3, #3 + 800c538: d028 beq.n 800c58c + errorstate = SDMMC_CmdSetRelAdd(hsd->Instance, &sd_rca); + 800c53a: 6820 ldr r0, [r4, #0] + 800c53c: f10d 0116 add.w r1, sp, #22 + 800c540: f001 f804 bl 800d54c + if(errorstate != HAL_SD_ERROR_NONE) + 800c544: 2800 cmp r0, #0 + 800c546: f47f af4e bne.w 800c3e6 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c54a: 6be3 ldr r3, [r4, #60] ; 0x3c + errorstate = SDMMC_CmdSendCSD(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c54c: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SECURED) + 800c54e: 2b03 cmp r3, #3 + 800c550: d01c beq.n 800c58c + hsd->SdCard.RelCardAdd = sd_rca; + 800c552: f8bd 1016 ldrh.w r1, [sp, #22] + 800c556: 64a1 str r1, [r4, #72] ; 0x48 + errorstate = SDMMC_CmdSendCSD(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c558: 0409 lsls r1, r1, #16 + 800c55a: f000 ff4f bl 800d3fc + if(errorstate != HAL_SD_ERROR_NONE) + 800c55e: 2800 cmp r0, #0 + 800c560: f47f af41 bne.w 800c3e6 + hsd->CSD[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c564: 4601 mov r1, r0 + 800c566: 6820 ldr r0, [r4, #0] + 800c568: f000 fc27 bl 800cdba + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c56c: 2104 movs r1, #4 + hsd->CSD[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c56e: 6620 str r0, [r4, #96] ; 0x60 + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c570: 6820 ldr r0, [r4, #0] + 800c572: f000 fc22 bl 800cdba + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c576: 2108 movs r1, #8 + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c578: 6660 str r0, [r4, #100] ; 0x64 + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c57a: 6820 ldr r0, [r4, #0] + 800c57c: f000 fc1d bl 800cdba + hsd->CSD[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c580: 210c movs r1, #12 + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c582: 66a0 str r0, [r4, #104] ; 0x68 + hsd->CSD[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c584: 6820 ldr r0, [r4, #0] + 800c586: f000 fc18 bl 800cdba + 800c58a: 66e0 str r0, [r4, #108] ; 0x6c + hsd->SdCard.Class = (SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2) >> 20U); + 800c58c: 2104 movs r1, #4 + 800c58e: 6820 ldr r0, [r4, #0] + 800c590: f000 fc13 bl 800cdba + 800c594: 0d00 lsrs r0, r0, #20 + 800c596: 6460 str r0, [r4, #68] ; 0x44 + if (HAL_SD_GetCardCSD(hsd, &CSD) != HAL_OK) + 800c598: a90d add r1, sp, #52 ; 0x34 + 800c59a: 4620 mov r0, r4 + 800c59c: f7ff fe18 bl 800c1d0 + 800c5a0: 4605 mov r5, r0 + 800c5a2: 2800 cmp r0, #0 + 800c5a4: f47f af2d bne.w 800c402 + errorstate = SDMMC_CmdSelDesel(hsd->Instance, (uint32_t)(((uint32_t)hsd->SdCard.RelCardAdd) << 16U)); + 800c5a8: 6ca2 ldr r2, [r4, #72] ; 0x48 + 800c5aa: 4603 mov r3, r0 + 800c5ac: 0412 lsls r2, r2, #16 + 800c5ae: 6820 ldr r0, [r4, #0] + 800c5b0: f000 fe02 bl 800d1b8 + if(errorstate != HAL_SD_ERROR_NONE) + 800c5b4: 2800 cmp r0, #0 + 800c5b6: f47f af16 bne.w 800c3e6 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800c5ba: 6820 ldr r0, [r4, #0] + 800c5bc: f44f 7100 mov.w r1, #512 ; 0x200 + 800c5c0: f000 fcda bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800c5c4: 2800 cmp r0, #0 + 800c5c6: f43f aece beq.w 800c366 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c5ca: 6823 ldr r3, [r4, #0] + 800c5cc: 4a06 ldr r2, [pc, #24] ; (800c5e8 ) + 800c5ce: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c5d0: 6ba3 ldr r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c5d2: 2501 movs r5, #1 + hsd->ErrorCode |= errorstate; + 800c5d4: 4318 orrs r0, r3 + 800c5d6: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c5d8: f884 5034 strb.w r5, [r4, #52] ; 0x34 + return HAL_ERROR; + 800c5dc: e6c3 b.n 800c366 + 800c5de: bf00 nop + 800c5e0: 000c3500 .word 0x000c3500 + 800c5e4: 00012110 .word 0x00012110 + 800c5e8: 1fe00fff .word 0x1fe00fff + 800c5ec: c1100000 .word 0xc1100000 + +0800c5f0 : +{ + 800c5f0: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800c5f4: b096 sub sp, #88 ; 0x58 + 800c5f6: 4604 mov r4, r0 + 800c5f8: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800c5fa: f7fa fd77 bl 80070ec + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c5fe: 2100 movs r1, #0 + uint32_t tickstart = HAL_GetTick(); + 800c600: 4606 mov r6, r0 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c602: 6820 ldr r0, [r4, #0] + 800c604: f000 fbd9 bl 800cdba + 800c608: 0183 lsls r3, r0, #6 + 800c60a: d50b bpl.n 800c624 + return HAL_SD_ERROR_LOCK_UNLOCK_FAILED; + 800c60c: f44f 6000 mov.w r0, #2048 ; 0x800 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c610: 6823 ldr r3, [r4, #0] + 800c612: 4a54 ldr r2, [pc, #336] ; (800c764 ) + 800c614: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c616: 6ba3 ldr r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c618: 2501 movs r5, #1 + hsd->ErrorCode |= errorstate; + 800c61a: 4318 orrs r0, r3 + 800c61c: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c61e: f884 5034 strb.w r5, [r4, #52] ; 0x34 + status = HAL_ERROR; + 800c622: e08a b.n 800c73a + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800c624: 6820 ldr r0, [r4, #0] + 800c626: 2140 movs r1, #64 ; 0x40 + 800c628: f000 fca6 bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800c62c: b110 cbz r0, 800c634 + hsd->ErrorCode |= HAL_SD_ERROR_NONE; + 800c62e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c630: 63a3 str r3, [r4, #56] ; 0x38 + return errorstate; + 800c632: e7ed b.n 800c610 + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c634: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800c636: 6820 ldr r0, [r4, #0] + 800c638: 0409 lsls r1, r1, #16 + 800c63a: f000 fdd6 bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c63e: 2800 cmp r0, #0 + 800c640: d1f5 bne.n 800c62e + config.DataLength = 64U; + 800c642: 2340 movs r3, #64 ; 0x40 + 800c644: f04f 37ff mov.w r7, #4294967295 ; 0xffffffff + 800c648: e9cd 7300 strd r7, r3, [sp] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800c64c: f04f 0c60 mov.w ip, #96 ; 0x60 + 800c650: 2302 movs r3, #2 + 800c652: e9cd c302 strd ip, r3, [sp, #8] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800c656: 9004 str r0, [sp, #16] + config.DPSM = SDMMC_DPSM_ENABLE; + 800c658: 2301 movs r3, #1 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c65a: 6820 ldr r0, [r4, #0] + config.DPSM = SDMMC_DPSM_ENABLE; + 800c65c: 9305 str r3, [sp, #20] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c65e: 4669 mov r1, sp + 800c660: f000 fbae bl 800cdc0 + errorstate = SDMMC_CmdStatusRegister(hsd->Instance); + 800c664: 6820 ldr r0, [r4, #0] + 800c666: f000 fe40 bl 800d2ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c66a: 2800 cmp r0, #0 + 800c66c: d1df bne.n 800c62e + uint32_t *pData = pSDstatus; + 800c66e: af06 add r7, sp, #24 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800c670: 6823 ldr r3, [r4, #0] + 800c672: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c674: f412 7f95 tst.w r2, #298 ; 0x12a + 800c678: d00a beq.n 800c690 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c67a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c67c: 0711 lsls r1, r2, #28 + 800c67e: d46f bmi.n 800c760 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c680: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c682: 0792 lsls r2, r2, #30 + 800c684: d46a bmi.n 800c75c + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800c686: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c688: 069b lsls r3, r3, #26 + 800c68a: d51e bpl.n 800c6ca + return HAL_SD_ERROR_RX_OVERRUN; + 800c68c: 2020 movs r0, #32 + 800c68e: e7bf b.n 800c610 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800c690: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c692: 0418 lsls r0, r3, #16 + 800c694: d508 bpl.n 800c6a8 + 800c696: f107 0820 add.w r8, r7, #32 + *pData = SDMMC_ReadFIFO(hsd->Instance); + 800c69a: 6820 ldr r0, [r4, #0] + 800c69c: f000 fb54 bl 800cd48 + 800c6a0: f847 0b04 str.w r0, [r7], #4 + for(count = 0U; count < 8U; count++) + 800c6a4: 45b8 cmp r8, r7 + 800c6a6: d1f8 bne.n 800c69a + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c6a8: f7fa fd20 bl 80070ec + 800c6ac: 1b80 subs r0, r0, r6 + 800c6ae: 3001 adds r0, #1 + 800c6b0: d1de bne.n 800c670 + return HAL_SD_ERROR_TIMEOUT; + 800c6b2: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + if(errorstate != HAL_SD_ERROR_NONE) + 800c6b6: e7ab b.n 800c610 + *pData = SDMMC_ReadFIFO(hsd->Instance); + 800c6b8: f000 fb46 bl 800cd48 + 800c6bc: f847 0b04 str.w r0, [r7], #4 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c6c0: f7fa fd14 bl 80070ec + 800c6c4: 1b80 subs r0, r0, r6 + 800c6c6: 3001 adds r0, #1 + 800c6c8: d0f3 beq.n 800c6b2 + while ((__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DPSMACT))) + 800c6ca: 6820 ldr r0, [r4, #0] + 800c6cc: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c6ce: f413 5380 ands.w r3, r3, #4096 ; 0x1000 + 800c6d2: d1f1 bne.n 800c6b8 + pStatus->DataBusWidth = (uint8_t)((sd_status[0] & 0xC0U) >> 6U); + 800c6d4: 9906 ldr r1, [sp, #24] + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c6d6: 4a24 ldr r2, [pc, #144] ; (800c768 ) + 800c6d8: 6382 str r2, [r0, #56] ; 0x38 + pStatus->DataBusWidth = (uint8_t)((sd_status[0] & 0xC0U) >> 6U); + 800c6da: f3c1 1281 ubfx r2, r1, #6, #2 + 800c6de: 702a strb r2, [r5, #0] + pStatus->SecuredMode = (uint8_t)((sd_status[0] & 0x20U) >> 5U); + 800c6e0: f3c1 1240 ubfx r2, r1, #5, #1 + 800c6e4: 706a strb r2, [r5, #1] + pStatus->CardType = (uint16_t)(((sd_status[0] & 0x00FF0000U) >> 8U) | ((sd_status[0] & 0xFF000000U) >> 24U)); + 800c6e6: 0a0a lsrs r2, r1, #8 + 800c6e8: f022 02ff bic.w r2, r2, #255 ; 0xff + 800c6ec: ea42 6211 orr.w r2, r2, r1, lsr #24 + 800c6f0: b292 uxth r2, r2 + 800c6f2: 806a strh r2, [r5, #2] + pStatus->ProtectedAreaSize = (((sd_status[1] & 0xFFU) << 24U) | ((sd_status[1] & 0xFF00U) << 8U) | + 800c6f4: 9a07 ldr r2, [sp, #28] + 800c6f6: ba12 rev r2, r2 + 800c6f8: 606a str r2, [r5, #4] + pStatus->SpeedClass = (uint8_t)(sd_status[2] & 0xFFU); + 800c6fa: 9a08 ldr r2, [sp, #32] + 800c6fc: b2d1 uxtb r1, r2 + 800c6fe: 7229 strb r1, [r5, #8] + pStatus->PerformanceMove = (uint8_t)((sd_status[2] & 0xFF00U) >> 8U); + 800c700: f3c2 2107 ubfx r1, r2, #8, #8 + 800c704: 7269 strb r1, [r5, #9] + pStatus->AllocationUnitSize = (uint8_t)((sd_status[2] & 0xF00000U) >> 20U); + 800c706: f3c2 5103 ubfx r1, r2, #20, #4 + 800c70a: 72a9 strb r1, [r5, #10] + pStatus->EraseSize = (uint16_t)(((sd_status[2] & 0xFF000000U) >> 16U) | (sd_status[3] & 0xFFU)); + 800c70c: 9909 ldr r1, [sp, #36] ; 0x24 + 800c70e: 0c12 lsrs r2, r2, #16 + 800c710: b2c8 uxtb r0, r1 + 800c712: f022 02ff bic.w r2, r2, #255 ; 0xff + 800c716: 4302 orrs r2, r0 + 800c718: 81aa strh r2, [r5, #12] + pStatus->EraseTimeout = (uint8_t)((sd_status[3] & 0xFC00U) >> 10U); + 800c71a: f3c1 2285 ubfx r2, r1, #10, #6 + 800c71e: 73aa strb r2, [r5, #14] + pStatus->EraseOffset = (uint8_t)((sd_status[3] & 0x0300U) >> 8U); + 800c720: f3c1 2201 ubfx r2, r1, #8, #2 + 800c724: 73ea strb r2, [r5, #15] + pStatus->UhsSpeedGrade = (uint8_t)((sd_status[3] & 0x00F0U) >> 4U); + 800c726: f3c1 1203 ubfx r2, r1, #4, #4 + 800c72a: 742a strb r2, [r5, #16] + pStatus->UhsAllocationUnitSize = (uint8_t)(sd_status[3] & 0x000FU) ; + 800c72c: f001 010f and.w r1, r1, #15 + pStatus->VideoSpeedClass = (uint8_t)((sd_status[4] & 0xFF000000U) >> 24U); + 800c730: f89d 202b ldrb.w r2, [sp, #43] ; 0x2b + pStatus->UhsAllocationUnitSize = (uint8_t)(sd_status[3] & 0x000FU) ; + 800c734: 7469 strb r1, [r5, #17] + pStatus->VideoSpeedClass = (uint8_t)((sd_status[4] & 0xFF000000U) >> 24U); + 800c736: 74aa strb r2, [r5, #18] + HAL_StatusTypeDef status = HAL_OK; + 800c738: 461d mov r5, r3 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800c73a: 6820 ldr r0, [r4, #0] + 800c73c: f44f 7100 mov.w r1, #512 ; 0x200 + 800c740: f000 fc1a bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800c744: b130 cbz r0, 800c754 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c746: 6823 ldr r3, [r4, #0] + 800c748: 4a06 ldr r2, [pc, #24] ; (800c764 ) + 800c74a: 639a str r2, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c74c: 2501 movs r5, #1 + hsd->ErrorCode = errorstate; + 800c74e: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c750: f884 5034 strb.w r5, [r4, #52] ; 0x34 +} + 800c754: 4628 mov r0, r5 + 800c756: b016 add sp, #88 ; 0x58 + 800c758: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + return HAL_SD_ERROR_DATA_CRC_FAIL; + 800c75c: 2002 movs r0, #2 + 800c75e: e757 b.n 800c610 + return HAL_SD_ERROR_DATA_TIMEOUT; + 800c760: 2008 movs r0, #8 + 800c762: e755 b.n 800c610 + 800c764: 1fe00fff .word 0x1fe00fff + 800c768: 18000f3a .word 0x18000f3a + +0800c76c : + pCardInfo->CardType = (uint32_t)(hsd->SdCard.CardType); + 800c76c: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800c76e: 600b str r3, [r1, #0] + pCardInfo->CardVersion = (uint32_t)(hsd->SdCard.CardVersion); + 800c770: 6c03 ldr r3, [r0, #64] ; 0x40 + 800c772: 604b str r3, [r1, #4] + pCardInfo->Class = (uint32_t)(hsd->SdCard.Class); + 800c774: 6c43 ldr r3, [r0, #68] ; 0x44 + 800c776: 608b str r3, [r1, #8] + pCardInfo->RelCardAdd = (uint32_t)(hsd->SdCard.RelCardAdd); + 800c778: 6c83 ldr r3, [r0, #72] ; 0x48 + 800c77a: 60cb str r3, [r1, #12] + pCardInfo->BlockNbr = (uint32_t)(hsd->SdCard.BlockNbr); + 800c77c: 6cc3 ldr r3, [r0, #76] ; 0x4c + 800c77e: 610b str r3, [r1, #16] + pCardInfo->BlockSize = (uint32_t)(hsd->SdCard.BlockSize); + 800c780: 6d03 ldr r3, [r0, #80] ; 0x50 + 800c782: 614b str r3, [r1, #20] + pCardInfo->LogBlockNbr = (uint32_t)(hsd->SdCard.LogBlockNbr); + 800c784: 6d43 ldr r3, [r0, #84] ; 0x54 + 800c786: 618b str r3, [r1, #24] + pCardInfo->LogBlockSize = (uint32_t)(hsd->SdCard.LogBlockSize); + 800c788: 6d83 ldr r3, [r0, #88] ; 0x58 + 800c78a: 61cb str r3, [r1, #28] +} + 800c78c: 2000 movs r0, #0 + 800c78e: 4770 bx lr + +0800c790 : +{ + 800c790: b530 push {r4, r5, lr} + hsd->State = HAL_SD_STATE_BUSY; + 800c792: 2303 movs r3, #3 + 800c794: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c798: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800c79a: 2b03 cmp r3, #3 +{ + 800c79c: b08b sub sp, #44 ; 0x2c + 800c79e: 4604 mov r4, r0 + 800c7a0: 460d mov r5, r1 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c7a2: d002 beq.n 800c7aa + if(WideMode == SDMMC_BUS_WIDE_8B) + 800c7a4: f5b1 4f00 cmp.w r1, #32768 ; 0x8000 + 800c7a8: d103 bne.n 800c7b2 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c7aa: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c7ac: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800c7b0: e049 b.n 800c846 + else if(WideMode == SDMMC_BUS_WIDE_4B) + 800c7b2: f5b1 4f80 cmp.w r1, #16384 ; 0x4000 + 800c7b6: d123 bne.n 800c800 + uint32_t scr[2U] = {0UL, 0UL}; + 800c7b8: 2100 movs r1, #0 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c7ba: 6800 ldr r0, [r0, #0] + uint32_t scr[2U] = {0UL, 0UL}; + 800c7bc: e9cd 1104 strd r1, r1, [sp, #16] + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c7c0: f000 fafb bl 800cdba + 800c7c4: 0180 lsls r0, r0, #6 + 800c7c6: d435 bmi.n 800c834 + errorstate = SD_FindSCR(hsd, scr); + 800c7c8: a904 add r1, sp, #16 + 800c7ca: 4620 mov r0, r4 + 800c7cc: f7ff fa32 bl 800bc34 + if(errorstate != HAL_SD_ERROR_NONE) + 800c7d0: b960 cbnz r0, 800c7ec + if((scr[1U] & SDMMC_WIDE_BUS_SUPPORT) != SDMMC_ALLZERO) + 800c7d2: 9b05 ldr r3, [sp, #20] + 800c7d4: 0359 lsls r1, r3, #13 + 800c7d6: d530 bpl.n 800c83a + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c7d8: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800c7da: 6820 ldr r0, [r4, #0] + 800c7dc: 0409 lsls r1, r1, #16 + 800c7de: f000 fd04 bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c7e2: b918 cbnz r0, 800c7ec + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 2U); + 800c7e4: 2102 movs r1, #2 + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 0U); + 800c7e6: 6820 ldr r0, [r4, #0] + 800c7e8: f000 fd18 bl 800d21c + hsd->ErrorCode |= errorstate; + 800c7ec: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c7ee: 4318 orrs r0, r3 + 800c7f0: 63a0 str r0, [r4, #56] ; 0x38 + if(hsd->ErrorCode != HAL_SD_ERROR_NONE) + 800c7f2: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c7f4: b34b cbz r3, 800c84a + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c7f6: 6823 ldr r3, [r4, #0] + 800c7f8: 4a42 ldr r2, [pc, #264] ; (800c904 ) + 800c7fa: 639a str r2, [r3, #56] ; 0x38 + status = HAL_ERROR; + 800c7fc: 2501 movs r5, #1 + 800c7fe: e054 b.n 800c8aa + else if(WideMode == SDMMC_BUS_WIDE_1B) + 800c800: b9f1 cbnz r1, 800c840 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c802: 6800 ldr r0, [r0, #0] + uint32_t scr[2U] = {0UL, 0UL}; + 800c804: e9cd 1104 strd r1, r1, [sp, #16] + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c808: f000 fad7 bl 800cdba + 800c80c: 0182 lsls r2, r0, #6 + 800c80e: d411 bmi.n 800c834 + errorstate = SD_FindSCR(hsd, scr); + 800c810: a904 add r1, sp, #16 + 800c812: 4620 mov r0, r4 + 800c814: f7ff fa0e bl 800bc34 + if(errorstate != HAL_SD_ERROR_NONE) + 800c818: 2800 cmp r0, #0 + 800c81a: d1e7 bne.n 800c7ec + if((scr[1U] & SDMMC_SINGLE_BUS_SUPPORT) != SDMMC_ALLZERO) + 800c81c: 9b05 ldr r3, [sp, #20] + 800c81e: 03db lsls r3, r3, #15 + 800c820: d50b bpl.n 800c83a + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c822: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800c824: 6820 ldr r0, [r4, #0] + 800c826: 0409 lsls r1, r1, #16 + 800c828: f000 fcdf bl 800d1ea + if(errorstate != HAL_SD_ERROR_NONE) + 800c82c: 2800 cmp r0, #0 + 800c82e: d1dd bne.n 800c7ec + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 0U); + 800c830: 4601 mov r1, r0 + 800c832: e7d8 b.n 800c7e6 + return HAL_SD_ERROR_LOCK_UNLOCK_FAILED; + 800c834: f44f 6000 mov.w r0, #2048 ; 0x800 + 800c838: e7d8 b.n 800c7ec + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800c83a: f04f 6080 mov.w r0, #67108864 ; 0x4000000 + 800c83e: e7d5 b.n 800c7ec + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800c840: 6b83 ldr r3, [r0, #56] ; 0x38 + 800c842: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c846: 63a3 str r3, [r4, #56] ; 0x38 + 800c848: e7d3 b.n 800c7f2 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800c84a: f44f 2000 mov.w r0, #524288 ; 0x80000 + 800c84e: f7fd f8af bl 80099b0 + if (sdmmc_clk != 0U) + 800c852: 2800 cmp r0, #0 + 800c854: d051 beq.n 800c8fa + Init.ClockEdge = hsd->Init.ClockEdge; + 800c856: 6863 ldr r3, [r4, #4] + 800c858: 9304 str r3, [sp, #16] + Init.ClockPowerSave = hsd->Init.ClockPowerSave; + 800c85a: 68a3 ldr r3, [r4, #8] + if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ))) + 800c85c: 492a ldr r1, [pc, #168] ; (800c908 ) + 800c85e: fbb0 f2f1 udiv r2, r0, r1 + Init.BusWide = WideMode; + 800c862: e9cd 3505 strd r3, r5, [sp, #20] + Init.HardwareFlowControl = hsd->Init.HardwareFlowControl; + 800c866: 6923 ldr r3, [r4, #16] + 800c868: 9307 str r3, [sp, #28] + if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ))) + 800c86a: 6963 ldr r3, [r4, #20] + 800c86c: 4293 cmp r3, r2 + 800c86e: d301 bcc.n 800c874 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800c870: 9308 str r3, [sp, #32] + 800c872: e00d b.n 800c890 + else if (hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) + 800c874: 6de5 ldr r5, [r4, #92] ; 0x5c + 800c876: f5b5 7f00 cmp.w r5, #512 ; 0x200 + 800c87a: d0f9 beq.n 800c870 + else if (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800c87c: f5b5 7f80 cmp.w r5, #256 ; 0x100 + 800c880: d12e bne.n 800c8e0 + if (hsd->Init.ClockDiv == 0U) + 800c882: bb3b cbnz r3, 800c8d4 + if (sdmmc_clk > SD_HIGH_SPEED_FREQ) + 800c884: 4288 cmp r0, r1 + 800c886: d923 bls.n 800c8d0 + Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ); + 800c888: 4b20 ldr r3, [pc, #128] ; (800c90c ) + 800c88a: fbb0 f0f3 udiv r0, r0, r3 + 800c88e: 9008 str r0, [sp, #32] + Init.Transceiver = hsd->Init.Transceiver; + 800c890: 69a3 ldr r3, [r4, #24] + 800c892: 9309 str r3, [sp, #36] ; 0x24 + (void)SDMMC_Init(hsd->Instance, Init); + 800c894: ab0a add r3, sp, #40 ; 0x28 + 800c896: e913 0007 ldmdb r3, {r0, r1, r2} + 800c89a: e88d 0007 stmia.w sp, {r0, r1, r2} + 800c89e: ab04 add r3, sp, #16 + 800c8a0: cb0e ldmia r3, {r1, r2, r3} + 800c8a2: 6820 ldr r0, [r4, #0] + 800c8a4: f000 fa36 bl 800cd14 + HAL_StatusTypeDef status = HAL_OK; + 800c8a8: 2500 movs r5, #0 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800c8aa: 6820 ldr r0, [r4, #0] + 800c8ac: f44f 7100 mov.w r1, #512 ; 0x200 + 800c8b0: f000 fb62 bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800c8b4: b130 cbz r0, 800c8c4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c8b6: 6823 ldr r3, [r4, #0] + 800c8b8: 4a12 ldr r2, [pc, #72] ; (800c904 ) + 800c8ba: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c8bc: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c8be: 4318 orrs r0, r3 + 800c8c0: 63a0 str r0, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800c8c2: 2501 movs r5, #1 + hsd->State = HAL_SD_STATE_READY; + 800c8c4: 2301 movs r3, #1 +} + 800c8c6: 4628 mov r0, r5 + hsd->State = HAL_SD_STATE_READY; + 800c8c8: f884 3034 strb.w r3, [r4, #52] ; 0x34 +} + 800c8cc: b00b add sp, #44 ; 0x2c + 800c8ce: bd30 pop {r4, r5, pc} + Init.ClockDiv = hsd->Init.ClockDiv; + 800c8d0: 2300 movs r3, #0 + 800c8d2: e7cd b.n 800c870 + if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_HIGH_SPEED_FREQ) + 800c8d4: 005a lsls r2, r3, #1 + 800c8d6: fbb0 f2f2 udiv r2, r0, r2 + 800c8da: 428a cmp r2, r1 + 800c8dc: d9c8 bls.n 800c870 + 800c8de: e7d3 b.n 800c888 + if (hsd->Init.ClockDiv == 0U) + 800c8e0: 490b ldr r1, [pc, #44] ; (800c910 ) + 800c8e2: b91b cbnz r3, 800c8ec + if (sdmmc_clk > SD_NORMAL_SPEED_FREQ) + 800c8e4: 4288 cmp r0, r1 + 800c8e6: d9f3 bls.n 800c8d0 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800c8e8: 9208 str r2, [sp, #32] + 800c8ea: e7d1 b.n 800c890 + if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_NORMAL_SPEED_FREQ) + 800c8ec: 005d lsls r5, r3, #1 + 800c8ee: fbb0 f0f5 udiv r0, r0, r5 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800c8f2: 4288 cmp r0, r1 + 800c8f4: bf88 it hi + 800c8f6: 4613 movhi r3, r2 + 800c8f8: e7ba b.n 800c870 + hsd->ErrorCode |= SDMMC_ERROR_INVALID_PARAMETER; + 800c8fa: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c8fc: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + 800c900: 63a3 str r3, [r4, #56] ; 0x38 + 800c902: e77b b.n 800c7fc + 800c904: 1fe00fff .word 0x1fe00fff + 800c908: 02faf080 .word 0x02faf080 + 800c90c: 05f5e100 .word 0x05f5e100 + 800c910: 017d7840 .word 0x017d7840 + +0800c914 : + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c914: 6c81 ldr r1, [r0, #72] ; 0x48 +{ + 800c916: b510 push {r4, lr} + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c918: 0409 lsls r1, r1, #16 +{ + 800c91a: 4604 mov r4, r0 + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c91c: 6800 ldr r0, [r0, #0] + 800c91e: f000 fccb bl 800d2b8 + if(errorstate != HAL_SD_ERROR_NONE) + 800c922: 4601 mov r1, r0 + 800c924: b928 cbnz r0, 800c932 + *pCardStatus = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c926: 6820 ldr r0, [r4, #0] + 800c928: f000 fa47 bl 800cdba +} + 800c92c: f3c0 2043 ubfx r0, r0, #9, #4 + 800c930: bd10 pop {r4, pc} + hsd->ErrorCode |= errorstate; + 800c932: 6ba0 ldr r0, [r4, #56] ; 0x38 + 800c934: 4308 orrs r0, r1 + 800c936: 63a0 str r0, [r4, #56] ; 0x38 + uint32_t resp1 = 0; + 800c938: 2000 movs r0, #0 + 800c93a: e7f7 b.n 800c92c + +0800c93c : +{ + 800c93c: b570 push {r4, r5, r6, lr} + if(hsd == NULL) + 800c93e: 4604 mov r4, r0 +{ + 800c940: b086 sub sp, #24 + if(hsd == NULL) + 800c942: b918 cbnz r0, 800c94c + return HAL_ERROR; + 800c944: 2501 movs r5, #1 +} + 800c946: 4628 mov r0, r5 + 800c948: b006 add sp, #24 + 800c94a: bd70 pop {r4, r5, r6, pc} + if(hsd->State == HAL_SD_STATE_RESET) + 800c94c: f890 3034 ldrb.w r3, [r0, #52] ; 0x34 + 800c950: f003 02ff and.w r2, r3, #255 ; 0xff + 800c954: b913 cbnz r3, 800c95c + hsd->Lock = HAL_UNLOCKED; + 800c956: 7702 strb r2, [r0, #28] + HAL_SD_MspInit(hsd); + 800c958: f7ff fa5a bl 800be10 + hsd->State = HAL_SD_STATE_BUSY; + 800c95c: 2303 movs r3, #3 + 800c95e: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if (HAL_SD_InitCard(hsd) != HAL_OK) + 800c962: 4620 mov r0, r4 + 800c964: f7ff fcea bl 800c33c + 800c968: 2800 cmp r0, #0 + 800c96a: d1eb bne.n 800c944 + if( HAL_SD_GetCardStatus(hsd, &CardStatus) != HAL_OK) + 800c96c: a901 add r1, sp, #4 + 800c96e: 4620 mov r0, r4 + 800c970: f7ff fe3e bl 800c5f0 + 800c974: 2800 cmp r0, #0 + 800c976: d1e5 bne.n 800c944 + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800c978: 6be1 ldr r1, [r4, #60] ; 0x3c + speedgrade = CardStatus.UhsSpeedGrade; + 800c97a: f89d 2014 ldrb.w r2, [sp, #20] + unitsize = CardStatus.UhsAllocationUnitSize; + 800c97e: f89d 3015 ldrb.w r3, [sp, #21] + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800c982: 2901 cmp r1, #1 + speedgrade = CardStatus.UhsSpeedGrade; + 800c984: b2d2 uxtb r2, r2 + unitsize = CardStatus.UhsAllocationUnitSize; + 800c986: b2db uxtb r3, r3 + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800c988: d11c bne.n 800c9c4 + 800c98a: 4313 orrs r3, r2 + hsd->SdCard.CardSpeed = CARD_ULTRA_HIGH_SPEED; + 800c98c: bf14 ite ne + 800c98e: f44f 7300 movne.w r3, #512 ; 0x200 + hsd->SdCard.CardSpeed = CARD_HIGH_SPEED; + 800c992: f44f 7380 moveq.w r3, #256 ; 0x100 + 800c996: 65e3 str r3, [r4, #92] ; 0x5c + if(HAL_SD_ConfigWideBusOperation(hsd, hsd->Init.BusWide) != HAL_OK) + 800c998: 68e1 ldr r1, [r4, #12] + 800c99a: 4620 mov r0, r4 + 800c99c: f7ff fef8 bl 800c790 + 800c9a0: 4605 mov r5, r0 + 800c9a2: 2800 cmp r0, #0 + 800c9a4: d1ce bne.n 800c944 + tickstart = HAL_GetTick(); + 800c9a6: f7fa fba1 bl 80070ec + 800c9aa: 4606 mov r6, r0 + while((HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)) + 800c9ac: 4620 mov r0, r4 + 800c9ae: f7ff ffb1 bl 800c914 + 800c9b2: 2804 cmp r0, #4 + 800c9b4: d108 bne.n 800c9c8 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c9b6: 2300 movs r3, #0 + 800c9b8: 63a3 str r3, [r4, #56] ; 0x38 + hsd->Context = SD_CONTEXT_NONE; + 800c9ba: 6323 str r3, [r4, #48] ; 0x30 + hsd->State = HAL_SD_STATE_READY; + 800c9bc: 2301 movs r3, #1 + 800c9be: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800c9c2: e7c0 b.n 800c946 + hsd->SdCard.CardSpeed = CARD_NORMAL_SPEED; + 800c9c4: 65e0 str r0, [r4, #92] ; 0x5c + 800c9c6: e7e7 b.n 800c998 + if((HAL_GetTick()-tickstart) >= SDMMC_DATATIMEOUT) + 800c9c8: f7fa fb90 bl 80070ec + 800c9cc: 1b80 subs r0, r0, r6 + 800c9ce: 3001 adds r0, #1 + 800c9d0: d1ec bne.n 800c9ac + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800c9d2: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800c9d6: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800c9d8: 2301 movs r3, #1 + 800c9da: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c9de: 2300 movs r3, #0 + 800c9e0: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800c9e2: 2503 movs r5, #3 + 800c9e4: e7af b.n 800c946 + ... + +0800c9e8 : +{ + 800c9e8: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + uint32_t SD_hs[16] = {0}; + 800c9ec: 2640 movs r6, #64 ; 0x40 +{ + 800c9ee: b096 sub sp, #88 ; 0x58 + 800c9f0: 4605 mov r5, r0 + uint32_t SD_hs[16] = {0}; + 800c9f2: 4632 mov r2, r6 + 800c9f4: 2100 movs r1, #0 + 800c9f6: a806 add r0, sp, #24 + 800c9f8: f000 fe3c bl 800d674 + uint32_t Timeout = HAL_GetTick(); + 800c9fc: f7fa fb76 bl 80070ec + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800ca00: 6deb ldr r3, [r5, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800ca02: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800ca04: 2b00 cmp r3, #0 + 800ca06: d066 beq.n 800cad6 + if(hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800ca08: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800ca0c: d004 beq.n 800ca18 + uint32_t errorstate = HAL_SD_ERROR_NONE; + 800ca0e: 2400 movs r4, #0 +} + 800ca10: 4620 mov r0, r4 + 800ca12: b016 add sp, #88 ; 0x58 + 800ca14: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hsd->Instance->DCTRL = 0; + 800ca18: 6828 ldr r0, [r5, #0] + 800ca1a: 2300 movs r3, #0 + 800ca1c: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800ca1e: 4631 mov r1, r6 + 800ca20: f000 faaa bl 800cf78 + if (errorstate != HAL_SD_ERROR_NONE) + 800ca24: 4604 mov r4, r0 + 800ca26: 2800 cmp r0, #0 + 800ca28: d1f2 bne.n 800ca10 + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800ca2a: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800ca2e: e9cd 3600 strd r3, r6, [sp] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800ca32: 2260 movs r2, #96 ; 0x60 + 800ca34: 2302 movs r3, #2 + 800ca36: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800ca3a: 9004 str r0, [sp, #16] + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800ca3c: 2301 movs r3, #1 + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800ca3e: 6828 ldr r0, [r5, #0] + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800ca40: 9305 str r3, [sp, #20] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800ca42: 4669 mov r1, sp + 800ca44: f000 f9bc bl 800cdc0 + 800ca48: 2800 cmp r0, #0 + 800ca4a: d147 bne.n 800cadc + errorstate = SDMMC_CmdSwitch(hsd->Instance,SDMMC_SDR25_SWITCH_PATTERN); + 800ca4c: 4925 ldr r1, [pc, #148] ; (800cae4 ) + 800ca4e: 6828 ldr r0, [r5, #0] + 800ca50: f000 fbfd bl 800d24e + if(errorstate != HAL_SD_ERROR_NONE) + 800ca54: 4604 mov r4, r0 + 800ca56: 2800 cmp r0, #0 + 800ca58: d1da bne.n 800ca10 + uint32_t count, loop = 0 ; + 800ca5a: 4607 mov r7, r0 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800ca5c: f240 592a movw r9, #1322 ; 0x52a + 800ca60: 682b ldr r3, [r5, #0] + 800ca62: 6b5e ldr r6, [r3, #52] ; 0x34 + 800ca64: ea16 0609 ands.w r6, r6, r9 + 800ca68: d005 beq.n 800ca76 + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800ca6a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800ca6c: 0710 lsls r0, r2, #28 + 800ca6e: d51e bpl.n 800caae + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800ca70: 2208 movs r2, #8 + 800ca72: 639a str r2, [r3, #56] ; 0x38 + return errorstate; + 800ca74: e7cc b.n 800ca10 + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800ca76: 6b5b ldr r3, [r3, #52] ; 0x34 + 800ca78: 041b lsls r3, r3, #16 + 800ca7a: d50b bpl.n 800ca94 + 800ca7c: ab06 add r3, sp, #24 + 800ca7e: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800ca82: 6828 ldr r0, [r5, #0] + 800ca84: f000 f960 bl 800cd48 + for (count = 0U; count < 8U; count++) + 800ca88: 3601 adds r6, #1 + 800ca8a: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800ca8c: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800ca90: d1f7 bne.n 800ca82 + loop ++; + 800ca92: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800ca94: f7fa fb2a bl 80070ec + 800ca98: eba0 0008 sub.w r0, r0, r8 + 800ca9c: 3001 adds r0, #1 + 800ca9e: d1df bne.n 800ca60 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800caa0: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + hsd->State= HAL_SD_STATE_READY; + 800caa4: 2301 movs r3, #1 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800caa6: 63ac str r4, [r5, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800caa8: f885 3034 strb.w r3, [r5, #52] ; 0x34 + return HAL_SD_ERROR_TIMEOUT; + 800caac: e7b0 b.n 800ca10 + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800caae: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cab0: 0791 lsls r1, r2, #30 + 800cab2: d502 bpl.n 800caba + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800cab4: 2402 movs r4, #2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cab6: 639c str r4, [r3, #56] ; 0x38 + return errorstate; + 800cab8: e7aa b.n 800ca10 + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800caba: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cabc: 0692 lsls r2, r2, #26 + 800cabe: d501 bpl.n 800cac4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cac0: 2420 movs r4, #32 + 800cac2: e7f8 b.n 800cab6 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800cac4: 4a08 ldr r2, [pc, #32] ; (800cae8 ) + 800cac6: 639a str r2, [r3, #56] ; 0x38 + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800cac8: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800cacc: 079b lsls r3, r3, #30 + 800cace: d49e bmi.n 800ca0e + errorstate = SDMMC_ERROR_UNSUPPORTED_FEATURE; + 800cad0: f04f 5480 mov.w r4, #268435456 ; 0x10000000 + 800cad4: e79c b.n 800ca10 + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800cad6: f04f 6480 mov.w r4, #67108864 ; 0x4000000 + 800cada: e799 b.n 800ca10 + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + 800cadc: f44f 3480 mov.w r4, #65536 ; 0x10000 + 800cae0: e796 b.n 800ca10 + 800cae2: bf00 nop + 800cae4: 80ffff01 .word 0x80ffff01 + 800cae8: 18000f3a .word 0x18000f3a + +0800caec : +{ + 800caec: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + hsd->State = HAL_SD_STATE_BUSY; + 800caf0: 2303 movs r3, #3 + 800caf2: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800caf6: 6983 ldr r3, [r0, #24] + 800caf8: 2b01 cmp r3, #1 +{ + 800cafa: b096 sub sp, #88 ; 0x58 + 800cafc: 4604 mov r4, r0 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800cafe: f040 80cf bne.w 800cca0 + switch (SpeedMode) + 800cb02: 2904 cmp r1, #4 + 800cb04: f200 80eb bhi.w 800ccde + 800cb08: e8df f011 tbh [pc, r1, lsl #1] + 800cb0c: 00150005 .word 0x00150005 + 800cb10: 001e00dc .word 0x001e00dc + 800cb14: 0031 .short 0x0031 + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cb16: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800cb18: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cb1c: d002 beq.n 800cb24 + 800cb1e: 6bc2 ldr r2, [r0, #60] ; 0x3c + 800cb20: 2a01 cmp r2, #1 + 800cb22: d10a bne.n 800cb3a + hsd->Instance->CLKCR |= 0x00100000U; + 800cb24: 6822 ldr r2, [r4, #0] + 800cb26: 6853 ldr r3, [r2, #4] + 800cb28: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800cb2c: 6053 str r3, [r2, #4] + if (SD_UltraHighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800cb2e: 4620 mov r0, r4 + 800cb30: f7ff f8e6 bl 800bd00 + 800cb34: b920 cbnz r0, 800cb40 + switch (SpeedMode) + 800cb36: 2500 movs r5, #0 + 800cb38: e063 b.n 800cc02 + else if (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800cb3a: f5b3 7f80 cmp.w r3, #256 ; 0x100 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800cb3e: d1fa bne.n 800cb36 + if (SD_HighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800cb40: 4620 mov r0, r4 + 800cb42: f7ff ff51 bl 800c9e8 + 800cb46: e00f b.n 800cb68 + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cb48: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800cb4a: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cb4e: d003 beq.n 800cb58 + 800cb50: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800cb52: 2b01 cmp r3, #1 + 800cb54: f040 8089 bne.w 800cc6a + hsd->Instance->CLKCR |= 0x00100000U; + 800cb58: 6822 ldr r2, [r4, #0] + 800cb5a: 6853 ldr r3, [r2, #4] + 800cb5c: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800cb60: 6053 str r3, [r2, #4] + if (SD_UltraHighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800cb62: 4620 mov r0, r4 + 800cb64: f7ff f8cc bl 800bd00 + if (SD_HighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800cb68: 2800 cmp r0, #0 + 800cb6a: d0e4 beq.n 800cb36 + 800cb6c: e07d b.n 800cc6a + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cb6e: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800cb70: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cb74: d002 beq.n 800cb7c + 800cb76: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800cb78: 2b01 cmp r3, #1 + 800cb7a: d176 bne.n 800cc6a + hsd->Instance->CLKCR |= 0x00100000U; + 800cb7c: 6822 ldr r2, [r4, #0] + 800cb7e: 6853 ldr r3, [r2, #4] + */ +static uint32_t SD_DDR_Mode(SD_HandleTypeDef *hsd) +{ + uint32_t errorstate = HAL_SD_ERROR_NONE; + SDMMC_DataInitTypeDef sdmmc_datainitstructure; + uint32_t SD_hs[16] = {0}; + 800cb80: 2540 movs r5, #64 ; 0x40 + hsd->Instance->CLKCR |= 0x00100000U; + 800cb82: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800cb86: 6053 str r3, [r2, #4] + uint32_t SD_hs[16] = {0}; + 800cb88: 2100 movs r1, #0 + 800cb8a: 462a mov r2, r5 + 800cb8c: a806 add r0, sp, #24 + 800cb8e: f000 fd71 bl 800d674 + uint32_t count, loop = 0 ; + uint32_t Timeout = HAL_GetTick(); + 800cb92: f7fa faab bl 80070ec + + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800cb96: 6de3 ldr r3, [r4, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800cb98: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800cb9a: 2b00 cmp r3, #0 + 800cb9c: d065 beq.n 800cc6a + { + /* Standard Speed Card <= 12.5Mhz */ + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + } + + if((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) && + 800cb9e: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cba2: d1c8 bne.n 800cb36 + 800cba4: 69a6 ldr r6, [r4, #24] + 800cba6: 2e01 cmp r6, #1 + 800cba8: d1c5 bne.n 800cb36 + (hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE)) + { + /* Initialize the Data control register */ + hsd->Instance->DCTRL = 0; + 800cbaa: 6820 ldr r0, [r4, #0] + 800cbac: 2300 movs r3, #0 + 800cbae: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800cbb0: 4629 mov r1, r5 + 800cbb2: f000 f9e1 bl 800cf78 + + if (errorstate != HAL_SD_ERROR_NONE) + 800cbb6: 2800 cmp r0, #0 + 800cbb8: d157 bne.n 800cc6a + { + return errorstate; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800cbba: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800cbbe: e9cd 3500 strd r3, r5, [sp] + sdmmc_datainitstructure.DataBlockSize = SDMMC_DATABLOCK_SIZE_64B ; + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800cbc2: e9cd 0604 strd r0, r6, [sp, #16] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800cbc6: 2260 movs r2, #96 ; 0x60 + 800cbc8: 2302 movs r3, #2 + + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800cbca: 6820 ldr r0, [r4, #0] + 800cbcc: 4669 mov r1, sp + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800cbce: e9cd 2302 strd r2, r3, [sp, #8] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800cbd2: f000 f8f5 bl 800cdc0 + 800cbd6: 4605 mov r5, r0 + 800cbd8: 2800 cmp r0, #0 + 800cbda: d146 bne.n 800cc6a + { + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + } + + errorstate = SDMMC_CmdSwitch(hsd->Instance, SDMMC_DDR50_SWITCH_PATTERN); + 800cbdc: 494a ldr r1, [pc, #296] ; (800cd08 ) + 800cbde: 6820 ldr r0, [r4, #0] + 800cbe0: f000 fb35 bl 800d24e + if(errorstate != HAL_SD_ERROR_NONE) + 800cbe4: 4607 mov r7, r0 + 800cbe6: 2800 cmp r0, #0 + 800cbe8: d13f bne.n 800cc6a + { + return errorstate; + } + + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800cbea: f240 592a movw r9, #1322 ; 0x52a + 800cbee: 6823 ldr r3, [r4, #0] + 800cbf0: 6b5e ldr r6, [r3, #52] ; 0x34 + 800cbf2: ea16 0609 ands.w r6, r6, r9 + 800cbf6: d01d beq.n 800cc34 + hsd->State= HAL_SD_STATE_READY; + return HAL_SD_ERROR_TIMEOUT; + } + } + + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800cbf8: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cbfa: 0710 lsls r0, r2, #28 + 800cbfc: d53b bpl.n 800cc76 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800cbfe: 2208 movs r2, #8 + 800cc00: 639a str r2, [r3, #56] ; 0x38 + tickstart = HAL_GetTick(); + 800cc02: f7fa fa73 bl 80070ec + 800cc06: 4606 mov r6, r0 + while ((HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)) + 800cc08: 4620 mov r0, r4 + 800cc0a: f7ff fe83 bl 800c914 + 800cc0e: 2804 cmp r0, #4 + 800cc10: d169 bne.n 800cce6 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800cc12: 6820 ldr r0, [r4, #0] + 800cc14: f44f 7100 mov.w r1, #512 ; 0x200 + 800cc18: f000 f9ae bl 800cf78 + if(errorstate != HAL_SD_ERROR_NONE) + 800cc1c: b130 cbz r0, 800cc2c + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800cc1e: 6823 ldr r3, [r4, #0] + 800cc20: 4a3a ldr r2, [pc, #232] ; (800cd0c ) + 800cc22: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800cc24: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cc26: 4318 orrs r0, r3 + 800cc28: 63a0 str r0, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800cc2a: 2501 movs r5, #1 + hsd->State = HAL_SD_STATE_READY; + 800cc2c: 2301 movs r3, #1 + 800cc2e: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return status; + 800cc32: e064 b.n 800ccfe + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800cc34: 6b5b ldr r3, [r3, #52] ; 0x34 + 800cc36: 041b lsls r3, r3, #16 + 800cc38: d50b bpl.n 800cc52 + 800cc3a: ab06 add r3, sp, #24 + 800cc3c: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cc40: 6820 ldr r0, [r4, #0] + 800cc42: f000 f881 bl 800cd48 + for (count = 0U; count < 8U; count++) + 800cc46: 3601 adds r6, #1 + 800cc48: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cc4a: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800cc4e: d1f7 bne.n 800cc40 + loop ++; + 800cc50: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800cc52: f7fa fa4b bl 80070ec + 800cc56: eba0 0008 sub.w r0, r0, r8 + 800cc5a: 3001 adds r0, #1 + 800cc5c: d1c7 bne.n 800cbee + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800cc5e: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800cc62: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800cc64: 2301 movs r3, #1 + 800cc66: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800cc6a: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cc6c: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800cc70: 63a3 str r3, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800cc72: 2501 movs r5, #1 + break; + 800cc74: e7c5 b.n 800cc02 + + return errorstate; + } + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800cc76: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cc78: 0791 lsls r1, r2, #30 + 800cc7a: d502 bpl.n 800cc82 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800cc7c: 2202 movs r2, #2 + + return errorstate; + } + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cc7e: 639a str r2, [r3, #56] ; 0x38 + + errorstate = SDMMC_ERROR_RX_OVERRUN; + + return errorstate; + 800cc80: e7f3 b.n 800cc6a + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800cc82: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cc84: 0692 lsls r2, r2, #26 + 800cc86: d501 bpl.n 800cc8c + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cc88: 2220 movs r2, #32 + 800cc8a: e7f8 b.n 800cc7e + { + /* No error flag set */ + } + + /* Clear all the static flags */ + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800cc8c: 4a20 ldr r2, [pc, #128] ; (800cd10 ) + 800cc8e: 639a str r2, [r3, #56] ; 0x38 + + /* Test if the switch mode is ok */ + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800cc90: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800cc94: 079b lsls r3, r3, #30 + 800cc96: d5e8 bpl.n 800cc6a + else + { +#if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U) + hsd->DriveTransceiver_1_8V_Callback(SET); +#else + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800cc98: 2001 movs r0, #1 + 800cc9a: f7fa fa97 bl 80071cc + 800cc9e: e7b0 b.n 800cc02 + switch (SpeedMode) + 800cca0: 2901 cmp r1, #1 + 800cca2: f43f af48 beq.w 800cb36 + 800cca6: 2902 cmp r1, #2 + 800cca8: d00c beq.n 800ccc4 + 800ccaa: b9c1 cbnz r1, 800ccde + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800ccac: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800ccae: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ccb2: f43f af45 beq.w 800cb40 + 800ccb6: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800ccba: f43f af41 beq.w 800cb40 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800ccbe: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800ccc0: 2b01 cmp r3, #1 + 800ccc2: e73c b.n 800cb3e + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800ccc4: 6de3 ldr r3, [r4, #92] ; 0x5c + 800ccc6: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ccca: f43f af39 beq.w 800cb40 + 800ccce: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800ccd2: f43f af35 beq.w 800cb40 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800ccd6: 6be3 ldr r3, [r4, #60] ; 0x3c + 800ccd8: 2b01 cmp r3, #1 + 800ccda: d1c6 bne.n 800cc6a + 800ccdc: e730 b.n 800cb40 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800ccde: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cce0: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + 800cce4: e7c4 b.n 800cc70 + if ((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800cce6: f7fa fa01 bl 80070ec + 800ccea: 1b80 subs r0, r0, r6 + 800ccec: 3001 adds r0, #1 + 800ccee: d18b bne.n 800cc08 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800ccf0: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800ccf4: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800ccf6: 2301 movs r3, #1 + 800ccf8: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_TIMEOUT; + 800ccfc: 2503 movs r5, #3 +} + 800ccfe: 4628 mov r0, r5 + 800cd00: b016 add sp, #88 ; 0x58 + 800cd02: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 800cd06: bf00 nop + 800cd08: 80ffff04 .word 0x80ffff04 + 800cd0c: 1fe00fff .word 0x1fe00fff + 800cd10: 18000f3a .word 0x18000f3a + +0800cd14 : + * @param SDMMCx Pointer to SDMMC register base + * @param Init SDMMC initialization structure + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_Init(SDMMC_TypeDef *SDMMCx, SDMMC_InitTypeDef Init) +{ + 800cd14: b084 sub sp, #16 + 800cd16: b510 push {r4, lr} + 800cd18: ac03 add r4, sp, #12 + 800cd1a: e884 000e stmia.w r4, {r1, r2, r3} + + /* Set SDMMC configuration parameters */ +#if !defined(STM32L4P5xx) && !defined(STM32L4Q5xx) && !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx) + tmpreg |= Init.ClockBypass; +#endif + tmpreg |= (Init.ClockEdge |\ + 800cd1e: 9b03 ldr r3, [sp, #12] + Init.HardwareFlowControl |\ + Init.ClockDiv + ); + + /* Write to SDMMC CLKCR */ + MODIFY_REG(SDMMCx->CLKCR, CLKCR_CLEAR_MASK, tmpreg); + 800cd20: 6841 ldr r1, [r0, #4] + tmpreg |= (Init.ClockEdge |\ + 800cd22: 4313 orrs r3, r2 + Init.ClockPowerSave |\ + 800cd24: 9a05 ldr r2, [sp, #20] + 800cd26: 4313 orrs r3, r2 + Init.BusWide |\ + 800cd28: 9a06 ldr r2, [sp, #24] + 800cd2a: 4313 orrs r3, r2 + Init.HardwareFlowControl |\ + 800cd2c: 9a07 ldr r2, [sp, #28] + + return HAL_OK; +} + 800cd2e: e8bd 4010 ldmia.w sp!, {r4, lr} + Init.HardwareFlowControl |\ + 800cd32: 4313 orrs r3, r2 + MODIFY_REG(SDMMCx->CLKCR, CLKCR_CLEAR_MASK, tmpreg); + 800cd34: 4a03 ldr r2, [pc, #12] ; (800cd44 ) + 800cd36: 400a ands r2, r1 + 800cd38: 4313 orrs r3, r2 + 800cd3a: 6043 str r3, [r0, #4] +} + 800cd3c: b004 add sp, #16 + 800cd3e: 2000 movs r0, #0 + 800cd40: 4770 bx lr + 800cd42: bf00 nop + 800cd44: ffc02c00 .word 0xffc02c00 + +0800cd48 : + * @retval HAL status + */ +uint32_t SDMMC_ReadFIFO(SDMMC_TypeDef *SDMMCx) +{ + /* Read data from Rx FIFO */ + return (SDMMCx->FIFO); + 800cd48: f8d0 0080 ldr.w r0, [r0, #128] ; 0x80 +} + 800cd4c: 4770 bx lr + +0800cd4e : + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_WriteFIFO(SDMMC_TypeDef *SDMMCx, uint32_t *pWriteData) +{ + /* Write data to FIFO */ + SDMMCx->FIFO = *pWriteData; + 800cd4e: 680b ldr r3, [r1, #0] + 800cd50: f8c0 3080 str.w r3, [r0, #128] ; 0x80 + + return HAL_OK; +} + 800cd54: 2000 movs r0, #0 + 800cd56: 4770 bx lr + +0800cd58 : + * @brief Set SDMMC Power state to ON. + * @param SDMMCx Pointer to SDMMC register base + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_PowerState_ON(SDMMC_TypeDef *SDMMCx) +{ + 800cd58: b508 push {r3, lr} + /* Set power state to ON */ +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + SDMMCx->POWER |= SDMMC_POWER_PWRCTRL; + 800cd5a: 6803 ldr r3, [r0, #0] + 800cd5c: f043 0303 orr.w r3, r3, #3 + 800cd60: 6003 str r3, [r0, #0] + SDMMCx->POWER = SDMMC_POWER_PWRCTRL; +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + /* 1ms: required power up waiting time before starting the SD initialization + sequence */ + HAL_Delay(2); + 800cd62: 2002 movs r0, #2 + 800cd64: f7f6 fdd5 bl 8003912 + + return HAL_OK; +} + 800cd68: 2000 movs r0, #0 + 800cd6a: bd08 pop {r3, pc} + +0800cd6c : + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_PowerState_Cycle(SDMMC_TypeDef *SDMMCx) +{ + /* Set power state to Power Cycle*/ + SDMMCx->POWER |= SDMMC_POWER_PWRCTRL_1; + 800cd6c: 6803 ldr r3, [r0, #0] + 800cd6e: f043 0302 orr.w r3, r3, #2 + 800cd72: 6003 str r3, [r0, #0] + + return HAL_OK; +} + 800cd74: 2000 movs r0, #0 + 800cd76: 4770 bx lr + +0800cd78 : + */ +HAL_StatusTypeDef SDMMC_PowerState_OFF(SDMMC_TypeDef *SDMMCx) +{ + /* Set power state to OFF */ +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + SDMMCx->POWER &= ~(SDMMC_POWER_PWRCTRL); + 800cd78: 6803 ldr r3, [r0, #0] + 800cd7a: f023 0303 bic.w r3, r3, #3 + 800cd7e: 6003 str r3, [r0, #0] +#else + SDMMCx->POWER = (uint32_t)0x00000000; +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + return HAL_OK; +} + 800cd80: 2000 movs r0, #0 + 800cd82: 4770 bx lr + +0800cd84 : + * - 0x02: Power UP + * - 0x03: Power ON + */ +uint32_t SDMMC_GetPowerState(SDMMC_TypeDef *SDMMCx) +{ + return (SDMMCx->POWER & SDMMC_POWER_PWRCTRL); + 800cd84: 6800 ldr r0, [r0, #0] +} + 800cd86: f000 0003 and.w r0, r0, #3 + 800cd8a: 4770 bx lr + +0800cd8c : + assert_param(IS_SDMMC_RESPONSE(Command->Response)); + assert_param(IS_SDMMC_WAIT(Command->WaitForInterrupt)); + assert_param(IS_SDMMC_CPSM(Command->CPSM)); + + /* Set the SDMMC Argument value */ + SDMMCx->ARG = Command->Argument; + 800cd8c: 680b ldr r3, [r1, #0] +{ + 800cd8e: b510 push {r4, lr} + SDMMCx->ARG = Command->Argument; + 800cd90: 6083 str r3, [r0, #8] + + /* Set SDMMC command parameters */ + tmpreg |= (uint32_t)(Command->CmdIndex |\ + 800cd92: e9d1 3201 ldrd r3, r2, [r1, #4] + 800cd96: 4313 orrs r3, r2 + Command->Response |\ + 800cd98: 68ca ldr r2, [r1, #12] + Command->WaitForInterrupt |\ + Command->CPSM); + + /* Write to SDMMC CMD register */ + MODIFY_REG(SDMMCx->CMD, CMD_CLEAR_MASK, tmpreg); + 800cd9a: 68c4 ldr r4, [r0, #12] + Command->Response |\ + 800cd9c: 4313 orrs r3, r2 + Command->WaitForInterrupt |\ + 800cd9e: 690a ldr r2, [r1, #16] + 800cda0: 4313 orrs r3, r2 + MODIFY_REG(SDMMCx->CMD, CMD_CLEAR_MASK, tmpreg); + 800cda2: 4a03 ldr r2, [pc, #12] ; (800cdb0 ) + 800cda4: 4022 ands r2, r4 + 800cda6: 4313 orrs r3, r2 + 800cda8: 60c3 str r3, [r0, #12] + + return HAL_OK; +} + 800cdaa: 2000 movs r0, #0 + 800cdac: bd10 pop {r4, pc} + 800cdae: bf00 nop + 800cdb0: fffee0c0 .word 0xfffee0c0 + +0800cdb4 : + * @param SDMMCx Pointer to SDMMC register base + * @retval Command index of the last command response received + */ +uint8_t SDMMC_GetCommandResponse(SDMMC_TypeDef *SDMMCx) +{ + return (uint8_t)(SDMMCx->RESPCMD); + 800cdb4: 6900 ldr r0, [r0, #16] +} + 800cdb6: b2c0 uxtb r0, r0 + 800cdb8: 4770 bx lr + +0800cdba : + + /* Check the parameters */ + assert_param(IS_SDMMC_RESP(Response)); + + /* Get the response */ + tmp = (uint32_t)(&(SDMMCx->RESP1)) + Response; + 800cdba: 3014 adds r0, #20 + + return (*(__IO uint32_t *) tmp); + 800cdbc: 5840 ldr r0, [r0, r1] +} + 800cdbe: 4770 bx lr + +0800cdc0 : + assert_param(IS_SDMMC_TRANSFER_DIR(Data->TransferDir)); + assert_param(IS_SDMMC_TRANSFER_MODE(Data->TransferMode)); + assert_param(IS_SDMMC_DPSM(Data->DPSM)); + + /* Set the SDMMC Data TimeOut value */ + SDMMCx->DTIMER = Data->DataTimeOut; + 800cdc0: 680b ldr r3, [r1, #0] +{ + 800cdc2: b510 push {r4, lr} + SDMMCx->DTIMER = Data->DataTimeOut; + 800cdc4: 6243 str r3, [r0, #36] ; 0x24 + + /* Set the SDMMC DataLength value */ + SDMMCx->DLEN = Data->DataLength; + 800cdc6: 684b ldr r3, [r1, #4] + 800cdc8: 6283 str r3, [r0, #40] ; 0x28 + + /* Set the SDMMC data configuration parameters */ + tmpreg |= (uint32_t)(Data->DataBlockSize |\ + 800cdca: e9d1 3402 ldrd r3, r4, [r1, #8] + 800cdce: 4323 orrs r3, r4 + Data->TransferDir |\ + 800cdd0: 690c ldr r4, [r1, #16] + Data->TransferMode |\ + Data->DPSM); + + /* Write to SDMMC DCTRL */ + MODIFY_REG(SDMMCx->DCTRL, DCTRL_CLEAR_MASK, tmpreg); + 800cdd2: 6ac2 ldr r2, [r0, #44] ; 0x2c + Data->TransferMode |\ + 800cdd4: 6949 ldr r1, [r1, #20] + Data->TransferDir |\ + 800cdd6: 4323 orrs r3, r4 + Data->TransferMode |\ + 800cdd8: 430b orrs r3, r1 + MODIFY_REG(SDMMCx->DCTRL, DCTRL_CLEAR_MASK, tmpreg); + 800cdda: f022 02ff bic.w r2, r2, #255 ; 0xff + 800cdde: 4313 orrs r3, r2 + 800cde0: 62c3 str r3, [r0, #44] ; 0x2c + + return HAL_OK; + +} + 800cde2: 2000 movs r0, #0 + 800cde4: bd10 pop {r4, pc} + +0800cde6 : + * @param SDMMCx Pointer to SDMMC register base + * @retval Number of remaining data bytes to be transferred + */ +uint32_t SDMMC_GetDataCounter(SDMMC_TypeDef *SDMMCx) +{ + return (SDMMCx->DCOUNT); + 800cde6: 6b00 ldr r0, [r0, #48] ; 0x30 +} + 800cde8: 4770 bx lr + +0800cdea : + 800cdea: f8d0 0080 ldr.w r0, [r0, #128] ; 0x80 + 800cdee: 4770 bx lr + +0800cdf0 : +{ + /* Check the parameters */ + assert_param(IS_SDMMC_READWAIT_MODE(SDMMC_ReadWaitMode)); + + /* Set SDMMC read wait mode */ + MODIFY_REG(SDMMCx->DCTRL, SDMMC_DCTRL_RWMOD, SDMMC_ReadWaitMode); + 800cdf0: 6ac3 ldr r3, [r0, #44] ; 0x2c + 800cdf2: f423 6380 bic.w r3, r3, #1024 ; 0x400 + 800cdf6: 4319 orrs r1, r3 + 800cdf8: 62c1 str r1, [r0, #44] ; 0x2c + + return HAL_OK; +} + 800cdfa: 2000 movs r0, #0 + 800cdfc: 4770 bx lr + ... + +0800ce00 : + * @brief Send the Go Idle State command and check the response. + * @param SDMMCx Pointer to SDMMC register base + * @retval HAL status + */ +uint32_t SDMMC_CmdGoIdleState(SDMMC_TypeDef *SDMMCx) +{ + 800ce00: b510 push {r4, lr} + SDMMC_CmdInitTypeDef sdmmc_cmdinit; + uint32_t errorstate; + + sdmmc_cmdinit.Argument = 0U; + 800ce02: 2300 movs r3, #0 +{ + 800ce04: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_GO_IDLE_STATE; + 800ce06: e9cd 3301 strd r3, r3, [sp, #4] + sdmmc_cmdinit.Response = SDMMC_RESPONSE_NO; + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800ce0a: e9cd 3303 strd r3, r3, [sp, #12] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800ce0e: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800ce10: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800ce14: 9305 str r3, [sp, #20] +{ + 800ce16: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800ce18: f7ff ffb8 bl 800cd8c + */ +static uint32_t SDMMC_GetCmdError(SDMMC_TypeDef *SDMMCx) +{ + /* 8 is the number of required instructions cycles for the below loop statement. + The SDMMC_CMDTIMEOUT is expressed in ms */ + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800ce1c: 4b0a ldr r3, [pc, #40] ; (800ce48 ) + 800ce1e: f44f 52fa mov.w r2, #8000 ; 0x1f40 + 800ce22: 681b ldr r3, [r3, #0] + 800ce24: fbb3 f3f2 udiv r3, r3, r2 + 800ce28: f241 3288 movw r2, #5000 ; 0x1388 + 800ce2c: 4353 muls r3, r2 + + do + { + if (count-- == 0U) + 800ce2e: 3b01 subs r3, #1 + 800ce30: d307 bcc.n 800ce42 + { + return SDMMC_ERROR_TIMEOUT; + } + + }while(!__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CMDSENT)); + 800ce32: 6b62 ldr r2, [r4, #52] ; 0x34 + 800ce34: 0612 lsls r2, r2, #24 + 800ce36: d5fa bpl.n 800ce2e + + /* Clear all the static flags */ + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800ce38: 4b04 ldr r3, [pc, #16] ; (800ce4c ) + 800ce3a: 63a3 str r3, [r4, #56] ; 0x38 + + return SDMMC_ERROR_NONE; + 800ce3c: 2000 movs r0, #0 +} + 800ce3e: b006 add sp, #24 + 800ce40: bd10 pop {r4, pc} + return SDMMC_ERROR_TIMEOUT; + 800ce42: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + return errorstate; + 800ce46: e7fa b.n 800ce3e + 800ce48: 2009e2a8 .word 0x2009e2a8 + 800ce4c: 002000c5 .word 0x002000c5 + +0800ce50 : + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800ce50: 4b45 ldr r3, [pc, #276] ; (800cf68 ) +{ + 800ce52: b510 push {r4, lr} + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800ce54: 681b ldr r3, [r3, #0] +{ + 800ce56: 4604 mov r4, r0 + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800ce58: f44f 50fa mov.w r0, #8000 ; 0x1f40 + 800ce5c: fbb3 f3f0 udiv r3, r3, r0 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || + 800ce60: 4842 ldr r0, [pc, #264] ; (800cf6c ) + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800ce62: 435a muls r2, r3 + if (count-- == 0U) + 800ce64: 2a00 cmp r2, #0 + 800ce66: d048 beq.n 800cefa + sta_reg = SDMMCx->STA; + 800ce68: 6b63 ldr r3, [r4, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800ce6a: 4203 tst r3, r0 + 800ce6c: d007 beq.n 800ce7e + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || + 800ce6e: 049b lsls r3, r3, #18 + 800ce70: d405 bmi.n 800ce7e + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800ce72: 6b63 ldr r3, [r4, #52] ; 0x34 + 800ce74: 0758 lsls r0, r3, #29 + 800ce76: d504 bpl.n 800ce82 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800ce78: 2004 movs r0, #4 + 800ce7a: 63a0 str r0, [r4, #56] ; 0x38 +} + 800ce7c: bd10 pop {r4, pc} + 800ce7e: 3a01 subs r2, #1 + 800ce80: e7f0 b.n 800ce64 + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800ce82: 6b60 ldr r0, [r4, #52] ; 0x34 + 800ce84: f010 0001 ands.w r0, r0, #1 + 800ce88: d002 beq.n 800ce90 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800ce8a: 2301 movs r3, #1 + 800ce8c: 63a3 str r3, [r4, #56] ; 0x38 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800ce8e: e7f5 b.n 800ce7c + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800ce90: 4b37 ldr r3, [pc, #220] ; (800cf70 ) + 800ce92: 63a3 str r3, [r4, #56] ; 0x38 + return (uint8_t)(SDMMCx->RESPCMD); + 800ce94: 6923 ldr r3, [r4, #16] + if(SDMMC_GetCommandResponse(SDMMCx) != SD_CMD) + 800ce96: b2db uxtb r3, r3 + 800ce98: 4299 cmp r1, r3 + 800ce9a: d131 bne.n 800cf00 + return (*(__IO uint32_t *) tmp); + 800ce9c: 6963 ldr r3, [r4, #20] + if((response_r1 & SDMMC_OCR_ERRORBITS) == SDMMC_ALLZERO) + 800ce9e: 4835 ldr r0, [pc, #212] ; (800cf74 ) + 800cea0: 4018 ands r0, r3 + 800cea2: 2800 cmp r0, #0 + 800cea4: d0ea beq.n 800ce7c + else if((response_r1 & SDMMC_OCR_ADDR_OUT_OF_RANGE) == SDMMC_OCR_ADDR_OUT_OF_RANGE) + 800cea6: 2b00 cmp r3, #0 + 800cea8: db2c blt.n 800cf04 + else if((response_r1 & SDMMC_OCR_ADDR_MISALIGNED) == SDMMC_OCR_ADDR_MISALIGNED) + 800ceaa: 005a lsls r2, r3, #1 + 800ceac: d42d bmi.n 800cf0a + else if((response_r1 & SDMMC_OCR_BLOCK_LEN_ERR) == SDMMC_OCR_BLOCK_LEN_ERR) + 800ceae: 009c lsls r4, r3, #2 + 800ceb0: d42d bmi.n 800cf0e + else if((response_r1 & SDMMC_OCR_ERASE_SEQ_ERR) == SDMMC_OCR_ERASE_SEQ_ERR) + 800ceb2: 00d9 lsls r1, r3, #3 + 800ceb4: d42d bmi.n 800cf12 + else if((response_r1 & SDMMC_OCR_BAD_ERASE_PARAM) == SDMMC_OCR_BAD_ERASE_PARAM) + 800ceb6: 011a lsls r2, r3, #4 + 800ceb8: d42e bmi.n 800cf18 + else if((response_r1 & SDMMC_OCR_WRITE_PROT_VIOLATION) == SDMMC_OCR_WRITE_PROT_VIOLATION) + 800ceba: 015c lsls r4, r3, #5 + 800cebc: d42f bmi.n 800cf1e + else if((response_r1 & SDMMC_OCR_LOCK_UNLOCK_FAILED) == SDMMC_OCR_LOCK_UNLOCK_FAILED) + 800cebe: 01d9 lsls r1, r3, #7 + 800cec0: d430 bmi.n 800cf24 + else if((response_r1 & SDMMC_OCR_COM_CRC_FAILED) == SDMMC_OCR_COM_CRC_FAILED) + 800cec2: 021a lsls r2, r3, #8 + 800cec4: d431 bmi.n 800cf2a + else if((response_r1 & SDMMC_OCR_ILLEGAL_CMD) == SDMMC_OCR_ILLEGAL_CMD) + 800cec6: 025c lsls r4, r3, #9 + 800cec8: d432 bmi.n 800cf30 + else if((response_r1 & SDMMC_OCR_CARD_ECC_FAILED) == SDMMC_OCR_CARD_ECC_FAILED) + 800ceca: 0299 lsls r1, r3, #10 + 800cecc: d433 bmi.n 800cf36 + else if((response_r1 & SDMMC_OCR_CC_ERROR) == SDMMC_OCR_CC_ERROR) + 800cece: 02da lsls r2, r3, #11 + 800ced0: d434 bmi.n 800cf3c + else if((response_r1 & SDMMC_OCR_STREAM_READ_UNDERRUN) == SDMMC_OCR_STREAM_READ_UNDERRUN) + 800ced2: 035c lsls r4, r3, #13 + 800ced4: d435 bmi.n 800cf42 + else if((response_r1 & SDMMC_OCR_STREAM_WRITE_OVERRUN) == SDMMC_OCR_STREAM_WRITE_OVERRUN) + 800ced6: 0399 lsls r1, r3, #14 + 800ced8: d436 bmi.n 800cf48 + else if((response_r1 & SDMMC_OCR_CID_CSD_OVERWRITE) == SDMMC_OCR_CID_CSD_OVERWRITE) + 800ceda: 03da lsls r2, r3, #15 + 800cedc: d437 bmi.n 800cf4e + else if((response_r1 & SDMMC_OCR_WP_ERASE_SKIP) == SDMMC_OCR_WP_ERASE_SKIP) + 800cede: 041c lsls r4, r3, #16 + 800cee0: d438 bmi.n 800cf54 + else if((response_r1 & SDMMC_OCR_CARD_ECC_DISABLED) == SDMMC_OCR_CARD_ECC_DISABLED) + 800cee2: 0459 lsls r1, r3, #17 + 800cee4: d439 bmi.n 800cf5a + else if((response_r1 & SDMMC_OCR_ERASE_RESET) == SDMMC_OCR_ERASE_RESET) + 800cee6: 049a lsls r2, r3, #18 + 800cee8: d43a bmi.n 800cf60 + return SDMMC_ERROR_GENERAL_UNKNOWN_ERR; + 800ceea: f013 0f08 tst.w r3, #8 + 800ceee: bf14 ite ne + 800cef0: f44f 0000 movne.w r0, #8388608 ; 0x800000 + 800cef4: f44f 3080 moveq.w r0, #65536 ; 0x10000 + 800cef8: e7c0 b.n 800ce7c + return SDMMC_ERROR_TIMEOUT; + 800cefa: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800cefe: e7bd b.n 800ce7c + return SDMMC_ERROR_CMD_CRC_FAIL; + 800cf00: 2001 movs r0, #1 + 800cf02: e7bb b.n 800ce7c + return SDMMC_ERROR_ADDR_OUT_OF_RANGE; + 800cf04: f04f 7000 mov.w r0, #33554432 ; 0x2000000 + 800cf08: e7b8 b.n 800ce7c + return SDMMC_ERROR_ADDR_MISALIGNED; + 800cf0a: 2040 movs r0, #64 ; 0x40 + 800cf0c: e7b6 b.n 800ce7c + return SDMMC_ERROR_BLOCK_LEN_ERR; + 800cf0e: 2080 movs r0, #128 ; 0x80 + 800cf10: e7b4 b.n 800ce7c + return SDMMC_ERROR_ERASE_SEQ_ERR; + 800cf12: f44f 7080 mov.w r0, #256 ; 0x100 + 800cf16: e7b1 b.n 800ce7c + return SDMMC_ERROR_BAD_ERASE_PARAM; + 800cf18: f44f 7000 mov.w r0, #512 ; 0x200 + 800cf1c: e7ae b.n 800ce7c + return SDMMC_ERROR_WRITE_PROT_VIOLATION; + 800cf1e: f44f 6080 mov.w r0, #1024 ; 0x400 + 800cf22: e7ab b.n 800ce7c + return SDMMC_ERROR_LOCK_UNLOCK_FAILED; + 800cf24: f44f 6000 mov.w r0, #2048 ; 0x800 + 800cf28: e7a8 b.n 800ce7c + return SDMMC_ERROR_COM_CRC_FAILED; + 800cf2a: f44f 5080 mov.w r0, #4096 ; 0x1000 + 800cf2e: e7a5 b.n 800ce7c + return SDMMC_ERROR_ILLEGAL_CMD; + 800cf30: f44f 5000 mov.w r0, #8192 ; 0x2000 + 800cf34: e7a2 b.n 800ce7c + return SDMMC_ERROR_CARD_ECC_FAILED; + 800cf36: f44f 4080 mov.w r0, #16384 ; 0x4000 + 800cf3a: e79f b.n 800ce7c + return SDMMC_ERROR_CC_ERR; + 800cf3c: f44f 4000 mov.w r0, #32768 ; 0x8000 + 800cf40: e79c b.n 800ce7c + return SDMMC_ERROR_STREAM_READ_UNDERRUN; + 800cf42: f44f 3000 mov.w r0, #131072 ; 0x20000 + 800cf46: e799 b.n 800ce7c + return SDMMC_ERROR_STREAM_WRITE_OVERRUN; + 800cf48: f44f 2080 mov.w r0, #262144 ; 0x40000 + 800cf4c: e796 b.n 800ce7c + return SDMMC_ERROR_CID_CSD_OVERWRITE; + 800cf4e: f44f 2000 mov.w r0, #524288 ; 0x80000 + 800cf52: e793 b.n 800ce7c + return SDMMC_ERROR_WP_ERASE_SKIP; + 800cf54: f44f 1080 mov.w r0, #1048576 ; 0x100000 + 800cf58: e790 b.n 800ce7c + return SDMMC_ERROR_CARD_ECC_DISABLED; + 800cf5a: f44f 1000 mov.w r0, #2097152 ; 0x200000 + 800cf5e: e78d b.n 800ce7c + return SDMMC_ERROR_ERASE_RESET; + 800cf60: f44f 0080 mov.w r0, #4194304 ; 0x400000 + 800cf64: e78a b.n 800ce7c + 800cf66: bf00 nop + 800cf68: 2009e2a8 .word 0x2009e2a8 + 800cf6c: 00200045 .word 0x00200045 + 800cf70: 002000c5 .word 0x002000c5 + 800cf74: fdffe008 .word 0xfdffe008 + +0800cf78 : +{ + 800cf78: b530 push {r4, r5, lr} + 800cf7a: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800cf7c: 2510 movs r5, #16 + 800cf7e: f44f 7380 mov.w r3, #256 ; 0x100 + 800cf82: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800cf86: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cf88: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)BlockSize; + 800cf8c: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cf8e: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cf90: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cf92: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cf96: f7ff fef9 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SET_BLOCKLEN, SDMMC_CMDTIMEOUT); + 800cf9a: f241 3288 movw r2, #5000 ; 0x1388 + 800cf9e: 4629 mov r1, r5 + 800cfa0: 4620 mov r0, r4 + 800cfa2: f7ff ff55 bl 800ce50 +} + 800cfa6: b007 add sp, #28 + 800cfa8: bd30 pop {r4, r5, pc} + +0800cfaa : +{ + 800cfaa: b530 push {r4, r5, lr} + 800cfac: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800cfae: 2511 movs r5, #17 + 800cfb0: f44f 7380 mov.w r3, #256 ; 0x100 + 800cfb4: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800cfb8: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cfba: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)ReadAdd; + 800cfbe: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cfc0: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cfc2: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cfc4: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cfc8: f7ff fee0 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_READ_SINGLE_BLOCK, SDMMC_CMDTIMEOUT); + 800cfcc: f241 3288 movw r2, #5000 ; 0x1388 + 800cfd0: 4629 mov r1, r5 + 800cfd2: 4620 mov r0, r4 + 800cfd4: f7ff ff3c bl 800ce50 +} + 800cfd8: b007 add sp, #28 + 800cfda: bd30 pop {r4, r5, pc} + +0800cfdc : +{ + 800cfdc: b530 push {r4, r5, lr} + 800cfde: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800cfe0: 2512 movs r5, #18 + 800cfe2: f44f 7380 mov.w r3, #256 ; 0x100 + 800cfe6: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800cfea: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cfec: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)ReadAdd; + 800cff0: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cff2: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cff4: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800cff6: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800cffa: f7ff fec7 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_READ_MULT_BLOCK, SDMMC_CMDTIMEOUT); + 800cffe: f241 3288 movw r2, #5000 ; 0x1388 + 800d002: 4629 mov r1, r5 + 800d004: 4620 mov r0, r4 + 800d006: f7ff ff23 bl 800ce50 +} + 800d00a: b007 add sp, #28 + 800d00c: bd30 pop {r4, r5, pc} + +0800d00e : +{ + 800d00e: b530 push {r4, r5, lr} + 800d010: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d012: 2518 movs r5, #24 + 800d014: f44f 7380 mov.w r3, #256 ; 0x100 + 800d018: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d01c: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d01e: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)WriteAdd; + 800d022: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d024: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d026: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d028: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d02c: f7ff feae bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_WRITE_SINGLE_BLOCK, SDMMC_CMDTIMEOUT); + 800d030: f241 3288 movw r2, #5000 ; 0x1388 + 800d034: 4629 mov r1, r5 + 800d036: 4620 mov r0, r4 + 800d038: f7ff ff0a bl 800ce50 +} + 800d03c: b007 add sp, #28 + 800d03e: bd30 pop {r4, r5, pc} + +0800d040 : +{ + 800d040: b530 push {r4, r5, lr} + 800d042: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d044: 2519 movs r5, #25 + 800d046: f44f 7380 mov.w r3, #256 ; 0x100 + 800d04a: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d04e: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d050: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)WriteAdd; + 800d054: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d056: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d058: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d05a: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d05e: f7ff fe95 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_WRITE_MULT_BLOCK, SDMMC_CMDTIMEOUT); + 800d062: f241 3288 movw r2, #5000 ; 0x1388 + 800d066: 4629 mov r1, r5 + 800d068: 4620 mov r0, r4 + 800d06a: f7ff fef1 bl 800ce50 +} + 800d06e: b007 add sp, #28 + 800d070: bd30 pop {r4, r5, pc} + +0800d072 : +{ + 800d072: b530 push {r4, r5, lr} + 800d074: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d076: 2520 movs r5, #32 + 800d078: f44f 7380 mov.w r3, #256 ; 0x100 + 800d07c: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d080: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d082: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)StartAdd; + 800d086: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d088: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d08a: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d08c: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d090: f7ff fe7c bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_ERASE_GRP_START, SDMMC_CMDTIMEOUT); + 800d094: f241 3288 movw r2, #5000 ; 0x1388 + 800d098: 4629 mov r1, r5 + 800d09a: 4620 mov r0, r4 + 800d09c: f7ff fed8 bl 800ce50 +} + 800d0a0: b007 add sp, #28 + 800d0a2: bd30 pop {r4, r5, pc} + +0800d0a4 : +{ + 800d0a4: b530 push {r4, r5, lr} + 800d0a6: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d0a8: 2521 movs r5, #33 ; 0x21 + 800d0aa: f44f 7380 mov.w r3, #256 ; 0x100 + 800d0ae: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d0b2: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0b4: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)EndAdd; + 800d0b8: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0ba: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0bc: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0be: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0c2: f7ff fe63 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_ERASE_GRP_END, SDMMC_CMDTIMEOUT); + 800d0c6: f241 3288 movw r2, #5000 ; 0x1388 + 800d0ca: 4629 mov r1, r5 + 800d0cc: 4620 mov r0, r4 + 800d0ce: f7ff febf bl 800ce50 +} + 800d0d2: b007 add sp, #28 + 800d0d4: bd30 pop {r4, r5, pc} + +0800d0d6 : +{ + 800d0d6: b530 push {r4, r5, lr} + 800d0d8: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d0da: 2523 movs r5, #35 ; 0x23 + 800d0dc: f44f 7380 mov.w r3, #256 ; 0x100 + 800d0e0: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d0e4: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0e6: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)StartAdd; + 800d0ea: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0ec: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0ee: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0f0: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0f4: f7ff fe4a bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE_GRP_START, SDMMC_CMDTIMEOUT); + 800d0f8: f241 3288 movw r2, #5000 ; 0x1388 + 800d0fc: 4629 mov r1, r5 + 800d0fe: 4620 mov r0, r4 + 800d100: f7ff fea6 bl 800ce50 +} + 800d104: b007 add sp, #28 + 800d106: bd30 pop {r4, r5, pc} + +0800d108 : +{ + 800d108: b530 push {r4, r5, lr} + 800d10a: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d10c: 2524 movs r5, #36 ; 0x24 + 800d10e: f44f 7380 mov.w r3, #256 ; 0x100 + 800d112: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d116: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d118: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)EndAdd; + 800d11c: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d11e: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d120: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d122: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d126: f7ff fe31 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE_GRP_END, SDMMC_CMDTIMEOUT); + 800d12a: f241 3288 movw r2, #5000 ; 0x1388 + 800d12e: 4629 mov r1, r5 + 800d130: 4620 mov r0, r4 + 800d132: f7ff fe8d bl 800ce50 +} + 800d136: b007 add sp, #28 + 800d138: bd30 pop {r4, r5, pc} + +0800d13a : +{ + 800d13a: b530 push {r4, r5, lr} + 800d13c: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d13e: 2526 movs r5, #38 ; 0x26 + 800d140: f44f 7380 mov.w r3, #256 ; 0x100 + 800d144: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d148: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d14a: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = EraseType; + 800d14e: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d150: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d152: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d154: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d158: f7ff fe18 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE, SDMMC_MAXERASETIMEOUT); + 800d15c: f24f 6218 movw r2, #63000 ; 0xf618 + 800d160: 4629 mov r1, r5 + 800d162: 4620 mov r0, r4 + 800d164: f7ff fe74 bl 800ce50 +} + 800d168: b007 add sp, #28 + 800d16a: bd30 pop {r4, r5, pc} + +0800d16c : +{ + 800d16c: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d16e: 2300 movs r3, #0 +{ + 800d170: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d172: 250c movs r5, #12 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d174: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d178: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d17c: e9cd 3501 strd r3, r5, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d180: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d184: 9305 str r3, [sp, #20] + __SDMMC_CMDSTOP_ENABLE(SDMMCx); + 800d186: 68c3 ldr r3, [r0, #12] + 800d188: f043 0380 orr.w r3, r3, #128 ; 0x80 + 800d18c: 60c3 str r3, [r0, #12] + __SDMMC_CMDTRANS_DISABLE(SDMMCx); + 800d18e: 68c3 ldr r3, [r0, #12] + 800d190: f023 0340 bic.w r3, r3, #64 ; 0x40 +{ + 800d194: 4604 mov r4, r0 + __SDMMC_CMDTRANS_DISABLE(SDMMCx); + 800d196: 60c3 str r3, [r0, #12] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d198: a901 add r1, sp, #4 + 800d19a: f7ff fdf7 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_STOP_TRANSMISSION, SDMMC_STOPTRANSFERTIMEOUT); + 800d19e: 4a05 ldr r2, [pc, #20] ; (800d1b4 ) + 800d1a0: 4629 mov r1, r5 + 800d1a2: 4620 mov r0, r4 + 800d1a4: f7ff fe54 bl 800ce50 + __SDMMC_CMDSTOP_DISABLE(SDMMCx); + 800d1a8: 68e3 ldr r3, [r4, #12] + 800d1aa: f023 0380 bic.w r3, r3, #128 ; 0x80 + 800d1ae: 60e3 str r3, [r4, #12] +} + 800d1b0: b007 add sp, #28 + 800d1b2: bd30 pop {r4, r5, pc} + 800d1b4: 05f5e100 .word 0x05f5e100 + +0800d1b8 : +{ + 800d1b8: b530 push {r4, r5, lr} + 800d1ba: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d1bc: 2507 movs r5, #7 + 800d1be: f44f 7380 mov.w r3, #256 ; 0x100 + 800d1c2: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d1c6: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d1c8: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)Addr; + 800d1cc: 9201 str r2, [sp, #4] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d1ce: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d1d0: 2200 movs r2, #0 + 800d1d2: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d1d6: f7ff fdd9 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SEL_DESEL_CARD, SDMMC_CMDTIMEOUT); + 800d1da: f241 3288 movw r2, #5000 ; 0x1388 + 800d1de: 4629 mov r1, r5 + 800d1e0: 4620 mov r0, r4 + 800d1e2: f7ff fe35 bl 800ce50 +} + 800d1e6: b007 add sp, #28 + 800d1e8: bd30 pop {r4, r5, pc} + +0800d1ea : +{ + 800d1ea: b530 push {r4, r5, lr} + 800d1ec: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d1ee: 2537 movs r5, #55 ; 0x37 + 800d1f0: f44f 7380 mov.w r3, #256 ; 0x100 + 800d1f4: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d1f8: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d1fa: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)Argument; + 800d1fe: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d200: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d202: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d204: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d208: f7ff fdc0 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_APP_CMD, SDMMC_CMDTIMEOUT); + 800d20c: f241 3288 movw r2, #5000 ; 0x1388 + 800d210: 4629 mov r1, r5 + 800d212: 4620 mov r0, r4 + 800d214: f7ff fe1c bl 800ce50 +} + 800d218: b007 add sp, #28 + 800d21a: bd30 pop {r4, r5, pc} + +0800d21c : +{ + 800d21c: b530 push {r4, r5, lr} + 800d21e: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d220: 2506 movs r5, #6 + 800d222: f44f 7380 mov.w r3, #256 ; 0x100 + 800d226: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d22a: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d22c: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)BusWidth; + 800d230: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d232: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d234: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d236: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d23a: f7ff fda7 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_APP_SD_SET_BUSWIDTH, SDMMC_CMDTIMEOUT); + 800d23e: f241 3288 movw r2, #5000 ; 0x1388 + 800d242: 4629 mov r1, r5 + 800d244: 4620 mov r0, r4 + 800d246: f7ff fe03 bl 800ce50 +} + 800d24a: b007 add sp, #28 + 800d24c: bd30 pop {r4, r5, pc} + +0800d24e : + 800d24e: f7ff bfe5 b.w 800d21c + +0800d252 : +{ + 800d252: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d254: 2300 movs r3, #0 +{ + 800d256: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d258: 2533 movs r5, #51 ; 0x33 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d25a: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d25e: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d262: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d266: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d268: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d26c: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d26e: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d270: f7ff fd8c bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_APP_SEND_SCR, SDMMC_CMDTIMEOUT); + 800d274: f241 3288 movw r2, #5000 ; 0x1388 + 800d278: 4629 mov r1, r5 + 800d27a: 4620 mov r0, r4 + 800d27c: f7ff fde8 bl 800ce50 +} + 800d280: b007 add sp, #28 + 800d282: bd30 pop {r4, r5, pc} + +0800d284 : +{ + 800d284: b530 push {r4, r5, lr} + 800d286: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d288: 2503 movs r5, #3 + sdmmc_cmdinit.Argument = ((uint32_t)RCA << 16U); + 800d28a: 0409 lsls r1, r1, #16 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d28c: f44f 7380 mov.w r3, #256 ; 0x100 + 800d290: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d294: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d296: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = ((uint32_t)RCA << 16U); + 800d29a: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d29c: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d29e: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2a0: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2a4: f7ff fd72 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SET_REL_ADDR, SDMMC_CMDTIMEOUT); + 800d2a8: f241 3288 movw r2, #5000 ; 0x1388 + 800d2ac: 4629 mov r1, r5 + 800d2ae: 4620 mov r0, r4 + 800d2b0: f7ff fdce bl 800ce50 +} + 800d2b4: b007 add sp, #28 + 800d2b6: bd30 pop {r4, r5, pc} + +0800d2b8 : +{ + 800d2b8: b530 push {r4, r5, lr} + 800d2ba: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d2bc: 250d movs r5, #13 + 800d2be: f44f 7380 mov.w r3, #256 ; 0x100 + 800d2c2: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d2c6: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2c8: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = Argument; + 800d2cc: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2ce: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2d0: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2d2: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2d6: f7ff fd59 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SEND_STATUS, SDMMC_CMDTIMEOUT); + 800d2da: f241 3288 movw r2, #5000 ; 0x1388 + 800d2de: 4629 mov r1, r5 + 800d2e0: 4620 mov r0, r4 + 800d2e2: f7ff fdb5 bl 800ce50 +} + 800d2e6: b007 add sp, #28 + 800d2e8: bd30 pop {r4, r5, pc} + +0800d2ea : +{ + 800d2ea: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d2ec: 2300 movs r3, #0 +{ + 800d2ee: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d2f0: 250d movs r5, #13 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d2f2: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d2f6: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d2fa: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d2fe: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d300: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d304: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d306: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d308: f7ff fd40 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_APP_STATUS, SDMMC_CMDTIMEOUT); + 800d30c: f241 3288 movw r2, #5000 ; 0x1388 + 800d310: 4629 mov r1, r5 + 800d312: 4620 mov r0, r4 + 800d314: f7ff fd9c bl 800ce50 +} + 800d318: b007 add sp, #28 + 800d31a: bd30 pop {r4, r5, pc} + +0800d31c : +{ + 800d31c: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d31e: 2300 movs r3, #0 +{ + 800d320: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d322: 250b movs r5, #11 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d324: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d328: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d32c: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d330: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d332: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d336: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d338: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d33a: f7ff fd27 bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_VOLTAGE_SWITCH, SDMMC_CMDTIMEOUT); + 800d33e: f241 3288 movw r2, #5000 ; 0x1388 + 800d342: 4629 mov r1, r5 + 800d344: 4620 mov r0, r4 + 800d346: f7ff fd83 bl 800ce50 +} + 800d34a: b007 add sp, #28 + 800d34c: bd30 pop {r4, r5, pc} + +0800d34e : +{ + 800d34e: b530 push {r4, r5, lr} + 800d350: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d352: 2508 movs r5, #8 + 800d354: f44f 7380 mov.w r3, #256 ; 0x100 + 800d358: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d35c: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d35e: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = Argument; + 800d362: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d364: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d366: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d368: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d36c: f7ff fd0e bl 800cd8c + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_HS_SEND_EXT_CSD,SDMMC_CMDTIMEOUT); + 800d370: f241 3288 movw r2, #5000 ; 0x1388 + 800d374: 4629 mov r1, r5 + 800d376: 4620 mov r0, r4 + 800d378: f7ff fd6a bl 800ce50 +} + 800d37c: b007 add sp, #28 + 800d37e: bd30 pop {r4, r5, pc} + +0800d380 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d380: 4b11 ldr r3, [pc, #68] ; (800d3c8 ) + 800d382: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d386: 681b ldr r3, [r3, #0] + 800d388: fbb3 f3f1 udiv r3, r3, r1 + 800d38c: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d390: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d392: 434b muls r3, r1 + if (count-- == 0U) + 800d394: 3b01 subs r3, #1 + 800d396: d313 bcc.n 800d3c0 + sta_reg = SDMMCx->STA; + 800d398: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d39a: f011 0f45 tst.w r1, #69 ; 0x45 + 800d39e: d0f9 beq.n 800d394 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d3a0: 0489 lsls r1, r1, #18 + 800d3a2: d4f7 bmi.n 800d394 + if (__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d3a4: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d3a6: 075b lsls r3, r3, #29 + 800d3a8: d502 bpl.n 800d3b0 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d3aa: 2004 movs r0, #4 + 800d3ac: 6390 str r0, [r2, #56] ; 0x38 + return SDMMC_ERROR_CMD_RSP_TIMEOUT; + 800d3ae: 4770 bx lr + else if (__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d3b0: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d3b2: f010 0001 ands.w r0, r0, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d3b6: bf0c ite eq + 800d3b8: 4b04 ldreq r3, [pc, #16] ; (800d3cc ) + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d3ba: 2301 movne r3, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d3bc: 6393 str r3, [r2, #56] ; 0x38 + return SDMMC_ERROR_NONE; + 800d3be: 4770 bx lr + return SDMMC_ERROR_TIMEOUT; + 800d3c0: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d3c4: 4770 bx lr + 800d3c6: bf00 nop + 800d3c8: 2009e2a8 .word 0x2009e2a8 + 800d3cc: 002000c5 .word 0x002000c5 + +0800d3d0 : +{ + 800d3d0: b510 push {r4, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_ALL_SEND_CID; + 800d3d2: 2300 movs r3, #0 +{ + 800d3d4: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_ALL_SEND_CID; + 800d3d6: 2202 movs r2, #2 + 800d3d8: e9cd 3201 strd r3, r2, [sp, #4] + sdmmc_cmdinit.Response = SDMMC_RESPONSE_LONG; + 800d3dc: f44f 7240 mov.w r2, #768 ; 0x300 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d3e0: e9cd 2303 strd r2, r3, [sp, #12] +{ + 800d3e4: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3e6: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3ea: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3ec: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3ee: f7ff fccd bl 800cd8c + errorstate = SDMMC_GetCmdResp2(SDMMCx); + 800d3f2: 4620 mov r0, r4 + 800d3f4: f7ff ffc4 bl 800d380 +} + 800d3f8: b006 add sp, #24 + 800d3fa: bd10 pop {r4, pc} + +0800d3fc : +{ + 800d3fc: b510 push {r4, lr} + 800d3fe: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_LONG; + 800d400: 2209 movs r2, #9 + 800d402: f44f 7340 mov.w r3, #768 ; 0x300 + 800d406: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d40a: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d40c: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d410: 2100 movs r1, #0 + 800d412: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d416: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d418: a901 add r1, sp, #4 + 800d41a: f7ff fcb7 bl 800cd8c + errorstate = SDMMC_GetCmdResp2(SDMMCx); + 800d41e: 4620 mov r0, r4 + 800d420: f7ff ffae bl 800d380 +} + 800d424: b006 add sp, #24 + 800d426: bd10 pop {r4, pc} + +0800d428 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d428: 4b0e ldr r3, [pc, #56] ; (800d464 ) + 800d42a: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d42e: 681b ldr r3, [r3, #0] + 800d430: fbb3 f3f1 udiv r3, r3, r1 + 800d434: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d438: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d43a: 434b muls r3, r1 + if (count-- == 0U) + 800d43c: 3b01 subs r3, #1 + 800d43e: d30e bcc.n 800d45e + sta_reg = SDMMCx->STA; + 800d440: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d442: f011 0f45 tst.w r1, #69 ; 0x45 + 800d446: d0f9 beq.n 800d43c + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d448: 0489 lsls r1, r1, #18 + 800d44a: d4f7 bmi.n 800d43c + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d44c: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d44e: f010 0004 ands.w r0, r0, #4 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d452: bf15 itete ne + 800d454: 2004 movne r0, #4 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d456: 4b04 ldreq r3, [pc, #16] ; (800d468 ) + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d458: 6390 strne r0, [r2, #56] ; 0x38 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d45a: 6393 streq r3, [r2, #56] ; 0x38 + return SDMMC_ERROR_NONE; + 800d45c: 4770 bx lr + return SDMMC_ERROR_TIMEOUT; + 800d45e: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d462: 4770 bx lr + 800d464: 2009e2a8 .word 0x2009e2a8 + 800d468: 002000c5 .word 0x002000c5 + +0800d46c : +{ + 800d46c: b510 push {r4, lr} + 800d46e: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d470: 2229 movs r2, #41 ; 0x29 + 800d472: f44f 7380 mov.w r3, #256 ; 0x100 + 800d476: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d47a: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d47c: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d480: 2100 movs r1, #0 + 800d482: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d486: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d488: a901 add r1, sp, #4 + 800d48a: f7ff fc7f bl 800cd8c + errorstate = SDMMC_GetCmdResp3(SDMMCx); + 800d48e: 4620 mov r0, r4 + 800d490: f7ff ffca bl 800d428 +} + 800d494: b006 add sp, #24 + 800d496: bd10 pop {r4, pc} + +0800d498 : +{ + 800d498: b510 push {r4, lr} + 800d49a: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d49c: 2201 movs r2, #1 + 800d49e: f44f 7380 mov.w r3, #256 ; 0x100 + 800d4a2: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d4a6: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4a8: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d4ac: 2100 movs r1, #0 + 800d4ae: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d4b2: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d4b4: a901 add r1, sp, #4 + 800d4b6: f7ff fc69 bl 800cd8c + errorstate = SDMMC_GetCmdResp3(SDMMCx); + 800d4ba: 4620 mov r0, r4 + 800d4bc: f7ff ffb4 bl 800d428 +} + 800d4c0: b006 add sp, #24 + 800d4c2: bd10 pop {r4, pc} + +0800d4c4 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d4c4: 4b1f ldr r3, [pc, #124] ; (800d544 ) +{ + 800d4c6: b510 push {r4, lr} + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d4c8: 681b ldr r3, [r3, #0] +{ + 800d4ca: 4604 mov r4, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d4cc: f44f 50fa mov.w r0, #8000 ; 0x1f40 + 800d4d0: fbb3 f3f0 udiv r3, r3, r0 + 800d4d4: f241 3088 movw r0, #5000 ; 0x1388 + 800d4d8: 4343 muls r3, r0 + if (count-- == 0U) + 800d4da: 3b01 subs r3, #1 + 800d4dc: d329 bcc.n 800d532 + sta_reg = SDMMCx->STA; + 800d4de: 6b60 ldr r0, [r4, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d4e0: f010 0f45 tst.w r0, #69 ; 0x45 + 800d4e4: d0f9 beq.n 800d4da + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d4e6: 0480 lsls r0, r0, #18 + 800d4e8: d4f7 bmi.n 800d4da + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d4ea: 6b63 ldr r3, [r4, #52] ; 0x34 + 800d4ec: 0758 lsls r0, r3, #29 + 800d4ee: d502 bpl.n 800d4f6 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d4f0: 2004 movs r0, #4 + 800d4f2: 63a0 str r0, [r4, #56] ; 0x38 +} + 800d4f4: bd10 pop {r4, pc} + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d4f6: 6b60 ldr r0, [r4, #52] ; 0x34 + 800d4f8: f010 0001 ands.w r0, r0, #1 + 800d4fc: d002 beq.n 800d504 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d4fe: 2301 movs r3, #1 + 800d500: 63a3 str r3, [r4, #56] ; 0x38 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d502: e7f7 b.n 800d4f4 + return (uint8_t)(SDMMCx->RESPCMD); + 800d504: 6923 ldr r3, [r4, #16] + if(SDMMC_GetCommandResponse(SDMMCx) != SD_CMD) + 800d506: b2db uxtb r3, r3 + 800d508: 4299 cmp r1, r3 + 800d50a: d115 bne.n 800d538 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d50c: 4b0e ldr r3, [pc, #56] ; (800d548 ) + 800d50e: 63a3 str r3, [r4, #56] ; 0x38 + return (*(__IO uint32_t *) tmp); + 800d510: 6963 ldr r3, [r4, #20] + if((response_r1 & (SDMMC_R6_GENERAL_UNKNOWN_ERROR | SDMMC_R6_ILLEGAL_CMD | SDMMC_R6_COM_CRC_FAILED)) == SDMMC_ALLZERO) + 800d512: f413 4060 ands.w r0, r3, #57344 ; 0xe000 + 800d516: d102 bne.n 800d51e + *pRCA = (uint16_t) (response_r1 >> 16); + 800d518: 0c1b lsrs r3, r3, #16 + 800d51a: 8013 strh r3, [r2, #0] + return SDMMC_ERROR_NONE; + 800d51c: e7ea b.n 800d4f4 + else if((response_r1 & SDMMC_R6_ILLEGAL_CMD) == SDMMC_R6_ILLEGAL_CMD) + 800d51e: 045a lsls r2, r3, #17 + 800d520: d40c bmi.n 800d53c + return SDMMC_ERROR_GENERAL_UNKNOWN_ERR; + 800d522: f413 4f00 tst.w r3, #32768 ; 0x8000 + 800d526: bf14 ite ne + 800d528: f44f 5080 movne.w r0, #4096 ; 0x1000 + 800d52c: f44f 3080 moveq.w r0, #65536 ; 0x10000 + 800d530: e7e0 b.n 800d4f4 + return SDMMC_ERROR_TIMEOUT; + 800d532: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800d536: e7dd b.n 800d4f4 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d538: 2001 movs r0, #1 + 800d53a: e7db b.n 800d4f4 + return SDMMC_ERROR_ILLEGAL_CMD; + 800d53c: f44f 5000 mov.w r0, #8192 ; 0x2000 + 800d540: e7d8 b.n 800d4f4 + 800d542: bf00 nop + 800d544: 2009e2a8 .word 0x2009e2a8 + 800d548: 002000c5 .word 0x002000c5 + +0800d54c : +{ + 800d54c: b530 push {r4, r5, lr} + 800d54e: b089 sub sp, #36 ; 0x24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d550: 2300 movs r3, #0 +{ + 800d552: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d554: 2503 movs r5, #3 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d556: f44f 7180 mov.w r1, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d55a: e9cd 1305 strd r1, r3, [sp, #20] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d55e: e9cd 3503 strd r3, r5, [sp, #12] +{ + 800d562: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d564: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d568: a903 add r1, sp, #12 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d56a: 9307 str r3, [sp, #28] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d56c: f7ff fc0e bl 800cd8c + errorstate = SDMMC_GetCmdResp6(SDMMCx, SDMMC_CMD_SET_REL_ADDR, pRCA); + 800d570: 9a01 ldr r2, [sp, #4] + 800d572: 4629 mov r1, r5 + 800d574: 4620 mov r0, r4 + 800d576: f7ff ffa5 bl 800d4c4 +} + 800d57a: b009 add sp, #36 ; 0x24 + 800d57c: bd30 pop {r4, r5, pc} + ... + +0800d580 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d580: 4b13 ldr r3, [pc, #76] ; (800d5d0 ) + 800d582: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d586: 681b ldr r3, [r3, #0] + 800d588: fbb3 f3f1 udiv r3, r3, r1 + 800d58c: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d590: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d592: 434b muls r3, r1 + if (count-- == 0U) + 800d594: 3b01 subs r3, #1 + 800d596: d317 bcc.n 800d5c8 + sta_reg = SDMMCx->STA; + 800d598: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d59a: f011 0f45 tst.w r1, #69 ; 0x45 + 800d59e: d0f9 beq.n 800d594 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d5a0: 0488 lsls r0, r1, #18 + 800d5a2: d4f7 bmi.n 800d594 + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d5a4: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d5a6: 0759 lsls r1, r3, #29 + 800d5a8: d502 bpl.n 800d5b0 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d5aa: 2004 movs r0, #4 + 800d5ac: 6390 str r0, [r2, #56] ; 0x38 + return SDMMC_ERROR_CMD_RSP_TIMEOUT; + 800d5ae: 4770 bx lr + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d5b0: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d5b2: f010 0001 ands.w r0, r0, #1 + 800d5b6: d002 beq.n 800d5be + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d5b8: 2301 movs r3, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CMDREND); + 800d5ba: 6393 str r3, [r2, #56] ; 0x38 + 800d5bc: 4770 bx lr + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CMDREND)) + 800d5be: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d5c0: 065b lsls r3, r3, #25 + 800d5c2: d503 bpl.n 800d5cc + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CMDREND); + 800d5c4: 2340 movs r3, #64 ; 0x40 + 800d5c6: e7f8 b.n 800d5ba + return SDMMC_ERROR_TIMEOUT; + 800d5c8: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d5cc: 4770 bx lr + 800d5ce: bf00 nop + 800d5d0: 2009e2a8 .word 0x2009e2a8 + +0800d5d4 : +{ + 800d5d4: b510 push {r4, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_HS_SEND_EXT_CSD; + 800d5d6: f44f 72d5 mov.w r2, #426 ; 0x1aa +{ + 800d5da: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_HS_SEND_EXT_CSD; + 800d5dc: 2308 movs r3, #8 + 800d5de: e9cd 2301 strd r2, r3, [sp, #4] + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d5e2: f44f 7180 mov.w r1, #256 ; 0x100 + 800d5e6: 2300 movs r3, #0 + 800d5e8: e9cd 1303 strd r1, r3, [sp, #12] +{ + 800d5ec: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5ee: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5f2: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5f4: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5f6: f7ff fbc9 bl 800cd8c + errorstate = SDMMC_GetCmdResp7(SDMMCx); + 800d5fa: 4620 mov r0, r4 + 800d5fc: f7ff ffc0 bl 800d580 +} + 800d600: b006 add sp, #24 + 800d602: bd10 pop {r4, pc} + +0800d604 : + 800d604: b510 push {r4, lr} + 800d606: 3901 subs r1, #1 + 800d608: 4402 add r2, r0 + 800d60a: 4290 cmp r0, r2 + 800d60c: d101 bne.n 800d612 + 800d60e: 2000 movs r0, #0 + 800d610: e005 b.n 800d61e + 800d612: 7803 ldrb r3, [r0, #0] + 800d614: f811 4f01 ldrb.w r4, [r1, #1]! + 800d618: 42a3 cmp r3, r4 + 800d61a: d001 beq.n 800d620 + 800d61c: 1b18 subs r0, r3, r4 + 800d61e: bd10 pop {r4, pc} + 800d620: 3001 adds r0, #1 + 800d622: e7f2 b.n 800d60a + +0800d624 : + 800d624: 440a add r2, r1 + 800d626: 4291 cmp r1, r2 + 800d628: f100 33ff add.w r3, r0, #4294967295 ; 0xffffffff + 800d62c: d100 bne.n 800d630 + 800d62e: 4770 bx lr + 800d630: b510 push {r4, lr} + 800d632: f811 4b01 ldrb.w r4, [r1], #1 + 800d636: f803 4f01 strb.w r4, [r3, #1]! + 800d63a: 4291 cmp r1, r2 + 800d63c: d1f9 bne.n 800d632 + 800d63e: bd10 pop {r4, pc} + +0800d640 : + 800d640: 4288 cmp r0, r1 + 800d642: b510 push {r4, lr} + 800d644: eb01 0402 add.w r4, r1, r2 + 800d648: d902 bls.n 800d650 + 800d64a: 4284 cmp r4, r0 + 800d64c: 4623 mov r3, r4 + 800d64e: d807 bhi.n 800d660 + 800d650: 1e43 subs r3, r0, #1 + 800d652: 42a1 cmp r1, r4 + 800d654: d008 beq.n 800d668 + 800d656: f811 2b01 ldrb.w r2, [r1], #1 + 800d65a: f803 2f01 strb.w r2, [r3, #1]! + 800d65e: e7f8 b.n 800d652 + 800d660: 4402 add r2, r0 + 800d662: 4601 mov r1, r0 + 800d664: 428a cmp r2, r1 + 800d666: d100 bne.n 800d66a + 800d668: bd10 pop {r4, pc} + 800d66a: f813 4d01 ldrb.w r4, [r3, #-1]! + 800d66e: f802 4d01 strb.w r4, [r2, #-1]! + 800d672: e7f7 b.n 800d664 + +0800d674 : + 800d674: 4402 add r2, r0 + 800d676: 4603 mov r3, r0 + 800d678: 4293 cmp r3, r2 + 800d67a: d100 bne.n 800d67e + 800d67c: 4770 bx lr + 800d67e: f803 1b01 strb.w r1, [r3], #1 + 800d682: e7f9 b.n 800d678 + +0800d684 : + 800d684: 46ec mov ip, sp + 800d686: e8a0 5ff0 stmia.w r0!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} + 800d68a: f04f 0000 mov.w r0, #0 + 800d68e: 4770 bx lr + +0800d690 : + 800d690: e8b0 5ff0 ldmia.w r0!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} + 800d694: 46e5 mov sp, ip + 800d696: 0008 movs r0, r1 + 800d698: bf08 it eq + 800d69a: 2001 moveq r0, #1 + 800d69c: 4770 bx lr + 800d69e: bf00 nop + +0800d6a0 : + 800d6a0: 4603 mov r3, r0 + 800d6a2: f811 2b01 ldrb.w r2, [r1], #1 + 800d6a6: f803 2b01 strb.w r2, [r3], #1 + 800d6aa: 2a00 cmp r2, #0 + 800d6ac: d1f9 bne.n 800d6a2 + 800d6ae: 4770 bx lr + +0800d6b0 : + 800d6b0: b510 push {r4, lr} + 800d6b2: 460b mov r3, r1 + 800d6b4: b162 cbz r2, 800d6d0 + 800d6b6: 3a01 subs r2, #1 + 800d6b8: d008 beq.n 800d6cc + 800d6ba: f813 4b01 ldrb.w r4, [r3], #1 + 800d6be: f800 4b01 strb.w r4, [r0], #1 + 800d6c2: 2c00 cmp r4, #0 + 800d6c4: d1f7 bne.n 800d6b6 + 800d6c6: 1a58 subs r0, r3, r1 + 800d6c8: 3801 subs r0, #1 + 800d6ca: bd10 pop {r4, pc} + 800d6cc: 2200 movs r2, #0 + 800d6ce: 7002 strb r2, [r0, #0] + 800d6d0: f813 2b01 ldrb.w r2, [r3], #1 + 800d6d4: 2a00 cmp r2, #0 + 800d6d6: d1fb bne.n 800d6d0 + 800d6d8: e7f5 b.n 800d6c6 + +0800d6da : + 800d6da: 4603 mov r3, r0 + 800d6dc: f813 2b01 ldrb.w r2, [r3], #1 + 800d6e0: 2a00 cmp r2, #0 + 800d6e2: d1fb bne.n 800d6dc + 800d6e4: 1a18 subs r0, r3, r0 + 800d6e6: 3801 subs r0, #1 + 800d6e8: 4770 bx lr + 800d6ea: 0000 movs r0, r0 + 800d6ec: 0000 movs r0, r0 + ... + +0800d6f0 <__flash_burn_veneer>: + 800d6f0: f85f f000 ldr.w pc, [pc] ; 800d6f4 <__flash_burn_veneer+0x4> + 800d6f4: 2009e001 .word 0x2009e001 + +0800d6f8 <__flash_page_erase_veneer>: + 800d6f8: f85f f000 ldr.w pc, [pc] ; 800d6fc <__flash_page_erase_veneer+0x4> + 800d6fc: 2009e08d .word 0x2009e08d + 800d700: 6f636e69 .word 0x6f636e69 + 800d704: 006e .short 0x006e + 800d706: 6944 .short 0x6944 + 800d708: 44203a65 .word 0x44203a65 + 800d70c: 44005546 .word 0x44005546 + 800d710: 203a6569 .word 0x203a6569 + 800d714: 6e776f44 .word 0x6e776f44 + 800d718: 64617267 .word 0x64617267 + 800d71c: 69440065 .word 0x69440065 + 800d720: 42203a65 .word 0x42203a65 + 800d724: 6b6e616c .word 0x6b6e616c + 800d728: 00687369 .word 0x00687369 + 800d72c: 3a656944 .word 0x3a656944 + 800d730: 69724220 .word 0x69724220 + 800d734: 42006b63 .word 0x42006b63 + 800d738: 32746f6f .word 0x32746f6f + 800d73c: 00554644 .word 0x00554644 + 800d740: 43455441 .word 0x43455441 + 800d744: 38303643 .word 0x38303643 + 800d748: 53440a42 .word 0x53440a42 + 800d74c: 33433832 .word 0x33433832 + 800d750: 4c004236 .word 0x4c004236 + 800d754: 0052 .short 0x0052 + 800d756: 6e65 .short 0x6e65 + 800d758: 5f726574 .word 0x5f726574 + 800d75c: 28756664 .word 0x28756664 + 800d760: 0029 .short 0x0029 + 800d762: 0a0d .short 0x0a0d + 800d764: 346b4d0a .word 0x346b4d0a + 800d768: 6f6f4220 .word 0x6f6f4220 + 800d76c: 616f6c74 .word 0x616f6c74 + 800d770: 3a726564 .word 0x3a726564 + 800d774: 463e0020 .word 0x463e0020 + 800d778: 57455249 .word 0x57455249 + 800d77c: 454c4c41 .word 0x454c4c41 + 800d780: 70003c44 .word 0x70003c44 + 800d784: 2d726961 .word 0x2d726961 + 800d788: 63697262 .word 0x63697262 + 800d78c: 0064656b .word 0x0064656b + 800d790: 69726556 .word 0x69726556 + 800d794: 203a7966 .word 0x203a7966 + 800d798: 00000000 .word 0x00000000 + 800d79c: 00000150 .word 0x00000150 + 800d7a0: 00000001 .word 0x00000001 + 800d7a4: 00000000 .word 0x00000000 + 800d7a8: 00000001 .word 0x00000001 + 800d7ac: 00000000 .word 0x00000000 + +0800d7b0 : + 800d7b0: 0700262e ff000707 .&....../ + +0800d7b9 : + 800d7b9: 227f0021 !..".. + +0800d7bf : + 800d7bf: 400020ae c83fa8a1 12da00d3 f1d980d5 . .@..?......... + 800d7cf: ff8130db 148da6a4 .0....... + +0800d7d8 : + 800d7d8: 227f0021 !..".. + +0800d7de : + 800d7de: 007f007f 0034007f 000d8081 000d8081 ......4......... + 800d7ee: 00628081 01030183 0183000b 000b0103 ..b............. + 800d7fe: 01030183 007f007f 0034007f ..........4.. + +0800d80b : + 800d80b: 007f007f 002b007f 8803f881 0000f085 ......+......... + 800d81b: 400380c0 00008086 03d84040 04808100 ...@....@@...... + 800d82b: 00808a40 800000f8 80000040 80834004 @.......@....@.. + 800d83b: 40038000 50f88082 041f8100 000f8310 ...@...P........ + 800d84b: 8100091f 8100031f 8a10040f 021f0008 ................ + 800d85b: 10080403 12040f00 0f001383 08821003 ................ + 800d86b: 7f007f1f 2b007f00 .......+.. + +0800d875 : + 800d875: 0037007f 40c08086 03303060 05188190 ..7....@`00..... + 800d885: 08188510 68e0f018 f8808b00 38e1071c .......h.......8 + 800d895: 0103060c 82000b01 006820ff 0e7fc182 ......... h..... + 800d8a5: 80808700 0e3860c0 85006907 04060301 .....`8..i...... + 800d8b5: 82020406 02030606 01030383 f881005e ............^... + 800d8c5: 08868804 40400000 820003d8 400380c0 ......@@.......@ + 800d8d5: c000808a 40804040 04c00080 00c08300 ....@@.@........ + 800d8e5: 84400400 80c00080 80834003 40048000 ..@......@.....@ + 800d8f5: 08008087 30488808 1f810043 1f810009 ......H0C....... + 800d905: 1f810003 1f8f0006 00070000 100f001f ................ + 800d915: 0f100f08 11030e00 001f0984 8100061f ................ + 800d925: 8412040f 1b000013 0026007f ..........&.. + +0800d932 : + 800d932: 002c007f 00888003 98f09000 1640d0a0 ..,...........@. + 800d942: 80808400 180330e0 18031081 80e03084 .....0.......0.. + 800d952: 89004380 0c18f0c0 191373e6 89010519 .C.......s...... + 800d962: 07030100 000039ef 92001501 f9ede702 .....9.......... + 800d972: 783818c8 78381838 f9c81838 0042e7ed ..8x8.8x8.....B. + 800d982: e03d0f85 000a0180 70c08085 0017023f ..=........p?... + 800d992: 07fcf883 7e870304 640464fc 03047efc .......~.d.d.~.. + 800d9a2: f0fc0783 01840043 06020301 03028306 ....C........... + 800d9b2: 82001b01 06060703 06030781 06060781 ................ + 800d9c2: 2e030782 03f88100 e0108408 40040000 ...............@ + 800d9d2: c0008084 83400380 03800080 c0808440 ......@.....@... + 800d9e2: 40048000 c0008084 81400380 81000380 ...@......@..... + 800d9f2: 81000bf8 830804f0 04c00030 00c08300 ........0....... + 800da02: 84400480 f0400080 00834003 40048000 ..@...@..@.....@ + 800da12: c0008088 40804040 81000380 81001bf8 ....@@.@........ + 800da22: 8410031f 0e000708 09841103 041f001f ................ + 800da32: 001f8300 84880347 0f007f84 13831204 ....G........... + 800da42: 00081f00 000b1b81 10040f81 0f000c83 ................ + 800da52: 08841003 0409001f 000c8412 10030f00 ................ + 800da62: 0f000883 0f881004 00001f00 031f0007 ................ + 800da72: 7f1b8100 00001000 ........ + +0800da7a : + 800da7a: 0035007f 00f0f095 6060c080 10103030 ..5.......``00.. + 800da8a: 10101818 60603030 006b80c0 0c040f04 ....00``..k..... + 800da9a: ff820003 840007ff e0fc0f03 c083006c ............l... + 800daaa: 00058080 0603018c 80800006 0f3c70c0 .............p<. + 800daba: 8e006d01 03030101 06060202 03030202 .m.............. + 800daca: 00570101 0803f881 00e01084 83400480 ..W...........@. + 800dada: 04c00080 00c08400 400380c0 80008083 ...........@.... + 800daea: 80854003 80c000c0 80834003 40040000 .@.......@.....@ + 800dafa: 80008083 80844003 048000f8 00808740 .....@......@... + 800db0a: 48880808 81003c30 8410031f 0f000708 ...H0<.......... + 800db1a: 0f8a1004 08100f00 000f100f 8300041f ................ + 800db2a: 0347001f 7f848488 00061f00 11030e81 ..G............. + 800db3a: 001f0984 8410030f 0f001f08 13841204 ................ + 800db4a: 7f1b0000 00002200 .....".. + +0800db52 : + 800db52: 007f007f 0036007f 90fc9089 0090fc90 ......6......... + 800db62: 4203fc40 f000048b 00c00000 fc4000f0 @..B..........@. + 800db72: 04814203 03840066 03030000 05078100 .B..f........... + 800db82: 04038900 03040302 7f070000 7f007f00 ................ + 800db92: 00003900 .9.. + +0800db96 : + 800db96: 0035007f c0078081 00064081 6f808082 ..5......@.....o + 800dba6: ffff8200 c0070006 c7c3c189 f0f8dcce ................ + 800dbb6: 0068c0e0 06ff7f82 068081c0 70608800 ..h...........`p + 800dbc6: 070e1c38 007f0103 f8810050 80810006 8.......P....... + 800dbd6: 80834004 40038000 00c08084 83400480 .@.....@......@. + 800dbe6: 04c00080 00c08400 4003f040 f8810009 ........@..@.... + 800dbf6: 10840803 048000e0 00808440 400380c0 ........@......@ + 800dc06: 80008083 80814004 1f810034 00821005 .....@..4....... + 800dc16: 8310040f 0347000f 7f848488 10040f00 ......G......... + 800dc26: 0f000f83 08851003 0f00001f 08811003 ................ + 800dc36: 1f810008 08841003 040f0007 000f8310 ................ + 800dc46: 8300041f 040f001f 7f138112 00001b00 ................ + +0800dc56 : + 800dc56: 007f007f 0043007f 0c30c083 01060073 ......C...0.s... + 800dc66: 0c300084 06000403 7f007f01 39007f00 ..0............9 + ... + +0800dc78 : + 800dc78: 0031007f c0e06084 85001080 f030e0e0 ..1..`........0. + 800dc88: 863008f0 6020f0f0 004f80c0 c189c00c ..0... `..O..... + 800dc98: dccec7c3 c0e0f0f8 ff8f0009 0f0f00ff ................ + 800dca8: cc8c0c0c cccc4ccc 00030f8f feff0183 .....L.......... + 800dcb8: 808a0057 3870e0c0 03070e1c 82000a01 W.....p8........ + 800dcc8: 0004ffff 301f0689 30302030 0004061f .......00 00.... + 800dcd8: 57ffff82 01018200 01820012 82031101 ...W............ + 800dce8: 00410101 f8080889 00000808 400380c0 ..A............@ + 800dcf8: 80008083 80834004 40048000 c0008084 .....@.....@.... + 800dd08: 84400380 f0400080 00094003 0804f081 ..@...@..@...... + 800dd18: 00003083 80844004 0380c000 00808340 .0...@......@... + 800dd28: 82400380 0034f880 1f101088 00001010 ..@...4......... + 800dd38: 8300041f 0409001f 000c8312 8312040f ................ + 800dd48: 071f0013 030f8100 08088110 040f8100 ................ + 800dd58: 000c8310 8411030e 1f001f09 0f810006 ................ + 800dd68: 08821003 1b007f1f .......... + +0800dd72 : + 800dd72: 002c007f 00888003 98f09000 1640d0a0 ..,...........@. + 800dd82: 80808400 180330e0 18031081 80e03084 .....0.......0.. + 800dd92: 89004380 0c18f0c0 191373e6 89010519 .C.......s...... + 800dda2: 07030100 000039ef 92001501 f9ede702 .....9.......... + 800ddb2: 783818c8 78381838 f9c81838 0042e7ed ..8x8.8x8.....B. + 800ddc2: e03d0f85 000a0180 70c08085 0017023f ..=........p?... + 800ddd2: 07fcf883 7e870304 640464fc 03047efc .......~.d.d.~.. + 800dde2: f0fc0783 01840043 06020301 03028306 ....C........... + 800ddf2: 82001b01 06060703 06030781 06060781 ................ + 800de02: 2b030782 03f88100 e0108408 40040000 ...+...........@ + 800de12: c0008084 83400380 03800080 c0808440 ......@.....@... + 800de22: 40048000 c0008084 81400380 81000380 ...@......@..... + 800de32: 81000bf8 830804f0 04000030 00808340 ........0...@... + 800de42: 840004c0 f04000c0 00034003 d8404083 ......@..@...@@. + 800de52: 80810003 80844004 0380c000 03808140 .....@......@... + 800de62: 14f88100 031f8100 07088410 11030e00 ................ + 800de72: 001f0984 8300041f 0347001f 7f848488 ..........G..... + 800de82: 12040f00 1f001383 1b810008 0f81000b ................ + 800de92: 0c831004 11030e00 001f0984 8510030f ................ + 800dea2: 00001f08 8110030f 81000408 8100031f ................ + 800deb2: 8310040f 041f000f 031f8100 7f1b8100 ................ + 800dec2: 00000c00 .... + +0800dec6 : + 800dec6: 007f007f 002f007f 0804f881 8000f083 ....../......... + 800ded6: 80844004 0380c000 00808440 0005f800 .@......@....... + 800dee6: 0004c081 8000c083 80824003 880057c0 .........@...W.. + 800def6: 0503011f 0f001009 13841204 03047f00 ................ + 800df06: 00078408 10030f00 0f000083 08841003 ................ + 800df16: 0347001f 7f848288 007f007f 002e007f ..G............. + ... + +0800df27 : + 800df27: 0035007f 04808082 60c08300 83180530 ..5........`0... + 800df37: 04c07030 6b808100 ff018600 070606fe 0p.....k........ + 800df47: e6810604 07860604 fffe0607 03006901 .............i.. + 800df57: bf078401 000580e0 0005ff81 bfe08084 ................ + 800df67: 69010307 07068300 89010303 06060203 ...i............ + 800df77: 02020607 83010303 62060703 04f88100 ...........b.... + 800df87: 00f88900 0830c000 060000f8 70008980 ......0........p + 800df97: 08088888 04f80010 00088688 f8102040 ............@ .. + 800dfa7: 0f810059 0f831004 02030300 00021f83 Y............... + 800dfb7: 00890406 11101008 1f000e11 00041005 ................ + 800dfc7: 007f1f81 ....... + +0800dfce : + 800dfce: 0035007f 04808082 60c08300 83180530 ..5........`0... + 800dfde: 04c07030 6b808100 ff018600 070606fe 0p.....k........ + 800dfee: e6810604 07860604 fffe0607 03006901 .............i.. + 800dffe: bf078401 000580e0 0005ff81 bfe08084 ................ + 800e00e: 69010307 07068300 89010303 06060203 ...i............ + 800e01e: 02020607 83010303 62060703 04f88100 ...........b.... + 800e02e: 00f88300 824804f8 80060088 88700089 ......H.......p. + 800e03e: 10080888 8804f800 30000883 88820803 ...........0.... + 800e04e: 81005770 8310040f 0408000f 000f8210 pW.............. + 800e05e: 00890406 11101008 1f000e11 00871005 ................ + 800e06e: 11121418 007f1010 ........,.. + +0800e079 : + 800e079: 0028007f 2060c085 18061030 60301085 ..(...` 0.....0` + 800e089: 000f80c0 30e0e085 3008f0f0 20f0f086 .......0...0... + 800e099: 4c80c060 fffc8300 82000e01 000eff03 `..L............ + 800e0a9: 00ffff8f 0c0c0f0f 4ccccc8c 0f8fcccc ...........L.... + 800e0b9: 01830003 004bfeff 0c060389 20303018 ......K......00 + 800e0c9: 20036020 18103089 e0713f1e 000b80c0 `. .0...?q..... + 800e0d9: 04ffff82 1f068900 30203030 04061f30 ........00 00... + 800e0e9: ffff8200 0184005e 09060703 01018200 ....^........... + 800e0f9: 01820311 88003c01 08888870 80001008 .....<..p....... + 800e109: 80834004 40040000 c0008084 83400380 .@.....@......@. + 800e119: 04800080 00808440 400380f8 00008086 ....@......@.... + 800e129: 03d84040 80c08200 80834003 40038000 @@.......@.....@ + 800e139: 42c08082 10088800 0e111110 12040f00 ...B............ + 800e149: 0e001383 09841103 061f001f 040f8100 ................ + 800e159: 00088310 8100041f 8100041f 8100031f ................ + 800e169: 8300041f 0347001f 7f848788 38100000 ......G........8 + 800e179: 83000410 04103810 38108300 19007f10 .....8.....8.... + ... + +0800e18b : + 800e18b: 0035007f 0e80c082 6a800600 ffff8200 ..5........j.... + 800e19b: 80900005 603060c0 63c080c0 30181c37 .....`0`...c7..0 + 800e1ab: 691e3f20 ffff8800 ceccc0c0 c005c1c7 ?.i............ + 800e1bb: c009c181 007f8081 f8810056 f8840004 ........V....... + 800e1cb: 0380c000 00808340 85400380 c000c080 ....@.....@..... + 800e1db: 83400380 04000080 00808340 87400380 ..@.....@.....@. + 800e1eb: 0000f880 03d84040 80c08200 80834003 ....@@.......@.. + 800e1fb: 40038000 42c08082 040f8100 000f8410 ...@...B........ + 800e20b: 0803047f 47000783 84848803 061f007f .......G........ + 800e21b: 030e8100 1f098411 10030f00 041f0882 ................ + 800e22b: 031f8100 041f8100 001f8300 82880347 ............G... + 800e23b: 007f7f84 ....".. + +0800e242 : + 800e242: 0038007f 60c08085 10033020 1018188a ..8....` 0...... + 800e252: 60303010 6b80c060 fce08400 00070107 .00``..k........ + 800e262: 07ffff82 0f038400 0068e0fc 380f0187 ..........h....8 + 800e272: 8080c060 018c0005 00060703 70c08080 `..............p + 800e282: 6d010f3c 01018e00 02020303 02020606 <..m............ + 800e292: 01010303 f881005a f8830004 40048000 ....Z..........@ + 800e2a2: c0008084 86400380 40000080 0004d840 ......@....@@... + 800e2b2: 0803f081 c0001083 c0860004 40400000 ..............@@ + 800e2c2: 820003d8 400380c0 80008083 80824003 .......@.....@.. + 800e2d2: 880042c0 18180601 0f000106 13831204 .B.............. + 800e2e2: 00091f00 00031f81 031f0182 00008301 ................ + 800e2f2: 82880347 00047f84 00031f81 00041f81 G............... + 800e302: 47001f83 84828803 22007f7f ...G.......".. + +0800e310 : + 800e310: 0038007f 60c08084 82000420 0004f8f8 ..8....` ....... + 800e320: c0606084 84006b80 0307fce0 ff820007 .``..k.......... + 800e330: 840007ff e0fc0f03 01870068 c060380f ........h....8`. + 800e340: 000a8080 c0808087 010f3c70 018e006d ........p<..m... + 800e350: 02030301 02060602 01030302 88005701 .............W.. + 800e360: 08888870 80001008 80834004 40048000 p........@.....@ + 800e370: 80008083 80824003 810008f8 860004f8 .....@.......... + 800e380: 400000f8 0003d840 0380c082 00808340 ...@@.......@... + 800e390: 83400480 03800080 f8808240 0888003b ..@.....@...;... + 800e3a0: 11111010 040f000e 00138312 8312040f ................ + 800e3b0: 030f0013 1f088210 07860008 18070718 ................ + 800e3c0: 81000407 8200031f 0803047f 0f000783 ................ + 800e3d0: 13831204 10030f00 7f1f0882 00001e00 ................ + 800e3e0: 65737361 64007472 676e776f 65646172 assert.downgrade + 800e3f0: 67697300 69616620 6f6e006c 72696620 .sig fail.no fir + 800e400: 7261776d 61460065 726f7463 6f622079 mware.Factory bo + 800e410: 5700746f 3a4e5241 64655220 67696c20 ot.WARN: Red lig + 800e420: 57007468 3a4e5241 736e5520 656e6769 ht.WARN: Unsigne + 800e430: 69662064 61776d72 47006572 20646f6f d firmware.Good + 800e440: 6d726966 65726177 726f6300 74707572 firmware.corrupt + 800e450: 72696620 7261776d firmware. + +0800e45a : + 800e45a: 2641cbb4 f36ce1f7 71b4f28f 0123fb1d ..A&..l....q..#. + 800e46a: 66d6760d 6ca38aa7 f6f9539b 0518587b .v.f...l.S..{X.. + 800e47a: e93b0b58 b89fc431 113c0444 470f0896 X.;.1...D.<....G + 800e48a: 37ed2581 4a9e237a 3818b7af da0438ba .%.7z#.J...8.8.. + 800e49a: 1dc8a2d6 df5e811c 6d290ca6 8d8f57b8 ......^...)m.W.. + 800e4aa: 9269295e c178d1ce 31d7207b b596a17b ^)i...x.{ .1{... + 800e4ba: 0c1bef3d c31a79aa c8c45845 ffeb2d8a =....y..EX...-.. + 800e4ca: 01829bfe bc5e5f87 4fe5a596 9ffe68c7 ....._^....O.h.. + 800e4da: 0166ef42 95cfc456 38f0b5f4 c5261164 B.f.V......8d.&. + 800e4ea: 66c13999 14120632 689c254c bad38c35 .9.f2...L%.h5... + 800e4fa: 8cde7824 6cdfab52 7809bfb8 3a63bb03 $x..R..l...x..c: + 800e50a: 0ed90111 8f737aa4 7f3b18bf c87b0af0 .....zs...;...{. + 800e51a: 56546067 c5ec0c82 0882bc1d ef39c116 g`TV..........9. + 800e52a: 32babff5 e35fce7c d7621e74 4cc5fce9 ...2|._.t.b....L + 800e53a: 8d11e88a 13c2adc3 2a4f2992 a4f8d2ea .........)O*.... + 800e54a: fe7cd5c4 3b450512 07598954 88d7d6da ..|...E;T.Y..... + 800e55a: 37cfb143 1f897cd2 f3acfe5b 95fc33ba C..7.|..[....3.. + 800e56a: dde7d981 14ef9525 bb97efdd a7d8f333 ....%.......3... + 800e57a: 977a2b34 73aab3ba 32419de7 17a1fcd8 4+z....s..A2.... + 800e58a: fe0bb566 89214063 8e7b92c9 590bdf72 f...c@!...{.r..Y + 800e59a: 76dc5cd0 30dd3016 56f180c2 61a85c26 .\.v.0.0...V&\.a + 800e5aa: 69694fd7 3d57b8e5 582ae235 c69acedd .Oii..W=5.*X.... + 800e5ba: 2b1ca945 8efc010c 13513fbf 137c7e80 E..+.....?Q..~|. + 800e5ca: 5e4b4fd5 d59b4c9b e0d81d9e 2246c0ad .OK^.L........F" + 800e5da: 20314553 666e6f63 66206769 006c6961 SE1 config fail. + 800e5ea: 6c706572 72206775 69757165 00646572 replug required. + 800e5fa: 72726f63 20747075 72696170 63657320 corrupt pair sec + 800e60a: 75636d00 6c756620 7562006c 66206e72 .mcu full.burn f + 800e61a: 3a6c6961 76210020 64696c61 6162003f ail: .!valid?.ba + 800e62a: 61762064 66003f6c 20747361 63697262 d val?.fast bric + 800e63a: 2e2e2e6b 64200020 00656e6f 79706f43 k... . done.Copy + 800e64a: 68676972 30322074 202d3831 43207962 right 2018- by C + 800e65a: 6b6e696f 20657469 2e636e49 206f6e00 oinkite Inc..no + 800e66a: 00726573 66206b77 0016006c 01410800 ser.wk fl.....A. + ... + 800e686: 000000ee 006100e1 218f0000 438f808f ......a....!...C + 800e696: 430080af 20834300 43c343c3 43c343c3 ...C.C. .C.C.C.C + 800e6a6: 43c343c3 0000438f ffffffff 00000000 .C.C.C.......... + 800e6b6: ffffffff 00000000 00000000 000000f0 ................ + ... + 800e6ce: 00001502 003c0000 01bc005c 01bc01fc ......<.\....... + 800e6de: 01dc01dc 03dc03d1 03dc03dc 03dc03dc ................ + 800e6ee: 01dc03dc 0001003c 00120000 00000000 ....<........... + 800e6fe: 00010000 00080000 02000000 00020000 ................ + 800e70e: 00000000 00010000 00070000 .............. + +0800e71c : + 800e71c: 0d0c0b09 .... + +0800e720 : + 800e720: 2e322e33 69742031 323d656d 30353230 3.2.1 time=20250 + 800e730: 2e353134 39303930 67203533 6d3d7469 415.090935 git=m + 800e740: 65747361 64614072 63326663 0d006538 aster@adcf2c8e.. + 800e750: .. + +0800e752 : + 800e752: 33323130 37363534 62613938 66656463 0123456789abcdef + 800e762: 41525350 6166204d 50006c69 203a5253 PSRAM fail.PSR: + 800e772: 6164616e 52535000 6321203a 6b636568 nada.PSR: !check + 800e782: 52535000 6576203a 6f697372 fc00006e .PSR: version... + 800e792: 00020000 00000000 00030000 000a0000 ................ + 800e7a2: 00080000 00100000 6f6c0000 7220676e ..........long r + 800e7b2: 20646165 6c696166 55464400 72617020 ead fail.DFU par + 800e7c2: 66206573 006c6961 646f6f67 72696620 se fail.good fir + 800e7d2: 7261776d 72770065 20676e6f 6c726f77 mware.wrong worl + 800e7e2: 64730064 64726163 6165735f 3a686372 d.sdcard_search: + 800e7f2: 64730020 64726163 6f72705f 203a6562 .sdcard_probe: + 800e802: 696e6900 61662074 73006c69 64656570 .init fail.speed + 800e812: 64697700 73620065 3f657a69 006b6f00 .wide.bsize?.ok. + 800e822: 6c696166 61657220 66440064 00655375 fail read.DfuSe. + 800e832: 6e756f66 20402064 63655200 7265766f found @ .Recover + 800e842: 6f6d2079 002e6564 1f000000 00020000 y mode.......... + 800e852: 00010000 00030000 000c0000 00040000 ................ + 800e862: 00020000 00010000 00030000 000c0000 ................ + ... + +0800e874 : + 800e874: 01002008 fffffc2f fffffffe ffffffff . ../........... + 800e884: ffffffff ffffffff ffffffff ffffffff ................ + 800e894: ffffffff d0364141 bfd25e8c af48a03b ....AA6..^..;.H. + 800e8a4: baaedce6 fffffffe ffffffff ffffffff ................ + 800e8b4: ffffffff 16f81798 59f2815b 2dce28d9 ........[..Y.(.- + 800e8c4: 029bfcdb ce870b07 55a06295 f9dcbbac .........b.U.... + 800e8d4: 79be667e fb10d4b8 9c47d08f a6855419 ~f.y......G..T.. + 800e8e4: fd17b448 0e1108a8 5da4fbfc 26a3c465 H..........]e..& + 800e8f4: 483ada77 00000007 00000000 00000000 w.:H............ + ... + 800e918: 0800663d 08005ec1 08006097 08005cad =f...^...`...\.. + +0800e928 : + 800e928: 01002008 ffffffff ffffffff ffffffff . .............. + ... + 800e944: 00000001 ffffffff fc632551 f3b9cac2 ........Q%c..... + 800e954: a7179e84 bce6faad ffffffff ffffffff ................ + 800e964: 00000000 ffffffff d898c296 f4a13945 ............E9.. + 800e974: 2deb33a0 77037d81 63a440f2 f8bce6e5 .3.-.}.w.@.c.... + 800e984: e12c4247 6b17d1f2 37bf51f5 cbb64068 GB,....k.Q.7h@.. + 800e994: 6b315ece 2bce3357 7c0f9e16 8ee7eb4a .^1kW3.+...|J... + 800e9a4: fe1a7f9b 4fe342e2 27d2604b 3bce3c3e .....B.OK`.'><.; + 800e9b4: cc53b0f6 651d06b0 769886bc b3ebbd55 ..S....e...vU... + 800e9c4: aa3a93e7 5ac635d8 08006785 08005ec1 ..:..5.Z.g...^.. + 800e9d4: 0800672b 08005d29 +g..)].. + +0800e9dc : + ... + 800e9e4: 04030201 09080706 ........ + +0800e9ec : + 800e9ec: 00000000 04030201 ........ + +0800e9f4 : + 800e9f4: 000186a0 00030d40 00061a80 000c3500 ....@........5.. + 800ea04: 000f4240 001e8480 003d0900 007a1200 @B........=...z. + 800ea14: 00f42400 016e3600 01e84800 02dc6c00 .$...6n..H...l.. + 800ea24: 20727463 3f746573 00702100 00006000 ctr set?.!p..`.. + 800ea34: 00000012 00000000 00000003 00000004 ................ + +0800ea44 : + 800ea44: 00008000 .... + +Disassembly of section .relocate: + +2009e000 : +{ +2009e000: b530 push {r4, r5, lr} + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e002: 4920 ldr r1, [pc, #128] ; (2009e084 ) +2009e004: 690c ldr r4, [r1, #16] +2009e006: 03e5 lsls r5, r4, #15 +2009e008: d4fc bmi.n 2009e004 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e00a: 690d ldr r5, [r1, #16] + if(error) { +2009e00c: 4c1e ldr r4, [pc, #120] ; (2009e088 ) +2009e00e: 4225 tst r5, r4 +2009e010: d104 bne.n 2009e01c + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e012: 690c ldr r4, [r1, #16] +2009e014: 07e4 lsls r4, r4, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e016: bf44 itt mi +2009e018: 2401 movmi r4, #1 +2009e01a: 610c strmi r4, [r1, #16] + FLASH->SR = FLASH->SR & FLASH_FLAG_SR_ERRORS; +2009e01c: 4919 ldr r1, [pc, #100] ; (2009e084 ) +2009e01e: 4d1a ldr r5, [pc, #104] ; (2009e088 ) +2009e020: 690c ldr r4, [r1, #16] +2009e022: 402c ands r4, r5 +2009e024: 610c str r4, [r1, #16] + __HAL_FLASH_DATA_CACHE_DISABLE(); +2009e026: 680c ldr r4, [r1, #0] +2009e028: f424 6480 bic.w r4, r4, #1024 ; 0x400 +2009e02c: 600c str r4, [r1, #0] + CLEAR_BIT(FLASH->CR, (FLASH_CR_PG | FLASH_CR_MER1 | FLASH_CR_PER | FLASH_CR_PNB)); // added +2009e02e: 694c ldr r4, [r1, #20] +2009e030: f424 64ff bic.w r4, r4, #2040 ; 0x7f8 +2009e034: f024 0407 bic.w r4, r4, #7 +2009e038: 614c str r4, [r1, #20] + SET_BIT(FLASH->CR, FLASH_CR_PG); +2009e03a: 694c ldr r4, [r1, #20] +2009e03c: f044 0401 orr.w r4, r4, #1 +2009e040: 614c str r4, [r1, #20] + *(__IO uint32_t *)(address) = (uint32_t)val; +2009e042: 6002 str r2, [r0, #0] + __ASM volatile ("isb 0xF":::"memory"); +2009e044: f3bf 8f6f isb sy + *(__IO uint32_t *)(address+4) = (uint32_t)(val >> 32); +2009e048: 6043 str r3, [r0, #4] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e04a: 690b ldr r3, [r1, #16] +2009e04c: 03da lsls r2, r3, #15 +2009e04e: d4fc bmi.n 2009e04a + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e050: 6908 ldr r0, [r1, #16] + if(error) { +2009e052: 4028 ands r0, r5 +2009e054: d104 bne.n 2009e060 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e056: 690b ldr r3, [r1, #16] +2009e058: 07db lsls r3, r3, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e05a: bf44 itt mi +2009e05c: 2301 movmi r3, #1 +2009e05e: 610b strmi r3, [r1, #16] + CLEAR_BIT(FLASH->CR, FLASH_CR_PG); +2009e060: 4b08 ldr r3, [pc, #32] ; (2009e084 ) +2009e062: 695a ldr r2, [r3, #20] +2009e064: f022 0201 bic.w r2, r2, #1 +2009e068: 615a str r2, [r3, #20] + __HAL_FLASH_DATA_CACHE_RESET(); +2009e06a: 681a ldr r2, [r3, #0] +2009e06c: f442 5280 orr.w r2, r2, #4096 ; 0x1000 +2009e070: 601a str r2, [r3, #0] +2009e072: 681a ldr r2, [r3, #0] +2009e074: f422 5280 bic.w r2, r2, #4096 ; 0x1000 +2009e078: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); +2009e07a: 681a ldr r2, [r3, #0] +2009e07c: f442 6280 orr.w r2, r2, #1024 ; 0x400 +2009e080: 601a str r2, [r3, #0] +} +2009e082: bd30 pop {r4, r5, pc} +2009e084: 40022000 .word 0x40022000 +2009e088: 0002c3fa .word 0x0002c3fa + +2009e08c : + if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_ERASE_SIZE)) { +2009e08c: 4b2d ldr r3, [pc, #180] ; (2009e144 ) +2009e08e: 4003 ands r3, r0 +{ +2009e090: b510 push {r4, lr} + if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_ERASE_SIZE)) { +2009e092: 2b00 cmp r3, #0 +2009e094: d054 beq.n 2009e140 + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e096: 4b2c ldr r3, [pc, #176] ; (2009e148 ) + page_num &= 0xff; +2009e098: f3c0 3207 ubfx r2, r0, #12, #8 + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e09c: 6919 ldr r1, [r3, #16] +2009e09e: 03c9 lsls r1, r1, #15 +2009e0a0: d4fc bmi.n 2009e09c + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e0a2: 691c ldr r4, [r3, #16] + if(error) { +2009e0a4: 4929 ldr r1, [pc, #164] ; (2009e14c ) +2009e0a6: 420c tst r4, r1 +2009e0a8: d104 bne.n 2009e0b4 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e0aa: 6919 ldr r1, [r3, #16] +2009e0ac: 07cc lsls r4, r1, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e0ae: bf44 itt mi +2009e0b0: 2101 movmi r1, #1 +2009e0b2: 6119 strmi r1, [r3, #16] + FLASH->SR = FLASH->SR & 0xffff; +2009e0b4: 4b24 ldr r3, [pc, #144] ; (2009e148 ) +2009e0b6: 6919 ldr r1, [r3, #16] +2009e0b8: b289 uxth r1, r1 +2009e0ba: 6119 str r1, [r3, #16] + __HAL_FLASH_DATA_CACHE_DISABLE(); +2009e0bc: 6819 ldr r1, [r3, #0] +2009e0be: f421 6180 bic.w r1, r1, #1024 ; 0x400 +2009e0c2: 6019 str r1, [r3, #0] + SET_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0c4: 6959 ldr r1, [r3, #20] + if(bank2) { +2009e0c6: f010 6ffe tst.w r0, #133169152 ; 0x7f00000 + SET_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0ca: bf14 ite ne +2009e0cc: f441 6100 orrne.w r1, r1, #2048 ; 0x800 + CLEAR_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0d0: f421 6100 biceq.w r1, r1, #2048 ; 0x800 +2009e0d4: 6159 str r1, [r3, #20] + MODIFY_REG(FLASH->CR, FLASH_CR_PNB, (page_num << POSITION_VAL(FLASH_CR_PNB))); +2009e0d6: 6959 ldr r1, [r3, #20] + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +2009e0d8: f44f 63ff mov.w r3, #2040 ; 0x7f8 +2009e0dc: f421 61ff bic.w r1, r1, #2040 ; 0x7f8 +2009e0e0: fa93 f3a3 rbit r3, r3 + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +2009e0e4: fab3 f383 clz r3, r3 +2009e0e8: 409a lsls r2, r3 +2009e0ea: 4b17 ldr r3, [pc, #92] ; (2009e148 ) +2009e0ec: 430a orrs r2, r1 +2009e0ee: 615a str r2, [r3, #20] + SET_BIT(FLASH->CR, FLASH_CR_PER); +2009e0f0: 695a ldr r2, [r3, #20] +2009e0f2: f042 0202 orr.w r2, r2, #2 +2009e0f6: 615a str r2, [r3, #20] + SET_BIT(FLASH->CR, FLASH_CR_STRT); +2009e0f8: 695a ldr r2, [r3, #20] +2009e0fa: f442 3280 orr.w r2, r2, #65536 ; 0x10000 +2009e0fe: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e100: 691a ldr r2, [r3, #16] +2009e102: 03d1 lsls r1, r2, #15 +2009e104: d4fc bmi.n 2009e100 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e106: 6918 ldr r0, [r3, #16] +2009e108: 4a10 ldr r2, [pc, #64] ; (2009e14c ) + if(error) { +2009e10a: 4010 ands r0, r2 +2009e10c: d104 bne.n 2009e118 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e10e: 691a ldr r2, [r3, #16] +2009e110: 07d2 lsls r2, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e112: bf44 itt mi +2009e114: 2201 movmi r2, #1 +2009e116: 611a strmi r2, [r3, #16] + CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB)); +2009e118: 4b0b ldr r3, [pc, #44] ; (2009e148 ) +2009e11a: 695a ldr r2, [r3, #20] +2009e11c: f422 62ff bic.w r2, r2, #2040 ; 0x7f8 +2009e120: f022 0202 bic.w r2, r2, #2 +2009e124: 615a str r2, [r3, #20] + __HAL_FLASH_DATA_CACHE_RESET(); +2009e126: 681a ldr r2, [r3, #0] +2009e128: f442 5280 orr.w r2, r2, #4096 ; 0x1000 +2009e12c: 601a str r2, [r3, #0] +2009e12e: 681a ldr r2, [r3, #0] +2009e130: f422 5280 bic.w r2, r2, #4096 ; 0x1000 +2009e134: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); +2009e136: 681a ldr r2, [r3, #0] +2009e138: f442 6280 orr.w r2, r2, #1024 ; 0x400 +2009e13c: 601a str r2, [r3, #0] +} +2009e13e: bd10 pop {r4, pc} + return 1; +2009e140: 2001 movs r0, #1 +2009e142: e7fc b.n 2009e13e +2009e144: 07fe0000 .word 0x07fe0000 +2009e148: 40022000 .word 0x40022000 +2009e14c: 0002c3fa .word 0x0002c3fa diff --git a/stm32/mk4-bootloader/releases/README.md b/stm32/mk4-bootloader/releases/README.md index a32c35dce..65ea319ec 100644 --- a/stm32/mk4-bootloader/releases/README.md +++ b/stm32/mk4-bootloader/releases/README.md @@ -13,3 +13,4 @@ Github is nearly free, so why not capture all the actual bits! - V3.1.4 - 12-word duress wallets - V3.1.5 - bugfix so slot 10 of trick pins is usable - V3.2.0 - share code with Q bootrom, no changes for Mk4 operation. +- V3.2.1 - enable omitted if wrong PIN options & fix "Wipe -> Wallet" trick pin option diff --git a/stm32/mk4-bootloader/se2.c b/stm32/mk4-bootloader/se2.c index 778206a60..d8ffdd53f 100644 --- a/stm32/mk4-bootloader/se2.c +++ b/stm32/mk4-bootloader/se2.c @@ -729,12 +729,12 @@ se2_test_trick_pin(const char *pin, int pin_len, trick_slot_t *found_slot, bool uint16_t todo = found_slot->tc_flags; // hmm: don't need this data if safety is off.. but we have it anyway - if(found_slot->tc_flags & TC_WORD_WALLET) { + if(todo & TC_WORD_WALLET) { // it's a 12/24-word BIP-39 seed phrase, un-encrypted. if(found+1 < NUM_TRICKS) { memcpy(found_slot->xdata, &slots[found+1][0], 32); } - } else if(found_slot->tc_flags & TC_XPRV_WALLET) { + } else if(todo & TC_XPRV_WALLET) { // it's an xprv-based wallet if(found+2 < NUM_TRICKS) { memcpy(&found_slot->xdata[0], &slots[found+1][0], 32); @@ -875,14 +875,24 @@ se2_handle_bad_pin(int num_fails) if(slot.tc_flags & TC_WIPE) { // Wipe keys and stop. They can power cycle and keep trying // so only do this if a valid key currently exists. - bool valid; - const mcu_key_t *cur = mcu_key_get(&valid); - - if(valid) { - mcu_key_clear(cur); - oled_show(screen_wiped); + if(slot.tc_flags & TC_BRICK) { + // special case TC_WIPE|TC_BRICK + bool valid; + const mcu_key_t *cur = mcu_key_get(&valid); + if(valid) { + mcu_key_clear(cur); + oled_show(screen_wiped); + LOCKUP_FOREVER(); + } + // else fall-thru if no keys to wipe and WIPE|BRICK mode, will now brick + // used in "Last Chance" mode - LOCKUP_FOREVER(); + } else { + mcu_key_clear(NULL); // does valid key check + if(slot.tc_flags == TC_WIPE) { + oled_show(screen_wiped); + LOCKUP_FOREVER(); + } } } @@ -893,6 +903,13 @@ se2_handle_bad_pin(int num_fails) // brick code will happen. fast_brick(); } + + if(slot.tc_flags & TC_REBOOT) { + NVIC_SystemReset(); + } + //if(slot.tc_flags & TC_FAKE_OUT) {//nothing to do here - Silent Wipe} + // only used together with TC_WIPE. At this point we are already wiped + // EPIN_AUTH_FAIL handled by caller } } diff --git a/stm32/mk4-bootloader/version.h b/stm32/mk4-bootloader/version.h index 82a086f23..0a49027f5 100644 --- a/stm32/mk4-bootloader/version.h +++ b/stm32/mk4-bootloader/version.h @@ -6,7 +6,7 @@ // Public version number for humans. Lots more version data added by Makefile. // - update ../MK4-Makefile BOOTLOADER_VERSION once this is qualified version -#define RELEASE_VERSION "3.2.0" +#define RELEASE_VERSION "3.2.1" extern const char version_string[]; diff --git a/stm32/q1-bootloader/Makefile b/stm32/q1-bootloader/Makefile index 8be24566e..7b5e408fa 100644 --- a/stm32/q1-bootloader/Makefile +++ b/stm32/q1-bootloader/Makefile @@ -8,8 +8,11 @@ # clobber - delete all build products # -# for any file it cant find, look in ../mk4-bootloader -VPATH = ../mk4-bootloader +# for any source file needed, look also in ../mk4-bootloader +vpath %.c ../mk4-bootloader +vpath %.S ../mk4-bootloader +vpath %.h ../mk4-bootloader +vpath %.py ../mk4-bootloader # Toolchain TOOLCHAIN = arm-none-eabi- diff --git a/stm32/q1-bootloader/releases/1.1.0.txt b/stm32/q1-bootloader/releases/1.1.0.txt new file mode 100644 index 000000000..ffb133b96 --- /dev/null +++ b/stm32/q1-bootloader/releases/1.1.0.txt @@ -0,0 +1,4 @@ +f85eb3fcc2bbaa3056ef1efb5f5de94c1527eea17c21e21f0fb4fcd6a988c8b6 bootloader.dfu +62aaa45a663e9765125f1bd9d36bd498c402a94b0fd7dfc3c9cdb771c8f2384b bootloader.bin +e16d7e6a6f7379327799e3add8948a8c903f0f8b8429084b7731fd73b60d9274 bootloader.lss +1.1.0 time=20250415.093631 git=master@28926acd diff --git a/stm32/q1-bootloader/releases/1.1.0/bootloader.bin b/stm32/q1-bootloader/releases/1.1.0/bootloader.bin new file mode 100644 index 000000000..aeb9b20cd Binary files /dev/null and b/stm32/q1-bootloader/releases/1.1.0/bootloader.bin differ diff --git a/stm32/q1-bootloader/releases/1.1.0/bootloader.dfu b/stm32/q1-bootloader/releases/1.1.0/bootloader.dfu new file mode 100644 index 000000000..b458c40f4 Binary files /dev/null and b/stm32/q1-bootloader/releases/1.1.0/bootloader.dfu differ diff --git a/stm32/q1-bootloader/releases/1.1.0/bootloader.lss b/stm32/q1-bootloader/releases/1.1.0/bootloader.lss new file mode 100644 index 000000000..1a175a907 --- /dev/null +++ b/stm32/q1-bootloader/releases/1.1.0/bootloader.lss @@ -0,0 +1,35454 @@ + +bootloader.elf: file format elf32-littlearm + +Sections: +Idx Name Size VMA LMA File off Algn + 0 .text 00010ac4 08000000 08000000 00010000 2**8 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 1 .relocate 00000150 2009e000 08010ac4 0002e000 2**2 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 2 .bss 000002ec 2009e150 08010c14 0002e150 2**2 + ALLOC + 3 .stack 00000804 2009e43c 08010f00 0002e150 2**0 + ALLOC + 4 .debug_info 0002c7b2 00000000 00000000 0002e150 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 5 .debug_abbrev 000060d7 00000000 00000000 0005a902 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 6 .debug_loc 000148e6 00000000 00000000 000609d9 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 7 .debug_aranges 000010e8 00000000 00000000 000752bf 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 8 .debug_ranges 00002228 00000000 00000000 000763a7 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 9 .debug_macro 000325ab 00000000 00000000 000785cf 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 10 .debug_line 0001e38f 00000000 00000000 000aab7a 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 11 .debug_str 0011cf0b 00000000 00000000 000c8f09 2**0 + CONTENTS, READONLY, DEBUGGING, OCTETS + 12 .comment 00000049 00000000 00000000 001e5e14 2**0 + CONTENTS, READONLY + 13 .ARM.attributes 00000032 00000000 00000000 001e5e5d 2**0 + CONTENTS, READONLY + 14 .debug_frame 0000371c 00000000 00000000 001e5e90 2**2 + CONTENTS, READONLY, DEBUGGING, OCTETS + +Disassembly of section .text: + +08000000 <_sfixed>: + 8000000: 200a0000 .word 0x200a0000 + 8000004: 080000b5 .word 0x080000b5 + 8000008: 0800001d .word 0x0800001d + 800000c: 0800001f .word 0x0800001f + 8000010: 08000021 .word 0x08000021 + 8000014: 08000023 .word 0x08000023 + 8000018: 08000025 .word 0x08000025 + +0800001c : + 800001c: be01 bkpt 0x0001 + +0800001e : + 800001e: be02 bkpt 0x0002 + +08000020 : + 8000020: be03 bkpt 0x0003 + +08000022 : + 8000022: be04 bkpt 0x0004 + +08000024 : + 8000024: be05 bkpt 0x0005 + 8000026: e7fe b.n 8000026 + +08000028 : + ... + 8000040: 08000305 .word 0x08000305 + +08000044 : + 8000044: 00000200 .word 0x00000200 + ... + 8000060: 20296328 .word 0x20296328 + 8000064: 79706f43 .word 0x79706f43 + 8000068: 68676972 .word 0x68676972 + 800006c: 30322074 .word 0x30322074 + 8000070: 322d3831 .word 0x322d3831 + 8000074: 20323230 .word 0x20323230 + 8000078: 43207962 .word 0x43207962 + 800007c: 6b6e696f .word 0x6b6e696f + 8000080: 20657469 .word 0x20657469 + 8000084: 2e636e49 .word 0x2e636e49 + 8000088: 0a200a20 .word 0x0a200a20 + 800008c: 73696854 .word 0x73696854 + 8000090: 61707320 .word 0x61707320 + 8000094: 66206563 .word 0x66206563 + 8000098: 7220726f .word 0x7220726f + 800009c: 21746e65 .word 0x21746e65 + 80000a0: 73754a20 .word 0x73754a20 + 80000a4: 42312074 .word 0x42312074 + 80000a8: 792f4354 .word 0x792f4354 + 80000ac: 2e726165 .word 0x2e726165 + 80000b0: 0a200a20 .word 0x0a200a20 + +080000b4 : + 80000b4: f000 f816 bl 80000e4 + 80000b8: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 80000bc: f04f 0100 mov.w r1, #0 + 80000c0: f04f 0200 mov.w r2, #0 + 80000c4: f04f 0300 mov.w r3, #0 + 80000c8: f000 f91c bl 8000304 + 80000cc: f248 0120 movw r1, #32800 ; 0x8020 + 80000d0: ea4f 3101 mov.w r1, r1, lsl #12 + 80000d4: 6808 ldr r0, [r1, #0] + 80000d6: 4685 mov sp, r0 + 80000d8: f04f 0001 mov.w r0, #1 + 80000dc: f8d1 e004 ldr.w lr, [r1, #4] + 80000e0: 4770 bx lr + ... + +080000e4 : + void +firewall_setup(void) +{ + // This is critical: without the clock enabled to "SYSCFG" we + // can't tell the FW is enabled or not! Enabling it would also not work + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000e4: 4b1b ldr r3, [pc, #108] ; (8000154 ) +{ + 80000e6: b500 push {lr} + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000e8: 6e1a ldr r2, [r3, #96] ; 0x60 + 80000ea: f042 0201 orr.w r2, r2, #1 + 80000ee: 661a str r2, [r3, #96] ; 0x60 + 80000f0: 6e1b ldr r3, [r3, #96] ; 0x60 +{ + 80000f2: b08b sub sp, #44 ; 0x2c + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80000f4: f003 0301 and.w r3, r3, #1 + 80000f8: 9300 str r3, [sp, #0] + 80000fa: 9b00 ldr r3, [sp, #0] + + if(__HAL_FIREWALL_IS_ENABLED()) { + 80000fc: 4b16 ldr r3, [pc, #88] ; (8000158 ) + 80000fe: 685b ldr r3, [r3, #4] + 8000100: 07db lsls r3, r3, #31 + 8000102: d524 bpl.n 800014e + // REMINDERS: + // - cannot debug anything in boot loader w/ firewall enabled (no readback, no bkpt) + // - when RDP=2, this protection still important or else python can read pairing secret + // - in factory mode (RDP!=2), it's nice to have this disabled so we can debug still + // - could look at RDP level here, but it would be harder to completely reset the bag number! + if(check_all_ones_raw(rom_secrets->bag_number, sizeof(rom_secrets->bag_number))) { + 8000104: 4815 ldr r0, [pc, #84] ; (800015c ) + 8000106: 2120 movs r1, #32 + 8000108: f002 fb74 bl 80027f4 + 800010c: b9f8 cbnz r0, 800014e + // for debug builds, never enable firewall + return; +#endif + + extern int firewall_starts; // see startup.S ... aligned@256 (0x08000300) + uint32_t start = (uint32_t)&firewall_starts; + 800010e: 4b14 ldr r3, [pc, #80] ; (8000160 ) + uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE); + 8000110: 4a14 ldr r2, [pc, #80] ; (8000164 ) + // but sensitive stuff is still there (which would allow bypass) + // - so it's important to enable option bytes to set write-protect flash of entire bootloader + // - to disable debug and complete protection, must enable write-protect "level 2" (RDP=2) + // + + FIREWALL_InitTypeDef init = { + 8000112: 9302 str r3, [sp, #8] + uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE); + 8000114: 1ad3 subs r3, r2, r3 + FIREWALL_InitTypeDef init = { + 8000116: e9cd 3203 strd r3, r2, [sp, #12] + 800011a: f44f 4380 mov.w r3, #16384 ; 0x4000 + 800011e: e9cd 3005 strd r3, r0, [sp, #20] + 8000122: e9cd 0007 strd r0, r0, [sp, #28] + 8000126: 9009 str r0, [sp, #36] ; 0x24 + .VDataSegmentLength = 0, + .VolatileDataExecution = 0, + .VolatileDataShared = 0, + }; + + int rv = HAL_FIREWALL_Config((FIREWALL_InitTypeDef *)&init); + 8000128: a802 add r0, sp, #8 + 800012a: f000 f821 bl 8000170 + if(rv) { + 800012e: b110 cbz r0, 8000136 + INCONSISTENT("fw"); + 8000130: 480d ldr r0, [pc, #52] ; (8000168 ) + 8000132: f000 fc81 bl 8000a38 + } + + __HAL_FIREWALL_PREARM_DISABLE(); + 8000136: 4b0d ldr r3, [pc, #52] ; (800016c ) + 8000138: 6a1a ldr r2, [r3, #32] + 800013a: f022 0201 bic.w r2, r2, #1 + 800013e: 621a str r2, [r3, #32] + 8000140: 6a1b ldr r3, [r3, #32] + 8000142: f003 0301 and.w r3, r3, #1 + 8000146: 9301 str r3, [sp, #4] + 8000148: 9b01 ldr r3, [sp, #4] + HAL_FIREWALL_EnableFirewall(); + 800014a: f000 f88b bl 8000264 +} + 800014e: b00b add sp, #44 ; 0x2c + 8000150: f85d fb04 ldr.w pc, [sp], #4 + 8000154: 40021000 .word 0x40021000 + 8000158: 40010000 .word 0x40010000 + 800015c: 0801c050 .word 0x0801c050 + 8000160: 08000300 .word 0x08000300 + 8000164: 0801c000 .word 0x0801c000 + 8000168: 0800d9a0 .word 0x0800d9a0 + 800016c: 40011c00 .word 0x40011c00 + +08000170 : + * @param fw_init: Firewall initialization structure + * @note The API returns HAL_ERROR if the Firewall is already enabled. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_FIREWALL_Config(FIREWALL_InitTypeDef * fw_init) +{ + 8000170: b573 push {r0, r1, r4, r5, r6, lr} + /* Check the Firewall initialization structure allocation */ + if(fw_init == NULL) + 8000172: b910 cbnz r0, 800017a + { + return HAL_ERROR; + 8000174: 2001 movs r0, #1 + /* Set Firewall Configuration Register VDE and VDS bits + (volatile data execution and shared configuration) */ + MODIFY_REG(FIREWALL->CR, FW_CR_VDS|FW_CR_VDE, fw_init->VolatileDataExecution|fw_init->VolatileDataShared); + + return HAL_OK; +} + 8000176: b002 add sp, #8 + 8000178: bd70 pop {r4, r5, r6, pc} + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 800017a: 4b19 ldr r3, [pc, #100] ; (80001e0 ) + 800017c: 6e1a ldr r2, [r3, #96] ; 0x60 + 800017e: f042 0280 orr.w r2, r2, #128 ; 0x80 + 8000182: 661a str r2, [r3, #96] ; 0x60 + 8000184: 6e1b ldr r3, [r3, #96] ; 0x60 + 8000186: f003 0380 and.w r3, r3, #128 ; 0x80 + 800018a: 9301 str r3, [sp, #4] + 800018c: 9b01 ldr r3, [sp, #4] + if (__HAL_FIREWALL_IS_ENABLED() != RESET) + 800018e: 4b15 ldr r3, [pc, #84] ; (80001e4 ) + 8000190: 685b ldr r3, [r3, #4] + 8000192: 07db lsls r3, r3, #31 + 8000194: d5ee bpl.n 8000174 + if (fw_init->CodeSegmentLength != 0U) + 8000196: 6841 ldr r1, [r0, #4] + if (fw_init->NonVDataSegmentLength < 0x100U) + 8000198: 68c2 ldr r2, [r0, #12] + if (fw_init->CodeSegmentLength != 0U) + 800019a: b109 cbz r1, 80001a0 + if (fw_init->NonVDataSegmentLength < 0x100U) + 800019c: 2aff cmp r2, #255 ; 0xff + 800019e: d9e9 bls.n 8000174 + WRITE_REG(FIREWALL->CSSA, (FW_CSSA_ADD & fw_init->CodeSegmentStartAddress)); + 80001a0: 6803 ldr r3, [r0, #0] + 80001a2: 4e11 ldr r6, [pc, #68] ; (80001e8 ) + if (fw_init->VDataSegmentLength != 0U) + 80001a4: 6944 ldr r4, [r0, #20] + WRITE_REG(FIREWALL->CSSA, (FW_CSSA_ADD & fw_init->CodeSegmentStartAddress)); + 80001a6: ea03 0506 and.w r5, r3, r6 + 80001aa: 4b10 ldr r3, [pc, #64] ; (80001ec ) + 80001ac: 601d str r5, [r3, #0] + WRITE_REG(FIREWALL->CSL, (FW_CSL_LENG & fw_init->CodeSegmentLength)); + 80001ae: 4d10 ldr r5, [pc, #64] ; (80001f0 ) + 80001b0: 4029 ands r1, r5 + 80001b2: 6059 str r1, [r3, #4] + WRITE_REG(FIREWALL->NVDSSA, (FW_NVDSSA_ADD & fw_init->NonVDataSegmentStartAddress)); + 80001b4: 6881 ldr r1, [r0, #8] + WRITE_REG(FIREWALL->NVDSL, (FW_NVDSL_LENG & fw_init->NonVDataSegmentLength)); + 80001b6: 402a ands r2, r5 + WRITE_REG(FIREWALL->NVDSSA, (FW_NVDSSA_ADD & fw_init->NonVDataSegmentStartAddress)); + 80001b8: 4031 ands r1, r6 + 80001ba: 6099 str r1, [r3, #8] + WRITE_REG(FIREWALL->NVDSL, (FW_NVDSL_LENG & fw_init->NonVDataSegmentLength)); + 80001bc: 60da str r2, [r3, #12] + WRITE_REG(FIREWALL->VDSSA, (FW_VDSSA_ADD & fw_init->VDataSegmentStartAddress)); + 80001be: 6901 ldr r1, [r0, #16] + 80001c0: 4a0c ldr r2, [pc, #48] ; (80001f4 ) + 80001c2: 4011 ands r1, r2 + WRITE_REG(FIREWALL->VDSL, (FW_VDSL_LENG & fw_init->VDataSegmentLength)); + 80001c4: 4022 ands r2, r4 + WRITE_REG(FIREWALL->VDSSA, (FW_VDSSA_ADD & fw_init->VDataSegmentStartAddress)); + 80001c6: 6119 str r1, [r3, #16] + WRITE_REG(FIREWALL->VDSL, (FW_VDSL_LENG & fw_init->VDataSegmentLength)); + 80001c8: 615a str r2, [r3, #20] + MODIFY_REG(FIREWALL->CR, FW_CR_VDS|FW_CR_VDE, fw_init->VolatileDataExecution|fw_init->VolatileDataShared); + 80001ca: e9d0 2006 ldrd r2, r0, [r0, #24] + 80001ce: 6a19 ldr r1, [r3, #32] + 80001d0: 4302 orrs r2, r0 + 80001d2: f021 0106 bic.w r1, r1, #6 + 80001d6: 430a orrs r2, r1 + 80001d8: 621a str r2, [r3, #32] + return HAL_OK; + 80001da: 2000 movs r0, #0 + 80001dc: e7cb b.n 8000176 + 80001de: bf00 nop + 80001e0: 40021000 .word 0x40021000 + 80001e4: 40010000 .word 0x40010000 + 80001e8: 00ffff00 .word 0x00ffff00 + 80001ec: 40011c00 .word 0x40011c00 + 80001f0: 003fff00 .word 0x003fff00 + 80001f4: 0003ffc0 .word 0x0003ffc0 + +080001f8 : +void HAL_FIREWALL_GetConfig(FIREWALL_InitTypeDef * fw_config) +{ + + /* Enable Firewall clock, in case no Firewall configuration has been carried + out up to this point */ + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 80001f8: 4b15 ldr r3, [pc, #84] ; (8000250 ) + 80001fa: 6e1a ldr r2, [r3, #96] ; 0x60 +{ + 80001fc: b573 push {r0, r1, r4, r5, r6, lr} + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 80001fe: f042 0280 orr.w r2, r2, #128 ; 0x80 + 8000202: 661a str r2, [r3, #96] ; 0x60 + 8000204: 6e1b ldr r3, [r3, #96] ; 0x60 + + /* Retrieve code segment protection setting */ + fw_config->CodeSegmentStartAddress = (READ_REG(FIREWALL->CSSA) & FW_CSSA_ADD); + 8000206: 4e13 ldr r6, [pc, #76] ; (8000254 ) + fw_config->CodeSegmentLength = (READ_REG(FIREWALL->CSL) & FW_CSL_LENG); + 8000208: 4d13 ldr r5, [pc, #76] ; (8000258 ) + __HAL_RCC_FIREWALL_CLK_ENABLE(); + 800020a: f003 0380 and.w r3, r3, #128 ; 0x80 + 800020e: 9301 str r3, [sp, #4] + 8000210: 9b01 ldr r3, [sp, #4] + fw_config->CodeSegmentStartAddress = (READ_REG(FIREWALL->CSSA) & FW_CSSA_ADD); + 8000212: 4b12 ldr r3, [pc, #72] ; (800025c ) + 8000214: 681a ldr r2, [r3, #0] + 8000216: 4032 ands r2, r6 + 8000218: 6002 str r2, [r0, #0] + fw_config->CodeSegmentLength = (READ_REG(FIREWALL->CSL) & FW_CSL_LENG); + 800021a: 685c ldr r4, [r3, #4] + 800021c: 402c ands r4, r5 + 800021e: 6044 str r4, [r0, #4] + + /* Retrieve non volatile data segment protection setting */ + fw_config->NonVDataSegmentStartAddress = (READ_REG(FIREWALL->NVDSSA) & FW_NVDSSA_ADD); + 8000220: 6899 ldr r1, [r3, #8] + fw_config->NonVDataSegmentLength = (READ_REG(FIREWALL->NVDSL) & FW_NVDSL_LENG); + + /* Retrieve volatile data segment protection setting */ + fw_config->VDataSegmentStartAddress = (READ_REG(FIREWALL->VDSSA) & FW_VDSSA_ADD); + 8000222: 4c0f ldr r4, [pc, #60] ; (8000260 ) + fw_config->NonVDataSegmentStartAddress = (READ_REG(FIREWALL->NVDSSA) & FW_NVDSSA_ADD); + 8000224: 4031 ands r1, r6 + 8000226: 6081 str r1, [r0, #8] + fw_config->NonVDataSegmentLength = (READ_REG(FIREWALL->NVDSL) & FW_NVDSL_LENG); + 8000228: 68da ldr r2, [r3, #12] + 800022a: 402a ands r2, r5 + 800022c: 60c2 str r2, [r0, #12] + fw_config->VDataSegmentStartAddress = (READ_REG(FIREWALL->VDSSA) & FW_VDSSA_ADD); + 800022e: 6919 ldr r1, [r3, #16] + 8000230: 4021 ands r1, r4 + 8000232: 6101 str r1, [r0, #16] + fw_config->VDataSegmentLength = (READ_REG(FIREWALL->VDSL) & FW_VDSL_LENG); + 8000234: 695a ldr r2, [r3, #20] + 8000236: 4022 ands r2, r4 + 8000238: 6142 str r2, [r0, #20] + + /* Retrieve volatile data execution setting */ + fw_config->VolatileDataExecution = (READ_REG(FIREWALL->CR) & FW_CR_VDE); + 800023a: 6a1a ldr r2, [r3, #32] + 800023c: f002 0204 and.w r2, r2, #4 + 8000240: 6182 str r2, [r0, #24] + + /* Retrieve volatile data shared setting */ + fw_config->VolatileDataShared = (READ_REG(FIREWALL->CR) & FW_CR_VDS); + 8000242: 6a1b ldr r3, [r3, #32] + 8000244: f003 0302 and.w r3, r3, #2 + 8000248: 61c3 str r3, [r0, #28] + + return; +} + 800024a: b002 add sp, #8 + 800024c: bd70 pop {r4, r5, r6, pc} + 800024e: bf00 nop + 8000250: 40021000 .word 0x40021000 + 8000254: 00ffff00 .word 0x00ffff00 + 8000258: 003fff00 .word 0x003fff00 + 800025c: 40011c00 .word 0x40011c00 + 8000260: 0003ffc0 .word 0x0003ffc0 + +08000264 : + * @retval None + */ +void HAL_FIREWALL_EnableFirewall(void) +{ + /* Clears FWDIS bit of SYSCFG CFGR1 register */ + CLEAR_BIT(SYSCFG->CFGR1, SYSCFG_CFGR1_FWDIS); + 8000264: 4a02 ldr r2, [pc, #8] ; (8000270 ) + 8000266: 6853 ldr r3, [r2, #4] + 8000268: f023 0301 bic.w r3, r3, #1 + 800026c: 6053 str r3, [r2, #4] + +} + 800026e: 4770 bx lr + 8000270: 40010000 .word 0x40010000 + +08000274 : + * @retval None + */ +void HAL_FIREWALL_EnablePreArmFlag(void) +{ + /* Set FPA bit */ + SET_BIT(FIREWALL->CR, FW_CR_FPA); + 8000274: 4a02 ldr r2, [pc, #8] ; (8000280 ) + 8000276: 6a13 ldr r3, [r2, #32] + 8000278: f043 0301 orr.w r3, r3, #1 + 800027c: 6213 str r3, [r2, #32] +} + 800027e: 4770 bx lr + 8000280: 40011c00 .word 0x40011c00 + +08000284 : + * @retval None + */ +void HAL_FIREWALL_DisablePreArmFlag(void) +{ + /* Clear FPA bit */ + CLEAR_BIT(FIREWALL->CR, FW_CR_FPA); + 8000284: 4a02 ldr r2, [pc, #8] ; (8000290 ) + 8000286: 6a13 ldr r3, [r2, #32] + 8000288: f023 0301 bic.w r3, r3, #1 + 800028c: 6213 str r3, [r2, #32] +} + 800028e: 4770 bx lr + 8000290: 40011c00 .word 0x40011c00 + ... + +08000300 <_firewall_start>: + 8000300: 0f193a11 .word 0x0f193a11 + +08000304 : + 8000304: f24e 0900 movw r9, #57344 ; 0xe000 + 8000308: f2c2 0909 movt r9, #8201 ; 0x2009 + 800030c: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 8000310: 44ca add sl, r9 + +08000312 : + 8000312: f849 ab04 str.w sl, [r9], #4 + 8000316: 45d1 cmp r9, sl + 8000318: d1fb bne.n 8000312 + 800031a: 46ea mov sl, sp + 800031c: 46cd mov sp, r9 + 800031e: e92d 4400 stmdb sp!, {sl, lr} + +08000322 : + 8000322: f000 f841 bl 80003a8 + 8000326: e8bd 4400 ldmia.w sp!, {sl, lr} + 800032a: 46d5 mov sp, sl + 800032c: f24e 0900 movw r9, #57344 ; 0xe000 + 8000330: f2c2 0909 movt r9, #8201 ; 0x2009 + 8000334: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 8000338: 44ca add sl, r9 + +0800033a : + 800033a: f849 0b04 str.w r0, [r9], #4 + 800033e: 45d1 cmp r9, sl + 8000340: d1fb bne.n 800033a + 8000342: 4770 bx lr + +08000344 <__NVIC_SystemReset>: + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); + 8000344: f3bf 8f4f dsb sy +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8000348: 4905 ldr r1, [pc, #20] ; (8000360 <__NVIC_SystemReset+0x1c>) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 800034a: 4b06 ldr r3, [pc, #24] ; (8000364 <__NVIC_SystemReset+0x20>) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 800034c: 68ca ldr r2, [r1, #12] + 800034e: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8000352: 4313 orrs r3, r2 + 8000354: 60cb str r3, [r1, #12] + 8000356: f3bf 8f4f dsb sy + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + 800035a: bf00 nop + for(;;) /* wait until reset */ + 800035c: e7fd b.n 800035a <__NVIC_SystemReset+0x16> + 800035e: bf00 nop + 8000360: e000ed00 .word 0xe000ed00 + 8000364: 05fa0004 .word 0x05fa0004 + +08000368 : +good_addr(const uint8_t *b, int minlen, int len, bool readonly) +{ + uint32_t x = (uint32_t)b; + + if(minlen) { + if(!b) return EFAULT; // gave no buffer + 8000368: b198 cbz r0, 8000392 + if(len < minlen) return ERANGE; // too small + 800036a: 4291 cmp r1, r2 + 800036c: dc13 bgt.n 8000396 + } + + if((x >= SRAM1_BASE) && ((x+len) <= BL_SRAM_BASE)) { + 800036e: f1b0 5f00 cmp.w r0, #536870912 ; 0x20000000 + 8000372: d303 bcc.n 800037c + 8000374: 490b ldr r1, [pc, #44] ; (80003a4 ) + 8000376: 4402 add r2, r0 + 8000378: 428a cmp r2, r1 + 800037a: d90e bls.n 800039a + // ok: it's inside the SRAM areas, up to where we start + return 0; + } + + if(!readonly) { + 800037c: b17b cbz r3, 800039e + return EPERM; + } + + if((x >= FIRMWARE_START) && (x - FIRMWARE_START) < FW_MAX_LENGTH_MK4) { + 800037e: f100 4077 add.w r0, r0, #4143972352 ; 0xf7000000 + 8000382: f500 007e add.w r0, r0, #16646144 ; 0xfe0000 + // inside flash of main firmware (happens for QSTR's) + return 0; + } + + return EACCES; + 8000386: f5b0 1ff0 cmp.w r0, #1966080 ; 0x1e0000 + 800038a: bf34 ite cc + 800038c: 2000 movcc r0, #0 + 800038e: 200d movcs r0, #13 + 8000390: 4770 bx lr + if(!b) return EFAULT; // gave no buffer + 8000392: 200e movs r0, #14 + 8000394: 4770 bx lr + if(len < minlen) return ERANGE; // too small + 8000396: 2022 movs r0, #34 ; 0x22 + 8000398: 4770 bx lr + return 0; + 800039a: 2000 movs r0, #0 + 800039c: 4770 bx lr + return EPERM; + 800039e: 2001 movs r0, #1 +} + 80003a0: 4770 bx lr + 80003a2: bf00 nop + 80003a4: 2009e000 .word 0x2009e000 + +080003a8 : +// + __attribute__ ((used)) + int +firewall_dispatch(int method_num, uint8_t *buf_io, int len_in, + uint32_t arg2, uint32_t incoming_sp, uint32_t incoming_lr) +{ + 80003a8: b570 push {r4, r5, r6, lr} + 80003aa: b09e sub sp, #120 ; 0x78 + 80003ac: 460d mov r5, r1 + 80003ae: 9c23 ldr r4, [sp, #140] ; 0x8c + 80003b0: 9301 str r3, [sp, #4] + __ASM volatile ("cpsid i" : : : "memory"); + 80003b2: b672 cpsid i + // in case the caller didn't already, but would just lead to a crash anyway + __disable_irq(); + + // "1=any code executed outside the protected segment will close the Firewall" + // "0=.. will reset the processor" + __HAL_FIREWALL_PREARM_DISABLE(); + 80003b4: 4ba9 ldr r3, [pc, #676] ; (800065c ) + 80003b6: 6a19 ldr r1, [r3, #32] + 80003b8: f021 0101 bic.w r1, r1, #1 + 80003bc: 6219 str r1, [r3, #32] + 80003be: 6a1b ldr r3, [r3, #32] + 80003c0: f003 0301 and.w r3, r3, #1 + 80003c4: 9302 str r3, [sp, #8] + // using read/write in place. + // - use arg2 use when a simple number is needed; never a pointer! + // - mpy may provide a pointer to flash if we give it a qstr or small value, and if + // we're reading only, that's fine. + + if(len_in > 1024) { // arbitrary max, increase as needed + 80003c6: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + __HAL_FIREWALL_PREARM_DISABLE(); + 80003ca: 9b02 ldr r3, [sp, #8] + if(len_in > 1024) { // arbitrary max, increase as needed + 80003cc: f300 82ec bgt.w 80009a8 + + // Use these macros +#define REQUIRE_IN_ONLY(x) if((rv = good_addr(buf_io, (x), len_in, true))) { goto fail; } +#define REQUIRE_OUT(x) if((rv = good_addr(buf_io, (x), len_in, false))) { goto fail; } + + switch(method_num) { + 80003d0: 3001 adds r0, #1 + 80003d2: 281b cmp r0, #27 + 80003d4: f200 81c0 bhi.w 8000758 + 80003d8: e8df f010 tbh [pc, r0, lsl #1] + 80003dc: 001c02f4 .word 0x001c02f4 + 80003e0: 007f0033 .word 0x007f0033 + 80003e4: 00da00bc .word 0x00da00bc + 80003e8: 01fd00fb .word 0x01fd00fb + 80003ec: 01be01be .word 0x01be01be + 80003f0: 01be01be .word 0x01be01be + 80003f4: 010301be .word 0x010301be + 80003f8: 01be01be .word 0x01be01be + 80003fc: 012d010e .word 0x012d010e + 8000400: 0171015e .word 0x0171015e + 8000404: 020101b5 .word 0x020101b5 + 8000408: 02690210 .word 0x02690210 + 800040c: 02c002ac .word 0x02c002ac + 8000410: 02d802c8 .word 0x02d802c8 + case 0: { + REQUIRE_OUT(64); + 8000414: 2300 movs r3, #0 + 8000416: 2140 movs r1, #64 ; 0x40 + 8000418: 4628 mov r0, r5 + 800041a: 9200 str r2, [sp, #0] + 800041c: f7ff ffa4 bl 8000368 + 8000420: 4604 mov r4, r0 + 8000422: bb48 cbnz r0, 8000478 + + // Return my version string + memset(buf_io, 0, len_in); + 8000424: 4601 mov r1, r0 + 8000426: 9a00 ldr r2, [sp, #0] + 8000428: 4628 mov r0, r5 + 800042a: f00d fa7b bl 800d924 + strlcpy((char *)buf_io, version_string, len_in); + 800042e: 9a00 ldr r2, [sp, #0] + 8000430: 498b ldr r1, [pc, #556] ; (8000660 ) + 8000432: 4628 mov r0, r5 + 8000434: f00d fa8c bl 800d950 + + rv = strlen(version_string); + 8000438: 4889 ldr r0, [pc, #548] ; (8000660 ) + 800043a: f00d fa9e bl 800d97a + ae_setup(); + ae_keep_alive(); + switch(arg2) { + default: + case 0: // read state + rv = ae_get_gpio(); + 800043e: 4604 mov r4, r0 + break; + 8000440: e01a b.n 8000478 + REQUIRE_OUT(32); + 8000442: 2300 movs r3, #0 + 8000444: 2120 movs r1, #32 + 8000446: 4628 mov r0, r5 + 8000448: f7ff ff8e bl 8000368 + 800044c: 4604 mov r4, r0 + 800044e: b998 cbnz r0, 8000478 + sha256_init(&ctx); + 8000450: a80b add r0, sp, #44 ; 0x2c + 8000452: f005 f993 bl 800577c + sha256_update(&ctx, (void *)&arg2, 4); + 8000456: 2204 movs r2, #4 + 8000458: eb0d 0102 add.w r1, sp, r2 + 800045c: a80b add r0, sp, #44 ; 0x2c + 800045e: f005 f99b bl 8005798 + sha256_update(&ctx, (void *)BL_FLASH_BASE, BL_FLASH_SIZE); + 8000462: f04f 6100 mov.w r1, #134217728 ; 0x8000000 + 8000466: a80b add r0, sp, #44 ; 0x2c + 8000468: f44f 32e0 mov.w r2, #114688 ; 0x1c000 + 800046c: f005 f994 bl 8005798 + sha256_final(&ctx, buf_io); + 8000470: 4629 mov r1, r5 + 8000472: a80b add r0, sp, #44 ; 0x2c + 8000474: f005 f9d6 bl 8005824 + +fail: + + // Precaution: we don't want to leave SE1 authorized for any specific keys, + // perhaps due to an error path we didn't see. Always reset the chip. + ae_reset_chip(); + 8000478: f002 fb5c bl 8002b34 + + // Unlikely it matters, but clear flash memory cache. + __HAL_FLASH_DATA_CACHE_DISABLE(); + 800047c: 4b79 ldr r3, [pc, #484] ; (8000664 ) + 800047e: 681a ldr r2, [r3, #0] + 8000480: f422 6280 bic.w r2, r2, #1024 ; 0x400 + 8000484: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_RESET(); + 8000486: 681a ldr r2, [r3, #0] + 8000488: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800048c: 601a str r2, [r3, #0] + 800048e: 681a ldr r2, [r3, #0] + 8000490: f422 5280 bic.w r2, r2, #4096 ; 0x1000 + 8000494: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); + 8000496: 681a ldr r2, [r3, #0] + 8000498: f442 6280 orr.w r2, r2, #1024 ; 0x400 + 800049c: 601a str r2, [r3, #0] + + // .. and instruction memory (flash cache too?) + __HAL_FLASH_INSTRUCTION_CACHE_DISABLE(); + 800049e: 681a ldr r2, [r3, #0] + 80004a0: f422 7200 bic.w r2, r2, #512 ; 0x200 + 80004a4: 601a str r2, [r3, #0] + __HAL_FLASH_INSTRUCTION_CACHE_RESET(); + 80004a6: 681a ldr r2, [r3, #0] + 80004a8: f442 6200 orr.w r2, r2, #2048 ; 0x800 + 80004ac: 601a str r2, [r3, #0] + 80004ae: 681a ldr r2, [r3, #0] + 80004b0: f422 6200 bic.w r2, r2, #2048 ; 0x800 + 80004b4: 601a str r2, [r3, #0] + __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); + 80004b6: 681a ldr r2, [r3, #0] + 80004b8: f442 7200 orr.w r2, r2, #512 ; 0x200 + 80004bc: 601a str r2, [r3, #0] + + // authorize return from firewall into user's code + __HAL_FIREWALL_PREARM_ENABLE(); + 80004be: f5a3 3382 sub.w r3, r3, #66560 ; 0x10400 + + return rv; +} + 80004c2: 4620 mov r0, r4 + __HAL_FIREWALL_PREARM_ENABLE(); + 80004c4: 6a1a ldr r2, [r3, #32] + 80004c6: f042 0201 orr.w r2, r2, #1 + 80004ca: 621a str r2, [r3, #32] + 80004cc: 6a1b ldr r3, [r3, #32] + 80004ce: f003 0301 and.w r3, r3, #1 + 80004d2: 930b str r3, [sp, #44] ; 0x2c + 80004d4: 9b0b ldr r3, [sp, #44] ; 0x2c +} + 80004d6: b01e add sp, #120 ; 0x78 + 80004d8: bd70 pop {r4, r5, r6, pc} +// Write bag number (probably a string) +void flash_save_bag_number(const uint8_t new_number[32]); + +// Are we operating in level2? +static inline bool flash_is_security_level2(void) { + rng_delay(); + 80004da: f002 fa15 bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 80004de: 4b61 ldr r3, [pc, #388] ; (8000664 ) + 80004e0: 6a1b ldr r3, [r3, #32] + 80004e2: b2db uxtb r3, r3 + 80004e4: f1a3 02cc sub.w r2, r3, #204 ; 0xcc + 80004e8: 4255 negs r5, r2 + 80004ea: 4155 adcs r5, r2 + switch(arg2) { + 80004ec: 9a01 ldr r2, [sp, #4] + 80004ee: 2a02 cmp r2, #2 + 80004f0: d01c beq.n 800052c + 80004f2: 2a03 cmp r2, #3 + 80004f4: d01f beq.n 8000536 + 80004f6: 2a01 cmp r2, #1 + 80004f8: d013 beq.n 8000522 + if(secure) { + 80004fa: 2bcc cmp r3, #204 ; 0xcc + 80004fc: f000 8221 beq.w 8000942 + puts("Die: DFU"); + 8000500: 4859 ldr r0, [pc, #356] ; (8000668 ) + scr = screen_upgrading; // was screen_dfu, but limited audience + 8000502: 4c5a ldr r4, [pc, #360] ; (800066c ) + puts("Die: DFU"); + 8000504: f004 fdb0 bl 8005068 + bool secure = flash_is_security_level2(); + 8000508: 2500 movs r5, #0 + oled_setup(); + 800050a: f000 fc33 bl 8000d74 + oled_show(scr); + 800050e: 4620 mov r0, r4 + 8000510: f000 fdb0 bl 8001074 + wipe_all_sram(); + 8000514: f000 fa70 bl 80009f8 + psram_wipe(); + 8000518: f004 fece bl 80052b8 + if(secure) { + 800051c: b18d cbz r5, 8000542 + LOCKUP_FOREVER(); + 800051e: f003 fbab bl 8003c78 + puts("Die: Downgrade"); + 8000522: 4853 ldr r0, [pc, #332] ; (8000670 ) + scr = screen_downgrade; + 8000524: 4c53 ldr r4, [pc, #332] ; (8000674 ) + puts("Die: Downgrade"); + 8000526: f004 fd9f bl 8005068 + break; + 800052a: e7ee b.n 800050a + puts("Die: Blankish"); + 800052c: 4852 ldr r0, [pc, #328] ; (8000678 ) + scr = screen_blankish; + 800052e: 4c53 ldr r4, [pc, #332] ; (800067c ) + puts("Die: Blankish"); + 8000530: f004 fd9a bl 8005068 + break; + 8000534: e7e9 b.n 800050a + puts("Die: Brick"); + 8000536: 4852 ldr r0, [pc, #328] ; (8000680 ) + scr = screen_brick; + 8000538: 4c52 ldr r4, [pc, #328] ; (8000684 ) + puts("Die: Brick"); + 800053a: f004 fd95 bl 8005068 + secure = true; // no point going into DFU, if even possible + 800053e: 2501 movs r5, #1 + break; + 8000540: e7e3 b.n 800050a + memcpy(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)); + 8000542: 4951 ldr r1, [pc, #324] ; (8000688 ) + 8000544: 4a51 ldr r2, [pc, #324] ; (800068c ) + 8000546: 6808 ldr r0, [r1, #0] + 8000548: 6849 ldr r1, [r1, #4] + 800054a: 4613 mov r3, r2 + 800054c: c303 stmia r3!, {r0, r1} + dfu_flag->screen = scr; + 800054e: 6094 str r4, [r2, #8] + NVIC_SystemReset(); + 8000550: f7ff fef8 bl 8000344 <__NVIC_SystemReset> + switch(arg2) { + 8000554: 9b01 ldr r3, [sp, #4] + 8000556: 2b02 cmp r3, #2 + 8000558: d002 beq.n 8000560 + 800055a: 2b03 cmp r3, #3 + 800055c: d016 beq.n 800058c + 800055e: b913 cbnz r3, 8000566 + oled_show(screen_logout); + 8000560: 484b ldr r0, [pc, #300] ; (8000690 ) + oled_show(screen_poweroff); + 8000562: f000 fd87 bl 8001074 + wipe_all_sram(); + 8000566: f000 fa47 bl 80009f8 + psram_wipe(); + 800056a: f004 fea5 bl 80052b8 + if(arg2 == 3) { + 800056e: 9b01 ldr r3, [sp, #4] + 8000570: 2b03 cmp r3, #3 + 8000572: d104 bne.n 800057e + delay_ms(100); + 8000574: 2064 movs r0, #100 ; 0x64 + 8000576: f003 fa85 bl 8003a84 + turn_power_off(); + 800057a: f003 fb71 bl 8003c60 + if(arg2 == 2) { + 800057e: 9b01 ldr r3, [sp, #4] + 8000580: 2b02 cmp r3, #2 + 8000582: d1cc bne.n 800051e + delay_ms(100); + 8000584: 2064 movs r0, #100 ; 0x64 + 8000586: f003 fa7d bl 8003a84 + 800058a: e7e1 b.n 8000550 + oled_show(screen_poweroff); + 800058c: 4841 ldr r0, [pc, #260] ; (8000694 ) + 800058e: e7e8 b.n 8000562 + ae_setup(); + 8000590: f002 fade bl 8002b50 + ae_keep_alive(); + 8000594: f002 fb0e bl 8002bb4 + switch(arg2) { + 8000598: 9b01 ldr r3, [sp, #4] + 800059a: 2b02 cmp r3, #2 + 800059c: d00a beq.n 80005b4 + 800059e: 2b03 cmp r3, #3 + 80005a0: d00a beq.n 80005b8 + 80005a2: 2b01 cmp r3, #1 + 80005a4: d002 beq.n 80005ac + rv = ae_get_gpio(); + 80005a6: f003 f883 bl 80036b0 + 80005aa: e748 b.n 800043e + rv = ae_set_gpio(0); + 80005ac: 2000 movs r0, #0 + rv = ae_set_gpio(1); + 80005ae: f003 f851 bl 8003654 + 80005b2: e744 b.n 800043e + 80005b4: 2001 movs r0, #1 + 80005b6: e7fa b.n 80005ae + checksum_flash(fw_digest, world_digest, 0); + 80005b8: 2200 movs r2, #0 + 80005ba: a90b add r1, sp, #44 ; 0x2c + 80005bc: a803 add r0, sp, #12 + 80005be: f001 fb2d bl 8001c1c + rv = ae_set_gpio_secure(world_digest); + 80005c2: a80b add r0, sp, #44 ; 0x2c + 80005c4: f003 f85c bl 8003680 + 80005c8: 4604 mov r4, r0 + oled_show(screen_blankish); + 80005ca: 482c ldr r0, [pc, #176] ; (800067c ) + 80005cc: f000 fd52 bl 8001074 + break; + 80005d0: e752 b.n 8000478 + ae_setup(); + 80005d2: f002 fabd bl 8002b50 + rv = (ae_pair_unlock() != 0); + 80005d6: f002 fcb1 bl 8002f3c + 80005da: 1e04 subs r4, r0, #0 + 80005dc: bf18 it ne + 80005de: 2401 movne r4, #1 + break; + 80005e0: e74a b.n 8000478 + REQUIRE_OUT(1); + 80005e2: 2300 movs r3, #0 + 80005e4: 2101 movs r1, #1 + 80005e6: 4628 mov r0, r5 + 80005e8: f7ff febe bl 8000368 + 80005ec: 4604 mov r4, r0 + 80005ee: 2800 cmp r0, #0 + 80005f0: f47f af42 bne.w 8000478 + buf_io[0] = 0; // NOT SUPPORTED on Mk4 + 80005f4: 7028 strb r0, [r5, #0] + break; + 80005f6: e73f b.n 8000478 + if(len_in != 4 && len_in != 32 && len_in != 72) { + 80005f8: 2a04 cmp r2, #4 + 80005fa: d004 beq.n 8000606 + 80005fc: 2a20 cmp r2, #32 + 80005fe: d002 beq.n 8000606 + 8000600: 2a48 cmp r2, #72 ; 0x48 + 8000602: f040 81d1 bne.w 80009a8 + REQUIRE_OUT(4); + 8000606: 2300 movs r3, #0 + 8000608: 2104 movs r1, #4 + 800060a: 4628 mov r0, r5 + 800060c: 9200 str r2, [sp, #0] + 800060e: f7ff feab bl 8000368 + 8000612: 4604 mov r4, r0 + 8000614: 2800 cmp r0, #0 + 8000616: f47f af2f bne.w 8000478 + ae_setup(); + 800061a: f002 fa99 bl 8002b50 + if(ae_read_data_slot(arg2 & 0xf, buf_io, len_in)) { + 800061e: 9801 ldr r0, [sp, #4] + 8000620: 9a00 ldr r2, [sp, #0] + 8000622: 4629 mov r1, r5 + 8000624: f000 000f and.w r0, r0, #15 + 8000628: f002 ffce bl 80035c8 + if(rv) { + 800062c: 2800 cmp r0, #0 + 800062e: f000 80d2 beq.w 80007d6 + rv = EIO; + 8000632: 2405 movs r4, #5 + 8000634: e720 b.n 8000478 + REQUIRE_OUT(MAX_PIN_LEN); + 8000636: 2300 movs r3, #0 + 8000638: 2120 movs r1, #32 + 800063a: 4628 mov r0, r5 + 800063c: f7ff fe94 bl 8000368 + 8000640: 4604 mov r4, r0 + 8000642: 2800 cmp r0, #0 + 8000644: f47f af18 bne.w 8000478 + if((arg2 < 1) || (arg2 > MAX_PIN_LEN)) { + 8000648: 9901 ldr r1, [sp, #4] + 800064a: 1e4b subs r3, r1, #1 + 800064c: 2b1f cmp r3, #31 + 800064e: f200 81ab bhi.w 80009a8 + if(pin_prefix_words((char *)buf_io, arg2, (uint32_t *)buf_io)) { + 8000652: 462a mov r2, r5 + 8000654: 4628 mov r0, r5 + 8000656: f003 fdb1 bl 80041bc + 800065a: e7e7 b.n 800062c + 800065c: 40011c00 .word 0x40011c00 + 8000660: 0801079c .word 0x0801079c + 8000664: 40022000 .word 0x40022000 + 8000668: 0800d9a6 .word 0x0800d9a6 + 800066c: 0800ff35 .word 0x0800ff35 + 8000670: 0800d9af .word 0x0800d9af + 8000674: 0800e2f2 .word 0x0800e2f2 + 8000678: 0800d9be .word 0x0800d9be + 800067c: 0800da48 .word 0x0800da48 + 8000680: 0800d9cc .word 0x0800d9cc + 8000684: 0800da61 .word 0x0800da61 + 8000688: 0800d9d7 .word 0x0800d9d7 + 800068c: 20008000 .word 0x20008000 + 8000690: 0800e5f8 .word 0x0800e5f8 + 8000694: 0800e759 .word 0x0800e759 + REQUIRE_OUT(32); + 8000698: 2300 movs r3, #0 + 800069a: 2120 movs r1, #32 + 800069c: 4628 mov r0, r5 + 800069e: f7ff fe63 bl 8000368 + 80006a2: 4604 mov r4, r0 + 80006a4: 2800 cmp r0, #0 + 80006a6: f47f aee7 bne.w 8000478 + memset(buf_io, 0x55, 32); // to help show errors + 80006aa: 2220 movs r2, #32 + 80006ac: 2155 movs r1, #85 ; 0x55 + 80006ae: 4628 mov r0, r5 + 80006b0: f00d f938 bl 800d924 + rng_buffer(buf_io, 32); + 80006b4: 2120 movs r1, #32 + 80006b6: 4628 mov r0, r5 + 80006b8: f002 f910 bl 80028dc + break; + 80006bc: e6dc b.n 8000478 + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2); + 80006be: 2300 movs r3, #0 + 80006c0: f44f 718c mov.w r1, #280 ; 0x118 + 80006c4: 4628 mov r0, r5 + 80006c6: 9200 str r2, [sp, #0] + 80006c8: f7ff fe4e bl 8000368 + 80006cc: 4604 mov r4, r0 + 80006ce: 2800 cmp r0, #0 + 80006d0: f47f aed2 bne.w 8000478 + switch(arg2) { + 80006d4: e9dd 2300 ldrd r2, r3, [sp] + 80006d8: 2b08 cmp r3, #8 + 80006da: d83d bhi.n 8000758 + 80006dc: e8df f003 tbb [pc, r3] + 80006e0: 110d0905 .word 0x110d0905 + 80006e4: 221d1915 .word 0x221d1915 + 80006e8: 26 .byte 0x26 + 80006e9: 00 .byte 0x00 + rv = pin_setup_attempt(args); + 80006ea: 4628 mov r0, r5 + 80006ec: f003 fd84 bl 80041f8 + 80006f0: e6a5 b.n 800043e + rv = pin_delay(args); + 80006f2: 4628 mov r0, r5 + 80006f4: f003 fdee bl 80042d4 + 80006f8: e6a1 b.n 800043e + rv = pin_login_attempt(args); + 80006fa: 4628 mov r0, r5 + 80006fc: f003 fdec bl 80042d8 + 8000700: e69d b.n 800043e + rv = pin_change(args); + 8000702: 4628 mov r0, r5 + 8000704: f003 fef6 bl 80044f4 + 8000708: e699 b.n 800043e + rv = pin_fetch_secret(args); + 800070a: 4628 mov r0, r5 + 800070c: f003 ffaa bl 8004664 + 8000710: e695 b.n 800043e + rv = pin_firmware_greenlight(args); + 8000712: 4628 mov r0, r5 + 8000714: f004 f966 bl 80049e4 + 8000718: e691 b.n 800043e + rv = pin_long_secret(args, NULL); + 800071a: 2100 movs r1, #0 + rv = pin_long_secret(args, &buf_io[PIN_ATTEMPT_SIZE_V2]); + 800071c: 4628 mov r0, r5 + 800071e: f004 f8a3 bl 8004868 + 8000722: e68c b.n 800043e + rv = pin_firmware_upgrade(args); + 8000724: 4628 mov r0, r5 + 8000726: f004 f99d bl 8004a64 + 800072a: e688 b.n 800043e + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2 + AE_LONG_SECRET_LEN); + 800072c: 2300 movs r3, #0 + 800072e: f44f 712e mov.w r1, #696 ; 0x2b8 + 8000732: 4628 mov r0, r5 + 8000734: f7ff fe18 bl 8000368 + 8000738: 4604 mov r4, r0 + 800073a: 2800 cmp r0, #0 + 800073c: f47f ae9c bne.w 8000478 + rv = pin_long_secret(args, &buf_io[PIN_ATTEMPT_SIZE_V2]); + 8000740: f505 718c add.w r1, r5, #280 ; 0x118 + 8000744: e7ea b.n 800071c + switch(arg2) { + 8000746: 9b01 ldr r3, [sp, #4] + 8000748: 2b64 cmp r3, #100 ; 0x64 + 800074a: d041 beq.n 80007d0 + 800074c: d806 bhi.n 800075c + 800074e: 2b01 cmp r3, #1 + 8000750: d01e beq.n 8000790 + 8000752: 2b02 cmp r3, #2 + 8000754: d028 beq.n 80007a8 + 8000756: b13b cbz r3, 8000768 + 8000758: 2402 movs r4, #2 + 800075a: e68d b.n 8000478 + 800075c: 2b65 cmp r3, #101 ; 0x65 + 800075e: d03c beq.n 80007da + 8000760: 2b66 cmp r3, #102 ; 0x66 + 8000762: d1f9 bne.n 8000758 + flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this. + 8000764: 20cc movs r0, #204 ; 0xcc + 8000766: e034 b.n 80007d2 + REQUIRE_OUT(32); + 8000768: 2120 movs r1, #32 + 800076a: 4628 mov r0, r5 + 800076c: f7ff fdfc bl 8000368 + 8000770: 4604 mov r4, r0 + 8000772: 2800 cmp r0, #0 + 8000774: f47f ae80 bne.w 8000478 + memcpy(buf_io, rom_secrets->bag_number, 32); + 8000778: 4a99 ldr r2, [pc, #612] ; (80009e0 ) + 800077a: 4e9a ldr r6, [pc, #616] ; (80009e4 ) + 800077c: 4613 mov r3, r2 + 800077e: cb03 ldmia r3!, {r0, r1} + 8000780: 42b3 cmp r3, r6 + 8000782: 6028 str r0, [r5, #0] + 8000784: 6069 str r1, [r5, #4] + 8000786: 461a mov r2, r3 + 8000788: f105 0508 add.w r5, r5, #8 + 800078c: d1f6 bne.n 800077c + 800078e: e673 b.n 8000478 + REQUIRE_IN_ONLY(32); + 8000790: 2120 movs r1, #32 + 8000792: 4628 mov r0, r5 + 8000794: f7ff fde8 bl 8000368 + 8000798: 4604 mov r4, r0 + 800079a: 2800 cmp r0, #0 + 800079c: f47f ae6c bne.w 8000478 + flash_save_bag_number(buf_io); + 80007a0: 4628 mov r0, r5 + 80007a2: f001 fd7f bl 80022a4 + break; + 80007a6: e667 b.n 8000478 + REQUIRE_OUT(1); + 80007a8: 2300 movs r3, #0 + 80007aa: 2101 movs r1, #1 + 80007ac: 4628 mov r0, r5 + 80007ae: f7ff fddb bl 8000368 + 80007b2: 4604 mov r4, r0 + 80007b4: 2800 cmp r0, #0 + 80007b6: f47f ae5f bne.w 8000478 + rng_delay(); + 80007ba: f002 f8a5 bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 80007be: 4b8a ldr r3, [pc, #552] ; (80009e8 ) + 80007c0: 6a1b ldr r3, [r3, #32] + 80007c2: b2db uxtb r3, r3 + buf_io[0] = (flash_is_security_level2() ? 2 : 0xff); + 80007c4: 2bcc cmp r3, #204 ; 0xcc + 80007c6: bf0c ite eq + 80007c8: 2302 moveq r3, #2 + 80007ca: 23ff movne r3, #255 ; 0xff + buf_io[0] = 8; + 80007cc: 702b strb r3, [r5, #0] + break; + 80007ce: e653 b.n 8000478 + flash_lockdown_hard(OB_RDP_LEVEL_0); // wipes contents of flash (1->0) + 80007d0: 20aa movs r0, #170 ; 0xaa + flash_lockdown_hard(OB_RDP_LEVEL_2); // No change possible after this. + 80007d2: f001 fe9f bl 8002514 + int rv = 0; + 80007d6: 2400 movs r4, #0 + break; + 80007d8: e64e b.n 8000478 + flash_lockdown_hard(OB_RDP_LEVEL_1); // Can only do 0->1 (experiments) + 80007da: 20bb movs r0, #187 ; 0xbb + 80007dc: e7f9 b.n 80007d2 + REQUIRE_OUT(128); + 80007de: 2300 movs r3, #0 + 80007e0: 2180 movs r1, #128 ; 0x80 + 80007e2: 4628 mov r0, r5 + 80007e4: f7ff fdc0 bl 8000368 + 80007e8: 4604 mov r4, r0 + 80007ea: 2800 cmp r0, #0 + 80007ec: f47f ae44 bne.w 8000478 + ae_setup(); + 80007f0: f002 f9ae bl 8002b50 + rv = ae_config_read(buf_io); + 80007f4: 4628 mov r0, r5 + 80007f6: f002 ffac bl 8003752 + 80007fa: e717 b.n 800062c + switch(arg2) { + 80007fc: 9b01 ldr r3, [sp, #4] + 80007fe: 2b03 cmp r3, #3 + 8000800: d8aa bhi.n 8000758 + 8000802: e8df f003 tbb [pc, r3] + 8000806: 0f02 .short 0x0f02 + 8000808: 441d .short 0x441d + REQUIRE_OUT(8); + 800080a: 2300 movs r3, #0 + 800080c: 2108 movs r1, #8 + 800080e: 4628 mov r0, r5 + 8000810: f7ff fdaa bl 8000368 + 8000814: 4604 mov r4, r0 + 8000816: 2800 cmp r0, #0 + 8000818: f47f ae2e bne.w 8000478 + get_min_version(buf_io); + 800081c: 4628 mov r0, r5 + 800081e: f001 fa8d bl 8001d3c + break; + 8000822: e629 b.n 8000478 + REQUIRE_IN_ONLY(8); + 8000824: 2301 movs r3, #1 + 8000826: 2108 movs r1, #8 + 8000828: 4628 mov r0, r5 + 800082a: f7ff fd9d bl 8000368 + 800082e: 4604 mov r4, r0 + 8000830: 2800 cmp r0, #0 + 8000832: f47f ae21 bne.w 8000478 + rv = check_is_downgrade(buf_io, NULL); + 8000836: 4601 mov r1, r0 + 8000838: 4628 mov r0, r5 + 800083a: f001 fa9f bl 8001d7c + 800083e: e5fe b.n 800043e + REQUIRE_IN_ONLY(8); + 8000840: 2301 movs r3, #1 + 8000842: 2108 movs r1, #8 + 8000844: 4628 mov r0, r5 + 8000846: f7ff fd8f bl 8000368 + 800084a: 4604 mov r4, r0 + 800084c: 2800 cmp r0, #0 + 800084e: f47f ae13 bne.w 8000478 + if(buf_io[0] < 0x10 || buf_io[0] >= 0x40) { + 8000852: 782b ldrb r3, [r5, #0] + 8000854: 3b10 subs r3, #16 + rv = ERANGE; + 8000856: 2b2f cmp r3, #47 ; 0x2f + } if(check_is_downgrade(buf_io, NULL)) { + 8000858: 4601 mov r1, r0 + 800085a: 4628 mov r0, r5 + rv = ERANGE; + 800085c: bf88 it hi + 800085e: 2422 movhi r4, #34 ; 0x22 + } if(check_is_downgrade(buf_io, NULL)) { + 8000860: f001 fa8c bl 8001d7c + 8000864: 2800 cmp r0, #0 + 8000866: f040 80b9 bne.w 80009dc + get_min_version(min); + 800086a: a80b add r0, sp, #44 ; 0x2c + 800086c: f001 fa66 bl 8001d3c + if(memcmp(min, buf_io, 8) == 0) { + 8000870: 2208 movs r2, #8 + 8000872: 4629 mov r1, r5 + 8000874: a80b add r0, sp, #44 ; 0x2c + 8000876: f00d f837 bl 800d8e8 + 800087a: 2800 cmp r0, #0 + 800087c: f000 80ae beq.w 80009dc + if(record_highwater_version(buf_io)) { + 8000880: 4628 mov r0, r5 + 8000882: f001 fe61 bl 8002548 + rv = ENOMEM; + 8000886: 2800 cmp r0, #0 + 8000888: bf18 it ne + 800088a: 240c movne r4, #12 + 800088c: e5f4 b.n 8000478 + REQUIRE_OUT(4); + 800088e: 2300 movs r3, #0 + 8000890: 2104 movs r1, #4 + 8000892: 4628 mov r0, r5 + 8000894: f7ff fd68 bl 8000368 + 8000898: 4604 mov r4, r0 + 800089a: 2800 cmp r0, #0 + 800089c: f47f adec bne.w 8000478 + ae_setup(); + 80008a0: f002 f956 bl 8002b50 + rv = ae_get_counter((uint32_t *)buf_io, 0) ? EIO: 0; + 80008a4: 4621 mov r1, r4 + 80008a6: 4628 mov r0, r5 + 80008a8: f002 fd43 bl 8003332 + 80008ac: e6be b.n 800062c + REQUIRE_OUT(PIN_ATTEMPT_SIZE_V2 + sizeof(trick_slot_t)); + 80008ae: 2300 movs r3, #0 + 80008b0: f44f 71cc mov.w r1, #408 ; 0x198 + 80008b4: 4628 mov r0, r5 + 80008b6: f7ff fd57 bl 8000368 + 80008ba: 4604 mov r4, r0 + 80008bc: 2800 cmp r0, #0 + 80008be: f47f addb bne.w 8000478 + rv = pin_check_logged_in(args, &trick_mode); + 80008c2: a90b add r1, sp, #44 ; 0x2c + 80008c4: 4628 mov r0, r5 + 80008c6: f003 fde3 bl 8004490 + if(rv) goto fail; + 80008ca: 4604 mov r4, r0 + 80008cc: 2800 cmp r0, #0 + 80008ce: f47f add3 bne.w 8000478 + if(trick_mode) { + 80008d2: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 80008d6: b10b cbz r3, 80008dc + mcu_key_clear(NULL); + 80008d8: f001 fe84 bl 80025e4 + switch(arg2) { + 80008dc: 9b01 ldr r3, [sp, #4] + 80008de: 2b01 cmp r3, #1 + trick_slot_t *slot = (trick_slot_t *)(&buf_io[PIN_ATTEMPT_SIZE_V2]); + 80008e0: f505 728c add.w r2, r5, #280 ; 0x118 + switch(arg2) { + 80008e4: d00c beq.n 8000900 + 80008e6: 2b02 cmp r3, #2 + 80008e8: d01b beq.n 8000922 + 80008ea: 2b00 cmp r3, #0 + 80008ec: f47f af34 bne.w 8000758 + if(!trick_mode) { + 80008f0: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 80008f4: 2b00 cmp r3, #0 + 80008f6: f47f adbf bne.w 8000478 + se2_clear_tricks(); + 80008fa: f007 fb9f bl 800803c + 80008fe: e5bb b.n 8000478 + if(trick_mode) { + 8000900: f89d 102c ldrb.w r1, [sp, #44] ; 0x2c + 8000904: 2900 cmp r1, #0 + 8000906: f47f af27 bne.w 8000758 + if(slot->pin_len > 16) { + 800090a: f8d5 1170 ldr.w r1, [r5, #368] ; 0x170 + 800090e: 2910 cmp r1, #16 + 8000910: dc4a bgt.n 80009a8 + if(se2_test_trick_pin(slot->pin, slot->pin_len, slot, true)) { + 8000912: f505 70b0 add.w r0, r5, #352 ; 0x160 + 8000916: f007 fbf7 bl 8008108 + 800091a: 2800 cmp r0, #0 + 800091c: f47f adac bne.w 8000478 + 8000920: e71a b.n 8000758 + if(!trick_mode) { + 8000922: f89d 302c ldrb.w r3, [sp, #44] ; 0x2c + 8000926: 2b00 cmp r3, #0 + 8000928: f47f ada6 bne.w 8000478 + rv = se2_save_trick(slot); + 800092c: 4610 mov r0, r2 + 800092e: f007 fd05 bl 800833c + 8000932: e584 b.n 800043e + if(arg2 == 0xBeef) { + 8000934: 9b01 ldr r3, [sp, #4] + 8000936: f64b 62ef movw r2, #48879 ; 0xbeef + 800093a: 4293 cmp r3, r2 + 800093c: d103 bne.n 8000946 + fast_wipe(); + 800093e: f001 ff43 bl 80027c8 + rv = EPERM; + 8000942: 2401 movs r4, #1 + 8000944: e598 b.n 8000478 + } else if(arg2 == 0xDead) { + 8000946: f64d 62ad movw r2, #57005 ; 0xdead + 800094a: 4293 cmp r3, r2 + 800094c: d1f9 bne.n 8000942 + mcu_key_clear(NULL); + 800094e: 2000 movs r0, #0 + 8000950: f001 fe48 bl 80025e4 + oled_show(screen_wiped); + 8000954: 4825 ldr r0, [pc, #148] ; (80009ec ) + 8000956: f000 fb8d bl 8001074 + 800095a: e5e0 b.n 800051e + if(arg2 == 0xDead) fast_brick(); + 800095c: 9a01 ldr r2, [sp, #4] + 800095e: f64d 63ad movw r3, #57005 ; 0xdead + 8000962: 429a cmp r2, r3 + 8000964: d1ed bne.n 8000942 + 8000966: f001 ff01 bl 800276c + 800096a: e7ea b.n 8000942 + REQUIRE_OUT(8); + 800096c: 2300 movs r3, #0 + 800096e: 2108 movs r1, #8 + 8000970: 4628 mov r0, r5 + 8000972: f7ff fcf9 bl 8000368 + 8000976: 4604 mov r4, r0 + 8000978: 2800 cmp r0, #0 + 800097a: f47f ad7d bne.w 8000478 + mcu_key_usage(avail, consumed, total); + 800097e: f105 0208 add.w r2, r5, #8 + 8000982: 1d29 adds r1, r5, #4 + 8000984: 4628 mov r0, r5 + 8000986: f001 fe5b bl 8002640 + break; + 800098a: e575 b.n 8000478 + REQUIRE_OUT(33); + 800098c: 2300 movs r3, #0 + 800098e: 2121 movs r1, #33 ; 0x21 + 8000990: 4628 mov r0, r5 + 8000992: f7ff fce9 bl 8000368 + 8000996: 4604 mov r4, r0 + 8000998: 2800 cmp r0, #0 + 800099a: f47f ad6d bne.w 8000478 + switch(arg2) { + 800099e: 9b01 ldr r3, [sp, #4] + 80009a0: 2b01 cmp r3, #1 + 80009a2: d003 beq.n 80009ac + 80009a4: 2b02 cmp r3, #2 + 80009a6: d008 beq.n 80009ba + rv = ERANGE; + 80009a8: 2422 movs r4, #34 ; 0x22 + 80009aa: e565 b.n 8000478 + ae_setup(); + 80009ac: f002 f8d0 bl 8002b50 + ae_secure_random(&buf_io[1]); + 80009b0: 1c68 adds r0, r5, #1 + 80009b2: f002 fc35 bl 8003220 + buf_io[0] = 32; + 80009b6: 2320 movs r3, #32 + 80009b8: e708 b.n 80007cc + se2_read_rng(&buf_io[1]); + 80009ba: 1c68 adds r0, r5, #1 + 80009bc: f007 fea2 bl 8008704 + buf_io[0] = 8; + 80009c0: 2308 movs r3, #8 + 80009c2: e703 b.n 80007cc + if(incoming_lr <= BL_FLASH_BASE || incoming_lr >= (uint32_t)&firewall_starts) { + 80009c4: f1b4 6f00 cmp.w r4, #134217728 ; 0x8000000 + 80009c8: d902 bls.n 80009d0 + 80009ca: 4b09 ldr r3, [pc, #36] ; (80009f0 ) + 80009cc: 429c cmp r4, r3 + 80009ce: d302 bcc.n 80009d6 + fatal_error("LR"); + 80009d0: 4808 ldr r0, [pc, #32] ; (80009f4 ) + 80009d2: f000 f831 bl 8000a38 + system_startup(); + 80009d6: f000 f88d bl 8000af4 + break; + 80009da: e6fc b.n 80007d6 + rv = EAGAIN; + 80009dc: 240b movs r4, #11 + 80009de: e54b b.n 8000478 + 80009e0: 0801c050 .word 0x0801c050 + 80009e4: 0801c070 .word 0x0801c070 + 80009e8: 40022000 .word 0x40022000 + 80009ec: 08010174 .word 0x08010174 + 80009f0: 08000300 .word 0x08000300 + 80009f4: 0800d9e0 .word 0x0800d9e0 + +080009f8 : +// + static inline void +memset4(uint32_t *dest, uint32_t value, uint32_t byte_len) +{ + for(; byte_len; byte_len-=4, dest++) { + *dest = value; + 80009f8: 4a0a ldr r2, [pc, #40] ; (8000a24 ) + for(; byte_len; byte_len-=4, dest++) { + 80009fa: 490b ldr r1, [pc, #44] ; (8000a28 ) + +// wipe_all_sram() +// + void +wipe_all_sram(void) +{ + 80009fc: f04f 5300 mov.w r3, #536870912 ; 0x20000000 + *dest = value; + 8000a00: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a04: 428b cmp r3, r1 + 8000a06: d1fb bne.n 8000a00 + 8000a08: 4908 ldr r1, [pc, #32] ; (8000a2c ) + 8000a0a: f04f 5380 mov.w r3, #268435456 ; 0x10000000 + *dest = value; + 8000a0e: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a12: 428b cmp r3, r1 + 8000a14: d1fb bne.n 8000a0e + 8000a16: 4b06 ldr r3, [pc, #24] ; (8000a30 ) + 8000a18: 4906 ldr r1, [pc, #24] ; (8000a34 ) + *dest = value; + 8000a1a: f843 2b04 str.w r2, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 8000a1e: 428b cmp r3, r1 + 8000a20: d1fb bne.n 8000a1a + STATIC_ASSERT((SRAM3_BASE + SRAM3_SIZE) - BL_SRAM_BASE == 8192); + + memset4((void *)SRAM1_BASE, noise, SRAM1_SIZE_MAX); + memset4((void *)SRAM2_BASE, noise, SRAM2_SIZE); + memset4((void *)SRAM3_BASE, noise, SRAM3_SIZE - (BL_SRAM_BASE - SRAM3_BASE)); +} + 8000a22: 4770 bx lr + 8000a24: deadbeef .word 0xdeadbeef + 8000a28: 20030000 .word 0x20030000 + 8000a2c: 10010000 .word 0x10010000 + 8000a30: 20040000 .word 0x20040000 + 8000a34: 20042000 .word 0x20042000 + +08000a38 : + +// fatal_error(const char *msg) +// + void __attribute__((noreturn)) +fatal_error(const char *msgvoid) +{ + 8000a38: b508 push {r3, lr} + oled_setup(); + 8000a3a: f000 f99b bl 8000d74 + oled_show(screen_fatal); + 8000a3e: 4802 ldr r0, [pc, #8] ; (8000a48 ) + 8000a40: f000 fb18 bl 8001074 + BREAKPOINT; +#endif + + // Maybe should do a reset after a delay, like with + // the watchdog timer or something. + LOCKUP_FOREVER(); + 8000a44: f003 f918 bl 8003c78 + 8000a48: 0800e58d .word 0x0800e58d + +08000a4c : + +// fatal_mitm() +// + void __attribute__((noreturn)) +fatal_mitm(void) +{ + 8000a4c: b508 push {r3, lr} + oled_setup(); + 8000a4e: f000 f991 bl 8000d74 + oled_show(screen_mitm); + 8000a52: 4803 ldr r0, [pc, #12] ; (8000a60 ) + 8000a54: f000 fb0e bl 8001074 + +#ifdef RELEASE + wipe_all_sram(); + 8000a58: f7ff ffce bl 80009f8 +#endif + + LOCKUP_FOREVER(); + 8000a5c: f003 f90c bl 8003c78 + 8000a60: 0800e717 .word 0x0800e717 + +08000a64 : + +// enter_dfu() +// + void __attribute__((noreturn)) +enter_dfu(void) +{ + 8000a64: b507 push {r0, r1, r2, lr} + puts("enter_dfu()"); + 8000a66: 481f ldr r0, [pc, #124] ; (8000ae4 ) + 8000a68: f004 fafe bl 8005068 + + // clear the green light, if set + ae_setup(); + 8000a6c: f002 f870 bl 8002b50 + ae_set_gpio(0); + 8000a70: 2000 movs r0, #0 + 8000a72: f002 fdef bl 8003654 + + // Reset huge parts of the chip + __HAL_RCC_APB1_FORCE_RESET(); + 8000a76: 4b1c ldr r3, [pc, #112] ; (8000ae8 ) + 8000a78: f04f 31ff mov.w r1, #4294967295 ; 0xffffffff + __HAL_RCC_APB1_RELEASE_RESET(); + 8000a7c: 2200 movs r2, #0 + __HAL_RCC_APB1_FORCE_RESET(); + 8000a7e: 6399 str r1, [r3, #56] ; 0x38 + 8000a80: 63d9 str r1, [r3, #60] ; 0x3c + __HAL_RCC_APB1_RELEASE_RESET(); + 8000a82: 639a str r2, [r3, #56] ; 0x38 + 8000a84: 63da str r2, [r3, #60] ; 0x3c + + __HAL_RCC_APB2_FORCE_RESET(); + 8000a86: 6419 str r1, [r3, #64] ; 0x40 + __HAL_RCC_APB2_RELEASE_RESET(); + 8000a88: 641a str r2, [r3, #64] ; 0x40 + + __HAL_RCC_AHB1_FORCE_RESET(); + 8000a8a: 6299 str r1, [r3, #40] ; 0x28 + __HAL_RCC_AHB1_RELEASE_RESET(); + 8000a8c: 629a str r2, [r3, #40] ; 0x28 + // But not this; it borks things. + __HAL_RCC_AHB2_FORCE_RESET(); + __HAL_RCC_AHB2_RELEASE_RESET(); +#endif + + __HAL_RCC_AHB3_FORCE_RESET(); + 8000a8e: 6319 str r1, [r3, #48] ; 0x30 + __HAL_RCC_AHB3_RELEASE_RESET(); + 8000a90: 631a str r2, [r3, #48] ; 0x30 + + __HAL_FIREWALL_PREARM_ENABLE(); + 8000a92: f5a3 4374 sub.w r3, r3, #62464 ; 0xf400 + 8000a96: 6a1a ldr r2, [r3, #32] + 8000a98: f042 0201 orr.w r2, r2, #1 + 8000a9c: 621a str r2, [r3, #32] + 8000a9e: 6a1b ldr r3, [r3, #32] + 8000aa0: f003 0301 and.w r3, r3, #1 + 8000aa4: 9301 str r3, [sp, #4] + 8000aa6: 9b01 ldr r3, [sp, #4] + + // Wipe all of memory SRAM, just in case + // there is some way to trick us into DFU + // after sensitive content in place. + wipe_all_sram(); + 8000aa8: f7ff ffa6 bl 80009f8 + rng_delay(); + 8000aac: f001 ff2c bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000ab0: 4b0e ldr r3, [pc, #56] ; (8000aec ) + 8000ab2: 6a1b ldr r3, [r3, #32] + 8000ab4: b2db uxtb r3, r3 + + if(flash_is_security_level2()) { + 8000ab6: 2bcc cmp r3, #204 ; 0xcc + 8000ab8: d101 bne.n 8000abe + // cannot do DFU in RDP=2, so just die. Helps to preserve screen + LOCKUP_FOREVER(); + 8000aba: f003 f8dd bl 8003c78 + } + + // Reset clocks. + HAL_RCC_DeInit(); + 8000abe: f007 ff5f bl 8008980 + + // move system ROM into 0x0 + __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); + 8000ac2: 4a0b ldr r2, [pc, #44] ; (8000af0 ) + 8000ac4: 6813 ldr r3, [r2, #0] + 8000ac6: f023 0307 bic.w r3, r3, #7 + 8000aca: f043 0301 orr.w r3, r3, #1 + 8000ace: 6013 str r3, [r2, #0] + + // need this here?! + asm("nop; nop; nop; nop;"); + 8000ad0: bf00 nop + 8000ad2: bf00 nop + 8000ad4: bf00 nop + 8000ad6: bf00 nop + + // simulate a reset vector + __ASM volatile ("movs r0, #0\n" + 8000ad8: 2000 movs r0, #0 + 8000ada: 6803 ldr r3, [r0, #0] + 8000adc: f383 8808 msr MSP, r3 + 8000ae0: 6843 ldr r3, [r0, #4] + 8000ae2: 4798 blx r3 + "ldr r3, [r0, #4]\n" + "blx r3" + : : : "r0", "r3"); // also SP + + // NOT-REACHED. + __builtin_unreachable(); + 8000ae4: 0800d9e3 .word 0x0800d9e3 + 8000ae8: 40021000 .word 0x40021000 + 8000aec: 40022000 .word 0x40022000 + 8000af0: 40010000 .word 0x40010000 + +08000af4 : +{ + 8000af4: b510 push {r4, lr} + system_init0(); + 8000af6: f001 fa77 bl 8001fe8 + clocks_setup(); + 8000afa: f001 fa97 bl 800202c + rng_setup(); // needs to be super early + 8000afe: f001 fec1 bl 8002884 + rng_delay(); + 8000b02: f001 ff01 bl 8002908 + if(!check_all_ones(rom_secrets->bag_number, sizeof(rom_secrets->bag_number)) + 8000b06: 4838 ldr r0, [pc, #224] ; (8000be8 ) + 8000b08: 2120 movs r1, #32 + 8000b0a: f001 fe7f bl 800280c + 8000b0e: b948 cbnz r0, 8000b24 + rng_delay(); + 8000b10: f001 fefa bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000b14: 4b35 ldr r3, [pc, #212] ; (8000bec ) + 8000b16: 6a1b ldr r3, [r3, #32] + 8000b18: b2db uxtb r3, r3 + && !flash_is_security_level2() + 8000b1a: 2bcc cmp r3, #204 ; 0xcc + 8000b1c: d002 beq.n 8000b24 + flash_lockdown_hard(OB_RDP_LEVEL_2); + 8000b1e: 20cc movs r0, #204 ; 0xcc + 8000b20: f001 fcf8 bl 8002514 + gpio_setup(); + 8000b24: f002 ffbe bl 8003aa4 + uint32_t reset_reason = RCC->CSR; + 8000b28: 4c31 ldr r4, [pc, #196] ; (8000bf0 ) + console_setup(); + 8000b2a: f004 f9c3 bl 8004eb4 + puts2(BOOT_BANNER); + 8000b2e: 4831 ldr r0, [pc, #196] ; (8000bf4 ) + 8000b30: f004 fa0c bl 8004f4c + puts(version_string); + 8000b34: 4830 ldr r0, [pc, #192] ; (8000bf8 ) + 8000b36: f004 fa97 bl 8005068 + uint32_t reset_reason = RCC->CSR; + 8000b3a: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + if(reset_reason & RCC_CSR_FWRSTF) { + 8000b3e: 01db lsls r3, r3, #7 + 8000b40: d502 bpl.n 8000b48 + puts(">FIREWALLED<"); + 8000b42: 482e ldr r0, [pc, #184] ; (8000bfc ) + 8000b44: f004 fa90 bl 8005068 + SET_BIT(RCC->CSR, RCC_CSR_RMVF); + 8000b48: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8000b4c: f443 0300 orr.w r3, r3, #8388608 ; 0x800000 + 8000b50: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) { + 8000b54: 4c2a ldr r4, [pc, #168] ; (8000c00 ) + pin_setup0(); + 8000b56: f003 fa9b bl 8004090 + rng_delay(); + 8000b5a: f001 fed5 bl 8002908 + lcd_full_setup(); + 8000b5e: f000 f981 bl 8000e64 + if(memcmp(dfu_flag->magic, REBOOT_TO_DFU, sizeof(dfu_flag->magic)) == 0) { + 8000b62: 4928 ldr r1, [pc, #160] ; (8000c04 ) + 8000b64: 2208 movs r2, #8 + 8000b66: 4620 mov r0, r4 + 8000b68: f00c febe bl 800d8e8 + 8000b6c: b928 cbnz r0, 8000b7a + dfu_flag->magic[0] = 0; + 8000b6e: 7020 strb r0, [r4, #0] + oled_show(dfu_flag->screen); + 8000b70: 68a0 ldr r0, [r4, #8] + 8000b72: f000 fa7f bl 8001074 + enter_dfu(); + 8000b76: f7ff ff75 bl 8000a64 + rng_delay(); + 8000b7a: f001 fec5 bl 8002908 + oled_show_progress(screen_verify, 0); + 8000b7e: 2100 movs r1, #0 + 8000b80: 4821 ldr r0, [pc, #132] ; (8000c08 ) + 8000b82: f000 faf1 bl 8001168 + wipe_all_sram(); + 8000b86: f7ff ff37 bl 80009f8 + ae_setup(); + 8000b8a: f001 ffe1 bl 8002b50 + ae_set_gpio(0); // turn light red + 8000b8e: 2000 movs r0, #0 + 8000b90: f002 fd60 bl 8003654 + se2_setup(); + 8000b94: f007 fa0c bl 8007fb0 + se2_probe(); + 8000b98: f006 ff90 bl 8007abc + flash_setup(); + 8000b9c: f001 fbee bl 800237c + psram_setup(); + 8000ba0: f004 fa9a bl 80050d8 + if(ae_pair_unlock() != 0) { + 8000ba4: f002 f9ca bl 8002f3c + 8000ba8: b138 cbz r0, 8000bba + oled_show(screen_brick); + 8000baa: 4818 ldr r0, [pc, #96] ; (8000c0c ) + 8000bac: f000 fa62 bl 8001074 + puts("pair-bricked"); + 8000bb0: 4817 ldr r0, [pc, #92] ; (8000c10 ) + 8000bb2: f004 fa59 bl 8005068 + LOCKUP_FOREVER(); + 8000bb6: f003 f85f bl 8003c78 + puts2("Verify: "); + 8000bba: 4816 ldr r0, [pc, #88] ; (8000c14 ) + 8000bbc: f004 f9c6 bl 8004f4c + bool main_ok = verify_firmware(); + 8000bc0: f001 f996 bl 8001ef0 + if(main_ok) { + 8000bc4: b120 cbz r0, 8000bd0 +} + 8000bc6: e8bd 4010 ldmia.w sp!, {r4, lr} + oled_show(screen_blankish); + 8000bca: 4813 ldr r0, [pc, #76] ; (8000c18 ) + 8000bcc: f000 ba52 b.w 8001074 + psram_recover_firmware(); + 8000bd0: f004 fbd0 bl 8005374 + rng_delay(); + 8000bd4: f001 fe98 bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8000bd8: 4b04 ldr r3, [pc, #16] ; (8000bec ) + 8000bda: 6a1b ldr r3, [r3, #32] + 8000bdc: b2db uxtb r3, r3 + if(!flash_is_security_level2()) { + 8000bde: 2bcc cmp r3, #204 ; 0xcc + 8000be0: d1c9 bne.n 8000b76 + while(1) sdcard_recovery(); + 8000be2: f004 fd91 bl 8005708 + 8000be6: e7fc b.n 8000be2 + 8000be8: 0801c050 .word 0x0801c050 + 8000bec: 40022000 .word 0x40022000 + 8000bf0: 40021000 .word 0x40021000 + 8000bf4: 0800d9ef .word 0x0800d9ef + 8000bf8: 0801079c .word 0x0801079c + 8000bfc: 0800da02 .word 0x0800da02 + 8000c00: 20008000 .word 0x20008000 + 8000c04: 0800d9d7 .word 0x0800d9d7 + 8000c08: 0801004d .word 0x0801004d + 8000c0c: 0800da61 .word 0x0800da61 + 8000c10: 0800da0f .word 0x0800da0f + 8000c14: 0800da1c .word 0x0800da1c + 8000c18: 0800da48 .word 0x0800da48 + +08000c1c : + +// lcd_write_data() +// + static void +lcd_write_data(int len, const uint8_t *pixels) +{ + 8000c1c: b538 push {r3, r4, r5, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c1e: 2201 movs r2, #1 +{ + 8000c20: 4605 mov r5, r0 + 8000c22: 460c mov r4, r1 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c24: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c28: 2110 movs r1, #16 + 8000c2a: f000 fc5d bl 80014e8 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 1); + 8000c2e: 2201 movs r2, #1 + 8000c30: f44f 7180 mov.w r1, #256 ; 0x100 + 8000c34: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c38: f000 fc56 bl 80014e8 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000c3c: 2200 movs r2, #0 + 8000c3e: 2110 movs r1, #16 + 8000c40: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c44: f000 fc50 bl 80014e8 + HAL_SPI_Transmit(&spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); + 8000c48: b2aa uxth r2, r5 + 8000c4a: 4621 mov r1, r4 + 8000c4c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8000c50: 4805 ldr r0, [pc, #20] ; (8000c68 ) + 8000c52: f000 fce9 bl 8001628 + + write_bytes(len, pixels); + + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); +} + 8000c56: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000c5a: 2201 movs r2, #1 + 8000c5c: 2110 movs r1, #16 + 8000c5e: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000c62: f000 bc41 b.w 80014e8 + 8000c66: bf00 nop + 8000c68: 2009e158 .word 0x2009e158 + +08000c6c : + +// lcd_write_data1() +// + static void +lcd_write_data1(uint8_t data) +{ + 8000c6c: b507 push {r0, r1, r2, lr} + 8000c6e: f88d 0007 strb.w r0, [sp, #7] + lcd_write_data(1, &data); + 8000c72: f10d 0107 add.w r1, sp, #7 + 8000c76: 2001 movs r0, #1 + 8000c78: f7ff ffd0 bl 8000c1c +} + 8000c7c: b003 add sp, #12 + 8000c7e: f85d fb04 ldr.w pc, [sp], #4 + ... + +08000c84 : +static inline void wait_vsync(void) { + 8000c84: b538 push {r3, r4, r5, lr} + 8000c86: 4c08 ldr r4, [pc, #32] ; (8000ca8 ) + if(HAL_GPIO_ReadPin(GPIOB, TEAR_PIN) != 0) { + 8000c88: 4d08 ldr r5, [pc, #32] ; (8000cac ) + 8000c8a: f44f 6100 mov.w r1, #2048 ; 0x800 + 8000c8e: 4628 mov r0, r5 + 8000c90: f000 fc24 bl 80014dc + 8000c94: b930 cbnz r0, 8000ca4 + for(; timeout; timeout--) { + 8000c96: 3c01 subs r4, #1 + 8000c98: d1f7 bne.n 8000c8a +} + 8000c9a: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + puts("TEAR timeout"); + 8000c9e: 4804 ldr r0, [pc, #16] ; (8000cb0 ) + 8000ca0: f004 b9e2 b.w 8005068 +} + 8000ca4: bd38 pop {r3, r4, r5, pc} + 8000ca6: bf00 nop + 8000ca8: 000f4240 .word 0x000f4240 + 8000cac: 48000400 .word 0x48000400 + 8000cb0: 0800da25 .word 0x0800da25 + +08000cb4 : +{ + 8000cb4: b507 push {r0, r1, r2, lr} + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000cb6: 2201 movs r2, #1 +{ + 8000cb8: f88d 0007 strb.w r0, [sp, #7] + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000cbc: 2110 movs r1, #16 + 8000cbe: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cc2: f000 fc11 bl 80014e8 + HAL_GPIO_WritePin(GPIOA, DC_PIN, 0); + 8000cc6: 2200 movs r2, #0 + 8000cc8: f44f 7180 mov.w r1, #256 ; 0x100 + 8000ccc: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cd0: f000 fc0a bl 80014e8 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 0); + 8000cd4: 2200 movs r2, #0 + 8000cd6: 2110 movs r1, #16 + 8000cd8: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cdc: f000 fc04 bl 80014e8 + HAL_SPI_Transmit(&spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); + 8000ce0: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8000ce4: f10d 0107 add.w r1, sp, #7 + 8000ce8: 2201 movs r2, #1 + 8000cea: 4806 ldr r0, [pc, #24] ; (8000d04 ) + 8000cec: f000 fc9c bl 8001628 + HAL_GPIO_WritePin(GPIOA, CS_PIN, 1); + 8000cf0: 2201 movs r2, #1 + 8000cf2: 2110 movs r1, #16 + 8000cf4: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000cf8: f000 fbf6 bl 80014e8 +} + 8000cfc: b003 add sp, #12 + 8000cfe: f85d fb04 ldr.w pc, [sp], #4 + 8000d02: bf00 nop + 8000d04: 2009e158 .word 0x2009e158 + +08000d08 : +{ + 8000d08: b537 push {r0, r1, r2, r4, r5, lr} + 8000d0a: 460d mov r5, r1 + 8000d0c: 4614 mov r4, r2 + lcd_write_cmd(cmd); + 8000d0e: f7ff ffd1 bl 8000cb4 + uint8_t d[4] = { (a>>8), a&0xff, (b>>8), b&0xff }; + 8000d12: 0a2b lsrs r3, r5, #8 + 8000d14: f88d 3004 strb.w r3, [sp, #4] + lcd_write_data(4, d); + 8000d18: a901 add r1, sp, #4 + uint8_t d[4] = { (a>>8), a&0xff, (b>>8), b&0xff }; + 8000d1a: 0a23 lsrs r3, r4, #8 + lcd_write_data(4, d); + 8000d1c: 2004 movs r0, #4 + uint8_t d[4] = { (a>>8), a&0xff, (b>>8), b&0xff }; + 8000d1e: f88d 5005 strb.w r5, [sp, #5] + 8000d22: f88d 3006 strb.w r3, [sp, #6] + 8000d26: f88d 4007 strb.w r4, [sp, #7] + lcd_write_data(4, d); + 8000d2a: f7ff ff77 bl 8000c1c +} + 8000d2e: b003 add sp, #12 + 8000d30: bd30 pop {r4, r5, pc} + ... + +08000d34 : +// +// Just setup SPI, do not reset display, etc. +// + void +lcd_spi_setup(void) +{ + 8000d34: b538 push {r3, r4, r5, lr} +#ifndef DISABLE_LCD + // might already be setup + if(spi_port.Instance == SPI1) return; + 8000d36: 4c0d ldr r4, [pc, #52] ; (8000d6c ) + 8000d38: 4d0d ldr r5, [pc, #52] ; (8000d70 ) + 8000d3a: 6823 ldr r3, [r4, #0] + 8000d3c: 42ab cmp r3, r5 + 8000d3e: d014 beq.n 8000d6a + + memset(&spi_port, 0, sizeof(spi_port)); + 8000d40: f104 0008 add.w r0, r4, #8 + 8000d44: 225c movs r2, #92 ; 0x5c + 8000d46: 2100 movs r1, #0 + 8000d48: f00c fdec bl 800d924 + + spi_port.Instance = SPI1; + + // see SPI_InitTypeDef + spi_port.Init.Mode = SPI_MODE_MASTER; + 8000d4c: f44f 7382 mov.w r3, #260 ; 0x104 + 8000d50: 6063 str r3, [r4, #4] + spi_port.Init.Direction = SPI_DIRECTION_2LINES; + spi_port.Init.DataSize = SPI_DATASIZE_8BIT; + 8000d52: f44f 63e0 mov.w r3, #1792 ; 0x700 + 8000d56: 60e3 str r3, [r4, #12] + spi_port.Init.CLKPolarity = SPI_POLARITY_LOW; + spi_port.Init.CLKPhase = SPI_PHASE_1EDGE; + spi_port.Init.NSS = SPI_NSS_SOFT; + 8000d58: f44f 7300 mov.w r3, #512 ; 0x200 + spi_port.Instance = SPI1; + 8000d5c: 6025 str r5, [r4, #0] + spi_port.Init.NSS = SPI_NSS_SOFT; + 8000d5e: 61a3 str r3, [r4, #24] + spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; + spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB; + spi_port.Init.TIMode = SPI_TIMODE_DISABLED; + spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; + + HAL_SPI_Init(&spi_port); + 8000d60: 4620 mov r0, r4 +#endif +} + 8000d62: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + HAL_SPI_Init(&spi_port); + 8000d66: f000 bc01 b.w 800156c +} + 8000d6a: bd38 pop {r3, r4, r5, pc} + 8000d6c: 2009e158 .word 0x2009e158 + 8000d70: 40013000 .word 0x40013000 + +08000d74 : +// +// Ok to call this lots. +// + void +oled_setup(void) +{ + 8000d74: b530 push {r4, r5, lr} + puts("lcd disabled");return; // disable so I can use MCO +#endif + + static uint32_t inited; + + if(inited == 0x238a572F) { + 8000d76: 4b23 ldr r3, [pc, #140] ; (8000e04 ) + 8000d78: 4a23 ldr r2, [pc, #140] ; (8000e08 ) + 8000d7a: 6819 ldr r1, [r3, #0] + 8000d7c: 4291 cmp r1, r2 +{ + 8000d7e: b087 sub sp, #28 + if(inited == 0x238a572F) { + 8000d80: d03e beq.n 8000e00 + return; + } + inited = 0x238a572F; + 8000d82: 601a str r2, [r3, #0] + + // enable some internal clocks + __HAL_RCC_SPI1_CLK_ENABLE(); + 8000d84: 4b21 ldr r3, [pc, #132] ; (8000e0c ) + + // take over from GPU + // - can be issue when coming in via callgate from mpy which might have been showing menu + HAL_GPIO_WritePin(GPIOE, PIN_G_CTRL, 1); // set G_CTRL pin -- we have control + 8000d86: 4822 ldr r0, [pc, #136] ; (8000e10 ) + __HAL_RCC_SPI1_CLK_ENABLE(); + 8000d88: 6e1a ldr r2, [r3, #96] ; 0x60 + + // .. wait for GPU to finish it's work + // - might take 16+ms because waiting for next vsync, etc + for(int i=0; i<100; i++) { + if(HAL_GPIO_ReadPin(GPIOE, PIN_G_BUSY) == 0) break; + 8000d8a: 4d21 ldr r5, [pc, #132] ; (8000e10 ) + __HAL_RCC_SPI1_CLK_ENABLE(); + 8000d8c: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8000d90: 661a str r2, [r3, #96] ; 0x60 + 8000d92: 6e1b ldr r3, [r3, #96] ; 0x60 + 8000d94: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 8000d98: 9300 str r3, [sp, #0] + HAL_GPIO_WritePin(GPIOE, PIN_G_CTRL, 1); // set G_CTRL pin -- we have control + 8000d9a: 2201 movs r2, #1 + 8000d9c: 2120 movs r1, #32 + __HAL_RCC_SPI1_CLK_ENABLE(); + 8000d9e: 9b00 ldr r3, [sp, #0] + HAL_GPIO_WritePin(GPIOE, PIN_G_CTRL, 1); // set G_CTRL pin -- we have control + 8000da0: f000 fba2 bl 80014e8 + 8000da4: 2464 movs r4, #100 ; 0x64 + if(HAL_GPIO_ReadPin(GPIOE, PIN_G_BUSY) == 0) break; + 8000da6: 2104 movs r1, #4 + 8000da8: 4628 mov r0, r5 + 8000daa: f000 fb97 bl 80014dc + 8000dae: b120 cbz r0, 8000dba + delay_ms(1); + 8000db0: 2001 movs r0, #1 + 8000db2: f002 fe67 bl 8003a84 + for(int i=0; i<100; i++) { + 8000db6: 3c01 subs r4, #1 + 8000db8: d1f5 bne.n 8000da6 + } + + // Simple pins + // - must be opendrain to allow GPU to share + GPIO_InitTypeDef setup = { + 8000dba: 4d16 ldr r5, [pc, #88] ; (8000e14 ) + 8000dbc: cd0f ldmia r5!, {r0, r1, r2, r3} + 8000dbe: ac01 add r4, sp, #4 + 8000dc0: c40f stmia r4!, {r0, r1, r2, r3} + 8000dc2: 682b ldr r3, [r5, #0] + 8000dc4: 6023 str r3, [r4, #0] + .Mode = GPIO_MODE_OUTPUT_OD, + .Pull = GPIO_PULLUP, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = 0, + }; + HAL_GPIO_Init(GPIOA, &setup); + 8000dc6: a901 add r1, sp, #4 + 8000dc8: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000dcc: f000 fa12 bl 80011f4 + + // starting values + HAL_GPIO_WritePin(GPIOA, RESET_PIN | CS_PIN | DC_PIN, 1); + 8000dd0: 2201 movs r2, #1 + 8000dd2: f44f 71a8 mov.w r1, #336 ; 0x150 + 8000dd6: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000dda: f000 fb85 bl 80014e8 + + // SPI pins (same but with AF) + setup.Pin = SPI_SCK | SPI_MOSI; + 8000dde: 23a0 movs r3, #160 ; 0xa0 + 8000de0: 9301 str r3, [sp, #4] + setup.Alternate = GPIO_AF5_SPI1; + 8000de2: 2305 movs r3, #5 + 8000de4: 9305 str r3, [sp, #20] + setup.Mode = GPIO_MODE_AF_PP; + 8000de6: 2302 movs r3, #2 + setup.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + HAL_GPIO_Init(GPIOA, &setup); + 8000de8: a901 add r1, sp, #4 + 8000dea: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + setup.Mode = GPIO_MODE_AF_PP; + 8000dee: 9302 str r3, [sp, #8] + setup.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + 8000df0: 2303 movs r3, #3 + 8000df2: 9304 str r3, [sp, #16] + HAL_GPIO_Init(GPIOA, &setup); + 8000df4: f000 f9fe bl 80011f4 + + // config SPI port + lcd_spi_setup(); + 8000df8: f7ff ff9c bl 8000d34 + rng_delay(); + 8000dfc: f001 fd84 bl 8002908 +} + 8000e00: b007 add sp, #28 + 8000e02: bd30 pop {r4, r5, pc} + 8000e04: 2009e150 .word 0x2009e150 + 8000e08: 238a572f .word 0x238a572f + 8000e0c: 40021000 .word 0x40021000 + 8000e10: 48001000 .word 0x48001000 + 8000e14: 0800da34 .word 0x0800da34 + +08000e18 : + +// lcd_fill_solid() +// + void +lcd_fill_solid(uint16_t pattern) +{ + 8000e18: b5b0 push {r4, r5, r7, lr} + // note, MADCTL MV/MX/MY setting causes row vs. col swap here + lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x) + 8000e1a: f240 123f movw r2, #319 ; 0x13f +{ + 8000e1e: af00 add r7, sp, #0 + lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x) + 8000e20: 2100 movs r1, #0 +{ + 8000e22: 4604 mov r4, r0 + lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x) + 8000e24: 202a movs r0, #42 ; 0x2a + 8000e26: f7ff ff6f bl 8000d08 + lcd_write_cmd4(0x2b, 0, LCD_HEIGHT-1); // RASET - Row address set range (y) + 8000e2a: 22ef movs r2, #239 ; 0xef + 8000e2c: 2100 movs r1, #0 + 8000e2e: 202b movs r0, #43 ; 0x2b + 8000e30: f7ff ff6a bl 8000d08 + + lcd_write_cmd(0x2c); // RAMWR - memory write + 8000e34: 202c movs r0, #44 ; 0x2c + 8000e36: f7ff ff3d bl 8000cb4 + + uint16_t row[LCD_WIDTH]; + 8000e3a: f5ad 7d20 sub.w sp, sp, #640 ; 0x280 + 8000e3e: 466d mov r5, sp + for(; byte_len; byte_len-=2, dest++) { + 8000e40: f505 7220 add.w r2, r5, #640 ; 0x280 + uint16_t row[LCD_WIDTH]; + 8000e44: 462b mov r3, r5 + *dest = value; + 8000e46: f823 4b02 strh.w r4, [r3], #2 + for(; byte_len; byte_len-=2, dest++) { + 8000e4a: 429a cmp r2, r3 + 8000e4c: d1fb bne.n 8000e46 + 8000e4e: 24f0 movs r4, #240 ; 0xf0 + memset2(row, pattern, sizeof(row)); + + for(int y=0; y + for(int y=0; y + } +} + 8000e5e: 46bd mov sp, r7 + 8000e60: bdb0 pop {r4, r5, r7, pc} + ... + +08000e64 : +{ + 8000e64: b508 push {r3, lr} + oled_setup(); + 8000e66: f7ff ff85 bl 8000d74 + lcd_write_cmd(0x28); // DISPOFF + 8000e6a: 2028 movs r0, #40 ; 0x28 + 8000e6c: f7ff ff22 bl 8000cb4 + lcd_write_cmd(0x36); // MADCTL: memory addr ctrl, page 215 + 8000e70: 2036 movs r0, #54 ; 0x36 + 8000e72: f7ff ff1f bl 8000cb4 + lcd_write_data1(0x60); // MV=1 => horz mode, first byte=top-left corner, RGB order + 8000e76: 2060 movs r0, #96 ; 0x60 + 8000e78: f7ff fef8 bl 8000c6c + lcd_fill_solid(COL_BLACK); // works only on second+ reboots/resets + 8000e7c: 2000 movs r0, #0 + 8000e7e: f7ff ffcb bl 8000e18 + delay_ms(1); + 8000e82: 2001 movs r0, #1 + 8000e84: f002 fdfe bl 8003a84 + HAL_GPIO_WritePin(GPIOA, RESET_PIN, 0); + 8000e88: 2200 movs r2, #0 + 8000e8a: 2140 movs r1, #64 ; 0x40 + 8000e8c: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000e90: f000 fb2a bl 80014e8 + delay_ms(1); + 8000e94: 2001 movs r0, #1 + 8000e96: f002 fdf5 bl 8003a84 + HAL_GPIO_WritePin(GPIOA, RESET_PIN, 1); + 8000e9a: 2201 movs r2, #1 + 8000e9c: 2140 movs r1, #64 ; 0x40 + 8000e9e: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8000ea2: f000 fb21 bl 80014e8 + delay_ms(120); // 120ms - reset recovery time + 8000ea6: 2078 movs r0, #120 ; 0x78 + 8000ea8: f002 fdec bl 8003a84 + lcd_write_cmd(0x11); // SLPOUT: Sleep Out => turn off sleep mode + 8000eac: 2011 movs r0, #17 + 8000eae: f7ff ff01 bl 8000cb4 + delay_ms(5); // 5ms - wake up time + 8000eb2: 2005 movs r0, #5 + 8000eb4: f002 fde6 bl 8003a84 + lcd_write_cmd(0x36); // MADCTL: memory addr ctrl, page 215 + 8000eb8: 2036 movs r0, #54 ; 0x36 + 8000eba: f7ff fefb bl 8000cb4 + lcd_write_data1(0x60); // MV=1 => horz mode, first byte=top-left corner, RGB order + 8000ebe: 2060 movs r0, #96 ; 0x60 + 8000ec0: f7ff fed4 bl 8000c6c + lcd_write_cmd(0x3a); // COLMOD: pixel format + 8000ec4: 203a movs r0, #58 ; 0x3a + 8000ec6: f7ff fef5 bl 8000cb4 + lcd_write_data1(0x05); // => 16bit/pixel + 8000eca: 2005 movs r0, #5 + 8000ecc: f7ff fece bl 8000c6c + lcd_write_cmd(0xb2); // PORCTRL - porch control + 8000ed0: 20b2 movs r0, #178 ; 0xb2 + 8000ed2: f7ff feef bl 8000cb4 + lcd_write_data1(0x0c); + 8000ed6: 200c movs r0, #12 + 8000ed8: f7ff fec8 bl 8000c6c + lcd_write_data1(0x0c); + 8000edc: 200c movs r0, #12 + 8000ede: f7ff fec5 bl 8000c6c + lcd_write_data1(0x00); + 8000ee2: 2000 movs r0, #0 + 8000ee4: f7ff fec2 bl 8000c6c + lcd_write_data1(0x33); + 8000ee8: 2033 movs r0, #51 ; 0x33 + 8000eea: f7ff febf bl 8000c6c + lcd_write_data1(0x33); + 8000eee: 2033 movs r0, #51 ; 0x33 + 8000ef0: f7ff febc bl 8000c6c + lcd_write_cmd(0xb7); + 8000ef4: 20b7 movs r0, #183 ; 0xb7 + 8000ef6: f7ff fedd bl 8000cb4 + lcd_write_data1(0x35); + 8000efa: 2035 movs r0, #53 ; 0x35 + 8000efc: f7ff feb6 bl 8000c6c + lcd_write_cmd(0xbb); // VCOMS + 8000f00: 20bb movs r0, #187 ; 0xbb + 8000f02: f7ff fed7 bl 8000cb4 + lcd_write_data1(0x25); //35 20 + 8000f06: 2025 movs r0, #37 ; 0x25 + 8000f08: f7ff feb0 bl 8000c6c + lcd_write_cmd(0xc0); // LCM + 8000f0c: 20c0 movs r0, #192 ; 0xc0 + 8000f0e: f7ff fed1 bl 8000cb4 + lcd_write_data1(0x2c); + 8000f12: 202c movs r0, #44 ; 0x2c + 8000f14: f7ff feaa bl 8000c6c + lcd_write_cmd(0xc2); // VDVVRHEN + 8000f18: 20c2 movs r0, #194 ; 0xc2 + 8000f1a: f7ff fecb bl 8000cb4 + lcd_write_data1(0x01); + 8000f1e: 2001 movs r0, #1 + 8000f20: f7ff fea4 bl 8000c6c + lcd_write_cmd(0xc3); // VRHS + 8000f24: 20c3 movs r0, #195 ; 0xc3 + 8000f26: f7ff fec5 bl 8000cb4 + lcd_write_data1(0x13); //0e + 8000f2a: 2013 movs r0, #19 + 8000f2c: f7ff fe9e bl 8000c6c + lcd_write_cmd(0xc4); // VDVSET + 8000f30: 20c4 movs r0, #196 ; 0xc4 + 8000f32: f7ff febf bl 8000cb4 + lcd_write_data1(0x20); + 8000f36: 2020 movs r0, #32 + 8000f38: f7ff fe98 bl 8000c6c + lcd_write_cmd(0xc6); // FRCTR2 + 8000f3c: 20c6 movs r0, #198 ; 0xc6 + 8000f3e: f7ff feb9 bl 8000cb4 + lcd_write_data1(0x0f); + 8000f42: 200f movs r0, #15 + 8000f44: f7ff fe92 bl 8000c6c + lcd_write_cmd(0xd0); // PWCTRL1 + 8000f48: 20d0 movs r0, #208 ; 0xd0 + 8000f4a: f7ff feb3 bl 8000cb4 + lcd_write_data1(0xa4); + 8000f4e: 20a4 movs r0, #164 ; 0xa4 + 8000f50: f7ff fe8c bl 8000c6c + lcd_write_data1(0xa1); + 8000f54: 20a1 movs r0, #161 ; 0xa1 + 8000f56: f7ff fe89 bl 8000c6c + lcd_write_cmd(0xe0); // PVGAMCTRL + 8000f5a: 20e0 movs r0, #224 ; 0xe0 + 8000f5c: f7ff feaa bl 8000cb4 + lcd_write_data1(0xd0); + 8000f60: 20d0 movs r0, #208 ; 0xd0 + 8000f62: f7ff fe83 bl 8000c6c + lcd_write_data1(0x00); + 8000f66: 2000 movs r0, #0 + 8000f68: f7ff fe80 bl 8000c6c + lcd_write_data1(0x03); + 8000f6c: 2003 movs r0, #3 + 8000f6e: f7ff fe7d bl 8000c6c + lcd_write_data1(0x09); + 8000f72: 2009 movs r0, #9 + 8000f74: f7ff fe7a bl 8000c6c + lcd_write_data1(0x13); + 8000f78: 2013 movs r0, #19 + 8000f7a: f7ff fe77 bl 8000c6c + lcd_write_data1(0x1c); + 8000f7e: 201c movs r0, #28 + 8000f80: f7ff fe74 bl 8000c6c + lcd_write_data1(0x3a); + 8000f84: 203a movs r0, #58 ; 0x3a + 8000f86: f7ff fe71 bl 8000c6c + lcd_write_data1(0x55); + 8000f8a: 2055 movs r0, #85 ; 0x55 + 8000f8c: f7ff fe6e bl 8000c6c + lcd_write_data1(0x48); + 8000f90: 2048 movs r0, #72 ; 0x48 + 8000f92: f7ff fe6b bl 8000c6c + lcd_write_data1(0x18); + 8000f96: 2018 movs r0, #24 + 8000f98: f7ff fe68 bl 8000c6c + lcd_write_data1(0x12); + 8000f9c: 2012 movs r0, #18 + 8000f9e: f7ff fe65 bl 8000c6c + lcd_write_data1(0x0e); + 8000fa2: 200e movs r0, #14 + 8000fa4: f7ff fe62 bl 8000c6c + lcd_write_data1(0x19); + 8000fa8: 2019 movs r0, #25 + 8000faa: f7ff fe5f bl 8000c6c + lcd_write_data1(0x1e); + 8000fae: 201e movs r0, #30 + 8000fb0: f7ff fe5c bl 8000c6c + lcd_write_cmd(0xe1); // NVGAMCTRL + 8000fb4: 20e1 movs r0, #225 ; 0xe1 + 8000fb6: f7ff fe7d bl 8000cb4 + lcd_write_data1(0xd0); + 8000fba: 20d0 movs r0, #208 ; 0xd0 + 8000fbc: f7ff fe56 bl 8000c6c + lcd_write_data1(0x00); + 8000fc0: 2000 movs r0, #0 + 8000fc2: f7ff fe53 bl 8000c6c + lcd_write_data1(0x03); + 8000fc6: 2003 movs r0, #3 + 8000fc8: f7ff fe50 bl 8000c6c + lcd_write_data1(0x09); + 8000fcc: 2009 movs r0, #9 + 8000fce: f7ff fe4d bl 8000c6c + lcd_write_data1(0x05); + 8000fd2: 2005 movs r0, #5 + 8000fd4: f7ff fe4a bl 8000c6c + lcd_write_data1(0x25); + 8000fd8: 2025 movs r0, #37 ; 0x25 + 8000fda: f7ff fe47 bl 8000c6c + lcd_write_data1(0x3a); + 8000fde: 203a movs r0, #58 ; 0x3a + 8000fe0: f7ff fe44 bl 8000c6c + lcd_write_data1(0x55); + 8000fe4: 2055 movs r0, #85 ; 0x55 + 8000fe6: f7ff fe41 bl 8000c6c + lcd_write_data1(0x50); + 8000fea: 2050 movs r0, #80 ; 0x50 + 8000fec: f7ff fe3e bl 8000c6c + lcd_write_data1(0x3d); + 8000ff0: 203d movs r0, #61 ; 0x3d + 8000ff2: f7ff fe3b bl 8000c6c + lcd_write_data1(0x1c); + 8000ff6: 201c movs r0, #28 + 8000ff8: f7ff fe38 bl 8000c6c + lcd_write_data1(0x1d); + 8000ffc: 201d movs r0, #29 + 8000ffe: f7ff fe35 bl 8000c6c + lcd_write_data1(0x1d); + 8001002: 201d movs r0, #29 + 8001004: f7ff fe32 bl 8000c6c + lcd_write_data1(0x1e); + 8001008: 201e movs r0, #30 + 800100a: f7ff fe2f bl 8000c6c + lcd_write_cmd(0x35); // TEON - Tear signal on + 800100e: 2035 movs r0, #53 ; 0x35 + 8001010: f7ff fe50 bl 8000cb4 + lcd_write_data1(0x0); + 8001014: 2000 movs r0, #0 + 8001016: f7ff fe29 bl 8000c6c + lcd_fill_solid(COL_BLACK); + 800101a: 2000 movs r0, #0 + 800101c: f7ff fefc bl 8000e18 + lcd_write_cmd(0x21); // INVON + 8001020: 2021 movs r0, #33 ; 0x21 + 8001022: f7ff fe47 bl 8000cb4 + lcd_write_cmd(0x29); // DISPON + 8001026: 2029 movs r0, #41 ; 0x29 + 8001028: f7ff fe44 bl 8000cb4 + delay_ms(50); + 800102c: 2032 movs r0, #50 ; 0x32 + 800102e: f002 fd29 bl 8003a84 + last_screen = NULL; + 8001032: 4b02 ldr r3, [pc, #8] ; (800103c ) + 8001034: 2200 movs r2, #0 + 8001036: 601a str r2, [r3, #0] +} + 8001038: bd08 pop {r3, pc} + 800103a: bf00 nop + 800103c: 2009e154 .word 0x2009e154 + +08001040 : + +// lcd_write_rows() +// + void +lcd_write_rows(int y, int num_rows, uint16_t *pixels) +{ + 8001040: b570 push {r4, r5, r6, lr} + 8001042: 4606 mov r6, r0 + 8001044: 460c mov r4, r1 + 8001046: 4615 mov r5, r2 + lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x) + 8001048: 2100 movs r1, #0 + 800104a: f240 123f movw r2, #319 ; 0x13f + 800104e: 202a movs r0, #42 ; 0x2a + 8001050: f7ff fe5a bl 8000d08 + lcd_write_cmd4(0x2b, y, LCD_HEIGHT-1); // RASET - Row address set range (y) + 8001054: b2b1 uxth r1, r6 + 8001056: 22ef movs r2, #239 ; 0xef + 8001058: 202b movs r0, #43 ; 0x2b + 800105a: f7ff fe55 bl 8000d08 + + lcd_write_cmd(0x2c); // RAMWR - memory write + 800105e: 202c movs r0, #44 ; 0x2c + 8001060: f7ff fe28 bl 8000cb4 + + lcd_write_data(num_rows * 2 * LCD_WIDTH, (uint8_t *)pixels); + 8001064: f44f 7020 mov.w r0, #640 ; 0x280 + 8001068: 4629 mov r1, r5 + 800106a: 4360 muls r0, r4 +} + 800106c: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + lcd_write_data(num_rows * 2 * LCD_WIDTH, (uint8_t *)pixels); + 8001070: f7ff bdd4 b.w 8000c1c + +08001074 : +// +// Perform simple RLE decompression, and pixel expansion. +// + void +oled_show(const uint8_t *pixels) +{ + 8001074: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8001078: f5ad 6d07 sub.w sp, sp, #2160 ; 0x870 + 800107c: af00 add r7, sp, #0 + 800107e: 4606 mov r6, r0 + oled_setup(); + 8001080: f7ff fe78 bl 8000d74 + + // we are NOT fast enough to send entire screen during the + // vblanking time, so either we show torn stuff, or we flash display off a little + wait_vsync(); + 8001084: f7ff fdfe bl 8000c84 + lcd_write_cmd(0x28); // DISPOFF + 8001088: 2028 movs r0, #40 ; 0x28 + 800108a: f7ff fe13 bl 8000cb4 + + // always full update: minus part we aren't saving "TOP_CROP" at top edge + lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x) + 800108e: f240 123f movw r2, #319 ; 0x13f + 8001092: 2100 movs r1, #0 + 8001094: 202a movs r0, #42 ; 0x2a + 8001096: f7ff fe37 bl 8000d08 + // RASET - Row address set range (y) + lcd_write_cmd4(0x2b, SCREEN_TOP_CROP, LCD_HEIGHT-1); + 800109a: 22ef movs r2, #239 ; 0xef + 800109c: 210f movs r1, #15 + 800109e: 202b movs r0, #43 ; 0x2b + 80010a0: f7ff fe32 bl 8000d08 + lcd_write_cmd(0x2c); // RAMWR - memory write + 80010a4: 202c movs r0, #44 ; 0x2c + 80010a6: f7ff fe05 bl 8000cb4 + + uint8_t buf[127]; + uint16_t expand[sizeof(buf)*8]; + const uint8_t *p = pixels; + + uint16_t blk_row[LCD_WIDTH]; + 80010aa: f5ad 7d20 sub.w sp, sp, #640 ; 0x280 + 80010ae: 46e8 mov r8, sp + *dest = value; + 80010b0: f44f 7220 mov.w r2, #640 ; 0x280 + 80010b4: 2100 movs r1, #0 + 80010b6: 4640 mov r0, r8 + 80010b8: f00c fc34 bl 800d924 + const uint8_t *p = pixels; + 80010bc: 4634 mov r4, r6 + memset2(blk_row, COL_BLACK, sizeof(blk_row)); + + while(1) { + uint8_t hdr = *(p++); + 80010be: 7823 ldrb r3, [r4, #0] + if(!hdr) break; // end marker + 80010c0: 2b00 cmp r3, #0 + 80010c2: d042 beq.n 800114a + + uint8_t len = hdr & 0x7f; + if(hdr & 0x80) { + 80010c4: 061a lsls r2, r3, #24 + uint8_t len = hdr & 0x7f; + 80010c6: f003 057f and.w r5, r3, #127 ; 0x7f + if(hdr & 0x80) { + 80010ca: d514 bpl.n 80010f6 + uint8_t hdr = *(p++); + 80010cc: 3401 adds r4, #1 + // random bytes follow + memcpy(buf, p, len); + 80010ce: 4621 mov r1, r4 + 80010d0: 462a mov r2, r5 + 80010d2: 4638 mov r0, r7 + 80010d4: f00c fc18 bl 800d908 + p += len; + 80010d8: 442c add r4, r5 + p++; + } + + // expand 'len' packed monochrome into BGR565 16-bit data: buf => expand + uint16_t *out = expand; + for(int i=0; i>= 1, out++) { + if(packed & mask) { + *out = COL_FOREGROUND; + } else { + *out = COL_BLACK; + 80010e2: f246 0cfd movw ip, #24829 ; 0x60fd + for(int i=0; i + } + } + } + lcd_write_data(len*8*2, (uint8_t *)expand); + 80010ea: f107 0180 add.w r1, r7, #128 ; 0x80 + 80010ee: 0128 lsls r0, r5, #4 + 80010f0: f7ff fd94 bl 8000c1c + 80010f4: e7e3 b.n 80010be + } else if(hdr == 0x7f) { + 80010f6: 2b7f cmp r3, #127 ; 0x7f + for(int i=0; i + for(int i=0; i + lcd_write_data(2 * LCD_WIDTH, (uint8_t *)blk_row); + 8001106: 4641 mov r1, r8 + 8001108: f44f 7020 mov.w r0, #640 ; 0x280 + 800110c: f7ff fd86 bl 8000c1c + for(int i=0; i + 8001116: e7d2 b.n 80010be + memset(buf, *p, len); + 8001118: 462a mov r2, r5 + 800111a: 4649 mov r1, r9 + 800111c: 4638 mov r0, r7 + 800111e: f00c fc01 bl 800d924 + p++; + 8001122: e7da b.n 80010da + uint8_t packed = buf[i]; + 8001124: f810 ab01 ldrb.w sl, [r0], #1 + for(uint8_t mask = 0x80; mask; mask >>= 1, out++) { + 8001128: 2180 movs r1, #128 ; 0x80 + 800112a: f103 0e10 add.w lr, r3, #16 + *out = COL_BLACK; + 800112e: ea1a 0f01 tst.w sl, r1 + 8001132: bf14 ite ne + 8001134: 46e1 movne r9, ip + 8001136: f04f 0900 moveq.w r9, #0 + 800113a: f823 9b02 strh.w r9, [r3], #2 + for(uint8_t mask = 0x80; mask; mask >>= 1, out++) { + 800113e: 4573 cmp r3, lr + 8001140: ea4f 0151 mov.w r1, r1, lsr #1 + 8001144: d1f3 bne.n 800112e + for(int i=0; i + } + + lcd_write_cmd(0x29); // DISPON + 800114a: 2029 movs r0, #41 ; 0x29 + 800114c: f7ff fdb2 bl 8000cb4 + + last_screen = pixels; + 8001150: 4b04 ldr r3, [pc, #16] ; (8001164 ) + 8001152: 601e str r6, [r3, #0] + rng_delay(); + 8001154: f001 fbd8 bl 8002908 +} + 8001158: f507 6707 add.w r7, r7, #2160 ; 0x870 + 800115c: 46bd mov sp, r7 + 800115e: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 8001162: bf00 nop + 8001164: 2009e154 .word 0x2009e154 + +08001168 : +// +// Perform simple RLE decompression, and add a bar on final screen line. +// + void +oled_show_progress(const uint8_t *pixels, int progress) +{ + 8001168: b5b0 push {r4, r5, r7, lr} + 800116a: af00 add r7, sp, #0 + 800116c: 4605 mov r5, r0 + 800116e: 460c mov r4, r1 + oled_setup(); + 8001170: f7ff fe00 bl 8000d74 + + if(last_screen != pixels) { + 8001174: 4b1d ldr r3, [pc, #116] ; (80011ec ) + 8001176: 681b ldr r3, [r3, #0] + 8001178: 42ab cmp r3, r5 + 800117a: d002 beq.n 8001182 + oled_show(pixels); + 800117c: 4628 mov r0, r5 + 800117e: f7ff ff79 bl 8001074 + } + + uint32_t p_count = LCD_WIDTH * 10 * progress / 1000; + 8001182: f44f 6148 mov.w r1, #3200 ; 0xc80 + 8001186: 4361 muls r1, r4 + 8001188: f44f 737a mov.w r3, #1000 ; 0x3e8 + 800118c: fb91 f1f3 sdiv r1, r1, r3 + if(p_count > LCD_WIDTH) p_count = LCD_WIDTH-1; + 8001190: f240 133f movw r3, #319 ; 0x13f + 8001194: f5b1 7fa0 cmp.w r1, #320 ; 0x140 + 8001198: bf88 it hi + 800119a: 4619 movhi r1, r3 + if(p_count < 0) p_count = 0; + + // draw just the progress bar + uint16_t row[LCD_WIDTH]; + 800119c: f5ad 7d20 sub.w sp, sp, #640 ; 0x280 + 80011a0: 466c mov r4, sp + memset2(row, COL_FOREGROUND, 2*p_count); + 80011a2: 004a lsls r2, r1, #1 + 80011a4: b293 uxth r3, r2 + for(; byte_len; byte_len-=2, dest++) { + 80011a6: 4620 mov r0, r4 + *dest = value; + 80011a8: f246 05fd movw r5, #24829 ; 0x60fd + for(; byte_len; byte_len-=2, dest++) { + 80011ac: b9a3 cbnz r3, 80011d8 + memset2(&row[p_count], COL_BLACK, 2*(LCD_WIDTH-p_count)); + 80011ae: f5c1 71a0 rsb r1, r1, #320 ; 0x140 + 80011b2: 4422 add r2, r4 + 80011b4: 0049 lsls r1, r1, #1 + for(; byte_len; byte_len-=2, dest++) { + 80011b6: b289 uxth r1, r1 + 80011b8: b999 cbnz r1, 80011e2 + + wait_vsync(); + 80011ba: f7ff fd63 bl 8000c84 + 80011be: 25eb movs r5, #235 ; 0xeb + + for(int i=0; i + for(int i=0; i + } + + rng_delay(); + 80011d0: f001 fb9a bl 8002908 +} + 80011d4: 46bd mov sp, r7 + 80011d6: bdb0 pop {r4, r5, r7, pc} + for(; byte_len; byte_len-=2, dest++) { + 80011d8: 3b02 subs r3, #2 + *dest = value; + 80011da: f820 5b02 strh.w r5, [r0], #2 + for(; byte_len; byte_len-=2, dest++) { + 80011de: b29b uxth r3, r3 + 80011e0: e7e4 b.n 80011ac + *dest = value; + 80011e2: f822 3b02 strh.w r3, [r2], #2 + for(; byte_len; byte_len-=2, dest++) { + 80011e6: 3902 subs r1, #2 + 80011e8: e7e5 b.n 80011b6 + 80011ea: bf00 nop + 80011ec: 2009e154 .word 0x2009e154 + +080011f0 : +// + void +oled_factory_busy(void) +{ + // not implemented: would need to talk to GPU for this +} + 80011f0: 4770 bx lr + ... + +080011f4 : + * @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains + * the configuration information for the specified GPIO peripheral. + * @retval None + */ +void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) +{ + 80011f4: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + /*--------------------- EXTI Mode Configuration ------------------------*/ + /* Configure the External Interrupt or event for the current IO */ + if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) + { + /* Enable SYSCFG Clock */ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80011f8: f8df 81b4 ldr.w r8, [pc, #436] ; 80013b0 + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + SYSCFG->EXTICR[position >> 2] = temp; + + /* Clear EXTI line configuration */ + temp = EXTI->IMR1; + 80011fc: 4c6a ldr r4, [pc, #424] ; (80013a8 ) + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 80011fe: f8df 91b4 ldr.w r9, [pc, #436] ; 80013b4 +{ + 8001202: b085 sub sp, #20 + uint32_t position = 0x00; + 8001204: 2300 movs r3, #0 + while (((GPIO_Init->Pin) >> position) != RESET) + 8001206: 680a ldr r2, [r1, #0] + 8001208: fa32 f503 lsrs.w r5, r2, r3 + 800120c: d102 bne.n 8001214 + } + } + + position++; + } +} + 800120e: b005 add sp, #20 + 8001210: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + iocurrent = (GPIO_Init->Pin) & (1U << position); + 8001214: 2701 movs r7, #1 + 8001216: 409f lsls r7, r3 + if(iocurrent) + 8001218: 403a ands r2, r7 + 800121a: f000 80b4 beq.w 8001386 + if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) + 800121e: 684d ldr r5, [r1, #4] + 8001220: f025 0a10 bic.w sl, r5, #16 + 8001224: f1ba 0f02 cmp.w sl, #2 + 8001228: d116 bne.n 8001258 + temp = GPIOx->AFR[position >> 3]; + 800122a: ea4f 0ed3 mov.w lr, r3, lsr #3 + 800122e: eb00 0e8e add.w lr, r0, lr, lsl #2 + temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 8001232: f003 0b07 and.w fp, r3, #7 + temp = GPIOx->AFR[position >> 3]; + 8001236: f8de 6020 ldr.w r6, [lr, #32] + temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 800123a: ea4f 0b8b mov.w fp, fp, lsl #2 + 800123e: f04f 0c0f mov.w ip, #15 + 8001242: fa0c fc0b lsl.w ip, ip, fp + 8001246: ea26 0c0c bic.w ip, r6, ip + temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07) * 4)); + 800124a: 690e ldr r6, [r1, #16] + 800124c: fa06 f60b lsl.w r6, r6, fp + 8001250: ea46 060c orr.w r6, r6, ip + GPIOx->AFR[position >> 3] = temp; + 8001254: f8ce 6020 str.w r6, [lr, #32] + temp = GPIOx->MODER; + 8001258: f8d0 b000 ldr.w fp, [r0] + temp &= ~(GPIO_MODER_MODE0 << (position * 2)); + 800125c: ea4f 0e43 mov.w lr, r3, lsl #1 + 8001260: f04f 0c03 mov.w ip, #3 + 8001264: fa0c fc0e lsl.w ip, ip, lr + 8001268: ea6f 060c mvn.w r6, ip + 800126c: ea2b 0b0c bic.w fp, fp, ip + temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2)); + 8001270: f005 0c03 and.w ip, r5, #3 + 8001274: fa0c fc0e lsl.w ip, ip, lr + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 8001278: f10a 3aff add.w sl, sl, #4294967295 ; 0xffffffff + temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2)); + 800127c: ea4c 0c0b orr.w ip, ip, fp + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 8001280: f1ba 0f01 cmp.w sl, #1 + temp &= ~(GPIO_MODER_MODE0 << (position * 2)); + 8001284: 9601 str r6, [sp, #4] + GPIOx->MODER = temp; + 8001286: f8c0 c000 str.w ip, [r0] + if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || + 800128a: d815 bhi.n 80012b8 + temp = GPIOx->OSPEEDR; + 800128c: f8d0 c008 ldr.w ip, [r0, #8] + temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2)); + 8001290: ea06 0c0c and.w ip, r6, ip + temp |= (GPIO_Init->Speed << (position * 2)); + 8001294: 68ce ldr r6, [r1, #12] + 8001296: fa06 fa0e lsl.w sl, r6, lr + 800129a: ea4a 0c0c orr.w ip, sl, ip + GPIOx->OSPEEDR = temp; + 800129e: f8c0 c008 str.w ip, [r0, #8] + temp = GPIOx->OTYPER; + 80012a2: f8d0 c004 ldr.w ip, [r0, #4] + temp &= ~(GPIO_OTYPER_OT0 << position) ; + 80012a6: ea2c 0707 bic.w r7, ip, r7 + temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4) << position); + 80012aa: f3c5 1c00 ubfx ip, r5, #4, #1 + 80012ae: fa0c fc03 lsl.w ip, ip, r3 + 80012b2: ea4c 0707 orr.w r7, ip, r7 + GPIOx->OTYPER = temp; + 80012b6: 6047 str r7, [r0, #4] + temp = GPIOx->PUPDR; + 80012b8: 68c7 ldr r7, [r0, #12] + temp &= ~(GPIO_PUPDR_PUPD0 << (position * 2)); + 80012ba: 9e01 ldr r6, [sp, #4] + 80012bc: 4037 ands r7, r6 + temp |= ((GPIO_Init->Pull) << (position * 2)); + 80012be: 688e ldr r6, [r1, #8] + 80012c0: fa06 f60e lsl.w r6, r6, lr + 80012c4: 433e orrs r6, r7 + GPIOx->PUPDR = temp; + 80012c6: 60c6 str r6, [r0, #12] + if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) + 80012c8: 00ee lsls r6, r5, #3 + 80012ca: d55c bpl.n 8001386 + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80012cc: f8d8 6060 ldr.w r6, [r8, #96] ; 0x60 + 80012d0: f046 0601 orr.w r6, r6, #1 + 80012d4: f8c8 6060 str.w r6, [r8, #96] ; 0x60 + 80012d8: f8d8 6060 ldr.w r6, [r8, #96] ; 0x60 + 80012dc: f023 0703 bic.w r7, r3, #3 + 80012e0: f107 4780 add.w r7, r7, #1073741824 ; 0x40000000 + 80012e4: f006 0601 and.w r6, r6, #1 + 80012e8: f507 3780 add.w r7, r7, #65536 ; 0x10000 + 80012ec: 9603 str r6, [sp, #12] + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 80012ee: f003 0c03 and.w ip, r3, #3 + __HAL_RCC_SYSCFG_CLK_ENABLE(); + 80012f2: 9e03 ldr r6, [sp, #12] + temp = SYSCFG->EXTICR[position >> 2]; + 80012f4: f8d7 a008 ldr.w sl, [r7, #8] + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 80012f8: f04f 0e0f mov.w lr, #15 + 80012fc: ea4f 0c8c mov.w ip, ip, lsl #2 + 8001300: fa0e f60c lsl.w r6, lr, ip + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 8001304: f1b0 4f90 cmp.w r0, #1207959552 ; 0x48000000 + temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001308: ea2a 0e06 bic.w lr, sl, r6 + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 800130c: d03d beq.n 800138a + 800130e: 4e27 ldr r6, [pc, #156] ; (80013ac ) + 8001310: 42b0 cmp r0, r6 + 8001312: d03c beq.n 800138e + 8001314: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001318: 42b0 cmp r0, r6 + 800131a: d03a beq.n 8001392 + 800131c: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001320: 42b0 cmp r0, r6 + 8001322: d038 beq.n 8001396 + 8001324: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001328: 42b0 cmp r0, r6 + 800132a: d036 beq.n 800139a + 800132c: f506 6680 add.w r6, r6, #1024 ; 0x400 + 8001330: 42b0 cmp r0, r6 + 8001332: d034 beq.n 800139e + 8001334: 4548 cmp r0, r9 + 8001336: d034 beq.n 80013a2 + 8001338: f506 6600 add.w r6, r6, #2048 ; 0x800 + 800133c: 42b0 cmp r0, r6 + 800133e: bf0c ite eq + 8001340: 2607 moveq r6, #7 + 8001342: 2608 movne r6, #8 + 8001344: fa06 f60c lsl.w r6, r6, ip + 8001348: ea46 060e orr.w r6, r6, lr + SYSCFG->EXTICR[position >> 2] = temp; + 800134c: 60be str r6, [r7, #8] + temp = EXTI->IMR1; + 800134e: 6826 ldr r6, [r4, #0] + temp &= ~((uint32_t)iocurrent); + 8001350: 43d7 mvns r7, r2 + if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT) + 8001352: f415 3f80 tst.w r5, #65536 ; 0x10000 + temp &= ~((uint32_t)iocurrent); + 8001356: bf0c ite eq + 8001358: 403e andeq r6, r7 + temp |= iocurrent; + 800135a: 4316 orrne r6, r2 + EXTI->IMR1 = temp; + 800135c: 6026 str r6, [r4, #0] + temp = EXTI->EMR1; + 800135e: 6866 ldr r6, [r4, #4] + if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT) + 8001360: f415 3f00 tst.w r5, #131072 ; 0x20000 + temp &= ~((uint32_t)iocurrent); + 8001364: bf0c ite eq + 8001366: 403e andeq r6, r7 + temp |= iocurrent; + 8001368: 4316 orrne r6, r2 + EXTI->EMR1 = temp; + 800136a: 6066 str r6, [r4, #4] + temp = EXTI->RTSR1; + 800136c: 68a6 ldr r6, [r4, #8] + if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE) + 800136e: f415 1f80 tst.w r5, #1048576 ; 0x100000 + temp &= ~((uint32_t)iocurrent); + 8001372: bf0c ite eq + 8001374: 403e andeq r6, r7 + temp |= iocurrent; + 8001376: 4316 orrne r6, r2 + EXTI->RTSR1 = temp; + 8001378: 60a6 str r6, [r4, #8] + temp = EXTI->FTSR1; + 800137a: 68e6 ldr r6, [r4, #12] + if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE) + 800137c: 02ad lsls r5, r5, #10 + temp &= ~((uint32_t)iocurrent); + 800137e: bf54 ite pl + 8001380: 403e andpl r6, r7 + temp |= iocurrent; + 8001382: 4316 orrmi r6, r2 + EXTI->FTSR1 = temp; + 8001384: 60e6 str r6, [r4, #12] + position++; + 8001386: 3301 adds r3, #1 + 8001388: e73d b.n 8001206 + temp |= (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03))); + 800138a: 2600 movs r6, #0 + 800138c: e7da b.n 8001344 + 800138e: 2601 movs r6, #1 + 8001390: e7d8 b.n 8001344 + 8001392: 2602 movs r6, #2 + 8001394: e7d6 b.n 8001344 + 8001396: 2603 movs r6, #3 + 8001398: e7d4 b.n 8001344 + 800139a: 2604 movs r6, #4 + 800139c: e7d2 b.n 8001344 + 800139e: 2605 movs r6, #5 + 80013a0: e7d0 b.n 8001344 + 80013a2: 2606 movs r6, #6 + 80013a4: e7ce b.n 8001344 + 80013a6: bf00 nop + 80013a8: 40010400 .word 0x40010400 + 80013ac: 48000400 .word 0x48000400 + 80013b0: 40021000 .word 0x40021000 + 80013b4: 48001800 .word 0x48001800 + +080013b8 : + * @param GPIO_Pin: specifies the port bit to be written. + * This parameter can be one of GPIO_PIN_x where x can be (0..15). + * @retval None + */ +void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) +{ + 80013b8: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + { + tmp = ((uint32_t)0x0F) << (4 * (position & 0x03)); + SYSCFG->EXTICR[position >> 2] &= ~tmp; + + /* Clear EXTI line configuration */ + EXTI->IMR1 &= ~((uint32_t)iocurrent); + 80013bc: 4c43 ldr r4, [pc, #268] ; (80014cc ) + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 80013be: f8df a114 ldr.w sl, [pc, #276] ; 80014d4 + 80013c2: f8df b114 ldr.w fp, [pc, #276] ; 80014d8 + uint32_t position = 0x00; + 80013c6: 2200 movs r2, #0 + iocurrent = (GPIO_Pin) & (1U << position); + 80013c8: f04f 0901 mov.w r9, #1 + while ((GPIO_Pin >> position) != RESET) + 80013cc: fa31 f302 lsrs.w r3, r1, r2 + 80013d0: d101 bne.n 80013d6 + } + } + + position++; + } +} + 80013d2: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + iocurrent = (GPIO_Pin) & (1U << position); + 80013d6: fa09 f802 lsl.w r8, r9, r2 + if (iocurrent) + 80013da: ea18 0c01 ands.w ip, r8, r1 + 80013de: d064 beq.n 80014aa + GPIOx->MODER |= (GPIO_MODER_MODE0 << (position * 2)); + 80013e0: 6805 ldr r5, [r0, #0] + 80013e2: 2303 movs r3, #3 + 80013e4: 0056 lsls r6, r2, #1 + 80013e6: fa03 f606 lsl.w r6, r3, r6 + GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 80013ea: fa22 fe03 lsr.w lr, r2, r3 + GPIOx->MODER |= (GPIO_MODER_MODE0 << (position * 2)); + 80013ee: 4335 orrs r5, r6 + 80013f0: eb00 0e8e add.w lr, r0, lr, lsl #2 + 80013f4: 6005 str r5, [r0, #0] + GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; + 80013f6: f8de 5020 ldr.w r5, [lr, #32] + 80013fa: f002 0707 and.w r7, r2, #7 + 80013fe: 462b mov r3, r5 + 8001400: 00bf lsls r7, r7, #2 + 8001402: 250f movs r5, #15 + 8001404: fa05 f707 lsl.w r7, r5, r7 + 8001408: ea23 0707 bic.w r7, r3, r7 + 800140c: f8ce 7020 str.w r7, [lr, #32] + GPIOx->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2)); + 8001410: 6887 ldr r7, [r0, #8] + 8001412: ea27 0706 bic.w r7, r7, r6 + 8001416: 6087 str r7, [r0, #8] + GPIOx->OTYPER &= ~(GPIO_OTYPER_OT0 << position) ; + 8001418: 6847 ldr r7, [r0, #4] + 800141a: ea27 0708 bic.w r7, r7, r8 + 800141e: 6047 str r7, [r0, #4] + GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPD0 << (position * 2)); + 8001420: 68c7 ldr r7, [r0, #12] + 8001422: ea27 0606 bic.w r6, r7, r6 + 8001426: 60c6 str r6, [r0, #12] + tmp = SYSCFG->EXTICR[position >> 2]; + 8001428: f022 0603 bic.w r6, r2, #3 + 800142c: f106 4680 add.w r6, r6, #1073741824 ; 0x40000000 + 8001430: f506 3680 add.w r6, r6, #65536 ; 0x10000 + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001434: f002 0703 and.w r7, r2, #3 + tmp = SYSCFG->EXTICR[position >> 2]; + 8001438: f8d6 e008 ldr.w lr, [r6, #8] + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 800143c: 00bf lsls r7, r7, #2 + 800143e: 40bd lsls r5, r7 + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 8001440: f1b0 4f90 cmp.w r0, #1207959552 ; 0x48000000 + tmp &= (((uint32_t)0x0F) << (4 * (position & 0x03))); + 8001444: ea05 0e0e and.w lr, r5, lr + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 8001448: d031 beq.n 80014ae + 800144a: 4b21 ldr r3, [pc, #132] ; (80014d0 ) + 800144c: 4298 cmp r0, r3 + 800144e: d030 beq.n 80014b2 + 8001450: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001454: 4298 cmp r0, r3 + 8001456: d02e beq.n 80014b6 + 8001458: f503 6380 add.w r3, r3, #1024 ; 0x400 + 800145c: 4298 cmp r0, r3 + 800145e: d02c beq.n 80014ba + 8001460: f503 6380 add.w r3, r3, #1024 ; 0x400 + 8001464: 4298 cmp r0, r3 + 8001466: d02a beq.n 80014be + 8001468: f503 6380 add.w r3, r3, #1024 ; 0x400 + 800146c: 4298 cmp r0, r3 + 800146e: d028 beq.n 80014c2 + 8001470: 4550 cmp r0, sl + 8001472: d028 beq.n 80014c6 + 8001474: 4558 cmp r0, fp + 8001476: bf0c ite eq + 8001478: 2307 moveq r3, #7 + 800147a: 2308 movne r3, #8 + 800147c: 40bb lsls r3, r7 + 800147e: 4573 cmp r3, lr + 8001480: d113 bne.n 80014aa + SYSCFG->EXTICR[position >> 2] &= ~tmp; + 8001482: 68b3 ldr r3, [r6, #8] + 8001484: ea23 0505 bic.w r5, r3, r5 + 8001488: 60b5 str r5, [r6, #8] + EXTI->IMR1 &= ~((uint32_t)iocurrent); + 800148a: 6823 ldr r3, [r4, #0] + 800148c: ea23 030c bic.w r3, r3, ip + 8001490: 6023 str r3, [r4, #0] + EXTI->EMR1 &= ~((uint32_t)iocurrent); + 8001492: 6863 ldr r3, [r4, #4] + 8001494: ea23 030c bic.w r3, r3, ip + 8001498: 6063 str r3, [r4, #4] + EXTI->RTSR1 &= ~((uint32_t)iocurrent); + 800149a: 68a3 ldr r3, [r4, #8] + 800149c: ea23 030c bic.w r3, r3, ip + 80014a0: 60a3 str r3, [r4, #8] + EXTI->FTSR1 &= ~((uint32_t)iocurrent); + 80014a2: 68e3 ldr r3, [r4, #12] + 80014a4: ea23 030c bic.w r3, r3, ip + 80014a8: 60e3 str r3, [r4, #12] + position++; + 80014aa: 3201 adds r2, #1 + 80014ac: e78e b.n 80013cc + if(tmp == (GPIO_GET_INDEX(GPIOx) << (4 * (position & 0x03)))) + 80014ae: 2300 movs r3, #0 + 80014b0: e7e4 b.n 800147c + 80014b2: 2301 movs r3, #1 + 80014b4: e7e2 b.n 800147c + 80014b6: 2302 movs r3, #2 + 80014b8: e7e0 b.n 800147c + 80014ba: 2303 movs r3, #3 + 80014bc: e7de b.n 800147c + 80014be: 2304 movs r3, #4 + 80014c0: e7dc b.n 800147c + 80014c2: 2305 movs r3, #5 + 80014c4: e7da b.n 800147c + 80014c6: 2306 movs r3, #6 + 80014c8: e7d8 b.n 800147c + 80014ca: bf00 nop + 80014cc: 40010400 .word 0x40010400 + 80014d0: 48000400 .word 0x48000400 + 80014d4: 48001800 .word 0x48001800 + 80014d8: 48001c00 .word 0x48001c00 + +080014dc : + GPIO_PinState bitstatus; + + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET) + 80014dc: 6903 ldr r3, [r0, #16] + 80014de: 4219 tst r1, r3 + else + { + bitstatus = GPIO_PIN_RESET; + } + return bitstatus; +} + 80014e0: bf14 ite ne + 80014e2: 2001 movne r0, #1 + 80014e4: 2000 moveq r0, #0 + 80014e6: 4770 bx lr + +080014e8 : +{ + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + assert_param(IS_GPIO_PIN_ACTION(PinState)); + + if(PinState != GPIO_PIN_RESET) + 80014e8: b10a cbz r2, 80014ee + { + GPIOx->BSRR = (uint32_t)GPIO_Pin; + 80014ea: 6181 str r1, [r0, #24] + 80014ec: 4770 bx lr + } + else + { + GPIOx->BRR = (uint32_t)GPIO_Pin; + 80014ee: 6281 str r1, [r0, #40] ; 0x28 + } +} + 80014f0: 4770 bx lr + +080014f2 : +void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + /* Check the parameters */ + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + GPIOx->ODR ^= GPIO_Pin; + 80014f2: 6943 ldr r3, [r0, #20] + 80014f4: 4059 eors r1, r3 + 80014f6: 6141 str r1, [r0, #20] +} + 80014f8: 4770 bx lr + +080014fa : + * @param GPIO_Pin: specifies the port bits to be locked. + * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). + * @retval None + */ +HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + 80014fa: b082 sub sp, #8 + __IO uint32_t tmp = GPIO_LCKR_LCKK; + 80014fc: f44f 3380 mov.w r3, #65536 ; 0x10000 + 8001500: 9301 str r3, [sp, #4] + /* Check the parameters */ + assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx)); + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + /* Apply lock key write sequence */ + tmp |= GPIO_Pin; + 8001502: 9b01 ldr r3, [sp, #4] + 8001504: 430b orrs r3, r1 + 8001506: 9301 str r3, [sp, #4] + /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */ + GPIOx->LCKR = tmp; + 8001508: 9b01 ldr r3, [sp, #4] + 800150a: 61c3 str r3, [r0, #28] + /* Reset LCKx bit(s): LCKK='0' + LCK[15-0] */ + GPIOx->LCKR = GPIO_Pin; + 800150c: 61c1 str r1, [r0, #28] + /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */ + GPIOx->LCKR = tmp; + 800150e: 9b01 ldr r3, [sp, #4] + 8001510: 61c3 str r3, [r0, #28] + /* Read LCKK bit*/ + tmp = GPIOx->LCKR; + 8001512: 69c3 ldr r3, [r0, #28] + 8001514: 9301 str r3, [sp, #4] + + if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET) + 8001516: 69c0 ldr r0, [r0, #28] + 8001518: f480 3080 eor.w r0, r0, #65536 ; 0x10000 + } + else + { + return HAL_ERROR; + } +} + 800151c: f3c0 4000 ubfx r0, r0, #16, #1 + 8001520: b002 add sp, #8 + 8001522: 4770 bx lr + +08001524 : + UNUSED(GPIO_Pin); + + /* NOTE: This function should not be modified, when the callback is needed, + the HAL_GPIO_EXTI_Callback could be implemented in the user file + */ +} + 8001524: 4770 bx lr + ... + +08001528 : + if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) + 8001528: 4a04 ldr r2, [pc, #16] ; (800153c ) + 800152a: 6951 ldr r1, [r2, #20] + 800152c: 4201 tst r1, r0 +{ + 800152e: b508 push {r3, lr} + if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) + 8001530: d002 beq.n 8001538 + __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); + 8001532: 6150 str r0, [r2, #20] + HAL_GPIO_EXTI_Callback(GPIO_Pin); + 8001534: f7ff fff6 bl 8001524 +} + 8001538: bd08 pop {r3, pc} + 800153a: bf00 nop + 800153c: 40010400 .word 0x40010400 + +08001540 : +static HAL_StatusTypeDef SPI_WaitFifoStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Fifo, uint32_t State, + uint32_t Timeout, uint32_t Tickstart) +{ + __IO uint8_t tmpreg; + + while ((hspi->Instance->SR & Fifo) != State) + 8001540: 6803 ldr r3, [r0, #0] +static HAL_StatusTypeDef SPI_EndRxTxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart) + 8001542: b082 sub sp, #8 + while ((hspi->Instance->SR & Fifo) != State) + 8001544: 689a ldr r2, [r3, #8] + 8001546: f412 5fc0 tst.w r2, #6144 ; 0x1800 + 800154a: d1fb bne.n 8001544 + * @retval HAL status + */ +static HAL_StatusTypeDef SPI_WaitFlagStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Flag, uint32_t State, + uint32_t Timeout, uint32_t Tickstart) +{ + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 800154c: 689a ldr r2, [r3, #8] + 800154e: 0612 lsls r2, r2, #24 + 8001550: d4fc bmi.n 800154c + while ((hspi->Instance->SR & Fifo) != State) + 8001552: 6898 ldr r0, [r3, #8] + 8001554: f410 60c0 ands.w r0, r0, #1536 ; 0x600 + 8001558: d101 bne.n 800155e +} + 800155a: b002 add sp, #8 + 800155c: 4770 bx lr + tmpreg = *((__IO uint8_t *)&hspi->Instance->DR); + 800155e: 7b1a ldrb r2, [r3, #12] + 8001560: b2d2 uxtb r2, r2 + 8001562: f88d 2007 strb.w r2, [sp, #7] + UNUSED(tmpreg); + 8001566: f89d 2007 ldrb.w r2, [sp, #7] + 800156a: e7f2 b.n 8001552 + +0800156c : +{ + 800156c: b5f0 push {r4, r5, r6, r7, lr} + if (hspi == NULL) + 800156e: 2800 cmp r0, #0 + 8001570: d054 beq.n 800161c + if (hspi->State == HAL_SPI_STATE_RESET) + 8001572: f890 305d ldrb.w r3, [r0, #93] ; 0x5d + if (hspi->Init.TIMode == SPI_TIMODE_DISABLE) + 8001576: f8d0 c024 ldr.w ip, [r0, #36] ; 0x24 + if (hspi->State == HAL_SPI_STATE_RESET) + 800157a: f003 02ff and.w r2, r3, #255 ; 0xff + 800157e: b90b cbnz r3, 8001584 + hspi->Lock = HAL_UNLOCKED; + 8001580: f880 205c strb.w r2, [r0, #92] ; 0x5c + __HAL_SPI_DISABLE(hspi); + 8001584: 6801 ldr r1, [r0, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 8001586: 68c2 ldr r2, [r0, #12] + hspi->State = HAL_SPI_STATE_BUSY; + 8001588: 2302 movs r3, #2 + 800158a: f880 305d strb.w r3, [r0, #93] ; 0x5d + __HAL_SPI_DISABLE(hspi); + 800158e: 680b ldr r3, [r1, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 8001590: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + __HAL_SPI_DISABLE(hspi); + 8001594: f023 0340 bic.w r3, r3, #64 ; 0x40 + 8001598: 600b str r3, [r1, #0] + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 800159a: f04f 0300 mov.w r3, #0 + 800159e: d83f bhi.n 8001620 + frxth = SPI_RXFIFO_THRESHOLD_QF; + 80015a0: f44f 5580 mov.w r5, #4096 ; 0x1000 + if ((hspi->Init.DataSize != SPI_DATASIZE_16BIT) && (hspi->Init.DataSize != SPI_DATASIZE_8BIT)) + 80015a4: d000 beq.n 80015a8 + hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + 80015a6: 6283 str r3, [r0, #40] ; 0x28 + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_DATASIZE) + 80015a8: 6b03 ldr r3, [r0, #48] ; 0x30 + 80015aa: b92b cbnz r3, 80015b8 + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 80015ac: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + hspi->Init.CRCLength = SPI_CRC_LENGTH_16BIT; + 80015b0: bf8c ite hi + 80015b2: 2302 movhi r3, #2 + hspi->Init.CRCLength = SPI_CRC_LENGTH_8BIT; + 80015b4: 2301 movls r3, #1 + 80015b6: 6303 str r3, [r0, #48] ; 0x30 + WRITE_REG(hspi->Instance->CR1, (hspi->Init.Mode | hspi->Init.Direction | + 80015b8: e9d0 3701 ldrd r3, r7, [r0, #4] + 80015bc: 433b orrs r3, r7 + 80015be: 6907 ldr r7, [r0, #16] + 80015c0: 6984 ldr r4, [r0, #24] + 80015c2: 6a86 ldr r6, [r0, #40] ; 0x28 + 80015c4: 433b orrs r3, r7 + 80015c6: 6947 ldr r7, [r0, #20] + 80015c8: 433b orrs r3, r7 + 80015ca: 69c7 ldr r7, [r0, #28] + 80015cc: 433b orrs r3, r7 + 80015ce: 6a07 ldr r7, [r0, #32] + 80015d0: 433b orrs r3, r7 + 80015d2: 4333 orrs r3, r6 + 80015d4: f404 7700 and.w r7, r4, #512 ; 0x200 + 80015d8: 433b orrs r3, r7 + 80015da: 600b str r3, [r1, #0] + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT) + 80015dc: 6b03 ldr r3, [r0, #48] ; 0x30 + 80015de: 2b02 cmp r3, #2 + hspi->Instance->CR1 |= SPI_CR1_CRCL; + 80015e0: bf02 ittt eq + 80015e2: 680b ldreq r3, [r1, #0] + 80015e4: f443 6300 orreq.w r3, r3, #2048 ; 0x800 + 80015e8: 600b streq r3, [r1, #0] + WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode | + 80015ea: 6b43 ldr r3, [r0, #52] ; 0x34 + 80015ec: ea4c 0202 orr.w r2, ip, r2 + 80015f0: 0c24 lsrs r4, r4, #16 + 80015f2: 431a orrs r2, r3 + 80015f4: f004 0404 and.w r4, r4, #4 + 80015f8: 4322 orrs r2, r4 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80015fa: f5b6 5f00 cmp.w r6, #8192 ; 0x2000 + WRITE_REG(hspi->Instance->CRCPR, hspi->Init.CRCPolynomial); + 80015fe: bf08 it eq + 8001600: 6ac3 ldreq r3, [r0, #44] ; 0x2c + WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode | + 8001602: ea45 0502 orr.w r5, r5, r2 + 8001606: 604d str r5, [r1, #4] + hspi->State = HAL_SPI_STATE_READY; + 8001608: f04f 0201 mov.w r2, #1 + WRITE_REG(hspi->Instance->CRCPR, hspi->Init.CRCPolynomial); + 800160c: bf08 it eq + 800160e: 610b streq r3, [r1, #16] + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001610: 2300 movs r3, #0 + 8001612: 6603 str r3, [r0, #96] ; 0x60 + hspi->State = HAL_SPI_STATE_READY; + 8001614: f880 205d strb.w r2, [r0, #93] ; 0x5d + return HAL_OK; + 8001618: 4618 mov r0, r3 +} + 800161a: bdf0 pop {r4, r5, r6, r7, pc} + return HAL_ERROR; + 800161c: 2001 movs r0, #1 + 800161e: e7fc b.n 800161a + frxth = SPI_RXFIFO_THRESHOLD_HF; + 8001620: 461d mov r5, r3 + if ((hspi->Init.DataSize != SPI_DATASIZE_16BIT) && (hspi->Init.DataSize != SPI_DATASIZE_8BIT)) + 8001622: f5b2 6f70 cmp.w r2, #3840 ; 0xf00 + 8001626: e7bd b.n 80015a4 + +08001628 : +{ + 8001628: e92d 41f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, lr} + 800162c: 461e mov r6, r3 + __HAL_LOCK(hspi); + 800162e: f890 305c ldrb.w r3, [r0, #92] ; 0x5c + 8001632: 2b01 cmp r3, #1 +{ + 8001634: 4604 mov r4, r0 + 8001636: 460d mov r5, r1 + 8001638: 4690 mov r8, r2 + __HAL_LOCK(hspi); + 800163a: f000 809c beq.w 8001776 + 800163e: 2301 movs r3, #1 + 8001640: f880 305c strb.w r3, [r0, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8001644: f005 feca bl 80073dc + if (hspi->State != HAL_SPI_STATE_READY) + 8001648: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + 800164c: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 800164e: 4607 mov r7, r0 + if (hspi->State != HAL_SPI_STATE_READY) + 8001650: b2d8 uxtb r0, r3 + 8001652: f040 808e bne.w 8001772 + if ((pData == NULL) || (Size == 0U)) + 8001656: 2d00 cmp r5, #0 + 8001658: d07a beq.n 8001750 + 800165a: f1b8 0f00 cmp.w r8, #0 + 800165e: d077 beq.n 8001750 + hspi->State = HAL_SPI_STATE_BUSY_TX; + 8001660: 2303 movs r3, #3 + 8001662: f884 305d strb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001666: 68a3 ldr r3, [r4, #8] + SPI_1LINE_TX(hspi); + 8001668: 6822 ldr r2, [r4, #0] + hspi->pTxBuffPtr = (uint8_t *)pData; + 800166a: 63a5 str r5, [r4, #56] ; 0x38 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 800166c: 2100 movs r1, #0 + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 800166e: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001672: 6621 str r1, [r4, #96] ; 0x60 + hspi->TxXferCount = Size; + 8001674: f8a4 803e strh.w r8, [r4, #62] ; 0x3e + hspi->RxXferCount = 0U; + 8001678: f8a4 1046 strh.w r1, [r4, #70] ; 0x46 + SPI_1LINE_TX(hspi); + 800167c: bf08 it eq + 800167e: 6813 ldreq r3, [r2, #0] + hspi->TxXferSize = Size; + 8001680: f8a4 803c strh.w r8, [r4, #60] ; 0x3c + SPI_1LINE_TX(hspi); + 8001684: bf08 it eq + 8001686: f443 4380 orreq.w r3, r3, #16384 ; 0x4000 + hspi->RxISR = NULL; + 800168a: e9c4 1113 strd r1, r1, [r4, #76] ; 0x4c + hspi->pRxBuffPtr = (uint8_t *)NULL; + 800168e: 6421 str r1, [r4, #64] ; 0x40 + hspi->RxXferSize = 0U; + 8001690: f8a4 1044 strh.w r1, [r4, #68] ; 0x44 + SPI_1LINE_TX(hspi); + 8001694: bf08 it eq + 8001696: 6013 streq r3, [r2, #0] + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001698: 6aa3 ldr r3, [r4, #40] ; 0x28 + 800169a: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 800169e: d107 bne.n 80016b0 + SPI_RESET_CRC(hspi); + 80016a0: 6813 ldr r3, [r2, #0] + 80016a2: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 80016a6: 6013 str r3, [r2, #0] + 80016a8: 6813 ldr r3, [r2, #0] + 80016aa: f443 5300 orr.w r3, r3, #8192 ; 0x2000 + 80016ae: 6013 str r3, [r2, #0] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 80016b0: 6813 ldr r3, [r2, #0] + 80016b2: 0659 lsls r1, r3, #25 + __HAL_SPI_ENABLE(hspi); + 80016b4: bf5e ittt pl + 80016b6: 6813 ldrpl r3, [r2, #0] + 80016b8: f043 0340 orrpl.w r3, r3, #64 ; 0x40 + 80016bc: 6013 strpl r3, [r2, #0] + if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U)) + 80016be: 6863 ldr r3, [r4, #4] + 80016c0: b11b cbz r3, 80016ca + 80016c2: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80016c4: b29b uxth r3, r3 + 80016c6: 2b01 cmp r3, #1 + 80016c8: d110 bne.n 80016ec + if (hspi->TxXferCount > 1U) + 80016ca: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80016cc: b29b uxth r3, r3 + 80016ce: 2b01 cmp r3, #1 + 80016d0: d905 bls.n 80016de + hspi->Instance->DR = *((uint16_t *)pData); + 80016d2: f835 3b02 ldrh.w r3, [r5], #2 + 80016d6: 60d3 str r3, [r2, #12] + hspi->TxXferCount -= 2U; + 80016d8: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80016da: 3b02 subs r3, #2 + 80016dc: e004 b.n 80016e8 + *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++); + 80016de: f815 3b01 ldrb.w r3, [r5], #1 + 80016e2: 7313 strb r3, [r2, #12] + hspi->TxXferCount--; + 80016e4: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80016e6: 3b01 subs r3, #1 + 80016e8: b29b uxth r3, r3 + 80016ea: 87e3 strh r3, [r4, #62] ; 0x3e + while (hspi->TxXferCount > 0U) + 80016ec: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80016ee: b29b uxth r3, r3 + 80016f0: b9e3 cbnz r3, 800172c + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80016f2: 6aa3 ldr r3, [r4, #40] ; 0x28 + 80016f4: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 80016f8: bf01 itttt eq + 80016fa: 6822 ldreq r2, [r4, #0] + 80016fc: 6813 ldreq r3, [r2, #0] + 80016fe: f443 5380 orreq.w r3, r3, #4096 ; 0x1000 + 8001702: 6013 streq r3, [r2, #0] + if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK) + 8001704: 4620 mov r0, r4 + 8001706: f7ff ff1b bl 8001540 + 800170a: b108 cbz r0, 8001710 + hspi->ErrorCode = HAL_SPI_ERROR_FLAG; + 800170c: 2320 movs r3, #32 + 800170e: 6623 str r3, [r4, #96] ; 0x60 + if (hspi->Init.Direction == SPI_DIRECTION_2LINES) + 8001710: 68a3 ldr r3, [r4, #8] + 8001712: b933 cbnz r3, 8001722 + __HAL_SPI_CLEAR_OVRFLAG(hspi); + 8001714: 9301 str r3, [sp, #4] + 8001716: 6823 ldr r3, [r4, #0] + 8001718: 68da ldr r2, [r3, #12] + 800171a: 9201 str r2, [sp, #4] + 800171c: 689b ldr r3, [r3, #8] + 800171e: 9301 str r3, [sp, #4] + 8001720: 9b01 ldr r3, [sp, #4] + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 8001722: 6e20 ldr r0, [r4, #96] ; 0x60 + errorcode = HAL_BUSY; + 8001724: 3800 subs r0, #0 + 8001726: bf18 it ne + 8001728: 2001 movne r0, #1 +error: + 800172a: e011 b.n 8001750 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) + 800172c: 6823 ldr r3, [r4, #0] + 800172e: 689a ldr r2, [r3, #8] + 8001730: 0792 lsls r2, r2, #30 + 8001732: d50b bpl.n 800174c + if (hspi->TxXferCount > 1U) + 8001734: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001736: b292 uxth r2, r2 + 8001738: 2a01 cmp r2, #1 + 800173a: d903 bls.n 8001744 + hspi->Instance->DR = *((uint16_t *)pData); + 800173c: f835 2b02 ldrh.w r2, [r5], #2 + 8001740: 60da str r2, [r3, #12] + 8001742: e7c9 b.n 80016d8 + *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++); + 8001744: f815 2b01 ldrb.w r2, [r5], #1 + 8001748: 731a strb r2, [r3, #12] + hspi->TxXferCount--; + 800174a: e7cb b.n 80016e4 + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 800174c: b94e cbnz r6, 8001762 + errorcode = HAL_TIMEOUT; + 800174e: 2003 movs r0, #3 + hspi->State = HAL_SPI_STATE_READY; + 8001750: 2301 movs r3, #1 + 8001752: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 8001756: 2300 movs r3, #0 + 8001758: f884 305c strb.w r3, [r4, #92] ; 0x5c +} + 800175c: b002 add sp, #8 + 800175e: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 8001762: 1c73 adds r3, r6, #1 + 8001764: d0c2 beq.n 80016ec + 8001766: f005 fe39 bl 80073dc + 800176a: 1bc0 subs r0, r0, r7 + 800176c: 42b0 cmp r0, r6 + 800176e: d3bd bcc.n 80016ec + 8001770: e7ed b.n 800174e + errorcode = HAL_BUSY; + 8001772: 2002 movs r0, #2 + 8001774: e7ec b.n 8001750 + __HAL_LOCK(hspi); + 8001776: 2002 movs r0, #2 + 8001778: e7f0 b.n 800175c + +0800177a : + * @param Timeout: Timeout duration + * @retval HAL status + */ +HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, + uint32_t Timeout) +{ + 800177a: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + 800177e: 461e mov r6, r3 + uint32_t tmp = 0U, tmp1 = 0U; +#if (USE_SPI_CRC != 0U) + __IO uint16_t tmpreg = 0U; + 8001780: 2300 movs r3, #0 + 8001782: f8ad 3006 strh.w r3, [sp, #6] + + /* Check Direction parameter */ + assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction)); + + /* Process Locked */ + __HAL_LOCK(hspi); + 8001786: f890 305c ldrb.w r3, [r0, #92] ; 0x5c +{ + 800178a: f8dd 8028 ldr.w r8, [sp, #40] ; 0x28 + __HAL_LOCK(hspi); + 800178e: 2b01 cmp r3, #1 +{ + 8001790: 4604 mov r4, r0 + 8001792: 460d mov r5, r1 + 8001794: 4617 mov r7, r2 + __HAL_LOCK(hspi); + 8001796: f000 8124 beq.w 80019e2 + 800179a: 2301 movs r3, #1 + 800179c: f880 305c strb.w r3, [r0, #92] ; 0x5c + + /* Init tickstart for timeout management*/ + tickstart = HAL_GetTick(); + 80017a0: f005 fe1c bl 80073dc + + tmp = hspi->State; + 80017a4: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + tmp1 = hspi->Init.Mode; + 80017a8: 6861 ldr r1, [r4, #4] + + if (!((tmp == HAL_SPI_STATE_READY) || \ + 80017aa: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 80017ac: 4681 mov r9, r0 + tmp = hspi->State; + 80017ae: b2da uxtb r2, r3 + if (!((tmp == HAL_SPI_STATE_READY) || \ + 80017b0: d00a beq.n 80017c8 + 80017b2: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 80017b6: f040 8112 bne.w 80019de + ((tmp1 == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp == HAL_SPI_STATE_BUSY_RX)))) + 80017ba: 68a3 ldr r3, [r4, #8] + 80017bc: 2b00 cmp r3, #0 + 80017be: f040 810e bne.w 80019de + 80017c2: 2a04 cmp r2, #4 + 80017c4: f040 810b bne.w 80019de + { + errorcode = HAL_BUSY; + goto error; + } + + if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)) + 80017c8: b955 cbnz r5, 80017e0 + { + errorcode = HAL_ERROR; + 80017ca: 2101 movs r1, #1 + { + errorcode = HAL_ERROR; + } + +error : + hspi->State = HAL_SPI_STATE_READY; + 80017cc: 2301 movs r3, #1 + 80017ce: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 80017d2: 2300 movs r3, #0 + 80017d4: f884 305c strb.w r3, [r4, #92] ; 0x5c + return errorcode; +} + 80017d8: 4608 mov r0, r1 + 80017da: b003 add sp, #12 + 80017dc: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)) + 80017e0: 2f00 cmp r7, #0 + 80017e2: d0f2 beq.n 80017ca + 80017e4: 2e00 cmp r6, #0 + 80017e6: d0f0 beq.n 80017ca + if (hspi->State != HAL_SPI_STATE_BUSY_RX) + 80017e8: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80017ec: 6aa2 ldr r2, [r4, #40] ; 0x28 + hspi->pRxBuffPtr = (uint8_t *)pRxData; + 80017ee: 6427 str r7, [r4, #64] ; 0x40 + if (hspi->State != HAL_SPI_STATE_BUSY_RX) + 80017f0: 2b04 cmp r3, #4 + hspi->State = HAL_SPI_STATE_BUSY_TX_RX; + 80017f2: bf1c itt ne + 80017f4: 2305 movne r3, #5 + 80017f6: f884 305d strbne.w r3, [r4, #93] ; 0x5d + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 80017fa: 2300 movs r3, #0 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 80017fc: f5b2 5f00 cmp.w r2, #8192 ; 0x2000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001800: 6623 str r3, [r4, #96] ; 0x60 + hspi->TxISR = NULL; + 8001802: e9c4 3313 strd r3, r3, [r4, #76] ; 0x4c + hspi->RxXferCount = Size; + 8001806: f8a4 6046 strh.w r6, [r4, #70] ; 0x46 + SPI_RESET_CRC(hspi); + 800180a: 6823 ldr r3, [r4, #0] + hspi->RxXferSize = Size; + 800180c: f8a4 6044 strh.w r6, [r4, #68] ; 0x44 + hspi->pTxBuffPtr = (uint8_t *)pTxData; + 8001810: 63a5 str r5, [r4, #56] ; 0x38 + hspi->TxXferCount = Size; + 8001812: 87e6 strh r6, [r4, #62] ; 0x3e + hspi->TxXferSize = Size; + 8001814: 87a6 strh r6, [r4, #60] ; 0x3c + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001816: d107 bne.n 8001828 + SPI_RESET_CRC(hspi); + 8001818: 681a ldr r2, [r3, #0] + 800181a: f422 5200 bic.w r2, r2, #8192 ; 0x2000 + 800181e: 601a str r2, [r3, #0] + 8001820: 681a ldr r2, [r3, #0] + 8001822: f442 5200 orr.w r2, r2, #8192 ; 0x2000 + 8001826: 601a str r2, [r3, #0] + if ((hspi->Init.DataSize > SPI_DATASIZE_8BIT) || (hspi->RxXferCount > 1U)) + 8001828: 68e2 ldr r2, [r4, #12] + 800182a: f5b2 6fe0 cmp.w r2, #1792 ; 0x700 + 800182e: d804 bhi.n 800183a + 8001830: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001834: b292 uxth r2, r2 + 8001836: 2a01 cmp r2, #1 + 8001838: d94e bls.n 80018d8 + CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 800183a: 685a ldr r2, [r3, #4] + 800183c: f422 5280 bic.w r2, r2, #4096 ; 0x1000 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 8001840: 605a str r2, [r3, #4] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 8001842: 681a ldr r2, [r3, #0] + 8001844: 0650 lsls r0, r2, #25 + __HAL_SPI_ENABLE(hspi); + 8001846: bf5e ittt pl + 8001848: 681a ldrpl r2, [r3, #0] + 800184a: f042 0240 orrpl.w r2, r2, #64 ; 0x40 + 800184e: 601a strpl r2, [r3, #0] + if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U)) + 8001850: b119 cbz r1, 800185a + 8001852: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001854: b292 uxth r2, r2 + 8001856: 2a01 cmp r2, #1 + 8001858: d10a bne.n 8001870 + if (hspi->TxXferCount > 1U) + 800185a: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 800185c: b292 uxth r2, r2 + 800185e: 2a01 cmp r2, #1 + 8001860: d93e bls.n 80018e0 + hspi->Instance->DR = *((uint16_t *)pTxData); + 8001862: f835 2b02 ldrh.w r2, [r5], #2 + 8001866: 60da str r2, [r3, #12] + hspi->TxXferCount -= 2U; + 8001868: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 800186a: 3b02 subs r3, #2 + 800186c: b29b uxth r3, r3 + 800186e: 87e3 strh r3, [r4, #62] ; 0x3e + txallowed = 1U; + 8001870: 2601 movs r6, #1 + while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)) + 8001872: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 8001874: b29b uxth r3, r3 + 8001876: 2b00 cmp r3, #0 + 8001878: d138 bne.n 80018ec + 800187a: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 800187e: b29b uxth r3, r3 + 8001880: 2b00 cmp r3, #0 + 8001882: d133 bne.n 80018ec + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001884: 6aa2 ldr r2, [r4, #40] ; 0x28 + if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))) + 8001886: 6823 ldr r3, [r4, #0] + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001888: f5b2 5f00 cmp.w r2, #8192 ; 0x2000 + 800188c: d10d bne.n 80018aa + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 800188e: 689a ldr r2, [r3, #8] + 8001890: 07d1 lsls r1, r2, #31 + 8001892: d5fc bpl.n 800188e + if (hspi->Init.DataSize == SPI_DATASIZE_16BIT) + 8001894: 68e2 ldr r2, [r4, #12] + 8001896: f5b2 6f70 cmp.w r2, #3840 ; 0xf00 + 800189a: f040 8092 bne.w 80019c2 + tmpreg = hspi->Instance->DR; + 800189e: 68da ldr r2, [r3, #12] + 80018a0: b292 uxth r2, r2 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80018a2: f8ad 2006 strh.w r2, [sp, #6] + UNUSED(tmpreg); + 80018a6: f8bd 2006 ldrh.w r2, [sp, #6] + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)) + 80018aa: 6899 ldr r1, [r3, #8] + 80018ac: f011 0110 ands.w r1, r1, #16 + 80018b0: d007 beq.n 80018c2 + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC); + 80018b2: 6e22 ldr r2, [r4, #96] ; 0x60 + 80018b4: f042 0202 orr.w r2, r2, #2 + 80018b8: 6622 str r2, [r4, #96] ; 0x60 + __HAL_SPI_CLEAR_CRCERRFLAG(hspi); + 80018ba: f64f 72ef movw r2, #65519 ; 0xffef + 80018be: 609a str r2, [r3, #8] + errorcode = HAL_ERROR; + 80018c0: 2101 movs r1, #1 + if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK) + 80018c2: 4620 mov r0, r4 + 80018c4: f7ff fe3c bl 8001540 + 80018c8: b108 cbz r0, 80018ce + hspi->ErrorCode = HAL_SPI_ERROR_FLAG; + 80018ca: 2320 movs r3, #32 + 80018cc: 6623 str r3, [r4, #96] ; 0x60 + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 80018ce: 6e23 ldr r3, [r4, #96] ; 0x60 + 80018d0: 2b00 cmp r3, #0 + 80018d2: f47f af7a bne.w 80017ca + 80018d6: e779 b.n 80017cc + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 80018d8: 685a ldr r2, [r3, #4] + 80018da: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 80018de: e7af b.n 8001840 + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 80018e0: f815 2b01 ldrb.w r2, [r5], #1 + 80018e4: 731a strb r2, [r3, #12] + hspi->TxXferCount--; + 80018e6: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80018e8: 3b01 subs r3, #1 + 80018ea: e7bf b.n 800186c + if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))) + 80018ec: 2e00 cmp r6, #0 + 80018ee: d030 beq.n 8001952 + 80018f0: 8fe3 ldrh r3, [r4, #62] ; 0x3e + 80018f2: b29b uxth r3, r3 + 80018f4: 2b00 cmp r3, #0 + 80018f6: d02c beq.n 8001952 + 80018f8: 6823 ldr r3, [r4, #0] + 80018fa: 689a ldr r2, [r3, #8] + 80018fc: 0792 lsls r2, r2, #30 + 80018fe: d528 bpl.n 8001952 + if (hspi->TxXferCount > 1U) + 8001900: 8fe2 ldrh r2, [r4, #62] ; 0x3e + 8001902: b292 uxth r2, r2 + 8001904: 2a01 cmp r2, #1 + hspi->Instance->DR = *((uint16_t *)pTxData); + 8001906: bf8b itete hi + 8001908: f835 2b02 ldrhhi.w r2, [r5], #2 + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 800190c: f815 2b01 ldrbls.w r2, [r5], #1 + hspi->Instance->DR = *((uint16_t *)pTxData); + 8001910: 60da strhi r2, [r3, #12] + *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++); + 8001912: 731a strbls r2, [r3, #12] + hspi->TxXferCount -= 2U; + 8001914: bf8b itete hi + 8001916: 8fe3 ldrhhi r3, [r4, #62] ; 0x3e + hspi->TxXferCount--; + 8001918: 8fe3 ldrhls r3, [r4, #62] ; 0x3e + hspi->TxXferCount -= 2U; + 800191a: 3b02 subhi r3, #2 + hspi->TxXferCount--; + 800191c: f103 33ff addls.w r3, r3, #4294967295 ; 0xffffffff + 8001920: b29b uxth r3, r3 + 8001922: 87e3 strh r3, [r4, #62] ; 0x3e + if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)) + 8001924: 8fe6 ldrh r6, [r4, #62] ; 0x3e + 8001926: b2b6 uxth r6, r6 + 8001928: b996 cbnz r6, 8001950 + 800192a: 6aa3 ldr r3, [r4, #40] ; 0x28 + 800192c: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 8001930: d10f bne.n 8001952 + if (((hspi->Instance->CR1 & SPI_CR1_MSTR) == 0U) && ((hspi->Instance->CR2 & SPI_CR2_NSSP) == SPI_CR2_NSSP)) + 8001932: 6823 ldr r3, [r4, #0] + 8001934: 681a ldr r2, [r3, #0] + 8001936: 0756 lsls r6, r2, #29 + 8001938: d406 bmi.n 8001948 + 800193a: 685a ldr r2, [r3, #4] + 800193c: 0710 lsls r0, r2, #28 + SET_BIT(hspi->Instance->CR1, SPI_CR1_SSM); + 800193e: bf42 ittt mi + 8001940: 681a ldrmi r2, [r3, #0] + 8001942: f442 7200 orrmi.w r2, r2, #512 ; 0x200 + 8001946: 601a strmi r2, [r3, #0] + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 8001948: 681a ldr r2, [r3, #0] + 800194a: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800194e: 601a str r2, [r3, #0] + txallowed = 0U; + 8001950: 2600 movs r6, #0 + if ((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE))) + 8001952: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 8001956: b29b uxth r3, r3 + 8001958: b1e3 cbz r3, 8001994 + 800195a: 6821 ldr r1, [r4, #0] + 800195c: 688b ldr r3, [r1, #8] + 800195e: f013 0301 ands.w r3, r3, #1 + 8001962: d017 beq.n 8001994 + if (hspi->RxXferCount > 1U) + 8001964: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001968: b292 uxth r2, r2 + 800196a: 2a01 cmp r2, #1 + 800196c: d91f bls.n 80019ae + *((uint16_t *)pRxData) = hspi->Instance->DR; + 800196e: 68ca ldr r2, [r1, #12] + 8001970: f827 2b02 strh.w r2, [r7], #2 + hspi->RxXferCount -= 2U; + 8001974: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001978: 3a02 subs r2, #2 + 800197a: b292 uxth r2, r2 + 800197c: f8a4 2046 strh.w r2, [r4, #70] ; 0x46 + if (hspi->RxXferCount <= 1U) + 8001980: f8b4 2046 ldrh.w r2, [r4, #70] ; 0x46 + 8001984: b292 uxth r2, r2 + 8001986: 2a01 cmp r2, #1 + 8001988: d803 bhi.n 8001992 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 800198a: 684a ldr r2, [r1, #4] + 800198c: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8001990: 604a str r2, [r1, #4] + txallowed = 1U; + 8001992: 461e mov r6, r3 + if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout)) + 8001994: f1b8 3fff cmp.w r8, #4294967295 ; 0xffffffff + 8001998: f43f af6b beq.w 8001872 + 800199c: f005 fd1e bl 80073dc + 80019a0: eba0 0009 sub.w r0, r0, r9 + 80019a4: 4540 cmp r0, r8 + 80019a6: f4ff af64 bcc.w 8001872 + errorcode = HAL_TIMEOUT; + 80019aa: 2103 movs r1, #3 + 80019ac: e70e b.n 80017cc + (*(uint8_t *)pRxData++) = *(__IO uint8_t *)&hspi->Instance->DR; + 80019ae: 7b0a ldrb r2, [r1, #12] + 80019b0: f807 2b01 strb.w r2, [r7], #1 + hspi->RxXferCount--; + 80019b4: f8b4 1046 ldrh.w r1, [r4, #70] ; 0x46 + 80019b8: 3901 subs r1, #1 + 80019ba: b289 uxth r1, r1 + 80019bc: f8a4 1046 strh.w r1, [r4, #70] ; 0x46 + 80019c0: e7e7 b.n 8001992 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80019c2: 7b1a ldrb r2, [r3, #12] + 80019c4: f8ad 2006 strh.w r2, [sp, #6] + UNUSED(tmpreg); + 80019c8: f8bd 2006 ldrh.w r2, [sp, #6] + if (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT) + 80019cc: 6b22 ldr r2, [r4, #48] ; 0x30 + 80019ce: 2a02 cmp r2, #2 + 80019d0: f47f af6b bne.w 80018aa + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 80019d4: 689a ldr r2, [r3, #8] + 80019d6: 07d2 lsls r2, r2, #31 + 80019d8: d5fc bpl.n 80019d4 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 80019da: 7b1a ldrb r2, [r3, #12] + 80019dc: e761 b.n 80018a2 + errorcode = HAL_BUSY; + 80019de: 2102 movs r1, #2 + 80019e0: e6f4 b.n 80017cc + __HAL_LOCK(hspi); + 80019e2: 2102 movs r1, #2 + 80019e4: e6f8 b.n 80017d8 + +080019e6 : +{ + 80019e6: e92d 41ff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, lr} + 80019ea: 461f mov r7, r3 + __IO uint16_t tmpreg = 0U; + 80019ec: 2300 movs r3, #0 + 80019ee: f8ad 300e strh.w r3, [sp, #14] + if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES)) + 80019f2: 6843 ldr r3, [r0, #4] + 80019f4: f5b3 7f82 cmp.w r3, #260 ; 0x104 +{ + 80019f8: 4604 mov r4, r0 + 80019fa: 460e mov r6, r1 + 80019fc: 4615 mov r5, r2 + if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES)) + 80019fe: d10c bne.n 8001a1a + 8001a00: 6883 ldr r3, [r0, #8] + 8001a02: b953 cbnz r3, 8001a1a + hspi->State = HAL_SPI_STATE_BUSY_RX; + 8001a04: 2304 movs r3, #4 + 8001a06: f880 305d strb.w r3, [r0, #93] ; 0x5d + return HAL_SPI_TransmitReceive(hspi, pData, pData, Size, Timeout); + 8001a0a: 4613 mov r3, r2 + 8001a0c: 9700 str r7, [sp, #0] + 8001a0e: 460a mov r2, r1 + 8001a10: f7ff feb3 bl 800177a +} + 8001a14: b004 add sp, #16 + 8001a16: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + __HAL_LOCK(hspi); + 8001a1a: f894 305c ldrb.w r3, [r4, #92] ; 0x5c + 8001a1e: 2b01 cmp r3, #1 + 8001a20: f000 80dd beq.w 8001bde + 8001a24: 2301 movs r3, #1 + 8001a26: f884 305c strb.w r3, [r4, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8001a2a: f005 fcd7 bl 80073dc + if (hspi->State != HAL_SPI_STATE_READY) + 8001a2e: f894 305d ldrb.w r3, [r4, #93] ; 0x5d + 8001a32: 2b01 cmp r3, #1 + tickstart = HAL_GetTick(); + 8001a34: 4680 mov r8, r0 + if (hspi->State != HAL_SPI_STATE_READY) + 8001a36: b2d8 uxtb r0, r3 + 8001a38: f040 80cf bne.w 8001bda + if ((pData == NULL) || (Size == 0U)) + 8001a3c: 2e00 cmp r6, #0 + 8001a3e: f000 8092 beq.w 8001b66 + 8001a42: 2d00 cmp r5, #0 + 8001a44: f000 808f beq.w 8001b66 + hspi->State = HAL_SPI_STATE_BUSY_RX; + 8001a48: 2304 movs r3, #4 + 8001a4a: f884 305d strb.w r3, [r4, #93] ; 0x5d + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001a4e: 6aa3 ldr r3, [r4, #40] ; 0x28 + hspi->RxXferSize = Size; + 8001a50: f8a4 5044 strh.w r5, [r4, #68] ; 0x44 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001a54: 2100 movs r1, #0 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001a56: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + hspi->ErrorCode = HAL_SPI_ERROR_NONE; + 8001a5a: 6621 str r1, [r4, #96] ; 0x60 + hspi->TxISR = NULL; + 8001a5c: e9c4 1113 strd r1, r1, [r4, #76] ; 0x4c + hspi->RxXferCount = Size; + 8001a60: f8a4 5046 strh.w r5, [r4, #70] ; 0x46 + hspi->pRxBuffPtr = (uint8_t *)pData; + 8001a64: 6426 str r6, [r4, #64] ; 0x40 + SPI_RESET_CRC(hspi); + 8001a66: 6825 ldr r5, [r4, #0] + hspi->pTxBuffPtr = (uint8_t *)NULL; + 8001a68: 63a1 str r1, [r4, #56] ; 0x38 + hspi->TxXferSize = 0U; + 8001a6a: 87a1 strh r1, [r4, #60] ; 0x3c + hspi->TxXferCount = 0U; + 8001a6c: 87e1 strh r1, [r4, #62] ; 0x3e + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001a6e: d10d bne.n 8001a8c + SPI_RESET_CRC(hspi); + 8001a70: 682b ldr r3, [r5, #0] + 8001a72: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 8001a76: 602b str r3, [r5, #0] + 8001a78: 682b ldr r3, [r5, #0] + 8001a7a: f443 5300 orr.w r3, r3, #8192 ; 0x2000 + 8001a7e: 602b str r3, [r5, #0] + hspi->RxXferCount--; + 8001a80: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 8001a84: 3b01 subs r3, #1 + 8001a86: b29b uxth r3, r3 + 8001a88: f8a4 3046 strh.w r3, [r4, #70] ; 0x46 + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 8001a8c: 68e3 ldr r3, [r4, #12] + 8001a8e: f5b3 6fe0 cmp.w r3, #1792 ; 0x700 + CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 8001a92: 686b ldr r3, [r5, #4] + 8001a94: bf8c ite hi + 8001a96: f423 5380 bichi.w r3, r3, #4096 ; 0x1000 + SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD); + 8001a9a: f443 5380 orrls.w r3, r3, #4096 ; 0x1000 + 8001a9e: 606b str r3, [r5, #4] + if (hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001aa0: 68a3 ldr r3, [r4, #8] + 8001aa2: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + SPI_1LINE_RX(hspi); + 8001aa6: bf02 ittt eq + 8001aa8: 682b ldreq r3, [r5, #0] + 8001aaa: f423 4380 biceq.w r3, r3, #16384 ; 0x4000 + 8001aae: 602b streq r3, [r5, #0] + if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) + 8001ab0: 682b ldr r3, [r5, #0] + 8001ab2: 0658 lsls r0, r3, #25 + 8001ab4: d403 bmi.n 8001abe + __HAL_SPI_ENABLE(hspi); + 8001ab6: 682b ldr r3, [r5, #0] + 8001ab8: f043 0340 orr.w r3, r3, #64 ; 0x40 + 8001abc: 602b str r3, [r5, #0] + while (hspi->RxXferCount > 0U) + 8001abe: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) + 8001ac2: 6822 ldr r2, [r4, #0] + while (hspi->RxXferCount > 0U) + 8001ac4: b29b uxth r3, r3 + 8001ac6: 2b00 cmp r3, #0 + 8001ac8: d13e bne.n 8001b48 + if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) + 8001aca: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8001acc: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 8001ad0: d11c bne.n 8001b0c + SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT); + 8001ad2: 6813 ldr r3, [r2, #0] + 8001ad4: f443 5380 orr.w r3, r3, #4096 ; 0x1000 + 8001ad8: 6013 str r3, [r2, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001ada: 6893 ldr r3, [r2, #8] + 8001adc: 07df lsls r7, r3, #31 + 8001ade: d5fc bpl.n 8001ada + if (hspi->Init.DataSize > SPI_DATASIZE_8BIT) + 8001ae0: 68e3 ldr r3, [r4, #12] + 8001ae2: f5b3 6fe0 cmp.w r3, #1792 ; 0x700 + (*(uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 8001ae6: bf95 itete ls + 8001ae8: 7b13 ldrbls r3, [r2, #12] + *((uint16_t *)pData) = hspi->Instance->DR; + 8001aea: 68d3 ldrhi r3, [r2, #12] + (*(uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 8001aec: 7033 strbls r3, [r6, #0] + *((uint16_t *)pData) = hspi->Instance->DR; + 8001aee: 8033 strhhi r3, [r6, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001af0: 6823 ldr r3, [r4, #0] + 8001af2: 689a ldr r2, [r3, #8] + 8001af4: 07d6 lsls r6, r2, #31 + 8001af6: d5fc bpl.n 8001af2 + if (hspi->Init.DataSize == SPI_DATASIZE_16BIT) + 8001af8: 68e1 ldr r1, [r4, #12] + 8001afa: f5b1 6f70 cmp.w r1, #3840 ; 0xf00 + 8001afe: d142 bne.n 8001b86 + tmpreg = hspi->Instance->DR; + 8001b00: 68db ldr r3, [r3, #12] + 8001b02: b29b uxth r3, r3 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 8001b04: f8ad 300e strh.w r3, [sp, #14] + UNUSED(tmpreg); + 8001b08: f8bd 300e ldrh.w r3, [sp, #14] + * @param Tickstart: tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef SPI_EndRxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart) +{ + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001b0c: 6861 ldr r1, [r4, #4] + 8001b0e: 6823 ldr r3, [r4, #0] + 8001b10: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 8001b14: d10a bne.n 8001b2c + 8001b16: 68a2 ldr r2, [r4, #8] + 8001b18: f5b2 4f00 cmp.w r2, #32768 ; 0x8000 + 8001b1c: d002 beq.n 8001b24 + || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY))) + 8001b1e: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + 8001b22: d103 bne.n 8001b2c + { + /* Disable SPI peripheral */ + __HAL_SPI_DISABLE(hspi); + 8001b24: 681a ldr r2, [r3, #0] + 8001b26: f022 0240 bic.w r2, r2, #64 ; 0x40 + 8001b2a: 601a str r2, [r3, #0] + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001b2c: 689a ldr r2, [r3, #8] + 8001b2e: 0610 lsls r0, r2, #24 + 8001b30: d4fc bmi.n 8001b2c + { + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG); + return HAL_TIMEOUT; + } + + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001b32: f5b1 7f82 cmp.w r1, #260 ; 0x104 + 8001b36: d036 beq.n 8001ba6 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)) + 8001b38: 689a ldr r2, [r3, #8] + 8001b3a: 06d2 lsls r2, r2, #27 + 8001b3c: d445 bmi.n 8001bca + if (hspi->ErrorCode != HAL_SPI_ERROR_NONE) + 8001b3e: 6e20 ldr r0, [r4, #96] ; 0x60 + errorcode = HAL_BUSY; + 8001b40: 3800 subs r0, #0 + 8001b42: bf18 it ne + 8001b44: 2001 movne r0, #1 +error : + 8001b46: e00e b.n 8001b66 + if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) + 8001b48: 6893 ldr r3, [r2, #8] + 8001b4a: 07d9 lsls r1, r3, #31 + 8001b4c: d509 bpl.n 8001b62 + (* (uint8_t *)pData) = *(__IO uint8_t *)&hspi->Instance->DR; + 8001b4e: 7b13 ldrb r3, [r2, #12] + 8001b50: f806 3b01 strb.w r3, [r6], #1 + hspi->RxXferCount--; + 8001b54: f8b4 3046 ldrh.w r3, [r4, #70] ; 0x46 + 8001b58: 3b01 subs r3, #1 + 8001b5a: b29b uxth r3, r3 + 8001b5c: f8a4 3046 strh.w r3, [r4, #70] ; 0x46 + 8001b60: e7ad b.n 8001abe + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 8001b62: b93f cbnz r7, 8001b74 + errorcode = HAL_TIMEOUT; + 8001b64: 2003 movs r0, #3 + hspi->State = HAL_SPI_STATE_READY; + 8001b66: 2301 movs r3, #1 + 8001b68: f884 305d strb.w r3, [r4, #93] ; 0x5d + __HAL_UNLOCK(hspi); + 8001b6c: 2300 movs r3, #0 + 8001b6e: f884 305c strb.w r3, [r4, #92] ; 0x5c + return errorcode; + 8001b72: e74f b.n 8001a14 + if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >= Timeout))) + 8001b74: 1c7b adds r3, r7, #1 + 8001b76: d0a2 beq.n 8001abe + 8001b78: f005 fc30 bl 80073dc + 8001b7c: eba0 0008 sub.w r0, r0, r8 + 8001b80: 42b8 cmp r0, r7 + 8001b82: d39c bcc.n 8001abe + 8001b84: e7ee b.n 8001b64 + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 8001b86: 7b1a ldrb r2, [r3, #12] + 8001b88: f8ad 200e strh.w r2, [sp, #14] + if ((hspi->Init.DataSize == SPI_DATASIZE_8BIT) && (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT)) + 8001b8c: f5b1 6fe0 cmp.w r1, #1792 ; 0x700 + UNUSED(tmpreg); + 8001b90: f8bd 200e ldrh.w r2, [sp, #14] + if ((hspi->Init.DataSize == SPI_DATASIZE_8BIT) && (hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT)) + 8001b94: d1ba bne.n 8001b0c + 8001b96: 6b22 ldr r2, [r4, #48] ; 0x30 + 8001b98: 2a02 cmp r2, #2 + 8001b9a: d1b7 bne.n 8001b0c + while ((__HAL_SPI_GET_FLAG(hspi, Flag) ? SET : RESET) != State) + 8001b9c: 689a ldr r2, [r3, #8] + 8001b9e: 07d5 lsls r5, r2, #31 + 8001ba0: d5fc bpl.n 8001b9c + tmpreg = *(__IO uint8_t *)&hspi->Instance->DR; + 8001ba2: 7b1b ldrb r3, [r3, #12] + 8001ba4: e7ae b.n 8001b04 + if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE) + 8001ba6: 68a2 ldr r2, [r4, #8] + 8001ba8: f5b2 4f00 cmp.w r2, #32768 ; 0x8000 + 8001bac: d002 beq.n 8001bb4 + || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY))) + 8001bae: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + 8001bb2: d1c1 bne.n 8001b38 + while ((hspi->Instance->SR & Fifo) != State) + 8001bb4: 689a ldr r2, [r3, #8] + 8001bb6: f412 6fc0 tst.w r2, #1536 ; 0x600 + 8001bba: d0bd beq.n 8001b38 + tmpreg = *((__IO uint8_t *)&hspi->Instance->DR); + 8001bbc: 7b1a ldrb r2, [r3, #12] + 8001bbe: b2d2 uxtb r2, r2 + 8001bc0: f88d 200d strb.w r2, [sp, #13] + UNUSED(tmpreg); + 8001bc4: f89d 200d ldrb.w r2, [sp, #13] + 8001bc8: e7f4 b.n 8001bb4 + SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC); + 8001bca: 6e22 ldr r2, [r4, #96] ; 0x60 + 8001bcc: f042 0202 orr.w r2, r2, #2 + 8001bd0: 6622 str r2, [r4, #96] ; 0x60 + __HAL_SPI_CLEAR_CRCERRFLAG(hspi); + 8001bd2: f64f 72ef movw r2, #65519 ; 0xffef + 8001bd6: 609a str r2, [r3, #8] + 8001bd8: e7b1 b.n 8001b3e + errorcode = HAL_BUSY; + 8001bda: 2002 movs r0, #2 + 8001bdc: e7c3 b.n 8001b66 + __HAL_LOCK(hspi); + 8001bde: 2002 movs r0, #2 + 8001be0: e718 b.n 8001a14 + ... + +08001be4 : + +// checksum_more() +// + static void +checksum_more(SHA256_CTX *ctx, uint32_t *total, const uint8_t *addr, int len) +{ + 8001be4: b5f8 push {r3, r4, r5, r6, r7, lr} + 8001be6: 460c mov r4, r1 + // mk4 has hardware hash engine, and no DFU button + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001be8: 6809 ldr r1, [r1, #0] +{ + 8001bea: 461d mov r5, r3 + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001bec: 2364 movs r3, #100 ; 0x64 +{ + 8001bee: 4617 mov r7, r2 + 8001bf0: 4606 mov r6, r0 + int percent = ((*total) * 100) / TOTAL_CHECKSUM_LEN; + 8001bf2: 4359 muls r1, r3 + puts2("Verify %0x"); + puthex2(percent); + putchar('\n'); +#endif + + oled_show_progress(screen_verify, percent); + 8001bf4: 4807 ldr r0, [pc, #28] ; (8001c14 ) + 8001bf6: 4b08 ldr r3, [pc, #32] ; (8001c18 ) + 8001bf8: fbb1 f1f3 udiv r1, r1, r3 + 8001bfc: f7ff fab4 bl 8001168 + + sha256_update(ctx, addr, len); + 8001c00: 462a mov r2, r5 + 8001c02: 4639 mov r1, r7 + 8001c04: 4630 mov r0, r6 + 8001c06: f003 fdc7 bl 8005798 + *total += len; + 8001c0a: 6823 ldr r3, [r4, #0] + 8001c0c: 442b add r3, r5 + 8001c0e: 6023 str r3, [r4, #0] +} + 8001c10: bdf8 pop {r3, r4, r5, r6, r7, pc} + 8001c12: bf00 nop + 8001c14: 0801004d .word 0x0801004d + 8001c18: 0018541c .word 0x0018541c + +08001c1c : + +// checksum_flash() +// + void +checksum_flash(uint8_t fw_digest[32], uint8_t world_digest[32], uint32_t fw_length) +{ + 8001c1c: b570 push {r4, r5, r6, lr} + 8001c1e: b09c sub sp, #112 ; 0x70 + 8001c20: 4606 mov r6, r0 + 8001c22: 460d mov r5, r1 + 8001c24: 4614 mov r4, r2 + const uint8_t *start = (const uint8_t *)FIRMWARE_START; + + rng_delay(); + 8001c26: f000 fe6f bl 8002908 + + SHA256_CTX ctx; + uint32_t total_len = 0; + 8001c2a: 2300 movs r3, #0 + 8001c2c: 9300 str r3, [sp, #0] + + if(fw_length == 0) { + 8001c2e: 2c00 cmp r4, #0 + 8001c30: d15f bne.n 8001cf2 + uint8_t first[32]; + sha256_init(&ctx); + 8001c32: a809 add r0, sp, #36 ; 0x24 + 8001c34: f003 fda2 bl 800577c + + // use length from header in flash + fw_length = FW_HDR->firmware_length; + 8001c38: 4b36 ldr r3, [pc, #216] ; (8001d14 ) + + // start of firmware (just after we end) to header + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001c3a: 4a37 ldr r2, [pc, #220] ; (8001d18 ) + fw_length = FW_HDR->firmware_length; + 8001c3c: f8d3 4098 ldr.w r4, [r3, #152] ; 0x98 + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001c40: 4669 mov r1, sp + 8001c42: f44f 537f mov.w r3, #16320 ; 0x3fc0 + 8001c46: a809 add r0, sp, #36 ; 0x24 + 8001c48: f7ff ffcc bl 8001be4 + + // from after header to end + checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE, + 8001c4c: 4a33 ldr r2, [pc, #204] ; (8001d1c ) + 8001c4e: f5a4 4380 sub.w r3, r4, #16384 ; 0x4000 + 8001c52: 4669 mov r1, sp + 8001c54: a809 add r0, sp, #36 ; 0x24 + 8001c56: f7ff ffc5 bl 8001be4 + fw_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + + sha256_final(&ctx, first); + 8001c5a: a901 add r1, sp, #4 + 8001c5c: a809 add r0, sp, #36 ; 0x24 + 8001c5e: f003 fde1 bl 8005824 + + // double SHA256 + sha256_single(first, sizeof(first), fw_digest); + 8001c62: 4632 mov r2, r6 + 8001c64: 2120 movs r1, #32 + 8001c66: a801 add r0, sp, #4 + 8001c68: f003 fdf0 bl 800584c + // fw_digest should already be populated by caller + total_len = fw_length - 64; + } + + // start over, and get the rest of flash. All of it. + sha256_init(&ctx); + 8001c6c: a809 add r0, sp, #36 ; 0x24 + 8001c6e: f003 fd85 bl 800577c + + // .. and chain in what we have so far + sha256_update(&ctx, fw_digest, 32); + 8001c72: 2220 movs r2, #32 + 8001c74: 4631 mov r1, r6 + 8001c76: a809 add r0, sp, #36 ; 0x24 + 8001c78: f003 fd8e bl 8005798 + + // Bootloader, including pairing secret area, but excluding MCU keys. + const uint8_t *base = (const uint8_t *)BL_FLASH_BASE; + checksum_more(&ctx, &total_len, base, ((uint8_t *)MCU_KEYS)-base); + 8001c7c: f44f 33f0 mov.w r3, #122880 ; 0x1e000 + 8001c80: f04f 6200 mov.w r2, #134217728 ; 0x8000000 + 8001c84: 4669 mov r1, sp + 8001c86: a809 add r0, sp, #36 ; 0x24 + 8001c88: f7ff ffac bl 8001be4 + + // Probably-blank area after firmware, and filesystem area. + // Important: firmware images (fw_length) must be aligned with flash erase unit size (4k). + const uint8_t *fs = start + fw_length; + const uint8_t *last = base + MAIN_FLASH_SIZE; + checksum_more(&ctx, &total_len, fs, last-fs); + 8001c8c: f104 6200 add.w r2, r4, #134217728 ; 0x8000000 + 8001c90: f5c4 13b0 rsb r3, r4, #1441792 ; 0x160000 + 8001c94: f502 3200 add.w r2, r2, #131072 ; 0x20000 + 8001c98: 4669 mov r1, sp + 8001c9a: a809 add r0, sp, #36 ; 0x24 + 8001c9c: f7ff ffa2 bl 8001be4 + + rng_delay(); + 8001ca0: f000 fe32 bl 8002908 + + // OTP area + checksum_more(&ctx, &total_len, (void *)0x1fff7000, 0x400); + 8001ca4: 4a1e ldr r2, [pc, #120] ; (8001d20 ) + 8001ca6: f44f 6380 mov.w r3, #1024 ; 0x400 + 8001caa: 4669 mov r1, sp + 8001cac: a809 add r0, sp, #36 ; 0x24 + 8001cae: f7ff ff99 bl 8001be4 + + // "just in case" ... the option bytes (2 banks) + checksum_more(&ctx, &total_len, (void *)0x1fff7800, 0x28); + 8001cb2: 4a1c ldr r2, [pc, #112] ; (8001d24 ) + 8001cb4: 2328 movs r3, #40 ; 0x28 + 8001cb6: 4669 mov r1, sp + 8001cb8: a809 add r0, sp, #36 ; 0x24 + 8001cba: f7ff ff93 bl 8001be4 + checksum_more(&ctx, &total_len, (void *)0x1ffff800, 0x28); + 8001cbe: 4a1a ldr r2, [pc, #104] ; (8001d28 ) + 8001cc0: 2328 movs r3, #40 ; 0x28 + 8001cc2: 4669 mov r1, sp + 8001cc4: a809 add r0, sp, #36 ; 0x24 + 8001cc6: f7ff ff8d bl 8001be4 + + // System ROM (they say it can't change, but clearly + // implemented as flash cells) + checksum_more(&ctx, &total_len, (void *)0x1fff0000, 0x7000); + 8001cca: 4a18 ldr r2, [pc, #96] ; (8001d2c ) + 8001ccc: f44f 43e0 mov.w r3, #28672 ; 0x7000 + 8001cd0: 4669 mov r1, sp + 8001cd2: a809 add r0, sp, #36 ; 0x24 + 8001cd4: f7ff ff86 bl 8001be4 + + // device serial number, just for kicks + checksum_more(&ctx, &total_len, (void *)0x1fff7590, 12); + 8001cd8: 4a15 ldr r2, [pc, #84] ; (8001d30 ) + 8001cda: 230c movs r3, #12 + 8001cdc: 4669 mov r1, sp + 8001cde: a809 add r0, sp, #36 ; 0x24 + 8001ce0: f7ff ff80 bl 8001be4 + + ASSERT(total_len == TOTAL_CHECKSUM_LEN); + 8001ce4: 4b13 ldr r3, [pc, #76] ; (8001d34 ) + 8001ce6: 9a00 ldr r2, [sp, #0] + 8001ce8: 429a cmp r2, r3 + 8001cea: d006 beq.n 8001cfa + 8001cec: 4812 ldr r0, [pc, #72] ; (8001d38 ) + 8001cee: f7fe fea3 bl 8000a38 + total_len = fw_length - 64; + 8001cf2: f1a4 0340 sub.w r3, r4, #64 ; 0x40 + 8001cf6: 9300 str r3, [sp, #0] + 8001cf8: e7b8 b.n 8001c6c + + sha256_final(&ctx, world_digest); + 8001cfa: 4629 mov r1, r5 + 8001cfc: a809 add r0, sp, #36 ; 0x24 + 8001cfe: f003 fd91 bl 8005824 + + // double SHA256 (a bitcoin fetish) + sha256_single(world_digest, 32, world_digest); + 8001d02: 462a mov r2, r5 + 8001d04: 2120 movs r1, #32 + 8001d06: 4628 mov r0, r5 + 8001d08: f003 fda0 bl 800584c + + rng_delay(); + 8001d0c: f000 fdfc bl 8002908 +} + 8001d10: b01c add sp, #112 ; 0x70 + 8001d12: bd70 pop {r4, r5, r6, pc} + 8001d14: 08023f00 .word 0x08023f00 + 8001d18: 08020000 .word 0x08020000 + 8001d1c: 08024000 .word 0x08024000 + 8001d20: 1fff7000 .word 0x1fff7000 + 8001d24: 1fff7800 .word 0x1fff7800 + 8001d28: 1ffff800 .word 0x1ffff800 + 8001d2c: 1fff0000 .word 0x1fff0000 + 8001d30: 1fff7590 .word 0x1fff7590 + 8001d34: 0018541c .word 0x0018541c + 8001d38: 0801046c .word 0x0801046c + +08001d3c : +// Scan the OTP area and determine what the current min-version (timestamp) +// we can allow. All zeros if any if okay. +// + void +get_min_version(uint8_t min_version[8]) +{ + 8001d3c: b570 push {r4, r5, r6, lr} + 8001d3e: 4604 mov r4, r0 + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + 8001d40: 4d0c ldr r5, [pc, #48] ; (8001d74 ) + + rng_delay(); + memset(min_version, 0, 8); + + for(int i=0; i) + rng_delay(); + 8001d44: f000 fde0 bl 8002908 + memset(min_version, 0, 8); + 8001d48: 2300 movs r3, #0 + 8001d4a: 6023 str r3, [r4, #0] + 8001d4c: 6063 str r3, [r4, #4] + // is it programmed? + if(otp[0] == 0xff) continue; + + // is it a timestamp value? + if(otp[0] >= 0x40) continue; + if(otp[0] < 0x10) continue; + 8001d4e: 782b ldrb r3, [r5, #0] + 8001d50: 3b10 subs r3, #16 + 8001d52: 2b2f cmp r3, #47 ; 0x2f + 8001d54: d80a bhi.n 8001d6c + + if(memcmp(otp, min_version, 8) > 0) { + 8001d56: 4621 mov r1, r4 + 8001d58: 2208 movs r2, #8 + 8001d5a: 4628 mov r0, r5 + 8001d5c: f00b fdc4 bl 800d8e8 + 8001d60: 2800 cmp r0, #0 + memcpy(min_version, otp, 8); + 8001d62: bfc1 itttt gt + 8001d64: 462b movgt r3, r5 + 8001d66: cb03 ldmiagt r3!, {r0, r1} + 8001d68: 6020 strgt r0, [r4, #0] + 8001d6a: 6061 strgt r1, [r4, #4] + for(int i=0; i + } + } +} + 8001d72: bd70 pop {r4, r5, r6, pc} + 8001d74: 1fff7000 .word 0x1fff7000 + 8001d78: 1fff7400 .word 0x1fff7400 + +08001d7c : + +// check_is_downgrade() +// + bool +check_is_downgrade(const uint8_t timestamp[8], const char *version) +{ + 8001d7c: b513 push {r0, r1, r4, lr} + 8001d7e: 4604 mov r4, r0 + } +#endif + + // look at FW_HDR->timestamp and compare to a growing list in main flash OTP + uint8_t min[8]; + get_min_version(min); + 8001d80: 4668 mov r0, sp + 8001d82: f7ff ffdb bl 8001d3c + + return (memcmp(timestamp, min, 8) < 0); + 8001d86: 2208 movs r2, #8 + 8001d88: 4669 mov r1, sp + 8001d8a: 4620 mov r0, r4 + 8001d8c: f00b fdac bl 800d8e8 +} + 8001d90: 0fc0 lsrs r0, r0, #31 + 8001d92: b002 add sp, #8 + 8001d94: bd10 pop {r4, pc} + +08001d96 : + +// warn_fishy_firmware() +// + void +warn_fishy_firmware(const uint8_t *pixels) +{ + 8001d96: b538 push {r3, r4, r5, lr} + 8001d98: 4605 mov r5, r0 + const int wait = 100; +#else + const int wait = 10; +#endif + + for(int i=0; i < wait; i++) { + 8001d9a: 2400 movs r4, #0 + oled_show_progress(pixels, (i*100)/wait); + 8001d9c: 4621 mov r1, r4 + 8001d9e: 4628 mov r0, r5 + 8001da0: f7ff f9e2 bl 8001168 + for(int i=0; i < wait; i++) { + 8001da4: 3401 adds r4, #1 + + delay_ms(250); + 8001da6: 20fa movs r0, #250 ; 0xfa + 8001da8: f001 fe6c bl 8003a84 + for(int i=0; i < wait; i++) { + 8001dac: 2c64 cmp r4, #100 ; 0x64 + 8001dae: d1f5 bne.n 8001d9c + } +} + 8001db0: bd38 pop {r3, r4, r5, pc} + ... + +08001db4 : + +// verify_header() +// + bool +verify_header(const coldcardFirmwareHeader_t *hdr) +{ + 8001db4: b510 push {r4, lr} + 8001db6: 4604 mov r4, r0 + rng_delay(); + 8001db8: f000 fda6 bl 8002908 + + if(hdr->magic_value != FW_HEADER_MAGIC) goto fail; + 8001dbc: 6822 ldr r2, [r4, #0] + 8001dbe: 4b0b ldr r3, [pc, #44] ; (8001dec ) + 8001dc0: 429a cmp r2, r3 + 8001dc2: d110 bne.n 8001de6 + if(hdr->version_string[0] == 0x0) goto fail; + 8001dc4: 7b20 ldrb r0, [r4, #12] + 8001dc6: b168 cbz r0, 8001de4 + if(hdr->timestamp[0] >= 0x40) goto fail; // 22 yr product lifetime + 8001dc8: 7923 ldrb r3, [r4, #4] + 8001dca: 2b3f cmp r3, #63 ; 0x3f + 8001dcc: d80b bhi.n 8001de6 + if(hdr->firmware_length < FW_MIN_LENGTH) goto fail; + 8001dce: 69a3 ldr r3, [r4, #24] + 8001dd0: f5a3 2380 sub.w r3, r3, #262144 ; 0x40000 + 8001dd4: f5b3 1fd0 cmp.w r3, #1703936 ; 0x1a0000 + 8001dd8: d205 bcs.n 8001de6 + if(hdr->firmware_length >= FW_MAX_LENGTH_MK4) goto fail; + if(hdr->pubkey_num >= NUM_KNOWN_PUBKEYS) goto fail; + 8001dda: 6960 ldr r0, [r4, #20] + 8001ddc: 2805 cmp r0, #5 + 8001dde: bf8c ite hi + 8001de0: 2000 movhi r0, #0 + 8001de2: 2001 movls r0, #1 + + return true; +fail: + return false; +} + 8001de4: bd10 pop {r4, pc} + return false; + 8001de6: 2000 movs r0, #0 + 8001de8: e7fc b.n 8001de4 + 8001dea: bf00 nop + 8001dec: cc001234 .word 0xcc001234 + +08001df0 : +// +// Given double-sha256 over the firmware bytes, check the signature. +// + bool +verify_signature(const coldcardFirmwareHeader_t *hdr, const uint8_t fw_check[32]) +{ + 8001df0: b530 push {r4, r5, lr} + // this takes a few ms at least, not fast. + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001df2: 6943 ldr r3, [r0, #20] + 8001df4: 4d0b ldr r5, [pc, #44] ; (8001e24 ) +{ + 8001df6: b085 sub sp, #20 + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001df8: eb05 1583 add.w r5, r5, r3, lsl #6 +{ + 8001dfc: 4604 mov r4, r0 + 8001dfe: 9103 str r1, [sp, #12] + int ok = uECC_verify(approved_pubkeys[hdr->pubkey_num], fw_check, 32, + 8001e00: f004 fee8 bl 8006bd4 + 8001e04: f104 0340 add.w r3, r4, #64 ; 0x40 + 8001e08: 9903 ldr r1, [sp, #12] + 8001e0a: 9000 str r0, [sp, #0] + 8001e0c: 2220 movs r2, #32 + 8001e0e: 4628 mov r0, r5 + 8001e10: f005 f967 bl 80070e2 + 8001e14: 4604 mov r4, r0 + hdr->signature, uECC_secp256k1()); + + //puts(ok ? "Sig ok" : "Sig fail"); + rng_delay(); + 8001e16: f000 fd77 bl 8002908 + + return ok; +} + 8001e1a: 1e20 subs r0, r4, #0 + 8001e1c: bf18 it ne + 8001e1e: 2001 movne r0, #1 + 8001e20: b005 add sp, #20 + 8001e22: bd30 pop {r4, r5, pc} + 8001e24: 080104e6 .word 0x080104e6 + +08001e28 : +// Check hdr, and even signature of protential new firmware in PSRAM. +// Returns checksum needed for 608 +// + bool +verify_firmware_in_ram(const uint8_t *start, uint32_t len, uint8_t world_check[32]) +{ + 8001e28: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + const coldcardFirmwareHeader_t *hdr = (const coldcardFirmwareHeader_t *) + 8001e2c: f500 567e add.w r6, r0, #16256 ; 0x3f80 +{ + 8001e30: b09c sub sp, #112 ; 0x70 + 8001e32: 4605 mov r5, r0 + (start + FW_HEADER_OFFSET); + uint8_t fw_digest[32]; + + // check basics like verison, hw compat, etc + if(!verify_header(hdr)) goto fail; + 8001e34: 4630 mov r0, r6 +{ + 8001e36: 4617 mov r7, r2 + if(!verify_header(hdr)) goto fail; + 8001e38: f7ff ffbc bl 8001db4 + 8001e3c: 4604 mov r4, r0 + 8001e3e: b150 cbz r0, 8001e56 + + if(check_is_downgrade(hdr->timestamp, (const char *)hdr->version_string)) { + 8001e40: f106 010c add.w r1, r6, #12 + 8001e44: 1d30 adds r0, r6, #4 + 8001e46: f7ff ff99 bl 8001d7c + 8001e4a: 4604 mov r4, r0 + 8001e4c: b138 cbz r0, 8001e5e + puts("downgrade"); + 8001e4e: 481e ldr r0, [pc, #120] ; (8001ec8 ) + 8001e50: f003 f90a bl 8005068 + + checksum_flash(fw_digest, world_check, hdr->firmware_length); + + return true; +fail: + return false; + 8001e54: 2400 movs r4, #0 +} + 8001e56: 4620 mov r0, r4 + 8001e58: b01c add sp, #112 ; 0x70 + 8001e5a: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + rng_delay(); + 8001e5e: f000 fd53 bl 8002908 + hdr->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + 8001e62: f505 5840 add.w r8, r5, #12288 ; 0x3000 + sha256_init(&ctx); + 8001e66: a809 add r0, sp, #36 ; 0x24 + uint32_t total_len = 0; + 8001e68: 9400 str r4, [sp, #0] + sha256_init(&ctx); + 8001e6a: f003 fc87 bl 800577c + checksum_more(&ctx, &total_len, start, FW_HEADER_OFFSET + FW_HEADER_SIZE - 64); + 8001e6e: f44f 537f mov.w r3, #16320 ; 0x3fc0 + 8001e72: 462a mov r2, r5 + 8001e74: 4669 mov r1, sp + 8001e76: a809 add r0, sp, #36 ; 0x24 + 8001e78: f7ff feb4 bl 8001be4 + hdr->firmware_length - (FW_HEADER_OFFSET + FW_HEADER_SIZE)); + 8001e7c: f8d8 3f98 ldr.w r3, [r8, #3992] ; 0xf98 + checksum_more(&ctx, &total_len, start + FW_HEADER_OFFSET + FW_HEADER_SIZE, + 8001e80: f505 4280 add.w r2, r5, #16384 ; 0x4000 + 8001e84: f5a3 4380 sub.w r3, r3, #16384 ; 0x4000 + 8001e88: 4669 mov r1, sp + 8001e8a: a809 add r0, sp, #36 ; 0x24 + 8001e8c: f7ff feaa bl 8001be4 + sha256_final(&ctx, fw_digest); + 8001e90: a901 add r1, sp, #4 + 8001e92: a809 add r0, sp, #36 ; 0x24 + 8001e94: f003 fcc6 bl 8005824 + sha256_single(fw_digest, 32, fw_digest); + 8001e98: aa01 add r2, sp, #4 + 8001e9a: 4610 mov r0, r2 + 8001e9c: 2120 movs r1, #32 + 8001e9e: f003 fcd5 bl 800584c + rng_delay(); + 8001ea2: f000 fd31 bl 8002908 + if(!verify_signature(hdr, fw_digest)) { + 8001ea6: a901 add r1, sp, #4 + 8001ea8: 4630 mov r0, r6 + 8001eaa: f7ff ffa1 bl 8001df0 + 8001eae: 4604 mov r4, r0 + 8001eb0: b918 cbnz r0, 8001eba + puts("sig fail"); + 8001eb2: 4806 ldr r0, [pc, #24] ; (8001ecc ) + 8001eb4: f003 f8d8 bl 8005068 + goto fail; + 8001eb8: e7cd b.n 8001e56 + checksum_flash(fw_digest, world_check, hdr->firmware_length); + 8001eba: f8d8 2f98 ldr.w r2, [r8, #3992] ; 0xf98 + 8001ebe: 4639 mov r1, r7 + 8001ec0: a801 add r0, sp, #4 + 8001ec2: f7ff feab bl 8001c1c + return true; + 8001ec6: e7c6 b.n 8001e56 + 8001ec8: 08010473 .word 0x08010473 + 8001ecc: 0801047d .word 0x0801047d + +08001ed0 : +// - don't set the light at this point. +// - requires bootloader to have been unchanged since world_check recorded (debug issue) +// + bool +verify_world_checksum(const uint8_t world_check[32]) +{ + 8001ed0: b507 push {r0, r1, r2, lr} + 8001ed2: 9001 str r0, [sp, #4] + ae_setup(); + 8001ed4: f000 fe3c bl 8002b50 + ae_pair_unlock(); + 8001ed8: f001 f830 bl 8002f3c + + return (ae_checkmac_hard(KEYNUM_firmware, world_check) == 0); + 8001edc: 9901 ldr r1, [sp, #4] + 8001ede: 200e movs r0, #14 + 8001ee0: f001 f9ba bl 8003258 +} + 8001ee4: fab0 f080 clz r0, r0 + 8001ee8: 0940 lsrs r0, r0, #5 + 8001eea: b003 add sp, #12 + 8001eec: f85d fb04 ldr.w pc, [sp], #4 + +08001ef0 : + +// verify_firmware() +// + bool +verify_firmware(void) +{ + 8001ef0: b570 push {r4, r5, r6, lr} + STATIC_ASSERT(sizeof(coldcardFirmwareHeader_t) == FW_HEADER_SIZE); + + rng_delay(); + + // watch for unprogrammed header. and some + if(FW_HDR->version_string[0] == 0xff) goto blank; + 8001ef2: 4e2a ldr r6, [pc, #168] ; (8001f9c ) +{ + 8001ef4: b090 sub sp, #64 ; 0x40 + rng_delay(); + 8001ef6: f000 fd07 bl 8002908 + if(FW_HDR->version_string[0] == 0xff) goto blank; + 8001efa: f896 308c ldrb.w r3, [r6, #140] ; 0x8c + 8001efe: 2bff cmp r3, #255 ; 0xff + 8001f00: d107 bne.n 8001f12 + puts("corrupt firmware"); + oled_show(screen_corrupt); + return false; + +blank: + puts("no firmware"); + 8001f02: 4827 ldr r0, [pc, #156] ; (8001fa0 ) + puts("corrupt firmware"); + 8001f04: f003 f8b0 bl 8005068 + oled_show(screen_corrupt); + 8001f08: 4826 ldr r0, [pc, #152] ; (8001fa4 ) + 8001f0a: f7ff f8b3 bl 8001074 + return false; + 8001f0e: 2400 movs r4, #0 + 8001f10: e030 b.n 8001f74 + if(!verify_header(FW_HDR)) goto fail; + 8001f12: 4825 ldr r0, [pc, #148] ; (8001fa8 ) + 8001f14: f7ff ff4e bl 8001db4 + 8001f18: 2800 cmp r0, #0 + 8001f1a: d03c beq.n 8001f96 + rng_delay(); + 8001f1c: f000 fcf4 bl 8002908 + checksum_flash(fw_check, world_check, 0); + 8001f20: 2200 movs r2, #0 + 8001f22: a908 add r1, sp, #32 + 8001f24: 4668 mov r0, sp + 8001f26: f7ff fe79 bl 8001c1c + rng_delay(); + 8001f2a: f000 fced bl 8002908 + if(!verify_signature(FW_HDR, fw_check)) goto fail; + 8001f2e: 481e ldr r0, [pc, #120] ; (8001fa8 ) + 8001f30: 4669 mov r1, sp + 8001f32: f7ff ff5d bl 8001df0 + 8001f36: 4604 mov r4, r0 + 8001f38: b368 cbz r0, 8001f96 + int not_green = ae_set_gpio_secure(world_check); + 8001f3a: a808 add r0, sp, #32 + 8001f3c: f001 fba0 bl 8003680 + 8001f40: 4605 mov r5, r0 + rng_delay(); + 8001f42: f000 fce1 bl 8002908 + rng_delay(); + 8001f46: f000 fcdf bl 8002908 + return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC); + 8001f4a: 4b18 ldr r3, [pc, #96] ; (8001fac ) + 8001f4c: 6a1b ldr r3, [r3, #32] + 8001f4e: b2db uxtb r3, r3 + if(!flash_is_security_level2() && not_green) { + 8001f50: 2bcc cmp r3, #204 ; 0xcc + 8001f52: d008 beq.n 8001f66 + 8001f54: b18d cbz r5, 8001f7a + oled_show_progress(screen_verify, 100); + 8001f56: 4816 ldr r0, [pc, #88] ; (8001fb0 ) + 8001f58: 2164 movs r1, #100 ; 0x64 + 8001f5a: f7ff f905 bl 8001168 + puts("Factory boot"); + 8001f5e: 4815 ldr r0, [pc, #84] ; (8001fb4 ) + puts("Good firmware"); + 8001f60: f003 f882 bl 8005068 + 8001f64: e006 b.n 8001f74 + } else if(not_green) { + 8001f66: b145 cbz r5, 8001f7a + puts("WARN: Red light"); + 8001f68: 4813 ldr r0, [pc, #76] ; (8001fb8 ) + 8001f6a: f003 f87d bl 8005068 + warn_fishy_firmware(screen_red_light); + 8001f6e: 4813 ldr r0, [pc, #76] ; (8001fbc ) + warn_fishy_firmware(screen_devmode); + 8001f70: f7ff ff11 bl 8001d96 + oled_show(screen_corrupt); + + return false; +} + 8001f74: 4620 mov r0, r4 + 8001f76: b010 add sp, #64 ; 0x40 + 8001f78: bd70 pop {r4, r5, r6, pc} + } else if(FW_HDR->pubkey_num == 0) { + 8001f7a: f8d6 3094 ldr.w r3, [r6, #148] ; 0x94 + 8001f7e: b923 cbnz r3, 8001f8a + puts("WARN: Unsigned firmware"); + 8001f80: 480f ldr r0, [pc, #60] ; (8001fc0 ) + 8001f82: f003 f871 bl 8005068 + warn_fishy_firmware(screen_devmode); + 8001f86: 480f ldr r0, [pc, #60] ; (8001fc4 ) + 8001f88: e7f2 b.n 8001f70 + oled_show_progress(screen_verify, 100); + 8001f8a: 4809 ldr r0, [pc, #36] ; (8001fb0 ) + 8001f8c: 2164 movs r1, #100 ; 0x64 + 8001f8e: f7ff f8eb bl 8001168 + puts("Good firmware"); + 8001f92: 480d ldr r0, [pc, #52] ; (8001fc8 ) + 8001f94: e7e4 b.n 8001f60 + puts("corrupt firmware"); + 8001f96: 480d ldr r0, [pc, #52] ; (8001fcc ) + 8001f98: e7b4 b.n 8001f04 + 8001f9a: bf00 nop + 8001f9c: 08023f00 .word 0x08023f00 + 8001fa0: 08010486 .word 0x08010486 + 8001fa4: 0800db0d .word 0x0800db0d + 8001fa8: 08023f80 .word 0x08023f80 + 8001fac: 40022000 .word 0x40022000 + 8001fb0: 0801004d .word 0x0801004d + 8001fb4: 08010492 .word 0x08010492 + 8001fb8: 0801049f .word 0x0801049f + 8001fbc: 0800ec15 .word 0x0800ec15 + 8001fc0: 080104af .word 0x080104af + 8001fc4: 0800ddcf .word 0x0800ddcf + 8001fc8: 080104c7 .word 0x080104c7 + 8001fcc: 080104d5 .word 0x080104d5 + +08001fd0 : + void +systick_setup(void) +{ + const uint32_t ticks = HCLK_FREQUENCY/1000; + + SysTick->LOAD = (ticks - 1); + 8001fd0: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 8001fd4: 4a03 ldr r2, [pc, #12] ; (8001fe4 ) + 8001fd6: 615a str r2, [r3, #20] + SysTick->VAL = 0; + 8001fd8: 2200 movs r2, #0 + 8001fda: 619a str r2, [r3, #24] + SysTick->CTRL = SYSTICK_CLKSOURCE_HCLK | SysTick_CTRL_ENABLE_Msk; + 8001fdc: 2205 movs r2, #5 + 8001fde: 611a str r2, [r3, #16] +} + 8001fe0: 4770 bx lr + 8001fe2: bf00 nop + 8001fe4: 0001d4bf .word 0x0001d4bf + +08001fe8 : + SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; +#endif + + /* FPU settings ------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 20U)|(3UL << 22U)); /* set CP10 and CP11 Full Access */ + 8001fe8: 4a0e ldr r2, [pc, #56] ; (8002024 ) + 8001fea: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 8001fee: f443 0370 orr.w r3, r3, #15728640 ; 0xf00000 + 8001ff2: f8c2 3088 str.w r3, [r2, #136] ; 0x88 +#endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set MSION bit */ + RCC->CR |= RCC_CR_MSION; + 8001ff6: 4b0c ldr r3, [pc, #48] ; (8002028 ) + 8001ff8: 681a ldr r2, [r3, #0] + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000U; + 8001ffa: 2100 movs r1, #0 + RCC->CR |= RCC_CR_MSION; + 8001ffc: f042 0201 orr.w r2, r2, #1 + 8002000: 601a str r2, [r3, #0] + RCC->CFGR = 0x00000000U; + 8002002: 6099 str r1, [r3, #8] + + /* Reset HSEON, CSSON , HSION, and PLLON bits */ + RCC->CR &= 0xEAF6FFFFU; + 8002004: 681a ldr r2, [r3, #0] + 8002006: f022 52a8 bic.w r2, r2, #352321536 ; 0x15000000 + 800200a: f422 2210 bic.w r2, r2, #589824 ; 0x90000 + 800200e: 601a str r2, [r3, #0] + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x00001000U; + 8002010: f44f 5280 mov.w r2, #4096 ; 0x1000 + 8002014: 60da str r2, [r3, #12] + + /* Reset HSEBYP bit */ + RCC->CR &= 0xFFFBFFFFU; + 8002016: 681a ldr r2, [r3, #0] + 8002018: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 800201c: 601a str r2, [r3, #0] + + /* Disable all interrupts */ + RCC->CIER = 0x00000000U; + 800201e: 6199 str r1, [r3, #24] +} + 8002020: 4770 bx lr + 8002022: bf00 nop + 8002024: e000ed00 .word 0xe000ed00 + 8002028: 40021000 .word 0x40021000 + +0800202c : + +// clocks_setup() +// + void +clocks_setup(void) +{ + 800202c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + + // setup power supplies + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); + + // Configure LSE Drive Capability + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); + 8002030: 4c41 ldr r4, [pc, #260] ; (8002138 ) +{ + 8002032: b0c1 sub sp, #260 ; 0x104 + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); + 8002034: 2000 movs r0, #0 + 8002036: f005 f9e7 bl 8007408 + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); + 800203a: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 800203e: f023 0318 bic.w r3, r3, #24 + 8002042: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + + // Enable HSE Oscillator and activate PLL with HSE as source + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + 8002046: 2201 movs r2, #1 + 8002048: f44f 3380 mov.w r3, #65536 ; 0x10000 + 800204c: e9cd 230a strd r2, r3, [sp, #40] ; 0x28 + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + RCC_OscInitStruct.MSIState = RCC_MSI_OFF; + + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 8002050: 2703 movs r7, #3 + + // Select PLL as system clock source and configure + // the HCLK, PCLK1 and PCLK2 clocks dividers + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK + 8002052: 230f movs r3, #15 + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + 8002054: 2500 movs r5, #0 + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 8002056: 2602 movs r6, #2 + | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + 8002058: e9cd 3705 strd r3, r7, [sp, #20] + + RCC_OscInitStruct.PLL.PLLM = CKCC_CLK_PLLM; + RCC_OscInitStruct.PLL.PLLN = CKCC_CLK_PLLN; + RCC_OscInitStruct.PLL.PLLP = CKCC_CLK_PLLP; + 800205c: f04f 0807 mov.w r8, #7 + 8002060: 233c movs r3, #60 ; 0x3c + RCC_OscInitStruct.PLL.PLLQ = CKCC_CLK_PLLQ; + 8002062: f04f 0905 mov.w r9, #5 + + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + HAL_RCC_OscConfig(&RCC_OscInitStruct); + 8002066: a80a add r0, sp, #40 ; 0x28 + RCC_OscInitStruct.PLL.PLLP = CKCC_CLK_PLLP; + 8002068: e9cd 3817 strd r3, r8, [sp, #92] ; 0x5c + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + 800206c: e9cd 6714 strd r6, r7, [sp, #80] ; 0x50 + RCC_OscInitStruct.PLL.PLLR = CKCC_CLK_PLLR; + 8002070: e9cd 9619 strd r9, r6, [sp, #100] ; 0x64 + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + 8002074: e9cd 5507 strd r5, r5, [sp, #28] + RCC_OscInitStruct.LSEState = RCC_LSE_OFF; + 8002078: 950c str r5, [sp, #48] ; 0x30 + RCC_OscInitStruct.MSIState = RCC_MSI_OFF; + 800207a: 9510 str r5, [sp, #64] ; 0x40 + RCC_OscInitStruct.PLL.PLLM = CKCC_CLK_PLLM; + 800207c: 9616 str r6, [sp, #88] ; 0x58 + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + 800207e: 9509 str r5, [sp, #36] ; 0x24 + HAL_RCC_OscConfig(&RCC_OscInitStruct); + 8002080: f006 fd64 bl 8008b4c + + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); + 8002084: 4649 mov r1, r9 + 8002086: a805 add r0, sp, #20 + 8002088: f007 f80e bl 80090a8 + + // DIS-able MSI-Hardware auto calibration mode with LSE + CLEAR_BIT(RCC->CR, RCC_CR_MSIPLLEN); + 800208c: 6823 ldr r3, [r4, #0] + 800208e: f023 0304 bic.w r3, r3, #4 + 8002092: 6023 str r3, [r4, #0] + + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1|RCC_PERIPHCLK_I2C2 + 8002094: 4b29 ldr r3, [pc, #164] ; (800213c ) + 8002096: 931b str r3, [sp, #108] ; 0x6c + + // PLLSAI is used to clock USB, ADC, I2C1 and RNG. The frequency is + // HSE(8MHz)/PLLM(2)*PLLSAI1N(24)/PLLSAIQ(2) = 48MHz. + // + PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; + PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; + 8002098: f04f 5380 mov.w r3, #268435456 ; 0x10000000 + 800209c: 933b str r3, [sp, #236] ; 0xec + PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1; + 800209e: f04f 6380 mov.w r3, #67108864 ; 0x4000000 + 80020a2: 9338 str r3, [sp, #224] ; 0xe0 + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLLSAI1; + 80020a4: 933a str r3, [sp, #232] ; 0xe8 + + PeriphClkInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; + PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 2; + PeriphClkInitStruct.PLLSAI1.PLLSAI1N = 24; + 80020a6: 2318 movs r3, #24 + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + 80020a8: f44f 7240 mov.w r2, #768 ; 0x300 + PeriphClkInitStruct.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; + 80020ac: e9cd 381e strd r3, r8, [sp, #120] ; 0x78 + PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + |RCC_PLLSAI1_48M2CLK + |RCC_PLLSAI1_ADC1CLK; + + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + 80020b0: a81b add r0, sp, #108 ; 0x6c + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + 80020b2: 4b23 ldr r3, [pc, #140] ; (8002140 ) + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; // but unused + 80020b4: 923f str r2, [sp, #252] ; 0xfc + PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK + 80020b6: 9322 str r3, [sp, #136] ; 0x88 + PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 2; + 80020b8: e9cd 761c strd r7, r6, [sp, #112] ; 0x70 + PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; + 80020bc: e9cd 6620 strd r6, r6, [sp, #128] ; 0x80 + PeriphClkInitStruct.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; + 80020c0: 9531 str r5, [sp, #196] ; 0xc4 + PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; + 80020c2: 9536 str r5, [sp, #216] ; 0xd8 + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + 80020c4: f007 fb14 bl 80096f0 + + __HAL_RCC_RTC_ENABLE(); + 80020c8: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 80020cc: f443 4300 orr.w r3, r3, #32768 ; 0x8000 + 80020d0: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + __HAL_RCC_HASH_CLK_ENABLE(); // for SHA256 + 80020d4: 6ce3 ldr r3, [r4, #76] ; 0x4c + 80020d6: f443 3300 orr.w r3, r3, #131072 ; 0x20000 + 80020da: 64e3 str r3, [r4, #76] ; 0x4c + 80020dc: 6ce3 ldr r3, [r4, #76] ; 0x4c + 80020de: f403 3300 and.w r3, r3, #131072 ; 0x20000 + 80020e2: 9301 str r3, [sp, #4] + 80020e4: 9b01 ldr r3, [sp, #4] + __HAL_RCC_SPI1_CLK_ENABLE(); // for OLED + 80020e6: 6e23 ldr r3, [r4, #96] ; 0x60 + 80020e8: f443 5380 orr.w r3, r3, #4096 ; 0x1000 + 80020ec: 6623 str r3, [r4, #96] ; 0x60 + 80020ee: 6e23 ldr r3, [r4, #96] ; 0x60 + 80020f0: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 80020f4: 9302 str r3, [sp, #8] + 80020f6: 9b02 ldr r3, [sp, #8] + //__HAL_RCC_SPI2_CLK_ENABLE(); // for SPI flash + __HAL_RCC_DMAMUX1_CLK_ENABLE(); // (need this) because code missing in mpy? + 80020f8: 6ca3 ldr r3, [r4, #72] ; 0x48 + 80020fa: f043 0304 orr.w r3, r3, #4 + 80020fe: 64a3 str r3, [r4, #72] ; 0x48 + 8002100: 6ca3 ldr r3, [r4, #72] ; 0x48 + 8002102: f003 0304 and.w r3, r3, #4 + 8002106: 9303 str r3, [sp, #12] + 8002108: 9b03 ldr r3, [sp, #12] + + // for SE2 + __HAL_RCC_I2C2_CLK_ENABLE(); + 800210a: 6da3 ldr r3, [r4, #88] ; 0x58 + 800210c: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 8002110: 65a3 str r3, [r4, #88] ; 0x58 + 8002112: 6da3 ldr r3, [r4, #88] ; 0x58 + 8002114: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 8002118: 9304 str r3, [sp, #16] + 800211a: 9b04 ldr r3, [sp, #16] + __HAL_RCC_I2C2_FORCE_RESET(); + 800211c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800211e: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 8002122: 63a3 str r3, [r4, #56] ; 0x38 + __HAL_RCC_I2C2_RELEASE_RESET(); + 8002124: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8002126: f423 0380 bic.w r3, r3, #4194304 ; 0x400000 + 800212a: 63a3 str r3, [r4, #56] ; 0x38 + + // setup SYSTICK, but we don't have the irq hooked up and not using HAL + // but we use it in polling mode for delay_ms() + systick_setup(); + 800212c: f7ff ff50 bl 8001fd0 + +} + 8002130: b041 add sp, #260 ; 0x104 + 8002132: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 8002136: bf00 nop + 8002138: 40021000 .word 0x40021000 + 800213c: 00066880 .word 0x00066880 + 8002140: 01110000 .word 0x01110000 + +08002144 : + } else { + + // write changes to OB flash bytes + + // Set OPTSTRT bit + SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT); + 8002144: 4b13 ldr r3, [pc, #76] ; (8002194 ) + 8002146: 695a ldr r2, [r3, #20] + 8002148: f442 3200 orr.w r2, r2, #131072 ; 0x20000 + 800214c: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { + 800214e: 691a ldr r2, [r3, #16] + 8002150: 03d2 lsls r2, r2, #15 + 8002152: d4fc bmi.n 800214e + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); + 8002154: 6919 ldr r1, [r3, #16] + if(error) { + 8002156: 4a10 ldr r2, [pc, #64] ; (8002198 ) + 8002158: 4211 tst r1, r2 + 800215a: d104 bne.n 8002166 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { + 800215c: 691a ldr r2, [r3, #16] + 800215e: 07d0 lsls r0, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); + 8002160: bf44 itt mi + 8002162: 2201 movmi r2, #1 + 8002164: 611a strmi r2, [r3, #16] + + /// Wait for update to complete + _flash_wait_done(); + + // lock OB again. + SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK); + 8002166: 4b0b ldr r3, [pc, #44] ; (8002194 ) + 8002168: 695a ldr r2, [r3, #20] + 800216a: f042 4280 orr.w r2, r2, #1073741824 ; 0x40000000 + 800216e: 615a str r2, [r3, #20] + + // include "launch" to make them take effect NOW + SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH); + 8002170: 695a ldr r2, [r3, #20] + 8002172: f042 6200 orr.w r2, r2, #134217728 ; 0x8000000 + 8002176: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { + 8002178: 691a ldr r2, [r3, #16] + 800217a: 03d1 lsls r1, r2, #15 + 800217c: d4fc bmi.n 8002178 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); + 800217e: 6919 ldr r1, [r3, #16] + if(error) { + 8002180: 4a05 ldr r2, [pc, #20] ; (8002198 ) + 8002182: 4211 tst r1, r2 + 8002184: d104 bne.n 8002190 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { + 8002186: 691a ldr r2, [r3, #16] + 8002188: 07d2 lsls r2, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); + 800218a: bf44 itt mi + 800218c: 2201 movmi r2, #1 + 800218e: 611a strmi r2, [r3, #16] + + _flash_wait_done(); + } +} + 8002190: 4770 bx lr + 8002192: bf00 nop + 8002194: 40022000 .word 0x40022000 + 8002198: 0002c3fa .word 0x0002c3fa + +0800219c : +{ + 800219c: b507 push {r0, r1, r2, lr} + memcpy(&_srelocate, &_etext, ((uint32_t)&_erelocate)-(uint32_t)&_srelocate); + 800219e: 4809 ldr r0, [pc, #36] ; (80021c4 ) + 80021a0: 4a09 ldr r2, [pc, #36] ; (80021c8 ) + 80021a2: 490a ldr r1, [pc, #40] ; (80021cc ) + 80021a4: 1a12 subs r2, r2, r0 + 80021a6: f00b fbaf bl 800d908 + __HAL_RCC_FLASH_CLK_ENABLE(); + 80021aa: 4b09 ldr r3, [pc, #36] ; (80021d0 ) + 80021ac: 6c9a ldr r2, [r3, #72] ; 0x48 + 80021ae: f442 7280 orr.w r2, r2, #256 ; 0x100 + 80021b2: 649a str r2, [r3, #72] ; 0x48 + 80021b4: 6c9b ldr r3, [r3, #72] ; 0x48 + 80021b6: f403 7380 and.w r3, r3, #256 ; 0x100 + 80021ba: 9301 str r3, [sp, #4] + 80021bc: 9b01 ldr r3, [sp, #4] +} + 80021be: b003 add sp, #12 + 80021c0: f85d fb04 ldr.w pc, [sp], #4 + 80021c4: 2009e000 .word 0x2009e000 + 80021c8: 2009e150 .word 0x2009e150 + 80021cc: 08010ac4 .word 0x08010ac4 + 80021d0: 40021000 .word 0x40021000 + +080021d4 : + SET_BIT(FLASH->CR, FLASH_CR_LOCK); + 80021d4: 4a02 ldr r2, [pc, #8] ; (80021e0 ) + 80021d6: 6953 ldr r3, [r2, #20] + 80021d8: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 80021dc: 6153 str r3, [r2, #20] +} + 80021de: 4770 bx lr + 80021e0: 40022000 .word 0x40022000 + +080021e4 : +{ + 80021e4: b508 push {r3, lr} + if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) { + 80021e6: 4b08 ldr r3, [pc, #32] ; (8002208 ) + 80021e8: 695a ldr r2, [r3, #20] + 80021ea: 2a00 cmp r2, #0 + 80021ec: da0a bge.n 8002204 + WRITE_REG(FLASH->KEYR, FLASH_KEY1); + 80021ee: 4a07 ldr r2, [pc, #28] ; (800220c ) + 80021f0: 609a str r2, [r3, #8] + WRITE_REG(FLASH->KEYR, FLASH_KEY2); + 80021f2: f102 3288 add.w r2, r2, #2290649224 ; 0x88888888 + 80021f6: 609a str r2, [r3, #8] + if(READ_BIT(FLASH->CR, FLASH_CR_LOCK)) { + 80021f8: 695b ldr r3, [r3, #20] + 80021fa: 2b00 cmp r3, #0 + 80021fc: da02 bge.n 8002204 + INCONSISTENT("failed to unlock"); + 80021fe: 4804 ldr r0, [pc, #16] ; (8002210 ) + 8002200: f7fe fc1a bl 8000a38 +} + 8002204: bd08 pop {r3, pc} + 8002206: bf00 nop + 8002208: 40022000 .word 0x40022000 + 800220c: 45670123 .word 0x45670123 + 8002210: 0800d9a0 .word 0x0800d9a0 + +08002214 : +{ + 8002214: b510 push {r4, lr} + if(!lock) { + 8002216: b980 cbnz r0, 800223a + if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) { + 8002218: 4c0a ldr r4, [pc, #40] ; (8002244 ) + 800221a: 6963 ldr r3, [r4, #20] + 800221c: 005a lsls r2, r3, #1 + 800221e: d510 bpl.n 8002242 + flash_unlock(); + 8002220: f7ff ffe0 bl 80021e4 + WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1); + 8002224: 4b08 ldr r3, [pc, #32] ; (8002248 ) + 8002226: 60e3 str r3, [r4, #12] + WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2); + 8002228: f103 3344 add.w r3, r3, #1145324612 ; 0x44444444 + 800222c: 60e3 str r3, [r4, #12] + if(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) { + 800222e: 6963 ldr r3, [r4, #20] + 8002230: 005b lsls r3, r3, #1 + 8002232: d506 bpl.n 8002242 + INCONSISTENT("failed to OB unlock"); + 8002234: 4805 ldr r0, [pc, #20] ; (800224c ) + 8002236: f7fe fbff bl 8000a38 +} + 800223a: e8bd 4010 ldmia.w sp!, {r4, lr} + 800223e: f7ff bf81 b.w 8002144 + 8002242: bd10 pop {r4, pc} + 8002244: 40022000 .word 0x40022000 + 8002248: 08192a3b .word 0x08192a3b + 800224c: 0800d9a0 .word 0x0800d9a0 + +08002250 : +// +// Write the serial number of ATECC608 into flash forever. +// + void +flash_save_ae_serial(const uint8_t serial[9]) +{ + 8002250: b51f push {r0, r1, r2, r3, r4, lr} + 8002252: 4602 mov r2, r0 + uint64_t tmp[2]; + memset(&tmp, 0x0, sizeof(tmp)); + 8002254: 2300 movs r3, #0 + memcpy(&tmp, serial, 9); + 8002256: 6800 ldr r0, [r0, #0] + 8002258: 6851 ldr r1, [r2, #4] + 800225a: 7a12 ldrb r2, [r2, #8] + memset(&tmp, 0x0, sizeof(tmp)); + 800225c: e9cd 3302 strd r3, r3, [sp, #8] + memcpy(&tmp, serial, 9); + 8002260: 466b mov r3, sp + 8002262: c303 stmia r3!, {r0, r1} + 8002264: 701a strb r2, [r3, #0] + + flash_setup0(); + 8002266: f7ff ff99 bl 800219c + flash_unlock(); + 800226a: f7ff ffbb bl 80021e4 + + if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[0], tmp[0])) { + 800226e: e9dd 2300 ldrd r2, r3, [sp] + 8002272: 4809 ldr r0, [pc, #36] ; (8002298 ) + 8002274: f00b fb8c bl 800d990 <__flash_burn_veneer> + 8002278: b110 cbz r0, 8002280 + INCONSISTENT("fail1"); + 800227a: 4808 ldr r0, [pc, #32] ; (800229c ) + 800227c: f7fe fbdc bl 8000a38 + } + if(flash_burn((uint32_t)&rom_secrets->ae_serial_number[1], tmp[1])) { + 8002280: e9dd 2302 ldrd r2, r3, [sp, #8] + 8002284: 4806 ldr r0, [pc, #24] ; (80022a0 ) + 8002286: f00b fb83 bl 800d990 <__flash_burn_veneer> + 800228a: 2800 cmp r0, #0 + 800228c: d1f5 bne.n 800227a + INCONSISTENT("fail2"); + } + + flash_lock(); +} + 800228e: b005 add sp, #20 + 8002290: f85d eb04 ldr.w lr, [sp], #4 + flash_lock(); + 8002294: f7ff bf9e b.w 80021d4 + 8002298: 0801c040 .word 0x0801c040 + 800229c: 0800d9a0 .word 0x0800d9a0 + 80022a0: 0801c048 .word 0x0801c048 + +080022a4 : +// +// Write bag number (probably a string) +// + void +flash_save_bag_number(const uint8_t new_number[32]) +{ + 80022a4: b570 push {r4, r5, r6, lr} + 80022a6: b088 sub sp, #32 + uint32_t dest = (uint32_t)&rom_secrets->bag_number[0]; + uint64_t tmp[4] = { 0 }; + uint64_t *src = tmp; + + STATIC_ASSERT(sizeof(tmp) == 32); + memcpy(tmp, new_number, 32); + 80022a8: 4603 mov r3, r0 + 80022aa: 466c mov r4, sp + 80022ac: f100 0520 add.w r5, r0, #32 + 80022b0: 6818 ldr r0, [r3, #0] + 80022b2: 6859 ldr r1, [r3, #4] + 80022b4: 4622 mov r2, r4 + 80022b6: c203 stmia r2!, {r0, r1} + 80022b8: 3308 adds r3, #8 + 80022ba: 42ab cmp r3, r5 + 80022bc: 4614 mov r4, r2 + 80022be: d1f7 bne.n 80022b0 + + flash_setup0(); + 80022c0: f7ff ff6c bl 800219c + flash_unlock(); + 80022c4: f7ff ff8e bl 80021e4 + uint32_t dest = (uint32_t)&rom_secrets->bag_number[0]; + 80022c8: 4d09 ldr r5, [pc, #36] ; (80022f0 ) + + // NOTE: can only write once! No provision for read/check/update. + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80022ca: 4e0a ldr r6, [pc, #40] ; (80022f4 ) + 80022cc: 466c mov r4, sp + if(flash_burn(dest, *src)) { + 80022ce: e8f4 2302 ldrd r2, r3, [r4], #8 + 80022d2: 4628 mov r0, r5 + 80022d4: f00b fb5c bl 800d990 <__flash_burn_veneer> + 80022d8: b110 cbz r0, 80022e0 + INCONSISTENT("fail write"); + 80022da: 4807 ldr r0, [pc, #28] ; (80022f8 ) + 80022dc: f7fe fbac bl 8000a38 + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 80022e0: 3508 adds r5, #8 + 80022e2: 42b5 cmp r5, r6 + 80022e4: d1f3 bne.n 80022ce + } + } + + flash_lock(); +} + 80022e6: b008 add sp, #32 + 80022e8: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + flash_lock(); + 80022ec: f7ff bf72 b.w 80021d4 + 80022f0: 0801c050 .word 0x0801c050 + 80022f4: 0801c070 .word 0x0801c070 + 80022f8: 0800d9a0 .word 0x0800d9a0 + +080022fc : +// Save bunch of stuff related to SE2. Allow updates to sections that are +// given as ones at this point. +// + void +flash_save_se2_data(const se2_secrets_t *se2) +{ + 80022fc: e92d 41f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, lr} + 8002300: 4605 mov r5, r0 + uint8_t *dest = (uint8_t *)&rom_secrets->se2; + 8002302: 4c1a ldr r4, [pc, #104] ; (800236c ) + STATIC_ASSERT(offsetof(rom_secrets_t, se2) % 8 == 0); + + flash_setup0(); + flash_unlock(); + + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 8002304: f8df 8070 ldr.w r8, [pc, #112] ; 8002378 + flash_setup0(); + 8002308: f7ff ff48 bl 800219c + flash_unlock(); + 800230c: f7ff ff6a bl 80021e4 + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 8002310: 1b2d subs r5, r5, r4 + 8002312: eb05 0c04 add.w ip, r5, r4 + uint64_t val; + memcpy(&val, src, sizeof(val)); + 8002316: 5928 ldr r0, [r5, r4] + 8002318: f8dc 1004 ldr.w r1, [ip, #4] + 800231c: 466b mov r3, sp + + // don't write if all ones or already written correctly + if(val == ~0) continue; + 800231e: f1b1 3fff cmp.w r1, #4294967295 ; 0xffffffff + 8002322: bf08 it eq + 8002324: f1b0 3fff cmpeq.w r0, #4294967295 ; 0xffffffff + memcpy(&val, src, sizeof(val)); + 8002328: c303 stmia r3!, {r0, r1} + if(val == ~0) continue; + 800232a: 4607 mov r7, r0 + 800232c: 460e mov r6, r1 + 800232e: d015 beq.n 800235c + if(check_equal(dest, src, 8)) continue; + 8002330: 2208 movs r2, #8 + 8002332: 4661 mov r1, ip + 8002334: 4620 mov r0, r4 + 8002336: f000 fa82 bl 800283e + 800233a: b978 cbnz r0, 800235c + + // can't write if not ones already + ASSERT(check_all_ones(dest, 8)); + 800233c: 2108 movs r1, #8 + 800233e: 4620 mov r0, r4 + 8002340: f000 fa64 bl 800280c + 8002344: b910 cbnz r0, 800234c + 8002346: 480a ldr r0, [pc, #40] ; (8002370 ) + + if(flash_burn((uint32_t)dest, val)) { + INCONSISTENT("fail write"); + 8002348: f7fe fb76 bl 8000a38 + if(flash_burn((uint32_t)dest, val)) { + 800234c: 463a mov r2, r7 + 800234e: 4633 mov r3, r6 + 8002350: 4620 mov r0, r4 + 8002352: f00b fb1d bl 800d990 <__flash_burn_veneer> + 8002356: b108 cbz r0, 800235c + INCONSISTENT("fail write"); + 8002358: 4806 ldr r0, [pc, #24] ; (8002374 ) + 800235a: e7f5 b.n 8002348 + for(int i=0; i<(sizeof(se2_secrets_t)/8); i++, dest+=8, src+=8) { + 800235c: 3408 adds r4, #8 + 800235e: 4544 cmp r4, r8 + 8002360: d1d7 bne.n 8002312 + } + } + + flash_lock(); +} + 8002362: b002 add sp, #8 + 8002364: e8bd 41f0 ldmia.w sp!, {r4, r5, r6, r7, r8, lr} + flash_lock(); + 8002368: f7ff bf34 b.w 80021d4 + 800236c: 0801c0b0 .word 0x0801c0b0 + 8002370: 0801046c .word 0x0801046c + 8002374: 0800d9a0 .word 0x0800d9a0 + 8002378: 0801c190 .word 0x0801c190 + +0800237c : +// +// This is really a state-machine, to recover boards that are booted w/ missing AE chip. +// + void +flash_setup(void) +{ + 800237c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + + // see if we have picked a pairing secret yet. + // NOTE: critical section for glitching (at least in past versions) + // - check_all.. functions have a rng_delay in them already + rng_delay(); + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 8002380: 4e58 ldr r6, [pc, #352] ; (80024e4 ) +{ + 8002382: b08b sub sp, #44 ; 0x2c + flash_setup0(); + 8002384: f7ff ff0a bl 800219c + rng_delay(); + 8002388: f000 fabe bl 8002908 + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 800238c: 2120 movs r1, #32 + 800238e: 4630 mov r0, r6 + 8002390: f000 fa3c bl 800280c + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 8002394: 2120 movs r1, #32 + bool blank_ps = check_all_ones(rom_secrets->pairing_secret, 32); + 8002396: 4681 mov r9, r0 + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 8002398: 4630 mov r0, r6 + 800239a: f000 fa41 bl 8002820 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 800239e: 2120 movs r1, #32 + bool zeroed_ps = check_all_zeros(rom_secrets->pairing_secret, 32); + 80023a0: 4604 mov r4, r0 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 80023a2: 4851 ldr r0, [pc, #324] ; (80024e8 ) + 80023a4: f000 fa32 bl 800280c + bool blank_ae = (~rom_secrets->ae_serial_number[0] == 0); + 80023a8: e9d6 7810 ldrd r7, r8, [r6, #64] ; 0x40 + bool blank_xor = check_all_ones(rom_secrets->pairing_secret_xor, 32); + 80023ac: 4605 mov r5, r0 + rng_delay(); + 80023ae: f000 faab bl 8002908 + + if(zeroed_ps) { + 80023b2: b124 cbz r4, 80023be + // fast brick process leaves us w/ zero pairing secret + oled_show(screen_brick); + 80023b4: 484d ldr r0, [pc, #308] ; (80024ec ) + 80023b6: f7fe fe5d bl 8001074 + // Hardware fail speaking to AE chip ... be careful not to brick here. + // Do not continue!! We might fix the board, or add missing pullup, etc. + oled_show(screen_se1_issue); + puts("SE1 config fail"); + + LOCKUP_FOREVER(); + 80023ba: f001 fc5d bl 8003c78 + if(blank_ps) { + 80023be: f1b9 0f00 cmp.w r9, #0 + 80023c2: d03e beq.n 8002442 + rng_setup(); + 80023c4: f000 fa5e bl 8002884 + oled_show(screen_se_setup); + 80023c8: 4849 ldr r0, [pc, #292] ; (80024f0 ) + 80023ca: f7fe fe53 bl 8001074 + for(int i=0; i<8; i++) { + 80023ce: ae02 add r6, sp, #8 + oled_show(screen_se_setup); + 80023d0: 46b1 mov r9, r6 + secret[i] = rng_sample(); + 80023d2: f000 fa45 bl 8002860 + for(int i=0; i<8; i++) { + 80023d6: 3401 adds r4, #1 + 80023d8: 2c08 cmp r4, #8 + secret[i] = rng_sample(); + 80023da: f849 0b04 str.w r0, [r9], #4 + for(int i=0; i<8; i++) { + 80023de: d1f8 bne.n 80023d2 + while(secret[0] == ~0) { + 80023e0: 9b02 ldr r3, [sp, #8] + 80023e2: 3301 adds r3, #1 + 80023e4: d00d beq.n 8002402 + flash_unlock(); + 80023e6: f7ff fefd bl 80021e4 + uint32_t dest = (uint32_t)&rom_secrets->pairing_secret; + 80023ea: 4c3e ldr r4, [pc, #248] ; (80024e4 ) + for(int i=0; i<8; i+=2, dest += 8) { + 80023ec: f8df 90f8 ldr.w r9, [pc, #248] ; 80024e8 + if(flash_burn(dest, val)) { + 80023f0: e9d6 3200 ldrd r3, r2, [r6] + 80023f4: 4620 mov r0, r4 + 80023f6: f00b facb bl 800d990 <__flash_burn_veneer> + 80023fa: b130 cbz r0, 800240a + INCONSISTENT("flash fail"); + 80023fc: 483d ldr r0, [pc, #244] ; (80024f4 ) + 80023fe: f7fe fb1b bl 8000a38 + secret[0] = rng_sample(); + 8002402: f000 fa2d bl 8002860 + 8002406: 9002 str r0, [sp, #8] + 8002408: e7ea b.n 80023e0 + for(int i=0; i<8; i+=2, dest += 8) { + 800240a: 3408 adds r4, #8 + 800240c: 454c cmp r4, r9 + 800240e: f106 0608 add.w r6, r6, #8 + 8002412: d1ed bne.n 80023f0 + flash_lock(); + 8002414: f7ff fede bl 80021d4 + flash_unlock(); + 8002418: f7ff fee4 bl 80021e4 + uint32_t dest = (uint32_t)&rom_secrets->hash_cache_secret; + 800241c: 4c36 ldr r4, [pc, #216] ; (80024f8 ) + for(int i=0; i) + uint64_t val = ((uint64_t)rng_sample() << 32) | rng_sample(); + 8002420: f000 fa1e bl 8002860 + 8002424: 9001 str r0, [sp, #4] + 8002426: f000 fa1b bl 8002860 + if(flash_burn(dest, val)) { + 800242a: 9b01 ldr r3, [sp, #4] + uint64_t val = ((uint64_t)rng_sample() << 32) | rng_sample(); + 800242c: 4602 mov r2, r0 + if(flash_burn(dest, val)) { + 800242e: 4620 mov r0, r4 + 8002430: f00b faae bl 800d990 <__flash_burn_veneer> + 8002434: 2800 cmp r0, #0 + 8002436: d1e1 bne.n 80023fc + for(int i=0; i + flash_lock(); + 800243e: f7ff fec9 bl 80021d4 + if(blank_xor || blank_ae) { + 8002442: b92d cbnz r5, 8002450 + 8002444: f1b8 3fff cmp.w r8, #4294967295 ; 0xffffffff + 8002448: bf08 it eq + 800244a: f1b7 3fff cmpeq.w r7, #4294967295 ; 0xffffffff + 800244e: d126 bne.n 800249e + se2_setup_config(); + 8002450: f005 fb94 bl 8007b7c + int rv = ae_setup_config(); + 8002454: f001 f992 bl 800377c + 8002458: 4604 mov r4, r0 + rng_delay(); + 800245a: f000 fa55 bl 8002908 + if(rv) { + 800245e: b134 cbz r4, 800246e + oled_show(screen_se1_issue); + 8002460: 4827 ldr r0, [pc, #156] ; (8002500 ) + 8002462: f7fe fe07 bl 8001074 + puts("SE1 config fail"); + 8002466: 4827 ldr r0, [pc, #156] ; (8002504 ) + 8002468: f002 fdfe bl 8005068 + 800246c: e7a5 b.n 80023ba + } + + rng_delay(); + 800246e: f000 fa4b bl 8002908 + if(blank_xor) { + 8002472: b195 cbz r5, 800249a + flash_unlock(); + 8002474: f7ff feb6 bl 80021e4 + uint64_t *src = (uint64_t *)&rom_secrets->pairing_secret; + 8002478: 4c1a ldr r4, [pc, #104] ; (80024e4 ) + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 800247a: 4e1b ldr r6, [pc, #108] ; (80024e8 ) + uint64_t val = ~(*src); + 800247c: e9d4 2300 ldrd r2, r3, [r4] + if(flash_burn(dest, val)) { + 8002480: f104 0020 add.w r0, r4, #32 + 8002484: 43d2 mvns r2, r2 + 8002486: 43db mvns r3, r3 + 8002488: f00b fa82 bl 800d990 <__flash_burn_veneer> + 800248c: 2800 cmp r0, #0 + 800248e: d1b5 bne.n 80023fc + for(int i=0; i<(32/8); i++, dest+=8, src++) { + 8002490: 3408 adds r4, #8 + 8002492: 42b4 cmp r4, r6 + 8002494: d1f2 bne.n 800247c + flash_lock(); + 8002496: f7ff fe9d bl 80021d4 + + // real power cycle required now. +#ifdef FOR_Q1_ONLY + // Q: just do it (we warned them) + extern void turn_power_off(void); + turn_power_off(); + 800249a: f001 fbe1 bl 8003c60 + puts("replug required"); + LOCKUP_FOREVER(); +#endif + } + + rng_delay(); + 800249e: f000 fa33 bl 8002908 + if(!blank_ps && !blank_xor) { + 80024a2: b9e5 cbnz r5, 80024de + // check the XOR value also written: 2 phase commit + uint8_t tmp[32]; + memcpy(tmp, rom_secrets->pairing_secret, 32); + 80024a4: 4d0f ldr r5, [pc, #60] ; (80024e4 ) + 80024a6: cd0f ldmia r5!, {r0, r1, r2, r3} + 80024a8: ac02 add r4, sp, #8 + 80024aa: c40f stmia r4!, {r0, r1, r2, r3} + 80024ac: e895 000f ldmia.w r5, {r0, r1, r2, r3} + 80024b0: e884 000f stmia.w r4, {r0, r1, r2, r3} + 80024b4: ab02 add r3, sp, #8 + 80024b6: 4a0c ldr r2, [pc, #48] ; (80024e8 ) +bool check_equal(const void *aV, const void *bV, int len); + +// XOR-mixin more bytes; acc = acc XOR more for each byte +void static inline xor_mixin(uint8_t *acc, const uint8_t *more, int len) +{ + for(; len; len--, more++, acc++) { + 80024b8: 4c13 ldr r4, [pc, #76] ; (8002508 ) + 80024ba: 4618 mov r0, r3 + *(acc) ^= *(more); + 80024bc: 7819 ldrb r1, [r3, #0] + 80024be: f812 5b01 ldrb.w r5, [r2], #1 + 80024c2: 4069 eors r1, r5 + for(; len; len--, more++, acc++) { + 80024c4: 42a2 cmp r2, r4 + *(acc) ^= *(more); + 80024c6: f803 1b01 strb.w r1, [r3], #1 + for(; len; len--, more++, acc++) { + 80024ca: d1f7 bne.n 80024bc + xor_mixin(tmp, rom_secrets->pairing_secret_xor, 32); + + if(!check_all_ones(tmp, 32)) { + 80024cc: 2120 movs r1, #32 + 80024ce: f000 f99d bl 800280c + 80024d2: b920 cbnz r0, 80024de + oled_show(screen_corrupt); + 80024d4: 480d ldr r0, [pc, #52] ; (800250c ) + 80024d6: f7fe fdcd bl 8001074 + puts("corrupt pair sec"); + 80024da: 480d ldr r0, [pc, #52] ; (8002510 ) + 80024dc: e7c4 b.n 8002468 + // That's fine if we intend to ship units locked already. + + // Do NOT do write every boot, as it might wear-out + // the flash bits in OB. + +} + 80024de: b00b add sp, #44 ; 0x2c + 80024e0: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 80024e4: 0801c000 .word 0x0801c000 + 80024e8: 0801c020 .word 0x0801c020 + 80024ec: 0800da61 .word 0x0800da61 + 80024f0: 0800f671 .word 0x0800f671 + 80024f4: 0800d9a0 .word 0x0800d9a0 + 80024f8: 0801c070 .word 0x0801c070 + 80024fc: 0801c0b0 .word 0x0801c0b0 + 8002500: 0800f13a .word 0x0800f13a + 8002504: 08010666 .word 0x08010666 + 8002508: 0801c040 .word 0x0801c040 + 800250c: 0800db0d .word 0x0800db0d + 8002510: 08010676 .word 0x08010676 + +08002514 : +// +// This is a one-way trip. Might need power cycle to (fully?) take effect. +// + void +flash_lockdown_hard(uint8_t rdp_level_code) +{ + 8002514: b510 push {r4, lr} + 8002516: 4604 mov r4, r0 +#if RELEASE + flash_setup0(); + 8002518: f7ff fe40 bl 800219c + + // see FLASH_OB_WRPConfig() + + flash_ob_lock(false); + 800251c: 2000 movs r0, #0 + 800251e: f7ff fe79 bl 8002214 + // lock first 128k-8k against any writes + FLASH->WRP1AR = (num_pages_locked << 16); + 8002522: 4b08 ldr r3, [pc, #32] ; (8002544 ) + 8002524: f44f 2260 mov.w r2, #917504 ; 0xe0000 + 8002528: 62da str r2, [r3, #44] ; 0x2c + FLASH->WRP1BR = 0xff; // unused. + 800252a: 22ff movs r2, #255 ; 0xff + 800252c: 631a str r2, [r3, #48] ; 0x30 + FLASH->WRP2AR = 0xff; // unused. + 800252e: 64da str r2, [r3, #76] ; 0x4c + FLASH->WRP2BR = 0xff; // unused. + 8002530: 651a str r2, [r3, #80] ; 0x50 + // the RDP level is decreased from Level 1 to Level 0)." + // - D-bus access blocked, even for code running inside the PCROP area! (AN4758) + // So literal values and constant tables and such would need special linking. + + // set protection level + uint32_t was = FLASH->OPTR & ~0xff; + 8002532: 6a1a ldr r2, [r3, #32] + 8002534: f022 02ff bic.w r2, r2, #255 ; 0xff + FLASH->OPTR = was | rdp_level_code; // select level X, other values as observed + 8002538: 4322 orrs r2, r4 + 800253a: 621a str r2, [r3, #32] +#else + puts2("flash_lockdown_hard("); + puthex2(rdp_level_code); + puts(") skipped"); +#endif +} + 800253c: e8bd 4010 ldmia.w sp!, {r4, lr} + 8002540: f7ff be00 b.w 8002144 + 8002544: 40022000 .word 0x40022000 + +08002548 : + +// record_highwater_version() +// + int +record_highwater_version(const uint8_t timestamp[8]) +{ + 8002548: b537 push {r0, r1, r2, r4, r5, lr} + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + + ASSERT(timestamp[0] < 0x40); + ASSERT(timestamp[0] >= 0x10); + 800254a: 7802 ldrb r2, [r0, #0] + 800254c: 3a10 subs r2, #16 + 800254e: 2a2f cmp r2, #47 ; 0x2f +{ + 8002550: 4603 mov r3, r0 + ASSERT(timestamp[0] >= 0x10); + 8002552: d902 bls.n 800255a + ASSERT(timestamp[0] < 0x40); + 8002554: 4810 ldr r0, [pc, #64] ; (8002598 ) + 8002556: f7fe fa6f bl 8000a38 + + uint64_t val = 0; + memcpy(&val, timestamp, 8); + 800255a: 6800 ldr r0, [r0, #0] + 800255c: 6859 ldr r1, [r3, #4] + const uint8_t *otp = (const uint8_t *)OPT_FLASH_BASE; + 800255e: 4c0f ldr r4, [pc, #60] ; (800259c ) + + // just write to first blank slot we can find. + for(int i=0; i) + memcpy(&val, timestamp, 8); + 8002562: 466a mov r2, sp + 8002564: c203 stmia r2!, {r0, r1} + if(check_all_ones(otp, 8)) { + 8002566: 2108 movs r1, #8 + 8002568: 4620 mov r0, r4 + 800256a: f000 f94f bl 800280c + 800256e: b168 cbz r0, 800258c + // write here. + flash_setup0(); + 8002570: f7ff fe14 bl 800219c + flash_unlock(); + 8002574: f7ff fe36 bl 80021e4 + flash_burn((uint32_t)otp, val); + 8002578: e9dd 2300 ldrd r2, r3, [sp] + 800257c: 4620 mov r0, r4 + 800257e: f00b fa07 bl 800d990 <__flash_burn_veneer> + flash_lock(); + 8002582: f7ff fe27 bl 80021d4 + + return 0; + 8002586: 2000 movs r0, #0 + } + } + + // no space. + return 1; +} + 8002588: b003 add sp, #12 + 800258a: bd30 pop {r4, r5, pc} + for(int i=0; i + return 1; + 8002592: 2001 movs r0, #1 + 8002594: e7f8 b.n 8002588 + 8002596: bf00 nop + 8002598: 0801046c .word 0x0801046c + 800259c: 1fff7000 .word 0x1fff7000 + 80025a0: 1fff7400 .word 0x1fff7400 + +080025a4 : + +// mcu_key_get() +// + const mcu_key_t * +mcu_key_get(bool *valid) +{ + 80025a4: b570 push {r4, r5, r6, lr} + // get current "mcu_key" value; first byte will never be 0x0 or 0xff + // - except if no key set yet/recently wiped + // - if none set, returns ptr to first available slot which will be all ones + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + + for(int i=0; i) + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + 80025a8: 4c0d ldr r4, [pc, #52] ; (80025e0 ) +{ + 80025aa: 4606 mov r6, r0 + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; + 80025ac: 2500 movs r5, #0 + if(ptr->value[0] == 0xff) { + 80025ae: 7823 ldrb r3, [r4, #0] + 80025b0: 2bff cmp r3, #255 ; 0xff + 80025b2: d10b bne.n 80025cc + if(!avail) { + 80025b4: 2d00 cmp r5, #0 + 80025b6: bf08 it eq + 80025b8: 4625 moveq r5, r4 + for(int i=0; i + *valid = true; + return ptr; + } + } + + rng_delay(); + 80025c0: f000 f9a2 bl 8002908 + *valid = false; + 80025c4: 2300 movs r3, #0 + 80025c6: 7033 strb r3, [r6, #0] + return avail; + 80025c8: 462c mov r4, r5 + 80025ca: e005 b.n 80025d8 + } else if(ptr->value[0] != 0x00) { + 80025cc: 2b00 cmp r3, #0 + 80025ce: d0f4 beq.n 80025ba + rng_delay(); + 80025d0: f000 f99a bl 8002908 + *valid = true; + 80025d4: 2301 movs r3, #1 + 80025d6: 7033 strb r3, [r6, #0] +} + 80025d8: 4620 mov r0, r4 + 80025da: bd70 pop {r4, r5, r6, pc} + 80025dc: 08020000 .word 0x08020000 + 80025e0: 0801e000 .word 0x0801e000 + +080025e4 : + +// mcu_key_clear() +// + void +mcu_key_clear(const mcu_key_t *cur) +{ + 80025e4: b513 push {r0, r1, r4, lr} + if(!cur) { + 80025e6: 4604 mov r4, r0 + 80025e8: b938 cbnz r0, 80025fa + bool valid; + cur = mcu_key_get(&valid); + 80025ea: f10d 0007 add.w r0, sp, #7 + 80025ee: f7ff ffd9 bl 80025a4 + + if(!valid) return; + 80025f2: f89d 3007 ldrb.w r3, [sp, #7] + cur = mcu_key_get(&valid); + 80025f6: 4604 mov r4, r0 + if(!valid) return; + 80025f8: b1fb cbz r3, 800263a + } + + // no delays here since decision has been made, and don't + // want to give them more time to interrupt us + flash_setup0(); + 80025fa: f7ff fdcf bl 800219c + flash_unlock(); + 80025fe: f7ff fdf1 bl 80021e4 + uint32_t pos = (uint32_t)cur; + flash_burn(pos, 0); pos += 8; + 8002602: 2200 movs r2, #0 + 8002604: 2300 movs r3, #0 + 8002606: 4620 mov r0, r4 + 8002608: f00b f9c2 bl 800d990 <__flash_burn_veneer> + flash_burn(pos, 0); pos += 8; + 800260c: 2200 movs r2, #0 + 800260e: 2300 movs r3, #0 + 8002610: f104 0008 add.w r0, r4, #8 + 8002614: f00b f9bc bl 800d990 <__flash_burn_veneer> + flash_burn(pos, 0); pos += 8; + 8002618: 2200 movs r2, #0 + 800261a: 2300 movs r3, #0 + 800261c: f104 0010 add.w r0, r4, #16 + 8002620: f00b f9b6 bl 800d990 <__flash_burn_veneer> + flash_burn(pos, 0); + 8002624: 2200 movs r2, #0 + 8002626: 2300 movs r3, #0 + 8002628: f104 0018 add.w r0, r4, #24 + 800262c: f00b f9b0 bl 800d990 <__flash_burn_veneer> + flash_lock(); +} + 8002630: b002 add sp, #8 + 8002632: e8bd 4010 ldmia.w sp!, {r4, lr} + flash_lock(); + 8002636: f7ff bdcd b.w 80021d4 +} + 800263a: b002 add sp, #8 + 800263c: bd10 pop {r4, pc} + ... + +08002640 : + +// mcu_key_usage() +// + void +mcu_key_usage(int *avail_out, int *consumed_out, int *total_out) +{ + 8002640: b5f0 push {r4, r5, r6, r7, lr} + const mcu_key_t *ptr = MCU_KEYS; + int avail = 0, used = 0; + 8002642: 2300 movs r3, #0 + const mcu_key_t *ptr = MCU_KEYS; + 8002644: 4c09 ldr r4, [pc, #36] ; (800266c ) + + for(int i=0; i) + int avail = 0, used = 0; + 8002648: 461d mov r5, r3 + if(ptr->value[0] == 0xff) { + 800264a: 7826 ldrb r6, [r4, #0] + 800264c: 2eff cmp r6, #255 ; 0xff + 800264e: d109 bne.n 8002664 + avail ++; + 8002650: 3501 adds r5, #1 + for(int i=0; i + } else if(ptr->value[0] == 0x00) { + used ++; + } + } + + *avail_out = avail; + 8002658: 6005 str r5, [r0, #0] + *consumed_out = used; + 800265a: 600b str r3, [r1, #0] + *total_out = NUM_MCU_KEYS; + 800265c: f44f 7380 mov.w r3, #256 ; 0x100 + 8002660: 6013 str r3, [r2, #0] +} + 8002662: bdf0 pop {r4, r5, r6, r7, pc} + } else if(ptr->value[0] == 0x00) { + 8002664: 2e00 cmp r6, #0 + 8002666: d1f4 bne.n 8002652 + used ++; + 8002668: 3301 adds r3, #1 + 800266a: e7f2 b.n 8002652 + 800266c: 0801e000 .word 0x0801e000 + 8002670: 08020000 .word 0x08020000 + +08002674 : + +// mcu_key_pick() +// + const mcu_key_t * +mcu_key_pick(void) +{ + 8002674: b5f0 push {r4, r5, r6, r7, lr} + 8002676: b08b sub sp, #44 ; 0x2c + mcu_key_t n; + + // get some good entropy, and whiten it just in case. + do { + rng_buffer(n.value, 32); + 8002678: ad02 add r5, sp, #8 + 800267a: 2120 movs r1, #32 + 800267c: 4628 mov r0, r5 + 800267e: f000 f92d bl 80028dc + sha256_single(n.value, 32, n.value); + 8002682: 462a mov r2, r5 + 8002684: 2120 movs r1, #32 + 8002686: 4628 mov r0, r5 + 8002688: f003 f8e0 bl 800584c + sha256_single(n.value, 32, n.value); + 800268c: 462a mov r2, r5 + 800268e: 2120 movs r1, #32 + 8002690: 4628 mov r0, r5 + 8002692: f003 f8db bl 800584c + } while(n.value[0] == 0x0 || n.value[0] == 0xff); + 8002696: f89d 3008 ldrb.w r3, [sp, #8] + 800269a: 3b01 subs r3, #1 + 800269c: b2db uxtb r3, r3 + 800269e: 2bfd cmp r3, #253 ; 0xfd + 80026a0: d8eb bhi.n 800267a + + int err = 0; + const mcu_key_t *cur; + + do { + bool valid = false; + 80026a2: 2300 movs r3, #0 + cur = mcu_key_get(&valid); + 80026a4: 4668 mov r0, sp + bool valid = false; + 80026a6: f88d 3000 strb.w r3, [sp] + cur = mcu_key_get(&valid); + 80026aa: f7ff ff7b bl 80025a4 + + if(!cur) { + 80026ae: 4604 mov r4, r0 + 80026b0: b938 cbnz r0, 80026c2 + // no free slots. we are brick. + puts("mcu full"); + 80026b2: 4828 ldr r0, [pc, #160] ; (8002754 ) + 80026b4: f002 fcd8 bl 8005068 + oled_show(screen_brick); + 80026b8: 4827 ldr r0, [pc, #156] ; (8002758 ) + 80026ba: f7fe fcdb bl 8001074 + + LOCKUP_FOREVER(); + 80026be: f001 fadb bl 8003c78 + } + + if(valid) { + 80026c2: f89d 3000 ldrb.w r3, [sp] + 80026c6: b14b cbz r3, 80026dc + // clear existing key, if it's defined. + ASSERT(cur->value[0] != 0x00); + 80026c8: 7803 ldrb r3, [r0, #0] + 80026ca: 3b01 subs r3, #1 + 80026cc: b2db uxtb r3, r3 + 80026ce: 2bfd cmp r3, #253 ; 0xfd + 80026d0: d902 bls.n 80026d8 + 80026d2: 4822 ldr r0, [pc, #136] ; (800275c ) + 80026d4: f7fe f9b0 bl 8000a38 + ASSERT(cur->value[0] != 0xff); + + mcu_key_clear(cur); + 80026d8: f7ff ff84 bl 80025e4 + continue; + } + } while(0); + + // burn it + flash_setup0(); + 80026dc: f7ff fd5e bl 800219c + flash_unlock(); + 80026e0: f7ff fd80 bl 80021e4 + uint32_t pos = (uint32_t)cur; + const uint8_t *fr = n.value; + + for(int i=0; i<32; i+= 8, pos += 8, fr += 8) { + 80026e4: 2700 movs r7, #0 + uint64_t v; + memcpy(&v, fr, sizeof(v)); + 80026e6: 19ea adds r2, r5, r7 + 80026e8: 59e8 ldr r0, [r5, r7] + 80026ea: 6851 ldr r1, [r2, #4] + 80026ec: 466b mov r3, sp + 80026ee: c303 stmia r3!, {r0, r1} + + err = flash_burn(pos, v); + 80026f0: 19e0 adds r0, r4, r7 + 80026f2: e9dd 2300 ldrd r2, r3, [sp] + 80026f6: f00b f94b bl 800d990 <__flash_burn_veneer> + if(err) break; + 80026fa: 4606 mov r6, r0 + 80026fc: b910 cbnz r0, 8002704 + for(int i=0; i<32; i+= 8, pos += 8, fr += 8) { + 80026fe: 3708 adds r7, #8 + 8002700: 2f20 cmp r7, #32 + 8002702: d1f0 bne.n 80026e6 + } + flash_lock(); + 8002704: f7ff fd66 bl 80021d4 + + // NOTE: Errors not expected, but lets be graceful about them. + + if(err) { + 8002708: b166 cbz r6, 8002724 + // what to do? + puts("burn fail: "); + 800270a: 4815 ldr r0, [pc, #84] ; (8002760 ) + 800270c: f002 fcac bl 8005068 + puthex2(err); + 8002710: b2f0 uxtb r0, r6 + 8002712: f002 fc4d bl 8004fb0 + putchar('\n'); + 8002716: 200a movs r0, #10 + 8002718: f002 fc2c bl 8004f74 + return NULL; + } + + if(after != cur || !check_equal(after->value, n.value, 32)) { + puts("bad val?"); + return NULL; + 800271c: 2400 movs r4, #0 + } + + return cur; +} + 800271e: 4620 mov r0, r4 + 8002720: b00b add sp, #44 ; 0x2c + 8002722: bdf0 pop {r4, r5, r6, r7, pc} + const mcu_key_t *after = mcu_key_get(&valid); + 8002724: 4668 mov r0, sp + bool valid = false; + 8002726: f88d 6000 strb.w r6, [sp] + const mcu_key_t *after = mcu_key_get(&valid); + 800272a: f7ff ff3b bl 80025a4 + if(!valid) { + 800272e: f89d 2000 ldrb.w r2, [sp] + 8002732: b91a cbnz r2, 800273c + puts("!valid?"); + 8002734: 480b ldr r0, [pc, #44] ; (8002764 ) + puts("bad val?"); + 8002736: f002 fc97 bl 8005068 + 800273a: e7ef b.n 800271c + if(after != cur || !check_equal(after->value, n.value, 32)) { + 800273c: 4284 cmp r4, r0 + 800273e: d001 beq.n 8002744 + puts("bad val?"); + 8002740: 4809 ldr r0, [pc, #36] ; (8002768 ) + 8002742: e7f8 b.n 8002736 + if(after != cur || !check_equal(after->value, n.value, 32)) { + 8002744: 2220 movs r2, #32 + 8002746: 4629 mov r1, r5 + 8002748: f000 f879 bl 800283e + 800274c: 2800 cmp r0, #0 + 800274e: d1e6 bne.n 800271e + 8002750: e7f6 b.n 8002740 + 8002752: bf00 nop + 8002754: 08010687 .word 0x08010687 + 8002758: 0800da61 .word 0x0800da61 + 800275c: 0801046c .word 0x0801046c + 8002760: 08010690 .word 0x08010690 + 8002764: 0801069c .word 0x0801069c + 8002768: 080106a4 .word 0x080106a4 + +0800276c : + +// fast_brick() +// + void +fast_brick(void) +{ + 800276c: b538 push {r3, r4, r5, lr} +#ifndef RELEASE + puts2("DISABLED fast brick... "); + oled_show(screen_brick); +#else + // do a fast wipe of our key + mcu_key_clear(NULL); + 800276e: 2000 movs r0, #0 + 8002770: f7ff ff38 bl 80025e4 + + // brick SE1 for future + ae_brick_myself(); + 8002774: f001 f970 bl 8003a58 + + // NOTE: could brick SE1 (somewhat) by dec'ing the counter, which will + // invalidate all PIN hashes + + // no going back from that -- but for privacy, wipe more stuff + oled_show(screen_brick); + 8002778: 480e ldr r0, [pc, #56] ; (80027b4 ) + uint32_t bot = (uint32_t)MCU_KEYS; + flash_page_erase(bot); + + // 2: LFS area first, since holds settings (AES'ed w/ lost key, but yeah) + // 3: the firmware, not a secret anyway + for(uint32_t pos=(FLASH_BASE + 0x200000 - FLASH_ERASE_SIZE); + 800277a: 4c0f ldr r4, [pc, #60] ; (80027b8 ) + 800277c: 4d0f ldr r5, [pc, #60] ; (80027bc ) + oled_show(screen_brick); + 800277e: f7fe fc79 bl 8001074 + puts2("fast brick... "); + 8002782: 480f ldr r0, [pc, #60] ; (80027c0 ) + 8002784: f002 fbe2 bl 8004f4c + flash_setup0(); + 8002788: f7ff fd08 bl 800219c + flash_unlock(); + 800278c: f7ff fd2a bl 80021e4 + flash_page_erase(bot); + 8002790: 480a ldr r0, [pc, #40] ; (80027bc ) + 8002792: f00b f901 bl 800d998 <__flash_page_erase_veneer> + pos > bot; pos -= FLASH_ERASE_SIZE) { + flash_page_erase(pos); + 8002796: 4620 mov r0, r4 + pos > bot; pos -= FLASH_ERASE_SIZE) { + 8002798: f5a4 5480 sub.w r4, r4, #4096 ; 0x1000 + flash_page_erase(pos); + 800279c: f00b f8fc bl 800d998 <__flash_page_erase_veneer> + for(uint32_t pos=(FLASH_BASE + 0x200000 - FLASH_ERASE_SIZE); + 80027a0: 42ac cmp r4, r5 + 80027a2: d1f8 bne.n 8002796 + } + flash_lock(); + puts(" done"); + 80027a4: 4807 ldr r0, [pc, #28] ; (80027c4 ) + flash_lock(); + 80027a6: f7ff fd15 bl 80021d4 + puts(" done"); + 80027aa: f002 fc5d bl 8005068 +#endif + + LOCKUP_FOREVER(); + 80027ae: f001 fa63 bl 8003c78 + 80027b2: bf00 nop + 80027b4: 0800da61 .word 0x0800da61 + 80027b8: 081ff000 .word 0x081ff000 + 80027bc: 0801e000 .word 0x0801e000 + 80027c0: 080106ad .word 0x080106ad + 80027c4: 080106bc .word 0x080106bc + +080027c8 : + +// fast_wipe() +// + void +fast_wipe(void) +{ + 80027c8: b508 push {r3, lr} + // dump (part of) the main seed key and become a new Coldcard + // - lots of other code can and will detect a missing MCU key as "blank" + // - and the check value on main seed will be garbage now + mcu_key_clear(NULL); + 80027ca: 2000 movs r0, #0 + 80027cc: f7ff ff0a bl 80025e4 + __ASM volatile ("dsb 0xF":::"memory"); + 80027d0: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80027d4: 4905 ldr r1, [pc, #20] ; (80027ec ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80027d6: 4b06 ldr r3, [pc, #24] ; (80027f0 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80027d8: 68ca ldr r2, [r1, #12] + 80027da: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80027de: 4313 orrs r3, r2 + 80027e0: 60cb str r3, [r1, #12] + 80027e2: f3bf 8f4f dsb sy + __NOP(); + 80027e6: bf00 nop + for(;;) /* wait until reset */ + 80027e8: e7fd b.n 80027e6 + 80027ea: bf00 nop + 80027ec: e000ed00 .word 0xe000ed00 + 80027f0: 05fa0004 .word 0x05fa0004 + +080027f4 : +check_all_ones_raw(const void *ptrV, int len) +{ + uint8_t rv = 0xff; + const uint8_t *ptr = (const uint8_t *)ptrV; + + for(; len; len--, ptr++) { + 80027f4: 4401 add r1, r0 + uint8_t rv = 0xff; + 80027f6: 23ff movs r3, #255 ; 0xff + for(; len; len--, ptr++) { + 80027f8: 4288 cmp r0, r1 + 80027fa: d103 bne.n 8002804 + rv &= *ptr; + } + + return (rv == 0xff); +} + 80027fc: 3bff subs r3, #255 ; 0xff + 80027fe: 4258 negs r0, r3 + 8002800: 4158 adcs r0, r3 + 8002802: 4770 bx lr + rv &= *ptr; + 8002804: f810 2b01 ldrb.w r2, [r0], #1 + 8002808: 4013 ands r3, r2 + for(; len; len--, ptr++) { + 800280a: e7f5 b.n 80027f8 + +0800280c : +// +// Return T if all bytes are 0xFF +// + bool +check_all_ones(const void *ptrV, int len) +{ + 800280c: b507 push {r0, r1, r2, lr} + bool rv = check_all_ones_raw(ptrV, len); + 800280e: f7ff fff1 bl 80027f4 + 8002812: 9001 str r0, [sp, #4] + + rng_delay(); + 8002814: f000 f878 bl 8002908 + + return rv; +} + 8002818: 9801 ldr r0, [sp, #4] + 800281a: b003 add sp, #12 + 800281c: f85d fb04 ldr.w pc, [sp], #4 + +08002820 : +// +// Return T if all bytes are 0x00 +// + bool +check_all_zeros(const void *ptrV, int len) +{ + 8002820: b510 push {r4, lr} + 8002822: 4401 add r1, r0 + uint8_t rv = 0x0; + 8002824: 2400 movs r4, #0 + const uint8_t *ptr = (const uint8_t *)ptrV; + + for(; len; len--, ptr++) { + 8002826: 4288 cmp r0, r1 + 8002828: d105 bne.n 8002836 + rv |= *ptr; + } + + rng_delay(); + 800282a: f000 f86d bl 8002908 + return (rv == 0x00); +} + 800282e: fab4 f084 clz r0, r4 + 8002832: 0940 lsrs r0, r0, #5 + 8002834: bd10 pop {r4, pc} + rv |= *ptr; + 8002836: f810 3b01 ldrb.w r3, [r0], #1 + 800283a: 431c orrs r4, r3 + for(; len; len--, ptr++) { + 800283c: e7f3 b.n 8002826 + +0800283e : + const uint8_t *left = (const uint8_t *)aV; + const uint8_t *right = (const uint8_t *)bV; + uint8_t diff = 0; + int i; + + for (i = 0; i < len; i++) { + 800283e: 2300 movs r3, #0 +{ + 8002840: b570 push {r4, r5, r6, lr} + uint8_t diff = 0; + 8002842: 461c mov r4, r3 + for (i = 0; i < len; i++) { + 8002844: 4293 cmp r3, r2 + 8002846: db05 blt.n 8002854 + diff |= (left[i] ^ right[i]); + } + + rng_delay(); + 8002848: f000 f85e bl 8002908 + return (diff == 0); +} + 800284c: fab4 f084 clz r0, r4 + 8002850: 0940 lsrs r0, r0, #5 + 8002852: bd70 pop {r4, r5, r6, pc} + diff |= (left[i] ^ right[i]); + 8002854: 5cc5 ldrb r5, [r0, r3] + 8002856: 5cce ldrb r6, [r1, r3] + 8002858: 4075 eors r5, r6 + 800285a: 432c orrs r4, r5 + for (i = 0; i < len; i++) { + 800285c: 3301 adds r3, #1 + 800285e: e7f1 b.n 8002844 + +08002860 : + } + + // Get the new number + uint32_t rv = RNG->DR; + + if(rv != last_rng_result && rv) { + 8002860: 4b06 ldr r3, [pc, #24] ; (800287c ) + while(!(RNG->SR & RNG_FLAG_DRDY)) { + 8002862: 4a07 ldr r2, [pc, #28] ; (8002880 ) + if(rv != last_rng_result && rv) { + 8002864: 6819 ldr r1, [r3, #0] + while(!(RNG->SR & RNG_FLAG_DRDY)) { + 8002866: 6850 ldr r0, [r2, #4] + 8002868: 07c0 lsls r0, r0, #31 + 800286a: d5fc bpl.n 8002866 + uint32_t rv = RNG->DR; + 800286c: 6890 ldr r0, [r2, #8] + if(rv != last_rng_result && rv) { + 800286e: 4281 cmp r1, r0 + 8002870: d0f9 beq.n 8002866 + 8002872: 2800 cmp r0, #0 + 8002874: d0f7 beq.n 8002866 + last_rng_result = rv; + 8002876: 6018 str r0, [r3, #0] + + // keep trying if not a new number + } + + // NOT-REACHED +} + 8002878: 4770 bx lr + 800287a: bf00 nop + 800287c: 2009e1bc .word 0x2009e1bc + 8002880: 50060800 .word 0x50060800 + +08002884 : + if(RNG->CR & RNG_CR_RNGEN) { + 8002884: 4b12 ldr r3, [pc, #72] ; (80028d0 ) + 8002886: 681a ldr r2, [r3, #0] + 8002888: 0752 lsls r2, r2, #29 +{ + 800288a: b513 push {r0, r1, r4, lr} + if(RNG->CR & RNG_CR_RNGEN) { + 800288c: d41d bmi.n 80028ca + __HAL_RCC_RNG_CLK_ENABLE(); + 800288e: 4a11 ldr r2, [pc, #68] ; (80028d4 ) + 8002890: 6cd1 ldr r1, [r2, #76] ; 0x4c + 8002892: f441 2180 orr.w r1, r1, #262144 ; 0x40000 + 8002896: 64d1 str r1, [r2, #76] ; 0x4c + 8002898: 6cd2 ldr r2, [r2, #76] ; 0x4c + 800289a: f402 2280 and.w r2, r2, #262144 ; 0x40000 + 800289e: 9201 str r2, [sp, #4] + 80028a0: 9a01 ldr r2, [sp, #4] + RNG->CR |= RNG_CR_RNGEN; + 80028a2: 681a ldr r2, [r3, #0] + 80028a4: f042 0204 orr.w r2, r2, #4 + 80028a8: 601a str r2, [r3, #0] + uint32_t chk = rng_sample(); + 80028aa: f7ff ffd9 bl 8002860 + 80028ae: 4604 mov r4, r0 + uint32_t chk2 = rng_sample(); + 80028b0: f7ff ffd6 bl 8002860 + if(chk == 0 || chk == ~0 + 80028b4: 1e63 subs r3, r4, #1 + 80028b6: 3303 adds r3, #3 + 80028b8: d804 bhi.n 80028c4 + || chk2 == 0 || chk2 == ~0 + 80028ba: 1e43 subs r3, r0, #1 + 80028bc: 3303 adds r3, #3 + 80028be: d801 bhi.n 80028c4 + || chk == chk2 + 80028c0: 4284 cmp r4, r0 + 80028c2: d102 bne.n 80028ca + INCONSISTENT("bad rng"); + 80028c4: 4804 ldr r0, [pc, #16] ; (80028d8 ) + 80028c6: f7fe f8b7 bl 8000a38 +} + 80028ca: b002 add sp, #8 + 80028cc: bd10 pop {r4, pc} + 80028ce: bf00 nop + 80028d0: 50060800 .word 0x50060800 + 80028d4: 40021000 .word 0x40021000 + 80028d8: 0800d9a0 .word 0x0800d9a0 + +080028dc : + +// rng_buffer() +// + void +rng_buffer(uint8_t *result, int len) +{ + 80028dc: b573 push {r0, r1, r4, r5, r6, lr} + 80028de: 460c mov r4, r1 + 80028e0: 1845 adds r5, r0, r1 + while(len > 0) { + 80028e2: 2c00 cmp r4, #0 + 80028e4: eba5 0604 sub.w r6, r5, r4 + 80028e8: dc01 bgt.n 80028ee + memcpy(result, &t, MIN(4, len)); + + len -= 4; + result += 4; + } +} + 80028ea: b002 add sp, #8 + 80028ec: bd70 pop {r4, r5, r6, pc} + uint32_t t = rng_sample(); + 80028ee: f7ff ffb7 bl 8002860 + memcpy(result, &t, MIN(4, len)); + 80028f2: 2c04 cmp r4, #4 + 80028f4: 4622 mov r2, r4 + uint32_t t = rng_sample(); + 80028f6: 9001 str r0, [sp, #4] + memcpy(result, &t, MIN(4, len)); + 80028f8: bfa8 it ge + 80028fa: 2204 movge r2, #4 + 80028fc: a901 add r1, sp, #4 + 80028fe: 4630 mov r0, r6 + 8002900: f00b f802 bl 800d908 + len -= 4; + 8002904: 3c04 subs r4, #4 + result += 4; + 8002906: e7ec b.n 80028e2 + +08002908 : +// +// Call anytime. Delays for a random time period to fustrate glitchers. +// + void +rng_delay(void) +{ + 8002908: b508 push {r3, lr} + uint32_t r = rng_sample() % 8; + 800290a: f7ff ffa9 bl 8002860 + uint32_t cnt = (1< + cnt--; + } +} + 800291e: bd08 pop {r3, pc} + +08002920 <_send_byte>: + static inline void +_send_byte(uint8_t ch) +{ + // reset timeout timer (Systick) + uint32_t ticks = 0; + SysTick->VAL = 0; + 8002920: f04f 22e0 mov.w r2, #3758153728 ; 0xe000e000 +{ + 8002924: b510 push {r4, lr} + SysTick->VAL = 0; + 8002926: 2300 movs r3, #0 + + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 8002928: 4c07 ldr r4, [pc, #28] ; (8002948 <_send_byte+0x28>) + SysTick->VAL = 0; + 800292a: 6193 str r3, [r2, #24] + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 800292c: 230b movs r3, #11 + 800292e: 69e1 ldr r1, [r4, #28] + 8002930: 0609 lsls r1, r1, #24 + 8002932: d404 bmi.n 800293e <_send_byte+0x1e> + // busy-wait until able to send (no fifo?) + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8002934: 6911 ldr r1, [r2, #16] + 8002936: 03c9 lsls r1, r1, #15 + 8002938: d5f9 bpl.n 800292e <_send_byte+0xe> + // failsafe timeout + ticks += 1; + if(ticks > 10) break; + 800293a: 3b01 subs r3, #1 + 800293c: d1f7 bne.n 800292e <_send_byte+0xe> + } + } + MY_UART->TDR = ch; + 800293e: 4b02 ldr r3, [pc, #8] ; (8002948 <_send_byte+0x28>) + 8002940: b280 uxth r0, r0 + 8002942: 8518 strh r0, [r3, #40] ; 0x28 +} + 8002944: bd10 pop {r4, pc} + 8002946: bf00 nop + 8002948: 40004c00 .word 0x40004c00 + +0800294c <_send_bits>: + +// _send_bits() +// + static void +_send_bits(uint8_t tx) +{ + 800294c: b570 push {r4, r5, r6, lr} + 800294e: 4606 mov r6, r0 + 8002950: 2508 movs r5, #8 + // serialize and send one byte + uint8_t mask = 0x1; + 8002952: 2401 movs r4, #1 + + for(int i=0; i<8; i++, mask <<= 1) { + uint8_t h = (tx & mask) ? BIT1 : BIT0; + 8002954: 4226 tst r6, r4 + + _send_byte(h); + 8002956: bf14 ite ne + 8002958: 207f movne r0, #127 ; 0x7f + 800295a: 207d moveq r0, #125 ; 0x7d + 800295c: f7ff ffe0 bl 8002920 <_send_byte> + for(int i=0; i<8; i++, mask <<= 1) { + 8002960: 0064 lsls r4, r4, #1 + 8002962: 3d01 subs r5, #1 + 8002964: b2e4 uxtb r4, r4 + 8002966: d1f5 bne.n 8002954 <_send_bits+0x8> + } +} + 8002968: bd70 pop {r4, r5, r6, pc} + +0800296a <_send_serialized>: + +// _send_serialized() +// + static void +_send_serialized(const uint8_t *buf, int len) +{ + 800296a: b538 push {r3, r4, r5, lr} + 800296c: 4604 mov r4, r0 + 800296e: 1845 adds r5, r0, r1 + for(int i=0; i + for(int i=0; i + } +} + 800297c: bd38 pop {r3, r4, r5, pc} + ... + +08002980 <_flush_rx>: +// + static inline void +_flush_rx(void) +{ + // reset timeout timer (Systick) + SysTick->VAL = 0; + 8002980: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 8002984: 2200 movs r2, #0 + + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 8002986: 490b ldr r1, [pc, #44] ; (80029b4 <_flush_rx+0x34>) + SysTick->VAL = 0; + 8002988: 619a str r2, [r3, #24] + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 800298a: 69ca ldr r2, [r1, #28] + 800298c: 0652 lsls r2, r2, #25 + 800298e: d402 bmi.n 8002996 <_flush_rx+0x16> + // wait for last bit(byte) to be serialized and sent + + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8002990: 691a ldr r2, [r3, #16] + 8002992: 03d0 lsls r0, r2, #15 + 8002994: d5f9 bpl.n 800298a <_flush_rx+0xa> + break; + } + } + + // We actually need this delay here! + __NOP(); + 8002996: bf00 nop + __NOP(); + 8002998: bf00 nop + __NOP(); + 800299a: bf00 nop + __NOP(); + 800299c: bf00 nop + __NOP(); + 800299e: bf00 nop + __NOP(); + 80029a0: bf00 nop + __NOP(); + 80029a2: bf00 nop + __NOP(); + 80029a4: bf00 nop + + // clear junk in rx buffer + MY_UART->RQR = USART_RQR_RXFRQ; + 80029a6: 4b03 ldr r3, [pc, #12] ; (80029b4 <_flush_rx+0x34>) + 80029a8: 2208 movs r2, #8 + 80029aa: 831a strh r2, [r3, #24] + + // clear overrun error + // clear rx timeout flag + // clear framing error + MY_UART->ICR = USART_ICR_ORECF | USART_ICR_RTOCF | USART_ICR_FECF; + 80029ac: f640 020a movw r2, #2058 ; 0x80a + 80029b0: 621a str r2, [r3, #32] +} + 80029b2: 4770 bx lr + 80029b4: 40004c00 .word 0x40004c00 + +080029b8 : + uint16_t crc_register = 0; + uint16_t polynom = 0x8005; + uint8_t shift_register; + uint8_t data_bit, crc_bit; + + crc_register = (((uint16_t) crc[0]) & 0x00FF) | (((uint16_t) crc[1]) << 8); + 80029b8: 8813 ldrh r3, [r2, #0] +{ + 80029ba: b5f0 push {r4, r5, r6, r7, lr} + 80029bc: 4408 add r0, r1 + + // Shift CRC to the left by 1. + crc_register <<= 1; + + if ((data_bit ^ crc_bit) != 0) + crc_register ^= polynom; + 80029be: f248 0605 movw r6, #32773 ; 0x8005 + for (counter = 0; counter < length; counter++) { + 80029c2: 4281 cmp r1, r0 + 80029c4: d103 bne.n 80029ce + } + } + + crc[0] = (uint8_t) (crc_register & 0x00FF); + 80029c6: 7013 strb r3, [r2, #0] + crc[1] = (uint8_t) (crc_register >> 8); + 80029c8: 0a1b lsrs r3, r3, #8 + 80029ca: 7053 strb r3, [r2, #1] +} + 80029cc: bdf0 pop {r4, r5, r6, r7, pc} + data_bit = (data[counter] & shift_register) ? 1 : 0; + 80029ce: f811 7b01 ldrb.w r7, [r1], #1 + 80029d2: 2508 movs r5, #8 + for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) { + 80029d4: 2401 movs r4, #1 + data_bit = (data[counter] & shift_register) ? 1 : 0; + 80029d6: 4227 tst r7, r4 + crc_bit = crc_register >> 15; + 80029d8: ea4f 3cd3 mov.w ip, r3, lsr #15 + if ((data_bit ^ crc_bit) != 0) + 80029dc: bf18 it ne + 80029de: f04f 0e01 movne.w lr, #1 + crc_register <<= 1; + 80029e2: ea4f 0343 mov.w r3, r3, lsl #1 + if ((data_bit ^ crc_bit) != 0) + 80029e6: bf08 it eq + 80029e8: f04f 0e00 moveq.w lr, #0 + 80029ec: 45e6 cmp lr, ip + crc_register <<= 1; + 80029ee: b29b uxth r3, r3 + crc_register ^= polynom; + 80029f0: bf18 it ne + 80029f2: 4073 eorne r3, r6 + for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) { + 80029f4: 0064 lsls r4, r4, #1 + 80029f6: 3d01 subs r5, #1 + 80029f8: b2e4 uxtb r4, r4 + 80029fa: d1ec bne.n 80029d6 + 80029fc: e7e1 b.n 80029c2 + +080029fe : + +// ae_check_crc() +// + static bool +ae_check_crc(const uint8_t *data, uint8_t length) +{ + 80029fe: b573 push {r0, r1, r4, r5, r6, lr} + uint8_t obs[2] = { 0, 0 }; + + if(data[0] != length) { + 8002a00: 7806 ldrb r6, [r0, #0] + uint8_t obs[2] = { 0, 0 }; + 8002a02: 2400 movs r4, #0 + if(data[0] != length) { + 8002a04: 428e cmp r6, r1 +{ + 8002a06: 4605 mov r5, r0 + uint8_t obs[2] = { 0, 0 }; + 8002a08: f8ad 4004 strh.w r4, [sp, #4] + if(data[0] != length) { + 8002a0c: d113 bne.n 8002a36 + // length is wrong + STATS(crc_len_error++); + return false; + } + + crc16_chain(length-2, data, obs); + 8002a0e: 4629 mov r1, r5 + 8002a10: 1eb0 subs r0, r6, #2 + + return (obs[0] == data[length-2] && obs[1] == data[length-1]); + 8002a12: 4435 add r5, r6 + crc16_chain(length-2, data, obs); + 8002a14: aa01 add r2, sp, #4 + 8002a16: b2c0 uxtb r0, r0 + 8002a18: f7ff ffce bl 80029b8 + return (obs[0] == data[length-2] && obs[1] == data[length-1]); + 8002a1c: f89d 2004 ldrb.w r2, [sp, #4] + 8002a20: f815 3c02 ldrb.w r3, [r5, #-2] + 8002a24: 429a cmp r2, r3 + 8002a26: d106 bne.n 8002a36 + 8002a28: f815 4c01 ldrb.w r4, [r5, #-1] + 8002a2c: f89d 0005 ldrb.w r0, [sp, #5] + 8002a30: 1a23 subs r3, r4, r0 + 8002a32: 425c negs r4, r3 + 8002a34: 415c adcs r4, r3 + return false; + 8002a36: 4620 mov r0, r4 +} + 8002a38: b002 add sp, #8 + 8002a3a: bd70 pop {r4, r5, r6, pc} + +08002a3c : +{ + 8002a3c: b508 push {r3, lr} + _send_byte(0x00); + 8002a3e: 2000 movs r0, #0 + 8002a40: f7ff ff6e bl 8002920 <_send_byte> + delay_ms(3); // measured: ~2.9ms + 8002a44: 2003 movs r0, #3 + 8002a46: f001 f81d bl 8003a84 +} + 8002a4a: e8bd 4008 ldmia.w sp!, {r3, lr} + _flush_rx(); + 8002a4e: f7ff bf97 b.w 8002980 <_flush_rx> + +08002a52 : +{ + 8002a52: b508 push {r3, lr} + ae_wake(); + 8002a54: f7ff fff2 bl 8002a3c +} + 8002a58: e8bd 4008 ldmia.w sp!, {r3, lr} + _send_bits(IOFLAG_IDLE); + 8002a5c: 20bb movs r0, #187 ; 0xbb + 8002a5e: f7ff bf75 b.w 800294c <_send_bits> + ... + +08002a64 : +{ + 8002a64: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + int max_expect = (max_len+1) * 8; + 8002a68: 3101 adds r1, #1 + uint8_t raw[max_expect]; + 8002a6a: 466b mov r3, sp + 8002a6c: eba3 03c1 sub.w r3, r3, r1, lsl #3 +{ + 8002a70: af00 add r7, sp, #0 + 8002a72: 4606 mov r6, r0 + uint8_t raw[max_expect]; + 8002a74: 469d mov sp, r3 + _send_bits(IOFLAG_TX); + 8002a76: 2088 movs r0, #136 ; 0x88 + int max_expect = (max_len+1) * 8; + 8002a78: 00cd lsls r5, r1, #3 + _send_bits(IOFLAG_TX); + 8002a7a: f7ff ff67 bl 800294c <_send_bits> + _flush_rx(); + 8002a7e: f7ff ff7f bl 8002980 <_flush_rx> + int actual = 0; + 8002a82: 2200 movs r2, #0 + while(!(MY_UART->ISR & UART_FLAG_RXNE) && !(MY_UART->ISR & UART_FLAG_RTOF)) { + 8002a84: 4829 ldr r0, [pc, #164] ; (8002b2c ) + uint8_t raw[max_expect]; + 8002a86: 466c mov r4, sp + for(uint8_t *p = raw; ; actual++) { + 8002a88: 4669 mov r1, sp + SysTick->VAL = 0; + 8002a8a: f04f 2ce0 mov.w ip, #3758153728 ; 0xe000e000 + 8002a8e: 4696 mov lr, r2 + 8002a90: f8cc e018 str.w lr, [ip, #24] + while(!(MY_UART->ISR & UART_FLAG_RXNE) && !(MY_UART->ISR & UART_FLAG_RTOF)) { + 8002a94: 2305 movs r3, #5 + 8002a96: f8d0 801c ldr.w r8, [r0, #28] + 8002a9a: f018 0f20 tst.w r8, #32 + 8002a9e: d104 bne.n 8002aaa + 8002aa0: f8d0 801c ldr.w r8, [r0, #28] + 8002aa4: f418 6f00 tst.w r8, #2048 ; 0x800 + 8002aa8: d008 beq.n 8002abc + if(MY_UART->ISR & UART_FLAG_RXNE) { + 8002aaa: 69c3 ldr r3, [r0, #28] + 8002aac: 069b lsls r3, r3, #26 + 8002aae: d52e bpl.n 8002b0e + return MY_UART->RDR & 0x7f; + 8002ab0: 8c83 ldrh r3, [r0, #36] ; 0x24 + if(actual < max_expect) { + 8002ab2: 42aa cmp r2, r5 + return MY_UART->RDR & 0x7f; + 8002ab4: b29b uxth r3, r3 + if(actual < max_expect) { + 8002ab6: db34 blt.n 8002b22 + for(uint8_t *p = raw; ; actual++) { + 8002ab8: 3201 adds r2, #1 + 8002aba: e7e9 b.n 8002a90 + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8002abc: f8dc 8010 ldr.w r8, [ip, #16] + 8002ac0: f418 3f80 tst.w r8, #65536 ; 0x10000 + 8002ac4: d0e7 beq.n 8002a96 + if(ticks >= 5) { + 8002ac6: 3b01 subs r3, #1 + 8002ac8: d1e5 bne.n 8002a96 + actual &= ~7; + 8002aca: f022 0107 bic.w r1, r2, #7 + while(from_len > 0) { + 8002ace: 3d08 subs r5, #8 + 8002ad0: 4425 add r5, r4 + 8002ad2: 4623 mov r3, r4 + 8002ad4: 4421 add r1, r4 + 8002ad6: 1ac8 subs r0, r1, r3 + 8002ad8: 2800 cmp r0, #0 + 8002ada: dd14 ble.n 8002b06 + 8002adc: f103 3cff add.w ip, r3, #4294967295 ; 0xffffffff + uint8_t rv = 0, mask = 0x1; + 8002ae0: 2001 movs r0, #1 + 8002ae2: 2400 movs r4, #0 + for(int i=0; i<8; i++, mask <<= 1) { + 8002ae4: f103 0e07 add.w lr, r3, #7 + if(from[i] == BIT1) { + 8002ae8: f81c 8f01 ldrb.w r8, [ip, #1]! + 8002aec: f1b8 0f7f cmp.w r8, #127 ; 0x7f + rv |= mask; + 8002af0: bf08 it eq + 8002af2: 4304 orreq r4, r0 + for(int i=0; i<8; i++, mask <<= 1) { + 8002af4: 0040 lsls r0, r0, #1 + 8002af6: 45f4 cmp ip, lr + 8002af8: b2c0 uxtb r0, r0 + 8002afa: d1f5 bne.n 8002ae8 + from += 8; + 8002afc: 3308 adds r3, #8 + if(max_into <= 0) break; + 8002afe: 42ab cmp r3, r5 + *(into++) = rv; + 8002b00: f806 4b01 strb.w r4, [r6], #1 + if(max_into <= 0) break; + 8002b04: d1e7 bne.n 8002ad6 + return actual / 8; + 8002b06: 10d0 asrs r0, r2, #3 +} + 8002b08: 46bd mov sp, r7 + 8002b0a: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if(MY_UART->ISR & UART_FLAG_RTOF) { + 8002b0e: 69c3 ldr r3, [r0, #28] + 8002b10: 051b lsls r3, r3, #20 + 8002b12: d503 bpl.n 8002b1c + MY_UART->ICR = USART_ICR_RTOCF; + 8002b14: f44f 6300 mov.w r3, #2048 ; 0x800 + 8002b18: 6203 str r3, [r0, #32] + if(ch < 0) { + 8002b1a: e7d6 b.n 8002aca + INCONSISTENT("rxf"); + 8002b1c: 4804 ldr r0, [pc, #16] ; (8002b30 ) + 8002b1e: f7fd ff8b bl 8000a38 + *(p++) = ch; + 8002b22: f003 037f and.w r3, r3, #127 ; 0x7f + 8002b26: f801 3b01 strb.w r3, [r1], #1 + 8002b2a: e7c5 b.n 8002ab8 + 8002b2c: 40004c00 .word 0x40004c00 + 8002b30: 0800d9a0 .word 0x0800d9a0 + +08002b34 : + if(ae_chip_is_setup == AE_CHIP_IS_SETUP) { + 8002b34: 4b04 ldr r3, [pc, #16] ; (8002b48 ) + 8002b36: 681a ldr r2, [r3, #0] + 8002b38: 4b04 ldr r3, [pc, #16] ; (8002b4c ) + 8002b3a: 429a cmp r2, r3 + 8002b3c: d102 bne.n 8002b44 + _send_bits(IOFLAG_SLEEP); + 8002b3e: 20cc movs r0, #204 ; 0xcc + 8002b40: f7ff bf04 b.w 800294c <_send_bits> +} + 8002b44: 4770 bx lr + 8002b46: bf00 nop + 8002b48: 2009e1c0 .word 0x2009e1c0 + 8002b4c: 35d25d63 .word 0x35d25d63 + +08002b50 : + __HAL_RCC_UART4_CLK_ENABLE(); + 8002b50: 4b13 ldr r3, [pc, #76] ; (8002ba0 ) + 8002b52: 6d9a ldr r2, [r3, #88] ; 0x58 + 8002b54: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 8002b58: 659a str r2, [r3, #88] ; 0x58 + 8002b5a: 6d9b ldr r3, [r3, #88] ; 0x58 +{ + 8002b5c: b082 sub sp, #8 + __HAL_RCC_UART4_CLK_ENABLE(); + 8002b5e: f403 2300 and.w r3, r3, #524288 ; 0x80000 + 8002b62: 9301 str r3, [sp, #4] + 8002b64: 9b01 ldr r3, [sp, #4] + MY_UART->CR1 = 0; + 8002b66: 4b0f ldr r3, [pc, #60] ; (8002ba4 ) + 8002b68: 2200 movs r2, #0 + 8002b6a: 601a str r2, [r3, #0] + MY_UART->CR1 = 0x1000002d & ~(0 + 8002b6c: 4a0e ldr r2, [pc, #56] ; (8002ba8 ) + 8002b6e: 601a str r2, [r3, #0] + MY_UART->RTOR = 24; // timeout in bit periods: 3 chars or so + 8002b70: 2218 movs r2, #24 + 8002b72: 615a str r2, [r3, #20] + MY_UART->CR2 = USART_CR2_RTOEN; // rx timeout enable + 8002b74: f44f 0200 mov.w r2, #8388608 ; 0x800000 + 8002b78: 605a str r2, [r3, #4] + MY_UART->CR3 = USART_CR3_HDSEL | USART_CR3_ONEBIT; + 8002b7a: f640 0208 movw r2, #2056 ; 0x808 + 8002b7e: 609a str r2, [r3, #8] + MY_UART->BRR = 521; // 230400 bps @ 120 Mhz SYSCLK + 8002b80: f240 2209 movw r2, #521 ; 0x209 + 8002b84: 60da str r2, [r3, #12] + MY_UART->ICR = USART_ICR_RTOCF; + 8002b86: f44f 6200 mov.w r2, #2048 ; 0x800 + 8002b8a: 621a str r2, [r3, #32] + MY_UART->CR1 |= USART_CR1_UE; + 8002b8c: 681a ldr r2, [r3, #0] + 8002b8e: f042 0201 orr.w r2, r2, #1 + 8002b92: 601a str r2, [r3, #0] + ae_chip_is_setup = AE_CHIP_IS_SETUP; + 8002b94: 4b05 ldr r3, [pc, #20] ; (8002bac ) + 8002b96: 4a06 ldr r2, [pc, #24] ; (8002bb0 ) + 8002b98: 601a str r2, [r3, #0] +} + 8002b9a: b002 add sp, #8 + 8002b9c: 4770 bx lr + 8002b9e: bf00 nop + 8002ba0: 40021000 .word 0x40021000 + 8002ba4: 40004c00 .word 0x40004c00 + 8002ba8: 1000002c .word 0x1000002c + 8002bac: 2009e1c0 .word 0x2009e1c0 + 8002bb0: 35d25d63 .word 0x35d25d63 + +08002bb4 : + ae_send_idle(); + 8002bb4: f7ff bf4d b.w 8002a52 + +08002bb8 : +// Read a one-byte status/error code response from chip. It's wrapped as 4 bytes: +// (len=4) (value) (crc16) (crc16) +// + int +ae_read1(void) +{ + 8002bb8: b513 push {r0, r1, r4, lr} + 8002bba: 2408 movs r4, #8 + uint8_t msg[4]; + + for(int retry=7; retry >= 0; retry--) { + // tell it we want to read a response, read it, and deserialize + int rv = ae_read_response(msg, 4); + 8002bbc: 2104 movs r1, #4 + 8002bbe: eb0d 0001 add.w r0, sp, r1 + 8002bc2: f7ff ff4f bl 8002a64 + + if(rv == 0) { + 8002bc6: 4601 mov r1, r0 + 8002bc8: b938 cbnz r0, 8002bda + // nothing heard, it's probably still processing + ERR("not rdy"); + STATS(not_ready++); + + delay_ms(5); + 8002bca: 2005 movs r0, #5 + 8002bcc: f000 ff5a bl 8003a84 + for(int retry=7; retry >= 0; retry--) { + 8002bd0: 3c01 subs r4, #1 + 8002bd2: d1f3 bne.n 8002bbc + try_again: + STATS(l1_retry++); + } + + // fail. + return -1; + 8002bd4: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8002bd8: e008 b.n 8002bec + if(rv != 4) { + 8002bda: 2804 cmp r0, #4 + 8002bdc: d1f8 bne.n 8002bd0 + if(!ae_check_crc(msg, 4)) { + 8002bde: a801 add r0, sp, #4 + 8002be0: f7ff ff0d bl 80029fe + 8002be4: 2800 cmp r0, #0 + 8002be6: d0f3 beq.n 8002bd0 + return msg[1]; + 8002be8: f89d 0005 ldrb.w r0, [sp, #5] +} + 8002bec: b002 add sp, #8 + 8002bee: bd10 pop {r4, pc} + +08002bf0 : +// Read and check CRC over N bytes, wrapped in 3-bytes of framing overhead. +// Return -1 for timeout, zero for normal, and one-byte error code otherwise. +// + int +ae_read_n(uint8_t len, uint8_t *body) +{ + 8002bf0: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + uint8_t tmp[1+len+2]; + 8002bf4: f100 030a add.w r3, r0, #10 + 8002bf8: f403 73fc and.w r3, r3, #504 ; 0x1f8 +{ + 8002bfc: af00 add r7, sp, #0 + uint8_t tmp[1+len+2]; + 8002bfe: ebad 0d03 sub.w sp, sp, r3 +{ + 8002c02: 460d mov r5, r1 + uint8_t tmp[1+len+2]; + 8002c04: 1cc6 adds r6, r0, #3 + 8002c06: 46e8 mov r8, sp + 8002c08: f04f 0908 mov.w r9, #8 + + for(int retry=7; retry >= 0; retry--) { + + int actual = ae_read_response(tmp, len+3); + 8002c0c: 4631 mov r1, r6 + 8002c0e: 4640 mov r0, r8 + 8002c10: f7ff ff28 bl 8002a64 + if(actual < 4) { + 8002c14: 2803 cmp r0, #3 + int actual = ae_read_response(tmp, len+3); + 8002c16: 4604 mov r4, r0 + if(actual < 4) { + 8002c18: dc0b bgt.n 8002c32 + + if(actual == 0) { + 8002c1a: b910 cbnz r0, 8002c22 + // nothing heard, it's probably still processing + delay_ms(5); + 8002c1c: 2005 movs r0, #5 + 8002c1e: f000 ff31 bl 8003a84 + + return 0; + + try_again: + STATS(ln_retry++); + ae_wake(); + 8002c22: f7ff ff0b bl 8002a3c + for(int retry=7; retry >= 0; retry--) { + 8002c26: f1b9 0901 subs.w r9, r9, #1 + 8002c2a: d1ef bne.n 8002c0c + } + + return -1; + 8002c2c: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8002c30: e007 b.n 8002c42 + uint8_t resp_len = tmp[0]; + 8002c32: f898 3000 ldrb.w r3, [r8] + if(resp_len != (len + 3)) { + 8002c36: 42b3 cmp r3, r6 + 8002c38: d006 beq.n 8002c48 + if(resp_len == 4) { + 8002c3a: 2b04 cmp r3, #4 + 8002c3c: d1f1 bne.n 8002c22 + return tmp[1]; + 8002c3e: f898 0001 ldrb.w r0, [r8, #1] +} + 8002c42: 46bd mov sp, r7 + 8002c44: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + if(!ae_check_crc(tmp, actual)) { + 8002c48: b2c1 uxtb r1, r0 + 8002c4a: 4640 mov r0, r8 + 8002c4c: f7ff fed7 bl 80029fe + 8002c50: 2800 cmp r0, #0 + 8002c52: d0e6 beq.n 8002c22 + memcpy(body, tmp+1, actual-3); + 8002c54: 1ee2 subs r2, r4, #3 + 8002c56: f108 0101 add.w r1, r8, #1 + 8002c5a: 4628 mov r0, r5 + 8002c5c: f00a fe54 bl 800d908 + return 0; + 8002c60: 2000 movs r0, #0 + 8002c62: e7ee b.n 8002c42 + +08002c64 : + +// ae_send_n() +// + void +ae_send_n(aeopcode_t opcode, uint8_t p1, uint16_t p2, const uint8_t *data, uint8_t data_len) +{ + 8002c64: b530 push {r4, r5, lr} + 8002c66: b085 sub sp, #20 + 8002c68: 461d mov r5, r3 + 8002c6a: f89d 4020 ldrb.w r4, [sp, #32] + uint8_t framed_len; + uint8_t op; + uint8_t p1; + uint8_t p2_lsb; + uint8_t p2_msb; + } known = { + 8002c6e: f88d 200c strb.w r2, [sp, #12] + 8002c72: 2377 movs r3, #119 ; 0x77 + 8002c74: 0a12 lsrs r2, r2, #8 + 8002c76: f88d 3008 strb.w r3, [sp, #8] + .ioflag = IOFLAG_CMD, + .framed_len = (data_len + 7), // 7 = (1 len) + (4 bytes of msg) + (2 crc) + 8002c7a: 1de3 adds r3, r4, #7 + } known = { + 8002c7c: f88d 3009 strb.w r3, [sp, #9] + 8002c80: f88d 200d strb.w r2, [sp, #13] + 8002c84: f88d 000a strb.w r0, [sp, #10] + 8002c88: f88d 100b strb.w r1, [sp, #11] + STATS(last_op = opcode); + STATS(last_p1 = p1); + STATS(last_p2 = p2); + + // important to wake chip at this point. + ae_wake(); + 8002c8c: f7ff fed6 bl 8002a3c + + _send_serialized((const uint8_t *)&known, sizeof(known)); + 8002c90: 2106 movs r1, #6 + 8002c92: a802 add r0, sp, #8 + 8002c94: f7ff fe69 bl 800296a <_send_serialized> + + // CRC will start from frame_len onwards + uint8_t crc[2] = {0, 0}; + 8002c98: 2300 movs r3, #0 + crc16_chain(sizeof(known)-1, &known.framed_len, crc); + 8002c9a: aa01 add r2, sp, #4 + 8002c9c: f10d 0109 add.w r1, sp, #9 + 8002ca0: 2005 movs r0, #5 + uint8_t crc[2] = {0, 0}; + 8002ca2: f8ad 3004 strh.w r3, [sp, #4] + crc16_chain(sizeof(known)-1, &known.framed_len, crc); + 8002ca6: f7ff fe87 bl 80029b8 + + // insert a variable-length body area (sometimes) + if(data_len) { + 8002caa: b144 cbz r4, 8002cbe + _send_serialized(data, data_len); + 8002cac: 4621 mov r1, r4 + 8002cae: 4628 mov r0, r5 + 8002cb0: f7ff fe5b bl 800296a <_send_serialized> + + crc16_chain(data_len, data, crc); + 8002cb4: aa01 add r2, sp, #4 + 8002cb6: 4629 mov r1, r5 + 8002cb8: 4620 mov r0, r4 + 8002cba: f7ff fe7d bl 80029b8 + } + + // send final CRC bytes + _send_serialized(crc, 2); + 8002cbe: 2102 movs r1, #2 + 8002cc0: a801 add r0, sp, #4 + 8002cc2: f7ff fe52 bl 800296a <_send_serialized> +} + 8002cc6: b005 add sp, #20 + 8002cc8: bd30 pop {r4, r5, pc} + +08002cca : +{ + 8002cca: b507 push {r0, r1, r2, lr} + ae_send_n(opcode, p1, p2, NULL, 0); + 8002ccc: 2300 movs r3, #0 + 8002cce: 9300 str r3, [sp, #0] + 8002cd0: f7ff ffc8 bl 8002c64 +} + 8002cd4: b003 add sp, #12 + 8002cd6: f85d fb04 ldr.w pc, [sp], #4 + +08002cda : +// +// Do Info(p1=2) command, and return result. +// + uint16_t +ae_get_info(void) +{ + 8002cda: b507 push {r0, r1, r2, lr} + // not doing error checking here + ae_send(OP_Info, 0x2, 0); + 8002cdc: 2200 movs r2, #0 + 8002cde: 2102 movs r1, #2 + 8002ce0: 2030 movs r0, #48 ; 0x30 + 8002ce2: f7ff fff2 bl 8002cca + + // note: always returns 4 bytes, but most are garbage and unused. + uint8_t tmp[4]; + ae_read_n(4, tmp); + 8002ce6: a901 add r1, sp, #4 + 8002ce8: 2004 movs r0, #4 + 8002cea: f7ff ff81 bl 8002bf0 + + return (tmp[0] << 8) | tmp[1]; + 8002cee: f8bd 0004 ldrh.w r0, [sp, #4] + 8002cf2: ba40 rev16 r0, r0 +} + 8002cf4: b280 uxth r0, r0 + 8002cf6: b003 add sp, #12 + 8002cf8: f85d fb04 ldr.w pc, [sp], #4 + +08002cfc : +// Load Tempkey with a specific value. Resulting Tempkey cannot be +// used with many commands/keys, but is needed for signing. +// + int +ae_load_nonce(const uint8_t nonce[32]) +{ + 8002cfc: b507 push {r0, r1, r2, lr} + // p1=3 + ae_send_n(OP_Nonce, 3, 0, nonce, 32); // 608a ok + 8002cfe: 2220 movs r2, #32 +{ + 8002d00: 4603 mov r3, r0 + ae_send_n(OP_Nonce, 3, 0, nonce, 32); // 608a ok + 8002d02: 9200 str r2, [sp, #0] + 8002d04: 2103 movs r1, #3 + 8002d06: 2200 movs r2, #0 + 8002d08: 2016 movs r0, #22 + 8002d0a: f7ff ffab bl 8002c64 + + return ae_read1(); +} + 8002d0e: b003 add sp, #12 + 8002d10: f85d eb04 ldr.w lr, [sp], #4 + return ae_read1(); + 8002d14: f7ff bf50 b.w 8002bb8 + +08002d18 : +// Load 32bytes of message digest with a specific value. +// Needed for signing. +// + int +ae_load_msgdigest(const uint8_t md[32]) +{ + 8002d18: b507 push {r0, r1, r2, lr} + ae_send_n(OP_Nonce, (1<<6) | 3, 0, md, 32); + 8002d1a: 2220 movs r2, #32 +{ + 8002d1c: 4603 mov r3, r0 + ae_send_n(OP_Nonce, (1<<6) | 3, 0, md, 32); + 8002d1e: 9200 str r2, [sp, #0] + 8002d20: 2143 movs r1, #67 ; 0x43 + 8002d22: 2200 movs r2, #0 + 8002d24: 2016 movs r0, #22 + 8002d26: f7ff ff9d bl 8002c64 + + return ae_read1(); +} + 8002d2a: b003 add sp, #12 + 8002d2c: f85d eb04 ldr.w lr, [sp], #4 + return ae_read1(); + 8002d30: f7ff bf42 b.w 8002bb8 + +08002d34 : +// Load Tempkey with a nonce value that we both know, but +// is random and we both know is random! Tricky! +// + int +ae_pick_nonce(const uint8_t num_in[20], uint8_t tempkey[32]) +{ + 8002d34: b5f0 push {r4, r5, r6, r7, lr} + 8002d36: b09f sub sp, #124 ; 0x7c + // We provide some 20 bytes of randomness to chip + // The chip must provide 32-bytes of random-ness, + // so no choice in args to OP.Nonce here (due to ReqRandom). + ae_send_n(OP_Nonce, 0, 0, num_in, 20); + 8002d38: 2200 movs r2, #0 + 8002d3a: 2714 movs r7, #20 + 8002d3c: 4603 mov r3, r0 +{ + 8002d3e: 4605 mov r5, r0 + 8002d40: 460e mov r6, r1 + ae_send_n(OP_Nonce, 0, 0, num_in, 20); + 8002d42: 2016 movs r0, #22 + 8002d44: 4611 mov r1, r2 + 8002d46: 9700 str r7, [sp, #0] + 8002d48: f7ff ff8c bl 8002c64 + + // Nonce command returns the RNG result, but not contents of TempKey + uint8_t randout[32]; + int rv = ae_read_n(32, randout); + 8002d4c: a903 add r1, sp, #12 + 8002d4e: 2020 movs r0, #32 + 8002d50: f7ff ff4e bl 8002bf0 + RET_IF_BAD(rv); + 8002d54: 4604 mov r4, r0 + 8002d56: b9e0 cbnz r0, 8002d92 + // + // return sha256(rndout + num_in + b'\x16\0\0').digest() + // + SHA256_CTX ctx; + + sha256_init(&ctx); + 8002d58: a80b add r0, sp, #44 ; 0x2c + 8002d5a: f002 fd0f bl 800577c + sha256_update(&ctx, randout, 32); + 8002d5e: 2220 movs r2, #32 + 8002d60: a903 add r1, sp, #12 + 8002d62: a80b add r0, sp, #44 ; 0x2c + 8002d64: f002 fd18 bl 8005798 + sha256_update(&ctx, num_in, 20); + 8002d68: 463a mov r2, r7 + 8002d6a: 4629 mov r1, r5 + 8002d6c: a80b add r0, sp, #44 ; 0x2c + 8002d6e: f002 fd13 bl 8005798 + const uint8_t fixed[3] = { 0x16, 0, 0 }; + 8002d72: 4b09 ldr r3, [pc, #36] ; (8002d98 ) + 8002d74: 881a ldrh r2, [r3, #0] + 8002d76: f8ad 2008 strh.w r2, [sp, #8] + 8002d7a: 789b ldrb r3, [r3, #2] + 8002d7c: f88d 300a strb.w r3, [sp, #10] + sha256_update(&ctx, fixed, 3); + 8002d80: a902 add r1, sp, #8 + 8002d82: a80b add r0, sp, #44 ; 0x2c + 8002d84: 2203 movs r2, #3 + 8002d86: f002 fd07 bl 8005798 + + sha256_final(&ctx, tempkey); + 8002d8a: 4631 mov r1, r6 + 8002d8c: a80b add r0, sp, #44 ; 0x2c + 8002d8e: f002 fd49 bl 8005824 + + return 0; +} + 8002d92: 4620 mov r0, r4 + 8002d94: b01f add sp, #124 ; 0x7c + 8002d96: bdf0 pop {r4, r5, r6, r7, pc} + 8002d98: 080106f0 .word 0x080106f0 + +08002d9c : +// Check that TempKey is holding what we think it does. Uses the MAC +// command over contents of Tempkey and our shared secret. +// + bool +ae_is_correct_tempkey(const uint8_t expected_tempkey[32]) +{ + 8002d9c: b570 push {r4, r5, r6, lr} + const uint8_t mode = (1<<6) // include full serial number + | (0<<2) // TempKey.SourceFlag == 0 == 'rand' + | (0<<1) // first 32 bytes are the shared secret + | (1<<0); // second 32 bytes are tempkey + + ae_send(OP_MAC, mode, KEYNUM_pairing); + 8002d9e: 2141 movs r1, #65 ; 0x41 +{ + 8002da0: b0a8 sub sp, #160 ; 0xa0 + 8002da2: 4604 mov r4, r0 + ae_send(OP_MAC, mode, KEYNUM_pairing); + 8002da4: 2201 movs r2, #1 + 8002da6: 2008 movs r0, #8 + 8002da8: f7ff ff8f bl 8002cca + + // read chip's answer + uint8_t resp[32]; + int rv = ae_read_n(32, resp); + 8002dac: a905 add r1, sp, #20 + 8002dae: 2020 movs r0, #32 + 8002db0: f7ff ff1e bl 8002bf0 + if(rv) return false; + 8002db4: 2800 cmp r0, #0 + 8002db6: d135 bne.n 8002e24 + ae_send_idle(); + 8002db8: f7ff fe4b bl 8002a52 + ae_keep_alive(); + + // Duplicate the hash process, and then compare. + SHA256_CTX ctx; + + sha256_init(&ctx); + 8002dbc: a815 add r0, sp, #84 ; 0x54 + 8002dbe: f002 fcdd bl 800577c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8002dc2: 4919 ldr r1, [pc, #100] ; (8002e28 ) + 8002dc4: 2220 movs r2, #32 + 8002dc6: a815 add r0, sp, #84 ; 0x54 + 8002dc8: f002 fce6 bl 8005798 + sha256_update(&ctx, expected_tempkey, 32); + 8002dcc: 2220 movs r2, #32 + 8002dce: 4621 mov r1, r4 + 8002dd0: a815 add r0, sp, #84 ; 0x54 + 8002dd2: f002 fce1 bl 8005798 + + const uint8_t fixed[16] = { OP_MAC, mode, KEYNUM_pairing, 0x0, + 8002dd6: 4b15 ldr r3, [pc, #84] ; (8002e2c ) + 8002dd8: aa01 add r2, sp, #4 + 8002dda: f103 0610 add.w r6, r3, #16 + 8002dde: 4615 mov r5, r2 + 8002de0: 6818 ldr r0, [r3, #0] + 8002de2: 6859 ldr r1, [r3, #4] + 8002de4: 4614 mov r4, r2 + 8002de6: c403 stmia r4!, {r0, r1} + 8002de8: 3308 adds r3, #8 + 8002dea: 42b3 cmp r3, r6 + 8002dec: 4622 mov r2, r4 + 8002dee: d1f7 bne.n 8002de0 + 0,0,0,0, 0,0,0,0, // eight zeros + 0,0,0, // three zeros + 0xEE }; + sha256_update(&ctx, fixed, sizeof(fixed)); + 8002df0: 2210 movs r2, #16 + 8002df2: 4629 mov r1, r5 + 8002df4: a815 add r0, sp, #84 ; 0x54 + 8002df6: f002 fccf bl 8005798 + + sha256_update(&ctx, ((const uint8_t *)rom_secrets->ae_serial_number)+4, 4); + 8002dfa: 490d ldr r1, [pc, #52] ; (8002e30 ) + 8002dfc: 2204 movs r2, #4 + 8002dfe: a815 add r0, sp, #84 ; 0x54 + 8002e00: f002 fcca bl 8005798 + sha256_update(&ctx, ((const uint8_t *)rom_secrets->ae_serial_number)+0, 4); + 8002e04: 2204 movs r2, #4 + 8002e06: 490b ldr r1, [pc, #44] ; (8002e34 ) + 8002e08: a815 add r0, sp, #84 ; 0x54 + 8002e0a: f002 fcc5 bl 8005798 + // this verifies no problem. + ASSERT(ctx.datalen + (ctx.bitlen/8) == 32+32+1+1+2+8+3+1+4+2+2); // == 88 +#endif + + uint8_t actual[32]; + sha256_final(&ctx, actual); + 8002e0e: a90d add r1, sp, #52 ; 0x34 + 8002e10: a815 add r0, sp, #84 ; 0x54 + 8002e12: f002 fd07 bl 8005824 + + return check_equal(actual, resp, 32); + 8002e16: 2220 movs r2, #32 + 8002e18: a905 add r1, sp, #20 + 8002e1a: a80d add r0, sp, #52 ; 0x34 + 8002e1c: f7ff fd0f bl 800283e +} + 8002e20: b028 add sp, #160 ; 0xa0 + 8002e22: bd70 pop {r4, r5, r6, pc} + if(rv) return false; + 8002e24: 2000 movs r0, #0 + 8002e26: e7fb b.n 8002e20 + 8002e28: 0801c000 .word 0x0801c000 + 8002e2c: 080106f3 .word 0x080106f3 + 8002e30: 0801c044 .word 0x0801c044 + 8002e34: 0801c040 .word 0x0801c040 + +08002e38 : +// inside the 508a/608a, like use of a specific key, but not for us to +// authenticate the 508a/608a or its contents/state. +// + int +ae_checkmac(uint8_t keynum, const uint8_t secret[32]) +{ + 8002e38: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8002e3c: b0c2 sub sp, #264 ; 0x108 + + // Since this is part of the hash, we want random bytes + // for our "other data". Also a number for "numin" of nonce + uint8_t od[32], numin[20]; + + rng_buffer(od, sizeof(od)); + 8002e3e: ad0b add r5, sp, #44 ; 0x2c +{ + 8002e40: 4607 mov r7, r0 + 8002e42: 460e mov r6, r1 + rng_buffer(od, sizeof(od)); + 8002e44: 4628 mov r0, r5 + 8002e46: 2120 movs r1, #32 + 8002e48: f7ff fd48 bl 80028dc + rng_buffer(numin, sizeof(numin)); + 8002e4c: 2114 movs r1, #20 + 8002e4e: a806 add r0, sp, #24 + 8002e50: f7ff fd44 bl 80028dc + ae_send_idle(); + 8002e54: f7ff fdfd bl 8002a52 + + // need this one, want to reset watchdog to this point. + ae_keep_alive(); + + // - load tempkey with a known nonce value + uint8_t zeros[8] = {0}; + 8002e58: 2300 movs r3, #0 + uint8_t tempkey[32]; + rv = ae_pick_nonce(numin, tempkey); + 8002e5a: a913 add r1, sp, #76 ; 0x4c + 8002e5c: a806 add r0, sp, #24 + uint8_t zeros[8] = {0}; + 8002e5e: e9cd 3304 strd r3, r3, [sp, #16] + rv = ae_pick_nonce(numin, tempkey); + 8002e62: f7ff ff67 bl 8002d34 + RET_IF_BAD(rv); + 8002e66: 4604 mov r4, r0 + 8002e68: 2800 cmp r0, #0 + 8002e6a: d15d bne.n 8002f28 + + // - hash nonce and lots of other bits together + SHA256_CTX ctx; + sha256_init(&ctx); + 8002e6c: a81b add r0, sp, #108 ; 0x6c + 8002e6e: f002 fc85 bl 800577c + + // shared secret is 32 bytes from flash + sha256_update(&ctx, secret, 32); + 8002e72: 2220 movs r2, #32 + 8002e74: 4631 mov r1, r6 + 8002e76: a81b add r0, sp, #108 ; 0x6c + 8002e78: f002 fc8e bl 8005798 + + sha256_update(&ctx, tempkey, 32); + 8002e7c: 2220 movs r2, #32 + 8002e7e: a913 add r1, sp, #76 ; 0x4c + 8002e80: a81b add r0, sp, #108 ; 0x6c + 8002e82: f002 fc89 bl 8005798 + sha256_update(&ctx, &od[0], 4); + 8002e86: 2204 movs r2, #4 + 8002e88: 4629 mov r1, r5 + 8002e8a: a81b add r0, sp, #108 ; 0x6c + 8002e8c: f002 fc84 bl 8005798 + + sha256_update(&ctx, zeros, 8); + 8002e90: 2208 movs r2, #8 + 8002e92: a904 add r1, sp, #16 + 8002e94: a81b add r0, sp, #108 ; 0x6c + 8002e96: f002 fc7f bl 8005798 + + sha256_update(&ctx, &od[4], 3); + 8002e9a: 2203 movs r2, #3 + 8002e9c: a90c add r1, sp, #48 ; 0x30 + 8002e9e: a81b add r0, sp, #108 ; 0x6c + 8002ea0: f002 fc7a bl 8005798 + + uint8_t ee = 0xEE; + 8002ea4: 23ee movs r3, #238 ; 0xee + sha256_update(&ctx, &ee, 1); + 8002ea6: 2201 movs r2, #1 + 8002ea8: f10d 010b add.w r1, sp, #11 + 8002eac: a81b add r0, sp, #108 ; 0x6c + uint8_t ee = 0xEE; + 8002eae: f88d 300b strb.w r3, [sp, #11] + sha256_update(&ctx, &ee, 1); + 8002eb2: f002 fc71 bl 8005798 + sha256_update(&ctx, &od[7], 4); + 8002eb6: 2204 movs r2, #4 + 8002eb8: f10d 0133 add.w r1, sp, #51 ; 0x33 + 8002ebc: a81b add r0, sp, #108 ; 0x6c + 8002ebe: f002 fc6b bl 8005798 + + uint8_t snp[2] = { 0x01, 0x23 }; + 8002ec2: f242 3301 movw r3, #8961 ; 0x2301 + sha256_update(&ctx, snp, 2); + 8002ec6: 2202 movs r2, #2 + 8002ec8: a903 add r1, sp, #12 + 8002eca: a81b add r0, sp, #108 ; 0x6c + uint8_t snp[2] = { 0x01, 0x23 }; + 8002ecc: f8ad 300c strh.w r3, [sp, #12] + sha256_update(&ctx, snp, 2); + 8002ed0: f002 fc62 bl 8005798 + sha256_update(&ctx, &od[11], 2); + 8002ed4: 2202 movs r2, #2 + 8002ed6: f10d 0137 add.w r1, sp, #55 ; 0x37 + 8002eda: a81b add r0, sp, #108 ; 0x6c + 8002edc: f002 fc5c bl 8005798 + uint8_t resp[32]; + uint8_t od[13]; + } req; + + // content doesn't matter, but nice and visible: + memcpy(req.ch3, copyright_msg, 32); + 8002ee0: 4b15 ldr r3, [pc, #84] ; (8002f38 ) + 8002ee2: ac2e add r4, sp, #184 ; 0xb8 + 8002ee4: f103 0220 add.w r2, r3, #32 + 8002ee8: 46a0 mov r8, r4 + 8002eea: 6818 ldr r0, [r3, #0] + 8002eec: 6859 ldr r1, [r3, #4] + 8002eee: 4626 mov r6, r4 + 8002ef0: c603 stmia r6!, {r0, r1} + 8002ef2: 3308 adds r3, #8 + 8002ef4: 4293 cmp r3, r2 + 8002ef6: 4634 mov r4, r6 + 8002ef8: d1f7 bne.n 8002eea + // this verifies no problem. + int l = (ctx.blocks * 64) + ctx.npartial; + ASSERT(l == 32+32+4+8+3+1+4+2+2); // == 88 +#endif + + sha256_final(&ctx, req.resp); + 8002efa: a936 add r1, sp, #216 ; 0xd8 + 8002efc: a81b add r0, sp, #108 ; 0x6c + 8002efe: f002 fc91 bl 8005824 + memcpy(req.od, od, 13); + 8002f02: e895 000f ldmia.w r5, {r0, r1, r2, r3} + 8002f06: ac3e add r4, sp, #248 ; 0xf8 + 8002f08: c407 stmia r4!, {r0, r1, r2} + 8002f0a: 7023 strb r3, [r4, #0] + + STATIC_ASSERT(sizeof(req) == 32 + 32 + 13); + + // Give our answer to the chip. + ae_send_n(OP_CheckMac, 0x01, keynum, (uint8_t *)&req, sizeof(req)); + 8002f0c: 234d movs r3, #77 ; 0x4d + 8002f0e: 9300 str r3, [sp, #0] + 8002f10: 463a mov r2, r7 + 8002f12: 4643 mov r3, r8 + 8002f14: 2101 movs r1, #1 + 8002f16: 2028 movs r0, #40 ; 0x28 + 8002f18: f7ff fea4 bl 8002c64 + + rv = ae_read1(); + 8002f1c: f7ff fe4c bl 8002bb8 + if(rv != 0) { + 8002f20: 4604 mov r4, r0 + 8002f22: b928 cbnz r0, 8002f30 + ae_send_idle(); + 8002f24: f7ff fd95 bl 8002a52 + + // just in case ... always restart watchdog timer. + ae_keep_alive(); + + return 0; +} + 8002f28: 4620 mov r0, r4 + 8002f2a: b042 add sp, #264 ; 0x108 + 8002f2c: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + return -1; + 8002f30: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 8002f34: e7f8 b.n 8002f28 + 8002f36: bf00 nop + 8002f38: 080106c2 .word 0x080106c2 + +08002f3c : + return ae_checkmac(KEYNUM_pairing, rom_secrets->pairing_secret); + 8002f3c: 4901 ldr r1, [pc, #4] ; (8002f44 ) + 8002f3e: 2001 movs r0, #1 + 8002f40: f7ff bf7a b.w 8002e38 + 8002f44: 0801c000 .word 0x0801c000 + +08002f48 : +// Sign a message (already digested) +// + int +ae_sign_authed(uint8_t keynum, const uint8_t msg_hash[32], + uint8_t signature[64], int auth_kn, const uint8_t auth_digest[32]) +{ + 8002f48: b570 push {r4, r5, r6, lr} + 8002f4a: 460e mov r6, r1 + 8002f4c: 4604 mov r4, r0 + 8002f4e: 4615 mov r5, r2 + // indicate we know the PIN + ae_pair_unlock(); + 8002f50: f7ff fff4 bl 8002f3c + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + 8002f54: 9904 ldr r1, [sp, #16] + 8002f56: 2003 movs r0, #3 + 8002f58: f7ff ff6e bl 8002e38 + RET_IF_BAD(rv); + 8002f5c: b990 cbnz r0, 8002f84 + + // send what we need signed + rv = ae_load_msgdigest(msg_hash); + 8002f5e: 4630 mov r0, r6 + 8002f60: f7ff feda bl 8002d18 + RET_IF_BAD(rv); + 8002f64: b970 cbnz r0, 8002f84 + + do { + ae_send(OP_Sign, (7<<5), keynum); + 8002f66: b2a4 uxth r4, r4 + 8002f68: 4622 mov r2, r4 + 8002f6a: 21e0 movs r1, #224 ; 0xe0 + 8002f6c: 2041 movs r0, #65 ; 0x41 + 8002f6e: f7ff feac bl 8002cca + + delay_ms(60); // min time for processing + 8002f72: 203c movs r0, #60 ; 0x3c + 8002f74: f000 fd86 bl 8003a84 + + rv = ae_read_n(64, signature); + 8002f78: 4629 mov r1, r5 + 8002f7a: 2040 movs r0, #64 ; 0x40 + 8002f7c: f7ff fe38 bl 8002bf0 + } while(rv == AE_ECC_FAULT); + 8002f80: 2805 cmp r0, #5 + 8002f82: d0f1 beq.n 8002f68 + + return rv; +} + 8002f84: bd70 pop {r4, r5, r6, pc} + ... + +08002f88 : + +// ae_gen_ecc_key() +// + int +ae_gen_ecc_key(uint8_t keynum, uint8_t pubkey_out[64]) +{ + 8002f88: b530 push {r4, r5, lr} + int rv; + uint8_t junk[3] = { 0 }; + 8002f8a: 4b0f ldr r3, [pc, #60] ; (8002fc8 ) +{ + 8002f8c: b085 sub sp, #20 + uint8_t junk[3] = { 0 }; + 8002f8e: f8b3 3013 ldrh.w r3, [r3, #19] + 8002f92: f8ad 300c strh.w r3, [sp, #12] + 8002f96: 2300 movs r3, #0 +{ + 8002f98: 460c mov r4, r1 + uint8_t junk[3] = { 0 }; + 8002f9a: f88d 300e strb.w r3, [sp, #14] + + do { + ae_send_n(OP_GenKey, (1<<2), keynum, junk, 3); + 8002f9e: 4605 mov r5, r0 + 8002fa0: 2303 movs r3, #3 + 8002fa2: 462a mov r2, r5 + 8002fa4: 2104 movs r1, #4 + 8002fa6: 9300 str r3, [sp, #0] + 8002fa8: 2040 movs r0, #64 ; 0x40 + 8002faa: ab03 add r3, sp, #12 + 8002fac: f7ff fe5a bl 8002c64 + + delay_ms(100); // to avoid timeouts + 8002fb0: 2064 movs r0, #100 ; 0x64 + 8002fb2: f000 fd67 bl 8003a84 + + rv = ae_read_n(64, pubkey_out); + 8002fb6: 4621 mov r1, r4 + 8002fb8: 2040 movs r0, #64 ; 0x40 + 8002fba: f7ff fe19 bl 8002bf0 + } while(rv == AE_ECC_FAULT); + 8002fbe: 2805 cmp r0, #5 + 8002fc0: d0ee beq.n 8002fa0 + + return rv; +} + 8002fc2: b005 add sp, #20 + 8002fc4: bd30 pop {r4, r5, pc} + 8002fc6: bf00 nop + 8002fc8: 080106f0 .word 0x080106f0 + +08002fcc : +// 508a: Different opcode, OP_HMAC does exactly 32 bytes w/ less steps. +// 608a: Use old SHA256 command, but with new flags. +// + int +ae_hmac32(uint8_t keynum, const uint8_t msg[32], uint8_t digest[32]) +{ + 8002fcc: b530 push {r4, r5, lr} + 8002fce: b085 sub sp, #20 + 8002fd0: 4615 mov r5, r2 + 8002fd2: 9103 str r1, [sp, #12] + // Start SHA w/ HMAC setup + ae_send(OP_SHA, 4, keynum); // 4 = HMAC_Init + 8002fd4: 4602 mov r2, r0 + 8002fd6: 2104 movs r1, #4 + 8002fd8: 2047 movs r0, #71 ; 0x47 + 8002fda: f7ff fe76 bl 8002cca + + // expect zero, meaning "ready" + int rv = ae_read1(); + 8002fde: f7ff fdeb bl 8002bb8 + RET_IF_BAD(rv); + 8002fe2: b970 cbnz r0, 8003002 + + // send the contents to be hashed + ae_send_n(OP_SHA, (3<<6) | 2, 32, msg, 32); // 2 = Finalize, 3=Place output + 8002fe4: 2420 movs r4, #32 + 8002fe6: 9b03 ldr r3, [sp, #12] + 8002fe8: 9400 str r4, [sp, #0] + 8002fea: 4622 mov r2, r4 + 8002fec: 21c2 movs r1, #194 ; 0xc2 + 8002fee: 2047 movs r0, #71 ; 0x47 + 8002ff0: f7ff fe38 bl 8002c64 + + // read result + return ae_read_n(32, digest); + 8002ff4: 4629 mov r1, r5 + 8002ff6: 4620 mov r0, r4 +} + 8002ff8: b005 add sp, #20 + 8002ffa: e8bd 4030 ldmia.w sp!, {r4, r5, lr} + return ae_read_n(32, digest); + 8002ffe: f7ff bdf7 b.w 8002bf0 +} + 8003002: b005 add sp, #20 + 8003004: bd30 pop {r4, r5, pc} + +08003006 : +// +// Return the serial number: it's 9 bytes, altho 3 are fixed. +// + int +ae_get_serial(uint8_t serial[6]) +{ + 8003006: b510 push {r4, lr} + ae_send(OP_Read, 0x80, 0x0); + 8003008: 2200 movs r2, #0 +{ + 800300a: b08c sub sp, #48 ; 0x30 + ae_send(OP_Read, 0x80, 0x0); + 800300c: 2180 movs r1, #128 ; 0x80 +{ + 800300e: 4604 mov r4, r0 + ae_send(OP_Read, 0x80, 0x0); + 8003010: 2002 movs r0, #2 + 8003012: f7ff fe5a bl 8002cca + + uint8_t temp[32]; + int rv = ae_read_n(32, temp); + 8003016: a904 add r1, sp, #16 + 8003018: 2020 movs r0, #32 + 800301a: f7ff fde9 bl 8002bf0 + RET_IF_BAD(rv); + 800301e: 4603 mov r3, r0 + 8003020: b9b8 cbnz r0, 8003052 + + // reformat to 9 bytes. + uint8_t ts[9]; + memcpy(ts, &temp[0], 4); + memcpy(&ts[4], &temp[8], 5); + 8003022: e9dd 0106 ldrd r0, r1, [sp, #24] + 8003026: 9a04 ldr r2, [sp, #16] + 8003028: f88d 100c strb.w r1, [sp, #12] + + // check the hard-coded values + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 800302c: b2d1 uxtb r1, r2 + 800302e: 2901 cmp r1, #1 + memcpy(ts, &temp[0], 4); + 8003030: 9201 str r2, [sp, #4] + memcpy(&ts[4], &temp[8], 5); + 8003032: 9002 str r0, [sp, #8] + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 8003034: d110 bne.n 8003058 + 8003036: f3c2 2207 ubfx r2, r2, #8, #8 + 800303a: 2a23 cmp r2, #35 ; 0x23 + 800303c: d10c bne.n 8003058 + 800303e: f89d 200c ldrb.w r2, [sp, #12] + 8003042: 2aee cmp r2, #238 ; 0xee + 8003044: d10a bne.n 800305c + + // save only the unique bits. + memcpy(serial, ts+2, 6); + 8003046: f8dd 2006 ldr.w r2, [sp, #6] + 800304a: 6022 str r2, [r4, #0] + 800304c: f8bd 200a ldrh.w r2, [sp, #10] + 8003050: 80a2 strh r2, [r4, #4] + + return 0; +} + 8003052: 4618 mov r0, r3 + 8003054: b00c add sp, #48 ; 0x30 + 8003056: bd10 pop {r4, pc} + if((ts[0] != 0x01) || (ts[1] != 0x23) || (ts[8] != 0xEE)) return 1; + 8003058: 2301 movs r3, #1 + 800305a: e7fa b.n 8003052 + 800305c: 460b mov r3, r1 + 800305e: e7f8 b.n 8003052 + +08003060 : +{ + 8003060: b513 push {r0, r1, r4, lr} + ae_wake(); + 8003062: f7ff fceb bl 8002a3c + _send_bits(IOFLAG_SLEEP); + 8003066: 20cc movs r0, #204 ; 0xcc + 8003068: f7ff fc70 bl 800294c <_send_bits> + ae_wake(); + 800306c: f7ff fce6 bl 8002a3c + ae_read1(); + 8003070: f7ff fda2 bl 8002bb8 + uint8_t chk = ae_read1(); + 8003074: f7ff fda0 bl 8002bb8 + if(chk != AE_AFTER_WAKE) return "wk fl"; + 8003078: b2c0 uxtb r0, r0 + 800307a: 2811 cmp r0, #17 + 800307c: d10e bne.n 800309c + if(ae_get_serial(serial)) return "no ser"; + 800307e: 4668 mov r0, sp + 8003080: f7ff ffc1 bl 8003006 + 8003084: 4604 mov r4, r0 + 8003086: b938 cbnz r0, 8003098 + ae_wake(); + 8003088: f7ff fcd8 bl 8002a3c + _send_bits(IOFLAG_SLEEP); + 800308c: 20cc movs r0, #204 ; 0xcc + 800308e: f7ff fc5d bl 800294c <_send_bits> + return NULL; + 8003092: 4620 mov r0, r4 +} + 8003094: b002 add sp, #8 + 8003096: bd10 pop {r4, pc} + if(ae_get_serial(serial)) return "no ser"; + 8003098: 4801 ldr r0, [pc, #4] ; (80030a0 ) + 800309a: e7fb b.n 8003094 + if(chk != AE_AFTER_WAKE) return "wk fl"; + 800309c: 4801 ldr r0, [pc, #4] ; (80030a4 ) + 800309e: e7f9 b.n 8003094 + 80030a0: 080106e3 .word 0x080106e3 + 80030a4: 080106ea .word 0x080106ea + +080030a8 : +// +// -- can also lock it. +// + int +ae_write_data_slot(int slot_num, const uint8_t *data, int len, bool lock_it) +{ + 80030a8: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 80030ac: 4699 mov r9, r3 + ASSERT(len >= 32); + 80030ae: f1a2 0320 sub.w r3, r2, #32 +{ + 80030b2: b085 sub sp, #20 + ASSERT(len >= 32); + 80030b4: f5b3 7fc0 cmp.w r3, #384 ; 0x180 +{ + 80030b8: 4604 mov r4, r0 + 80030ba: af02 add r7, sp, #8 + 80030bc: 460d mov r5, r1 + 80030be: 4690 mov r8, r2 + ASSERT(len >= 32); + 80030c0: d902 bls.n 80030c8 + 80030c2: 482d ldr r0, [pc, #180] ; (8003178 ) + 80030c4: f7fd fcb8 bl 8000a38 + ASSERT(len <= 416); + + for(int blk=0, xlen=len; xlen>0; blk++, xlen-=32) { + // have to write each "block" of 32-bytes, separately + // zone => data + ae_send_n(OP_Write, 0x80|2, (blk<<8) | (slot_num<<3), data+(blk*32), 32); + 80030c8: ea4f 0ac0 mov.w sl, r0, lsl #3 + 80030cc: fa0f fa8a sxth.w sl, sl + 80030d0: 2600 movs r6, #0 + 80030d2: f04f 0b20 mov.w fp, #32 + 80030d6: ebc6 3246 rsb r2, r6, r6, lsl #13 + 80030da: ea4a 02c2 orr.w r2, sl, r2, lsl #3 + 80030de: b292 uxth r2, r2 + 80030e0: 1bab subs r3, r5, r6 + 80030e2: 2182 movs r1, #130 ; 0x82 + 80030e4: 2012 movs r0, #18 + 80030e6: f8cd b000 str.w fp, [sp] + 80030ea: f7ff fdbb bl 8002c64 + + int rv = ae_read1(); + 80030ee: f7ff fd63 bl 8002bb8 + RET_IF_BAD(rv); + 80030f2: 2800 cmp r0, #0 + 80030f4: d13c bne.n 8003170 + for(int blk=0, xlen=len; xlen>0; blk++, xlen-=32) { + 80030f6: 3e20 subs r6, #32 + 80030f8: eb06 0308 add.w r3, r6, r8 + 80030fc: 2b00 cmp r3, #0 + 80030fe: dcea bgt.n 80030d6 + } + + if(lock_it) { + 8003100: f1b9 0f00 cmp.w r9, #0 + 8003104: d034 beq.n 8003170 + ASSERT(slot_num != 8); // no support for mega slot 8 + 8003106: 2c08 cmp r4, #8 + if(lock_it) { + 8003108: 466e mov r6, sp + ASSERT(slot_num != 8); // no support for mega slot 8 + 800310a: d0da beq.n 80030c2 + ASSERT(len == 32); // probably not a limitation here + 800310c: f1b8 0f20 cmp.w r8, #32 + 8003110: d1d7 bne.n 80030c2 + + // Assume 36/72-byte long slot, which will be partially written, and rest + // should be ones. + const int slot_len = (slot_num <= 7) ? 36 : 72; + 8003112: 2c08 cmp r4, #8 + 8003114: bfb4 ite lt + 8003116: f04f 0824 movlt.w r8, #36 ; 0x24 + 800311a: f04f 0848 movge.w r8, #72 ; 0x48 + uint8_t copy[slot_len]; + 800311e: f108 0307 add.w r3, r8, #7 + 8003122: f003 03f8 and.w r3, r3, #248 ; 0xf8 + 8003126: ebad 0d03 sub.w sp, sp, r3 + 800312a: ab02 add r3, sp, #8 + + memset(copy, 0xff, slot_len); + 800312c: 4642 mov r2, r8 + 800312e: 21ff movs r1, #255 ; 0xff + 8003130: 4618 mov r0, r3 + 8003132: f00a fbf7 bl 800d924 + memcpy(copy, data, len); + 8003136: f105 0120 add.w r1, r5, #32 + memset(copy, 0xff, slot_len); + 800313a: 4603 mov r3, r0 + memcpy(copy, data, len); + 800313c: 4602 mov r2, r0 + 800313e: f855 0b04 ldr.w r0, [r5], #4 + 8003142: f842 0b04 str.w r0, [r2], #4 + 8003146: 428d cmp r5, r1 + 8003148: d1f9 bne.n 800313e + + // calc expected CRC + uint8_t crc[2] = {0, 0}; + 800314a: 2200 movs r2, #0 + crc16_chain(slot_len, copy, crc); + 800314c: 4619 mov r1, r3 + uint8_t crc[2] = {0, 0}; + 800314e: 80ba strh r2, [r7, #4] + crc16_chain(slot_len, copy, crc); + 8003150: 4640 mov r0, r8 + 8003152: 1d3a adds r2, r7, #4 + 8003154: f7ff fc30 bl 80029b8 + + // do the lock + ae_send(OP_Lock, 2 | (slot_num << 2), (crc[1]<<8) | crc[0]); + 8003158: 00a1 lsls r1, r4, #2 + 800315a: f041 0102 orr.w r1, r1, #2 + 800315e: 88ba ldrh r2, [r7, #4] + 8003160: f001 01fe and.w r1, r1, #254 ; 0xfe + 8003164: 2017 movs r0, #23 + 8003166: f7ff fdb0 bl 8002cca + + int rv = ae_read1(); + 800316a: f7ff fd25 bl 8002bb8 + RET_IF_BAD(rv); + 800316e: 46b5 mov sp, r6 + } + + return 0; +} + 8003170: 370c adds r7, #12 + 8003172: 46bd mov sp, r7 + 8003174: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + 8003178: 0801046c .word 0x0801046c + +0800317c : + +// ae_gendig_slot() +// + int +ae_gendig_slot(int slot_num, const uint8_t slot_contents[32], uint8_t digest[32]) +{ + 800317c: b5f0 push {r4, r5, r6, r7, lr} + 800317e: b0ab sub sp, #172 ; 0xac + 8003180: 4605 mov r5, r0 + 8003182: 460f mov r7, r1 + // Construct a digest on the device (and here) that depends on the secret + // contents of a specific slot. + uint8_t num_in[20], tempkey[32]; + + rng_buffer(num_in, sizeof(num_in)); + 8003184: a803 add r0, sp, #12 + 8003186: 2114 movs r1, #20 +{ + 8003188: 4616 mov r6, r2 + rng_buffer(num_in, sizeof(num_in)); + 800318a: f7ff fba7 bl 80028dc + int rv = ae_pick_nonce(num_in, tempkey); + 800318e: a90f add r1, sp, #60 ; 0x3c + 8003190: a803 add r0, sp, #12 + 8003192: f7ff fdcf bl 8002d34 + RET_IF_BAD(rv); + 8003196: 4604 mov r4, r0 + 8003198: 2800 cmp r0, #0 + 800319a: d13d bne.n 8003218 + + //using Zone=2="Data" => "KeyID specifies a slot in the Data zone" + ae_send(OP_GenDig, 0x2, slot_num); + 800319c: b2aa uxth r2, r5 + 800319e: 2102 movs r1, #2 + 80031a0: 2015 movs r0, #21 + 80031a2: f7ff fd92 bl 8002cca + + rv = ae_read1(); + 80031a6: f7ff fd07 bl 8002bb8 + RET_IF_BAD(rv); + 80031aa: 4604 mov r4, r0 + 80031ac: bba0 cbnz r0, 8003218 + ae_send_idle(); + 80031ae: f7ff fc50 bl 8002a52 + // msg = hkey + b'\x15\x02' + ustruct.pack(" + + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 80031b8: 2302 movs r3, #2 + 80031ba: f88d 3005 strb.w r3, [sp, #5] + 80031be: 23ee movs r3, #238 ; 0xee + 80031c0: f88d 3008 strb.w r3, [sp, #8] + 80031c4: 2301 movs r3, #1 + 80031c6: 2215 movs r2, #21 + 80031c8: f88d 3009 strb.w r3, [sp, #9] + uint8_t zeros[25] = { 0 }; + 80031cc: 4621 mov r1, r4 + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 80031ce: 2323 movs r3, #35 ; 0x23 + uint8_t zeros[25] = { 0 }; + 80031d0: a809 add r0, sp, #36 ; 0x24 + uint8_t args[7] = { OP_GenDig, 2, slot_num, 0, 0xEE, 0x01, 0x23 }; + 80031d2: f88d 300a strb.w r3, [sp, #10] + 80031d6: f88d 2004 strb.w r2, [sp, #4] + 80031da: f88d 5006 strb.w r5, [sp, #6] + 80031de: f88d 4007 strb.w r4, [sp, #7] + uint8_t zeros[25] = { 0 }; + 80031e2: 9408 str r4, [sp, #32] + 80031e4: f00a fb9e bl 800d924 + + sha256_update(&ctx, slot_contents, 32); + 80031e8: 2220 movs r2, #32 + 80031ea: 4639 mov r1, r7 + 80031ec: a817 add r0, sp, #92 ; 0x5c + 80031ee: f002 fad3 bl 8005798 + sha256_update(&ctx, args, sizeof(args)); + 80031f2: 2207 movs r2, #7 + 80031f4: a901 add r1, sp, #4 + 80031f6: a817 add r0, sp, #92 ; 0x5c + 80031f8: f002 face bl 8005798 + sha256_update(&ctx, zeros, sizeof(zeros)); + 80031fc: 2219 movs r2, #25 + 80031fe: a908 add r1, sp, #32 + 8003200: a817 add r0, sp, #92 ; 0x5c + 8003202: f002 fac9 bl 8005798 + sha256_update(&ctx, tempkey, 32); + 8003206: a90f add r1, sp, #60 ; 0x3c + 8003208: a817 add r0, sp, #92 ; 0x5c + 800320a: 2220 movs r2, #32 + 800320c: f002 fac4 bl 8005798 + + sha256_final(&ctx, digest); + 8003210: 4631 mov r1, r6 + 8003212: a817 add r0, sp, #92 ; 0x5c + 8003214: f002 fb06 bl 8005824 + + return 0; +} + 8003218: 4620 mov r0, r4 + 800321a: b02b add sp, #172 ; 0xac + 800321c: bdf0 pop {r4, r5, r6, r7, pc} + ... + +08003220 : +{ + 8003220: b507 push {r0, r1, r2, lr} + 8003222: 4602 mov r2, r0 + int rv = ae_gendig_slot(KEYNUM_pairing, rom_secrets->pairing_secret, randout); + 8003224: 9001 str r0, [sp, #4] + 8003226: 490b ldr r1, [pc, #44] ; (8003254 ) + 8003228: 2001 movs r0, #1 + 800322a: f7ff ffa7 bl 800317c + if(rv || !ae_is_correct_tempkey(randout)) { + 800322e: 9a01 ldr r2, [sp, #4] + 8003230: b108 cbz r0, 8003236 + fatal_mitm(); + 8003232: f7fd fc0b bl 8000a4c + if(rv || !ae_is_correct_tempkey(randout)) { + 8003236: 4610 mov r0, r2 + 8003238: 9201 str r2, [sp, #4] + 800323a: f7ff fdaf bl 8002d9c + 800323e: 2800 cmp r0, #0 + 8003240: d0f7 beq.n 8003232 + sha256_single(randout, 32, randout); + 8003242: 9a01 ldr r2, [sp, #4] + 8003244: 2120 movs r1, #32 + 8003246: 4610 mov r0, r2 +} + 8003248: b003 add sp, #12 + 800324a: f85d eb04 ldr.w lr, [sp], #4 + sha256_single(randout, 32, randout); + 800324e: f002 bafd b.w 800584c + 8003252: bf00 nop + 8003254: 0801c000 .word 0x0801c000 + +08003258 : +{ + 8003258: b510 push {r4, lr} + 800325a: b088 sub sp, #32 + int rv = ae_gendig_slot(keynum, secret, digest); + 800325c: 466a mov r2, sp + 800325e: f7ff ff8d bl 800317c + RET_IF_BAD(rv); + 8003262: 4604 mov r4, r0 + 8003264: b930 cbnz r0, 8003274 + if(!ae_is_correct_tempkey(digest)) return -2; + 8003266: 4668 mov r0, sp + 8003268: f7ff fd98 bl 8002d9c + 800326c: 2800 cmp r0, #0 + 800326e: bf08 it eq + 8003270: f06f 0401 mvneq.w r4, #1 +} + 8003274: 4620 mov r0, r4 + 8003276: b008 add sp, #32 + 8003278: bd10 pop {r4, pc} + +0800327a : +// the digest should be, and ask the chip to do the same. Verify we match +// using MAC command (done elsewhere). +// + int +ae_gendig_counter(int counter_num, const uint32_t expected_value, uint8_t digest[32]) +{ + 800327a: b5f0 push {r4, r5, r6, r7, lr} + 800327c: b0ad sub sp, #180 ; 0xb4 + 800327e: 4605 mov r5, r0 + 8003280: 9101 str r1, [sp, #4] + uint8_t num_in[20], tempkey[32]; + + rng_buffer(num_in, sizeof(num_in)); + 8003282: a804 add r0, sp, #16 + 8003284: 2114 movs r1, #20 +{ + 8003286: 4616 mov r6, r2 + rng_buffer(num_in, sizeof(num_in)); + 8003288: f7ff fb28 bl 80028dc + int rv = ae_pick_nonce(num_in, tempkey); + 800328c: a909 add r1, sp, #36 ; 0x24 + 800328e: a804 add r0, sp, #16 + 8003290: f7ff fd50 bl 8002d34 + RET_IF_BAD(rv); + 8003294: 4604 mov r4, r0 + 8003296: 2800 cmp r0, #0 + 8003298: d148 bne.n 800332c + + //using Zone=4="Counter" => "KeyID specifies the monotonic counter ID" + ae_send(OP_GenDig, 0x4, counter_num); + 800329a: b2aa uxth r2, r5 + 800329c: 2104 movs r1, #4 + 800329e: 2015 movs r0, #21 + 80032a0: f7ff fd13 bl 8002cca + + rv = ae_read1(); + 80032a4: f7ff fc88 bl 8002bb8 + RET_IF_BAD(rv); + 80032a8: 4604 mov r4, r0 + 80032aa: 2800 cmp r0, #0 + 80032ac: d13e bne.n 800332c + ae_send_idle(); + 80032ae: f7ff fbd0 bl 8002a52 + // msg = hkey + b'\x15\x02' + ustruct.pack(" + + uint8_t zeros[32] = { 0 }; + 80032b8: 221c movs r2, #28 + 80032ba: 4621 mov r1, r4 + 80032bc: a812 add r0, sp, #72 ; 0x48 + 80032be: 9411 str r4, [sp, #68] ; 0x44 + 80032c0: f00a fb30 bl 800d924 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 80032c4: 2315 movs r3, #21 + 80032c6: f88d 3008 strb.w r3, [sp, #8] + 80032ca: 23ee movs r3, #238 ; 0xee + 80032cc: f88d 300c strb.w r3, [sp, #12] + 80032d0: 2301 movs r3, #1 + 80032d2: 2704 movs r7, #4 + 80032d4: f88d 300d strb.w r3, [sp, #13] + + sha256_update(&ctx, zeros, 32); + 80032d8: 2220 movs r2, #32 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 80032da: 2323 movs r3, #35 ; 0x23 + sha256_update(&ctx, zeros, 32); + 80032dc: a911 add r1, sp, #68 ; 0x44 + 80032de: a819 add r0, sp, #100 ; 0x64 + uint8_t args[8] = { OP_GenDig, 0x4, counter_num, 0, 0xEE, 0x01, 0x23, 0x0 }; + 80032e0: f88d 300e strb.w r3, [sp, #14] + 80032e4: f88d 7009 strb.w r7, [sp, #9] + 80032e8: f88d 500a strb.w r5, [sp, #10] + 80032ec: f88d 400b strb.w r4, [sp, #11] + 80032f0: f88d 400f strb.w r4, [sp, #15] + sha256_update(&ctx, zeros, 32); + 80032f4: f002 fa50 bl 8005798 + sha256_update(&ctx, args, sizeof(args)); + 80032f8: 2208 movs r2, #8 + 80032fa: eb0d 0102 add.w r1, sp, r2 + 80032fe: a819 add r0, sp, #100 ; 0x64 + 8003300: f002 fa4a bl 8005798 + sha256_update(&ctx, (const uint8_t *)&expected_value, 4); + 8003304: 463a mov r2, r7 + 8003306: eb0d 0107 add.w r1, sp, r7 + 800330a: a819 add r0, sp, #100 ; 0x64 + 800330c: f002 fa44 bl 8005798 + sha256_update(&ctx, zeros, 20); + 8003310: 2214 movs r2, #20 + 8003312: a911 add r1, sp, #68 ; 0x44 + 8003314: a819 add r0, sp, #100 ; 0x64 + 8003316: f002 fa3f bl 8005798 + sha256_update(&ctx, tempkey, 32); + 800331a: a909 add r1, sp, #36 ; 0x24 + 800331c: a819 add r0, sp, #100 ; 0x64 + 800331e: 2220 movs r2, #32 + 8003320: f002 fa3a bl 8005798 + + sha256_final(&ctx, digest); + 8003324: 4631 mov r1, r6 + 8003326: a819 add r0, sp, #100 ; 0x64 + 8003328: f002 fa7c bl 8005824 + + return 0; +} + 800332c: 4620 mov r0, r4 + 800332e: b02d add sp, #180 ; 0xb4 + 8003330: bdf0 pop {r4, r5, r6, r7, pc} + +08003332 : +{ + 8003332: b570 push {r4, r5, r6, lr} + ae_send(OP_Counter, 0x0, counter_number); + 8003334: 460a mov r2, r1 +{ + 8003336: b088 sub sp, #32 + 8003338: 4606 mov r6, r0 + 800333a: 460d mov r5, r1 + ae_send(OP_Counter, 0x0, counter_number); + 800333c: 2024 movs r0, #36 ; 0x24 + 800333e: 2100 movs r1, #0 + 8003340: f7ff fcc3 bl 8002cca + int rv = ae_read_n(4, (uint8_t *)result); + 8003344: 4631 mov r1, r6 + 8003346: 2004 movs r0, #4 + 8003348: f7ff fc52 bl 8002bf0 + RET_IF_BAD(rv); + 800334c: 4604 mov r4, r0 + 800334e: b960 cbnz r0, 800336a + rv = ae_gendig_counter(counter_number, *result, digest); + 8003350: 6831 ldr r1, [r6, #0] + 8003352: 466a mov r2, sp + 8003354: 4628 mov r0, r5 + 8003356: f7ff ff90 bl 800327a + RET_IF_BAD(rv); + 800335a: 4604 mov r4, r0 + 800335c: b928 cbnz r0, 800336a + if(!ae_is_correct_tempkey(digest)) { + 800335e: 4668 mov r0, sp + 8003360: f7ff fd1c bl 8002d9c + 8003364: b908 cbnz r0, 800336a + fatal_mitm(); + 8003366: f7fd fb71 bl 8000a4c +} + 800336a: 4620 mov r0, r4 + 800336c: b008 add sp, #32 + 800336e: bd70 pop {r4, r5, r6, pc} + +08003370 : +{ + 8003370: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8003374: 4606 mov r6, r0 + 8003376: b089 sub sp, #36 ; 0x24 + 8003378: 460d mov r5, r1 + 800337a: 4617 mov r7, r2 + for(int i=0; i + int rv = ae_gendig_counter(counter_number, *result, digest); + 8003388: 6831 ldr r1, [r6, #0] + 800338a: 466a mov r2, sp + 800338c: 4628 mov r0, r5 + 800338e: f7ff ff74 bl 800327a + RET_IF_BAD(rv); + 8003392: 4604 mov r4, r0 + 8003394: b998 cbnz r0, 80033be + if(!ae_is_correct_tempkey(digest)) { + 8003396: 4668 mov r0, sp + 8003398: f7ff fd00 bl 8002d9c + 800339c: b978 cbnz r0, 80033be + fatal_mitm(); + 800339e: f7fd fb55 bl 8000a4c + ae_send(OP_Counter, 0x1, counter_number); + 80033a2: 464a mov r2, r9 + 80033a4: 2101 movs r1, #1 + 80033a6: 2024 movs r0, #36 ; 0x24 + 80033a8: f7ff fc8f bl 8002cca + int rv = ae_read_n(4, (uint8_t *)result); + 80033ac: 4631 mov r1, r6 + 80033ae: 2004 movs r0, #4 + 80033b0: f7ff fc1e bl 8002bf0 + RET_IF_BAD(rv); + 80033b4: 4604 mov r4, r0 + 80033b6: b910 cbnz r0, 80033be + for(int i=0; i +} + 80033be: 4620 mov r0, r4 + 80033c0: b009 add sp, #36 ; 0x24 + 80033c2: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + +080033c6 : +// ae_encrypted_read32() +// + int +ae_encrypted_read32(int data_slot, int blk, + int read_kn, const uint8_t read_key[32], uint8_t data[32]) +{ + 80033c6: b5f0 push {r4, r5, r6, r7, lr} + 80033c8: b08b sub sp, #44 ; 0x2c + 80033ca: 4617 mov r7, r2 + 80033cc: 460e mov r6, r1 + 80033ce: 9d10 ldr r5, [sp, #64] ; 0x40 + 80033d0: 9301 str r3, [sp, #4] + 80033d2: 4604 mov r4, r0 + uint8_t digest[32]; + + ae_pair_unlock(); + 80033d4: f7ff fdb2 bl 8002f3c + + int rv = ae_gendig_slot(read_kn, read_key, digest); + 80033d8: 9901 ldr r1, [sp, #4] + 80033da: aa02 add r2, sp, #8 + 80033dc: 4638 mov r0, r7 + 80033de: f7ff fecd bl 800317c + RET_IF_BAD(rv); + 80033e2: b9c0 cbnz r0, 8003416 + + // read nth 32-byte "block" + ae_send(OP_Read, 0x82, (blk << 8) | (data_slot<<3)); + 80033e4: 00e4 lsls r4, r4, #3 + 80033e6: ea44 2206 orr.w r2, r4, r6, lsl #8 + 80033ea: 2182 movs r1, #130 ; 0x82 + 80033ec: 2002 movs r0, #2 + 80033ee: b292 uxth r2, r2 + 80033f0: f7ff fc6b bl 8002cca + + rv = ae_read_n(32, data); + 80033f4: 4629 mov r1, r5 + 80033f6: 2020 movs r0, #32 + 80033f8: f7ff fbfa bl 8002bf0 + RET_IF_BAD(rv); + 80033fc: b958 cbnz r0, 8003416 + 80033fe: 1e6a subs r2, r5, #1 + 8003400: ab02 add r3, sp, #8 + 8003402: 351f adds r5, #31 + *(acc) ^= *(more); + 8003404: f812 1f01 ldrb.w r1, [r2, #1]! + 8003408: f813 4b01 ldrb.w r4, [r3], #1 + for(; len; len--, more++, acc++) { + 800340c: 4295 cmp r5, r2 + *(acc) ^= *(more); + 800340e: ea81 0104 eor.w r1, r1, r4 + 8003412: 7011 strb r1, [r2, #0] + for(; len; len--, more++, acc++) { + 8003414: d1f6 bne.n 8003404 + + xor_mixin(data, digest, 32); + + return 0; +} + 8003416: b00b add sp, #44 ; 0x2c + 8003418: bdf0 pop {r4, r5, r6, r7, pc} + ... + +0800341c : + +// ae_encrypted_read() +// + int +ae_encrypted_read(int data_slot, int read_kn, const uint8_t read_key[32], uint8_t *data, int len) +{ + 800341c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8003420: b08b sub sp, #44 ; 0x2c + 8003422: 4607 mov r7, r0 + 8003424: 9d12 ldr r5, [sp, #72] ; 0x48 + // not clear if chip supports 4-byte encrypted reads + ASSERT((len == 32) || (len == 72)); + 8003426: 2d20 cmp r5, #32 +{ + 8003428: 4688 mov r8, r1 + 800342a: 4691 mov r9, r2 + 800342c: 461e mov r6, r3 + ASSERT((len == 32) || (len == 72)); + 800342e: d004 beq.n 800343a + 8003430: 2d48 cmp r5, #72 ; 0x48 + 8003432: d002 beq.n 800343a + 8003434: 4815 ldr r0, [pc, #84] ; (800348c ) + 8003436: f7fd faff bl 8000a38 + + int rv = ae_encrypted_read32(data_slot, 0, read_kn, read_key, data); + 800343a: 9600 str r6, [sp, #0] + 800343c: 464b mov r3, r9 + 800343e: 4642 mov r2, r8 + 8003440: 2100 movs r1, #0 + 8003442: 4638 mov r0, r7 + 8003444: f7ff ffbf bl 80033c6 + RET_IF_BAD(rv); + 8003448: 4604 mov r4, r0 + 800344a: b9d0 cbnz r0, 8003482 + + if(len == 32) return 0; + 800344c: 2d20 cmp r5, #32 + 800344e: d018 beq.n 8003482 + + rv = ae_encrypted_read32(data_slot, 1, read_kn, read_key, data+32); + 8003450: f106 0320 add.w r3, r6, #32 + 8003454: 9300 str r3, [sp, #0] + 8003456: 4642 mov r2, r8 + 8003458: 464b mov r3, r9 + 800345a: 2101 movs r1, #1 + 800345c: 4638 mov r0, r7 + 800345e: f7ff ffb2 bl 80033c6 + RET_IF_BAD(rv); + 8003462: 4604 mov r4, r0 + 8003464: b968 cbnz r0, 8003482 + + uint8_t tmp[32]; + rv = ae_encrypted_read32(data_slot, 2, read_kn, read_key, tmp); + 8003466: ad02 add r5, sp, #8 + 8003468: 9500 str r5, [sp, #0] + 800346a: 464b mov r3, r9 + 800346c: 4642 mov r2, r8 + 800346e: 2102 movs r1, #2 + 8003470: 4638 mov r0, r7 + 8003472: f7ff ffa8 bl 80033c6 + RET_IF_BAD(rv); + 8003476: 4604 mov r4, r0 + 8003478: b918 cbnz r0, 8003482 + + memcpy(data+64, tmp, 72-64); + 800347a: 462a mov r2, r5 + 800347c: ca03 ldmia r2!, {r0, r1} + 800347e: 6430 str r0, [r6, #64] ; 0x40 + 8003480: 6471 str r1, [r6, #68] ; 0x44 + + return 0; +} + 8003482: 4620 mov r0, r4 + 8003484: b00b add sp, #44 ; 0x2c + 8003486: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + 800348a: bf00 nop + 800348c: 0801046c .word 0x0801046c + +08003490 : +// ae_encrypted_write() +// + int +ae_encrypted_write32(int data_slot, int blk, int write_kn, + const uint8_t write_key[32], const uint8_t data[32]) +{ + 8003490: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8003494: b0b8 sub sp, #224 ; 0xe0 + 8003496: 4617 mov r7, r2 + 8003498: 460d mov r5, r1 + 800349a: 9e3e ldr r6, [sp, #248] ; 0xf8 + 800349c: 9303 str r3, [sp, #12] + 800349e: 4604 mov r4, r0 + uint8_t digest[32]; + + ae_pair_unlock(); + 80034a0: f7ff fd4c bl 8002f3c + + // generate a hash over shared secret and rng + int rv = ae_gendig_slot(write_kn, write_key, digest); + 80034a4: 9903 ldr r1, [sp, #12] + 80034a6: aa0d add r2, sp, #52 ; 0x34 + 80034a8: 4638 mov r0, r7 + 80034aa: f7ff fe67 bl 800317c + RET_IF_BAD(rv); + 80034ae: 2800 cmp r0, #0 + 80034b0: d151 bne.n 8003556 + 80034b2: 1e72 subs r2, r6, #1 + 80034b4: af0d add r7, sp, #52 ; 0x34 + 80034b6: a915 add r1, sp, #84 ; 0x54 + 80034b8: f106 0c1f add.w ip, r6, #31 + + // encrypt the data to be written, and append an authenticating MAC + uint8_t body[32 + 32]; + + for(int i=0; i<32; i++) { + body[i] = data[i] ^ digest[i]; + 80034bc: f812 ef01 ldrb.w lr, [r2, #1]! + 80034c0: f817 0b01 ldrb.w r0, [r7], #1 + for(int i=0; i<32; i++) { + 80034c4: 4562 cmp r2, ip + body[i] = data[i] ^ digest[i]; + 80034c6: ea80 000e eor.w r0, r0, lr + 80034ca: f801 0b01 strb.w r0, [r1], #1 + for(int i=0; i<32; i++) { + 80034ce: d1f5 bne.n 80034bc + // + (b'\0'*25) + // + new_value) + // assert len(msg) == 32+1+1+2+1+2+25+32 + // + SHA256_CTX ctx; + sha256_init(&ctx); + 80034d0: a825 add r0, sp, #148 ; 0x94 + 80034d2: f002 f953 bl 800577c + + uint8_t p1 = 0x80|2; // 32 bytes into a data slot + uint8_t p2_lsb = (data_slot << 3); + uint8_t p2_msb = blk; + + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 80034d6: 22ee movs r2, #238 ; 0xee + 80034d8: f88d 2014 strb.w r2, [sp, #20] + 80034dc: 2201 movs r2, #1 + 80034de: f88d 2015 strb.w r2, [sp, #21] + uint8_t p2_lsb = (data_slot << 3); + 80034e2: 00e4 lsls r4, r4, #3 + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 80034e4: 2223 movs r2, #35 ; 0x23 + uint8_t zeros[25] = { 0 }; + 80034e6: 2100 movs r1, #0 + uint8_t p2_lsb = (data_slot << 3); + 80034e8: b2e4 uxtb r4, r4 + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 80034ea: 2712 movs r7, #18 + 80034ec: f04f 0882 mov.w r8, #130 ; 0x82 + 80034f0: f88d 2016 strb.w r2, [sp, #22] + uint8_t zeros[25] = { 0 }; + 80034f4: a807 add r0, sp, #28 + 80034f6: 2215 movs r2, #21 + 80034f8: 9106 str r1, [sp, #24] + uint8_t args[7] = { OP_Write, p1, p2_lsb, p2_msb, 0xEE, 0x01, 0x23 }; + 80034fa: f88d 7010 strb.w r7, [sp, #16] + 80034fe: f88d 8011 strb.w r8, [sp, #17] + 8003502: f88d 4012 strb.w r4, [sp, #18] + uint8_t p2_msb = blk; + 8003506: f88d 5013 strb.w r5, [sp, #19] + uint8_t zeros[25] = { 0 }; + 800350a: f00a fa0b bl 800d924 + + sha256_update(&ctx, digest, 32); + 800350e: 2220 movs r2, #32 + 8003510: a90d add r1, sp, #52 ; 0x34 + 8003512: a825 add r0, sp, #148 ; 0x94 + 8003514: f002 f940 bl 8005798 + sha256_update(&ctx, args, sizeof(args)); + 8003518: 2207 movs r2, #7 + 800351a: a904 add r1, sp, #16 + 800351c: a825 add r0, sp, #148 ; 0x94 + 800351e: f002 f93b bl 8005798 + sha256_update(&ctx, zeros, sizeof(zeros)); + 8003522: 2219 movs r2, #25 + 8003524: a906 add r1, sp, #24 + 8003526: a825 add r0, sp, #148 ; 0x94 + 8003528: f002 f936 bl 8005798 + sha256_update(&ctx, data, 32); + 800352c: 2220 movs r2, #32 + 800352e: 4631 mov r1, r6 + 8003530: a825 add r0, sp, #148 ; 0x94 + 8003532: f002 f931 bl 8005798 + + sha256_final(&ctx, &body[32]); + 8003536: a91d add r1, sp, #116 ; 0x74 + 8003538: a825 add r0, sp, #148 ; 0x94 + 800353a: f002 f973 bl 8005824 + + ae_send_n(OP_Write, p1, (p2_msb << 8) | p2_lsb, body, sizeof(body)); + 800353e: 2140 movs r1, #64 ; 0x40 + 8003540: ea44 2205 orr.w r2, r4, r5, lsl #8 + 8003544: b292 uxth r2, r2 + 8003546: 9100 str r1, [sp, #0] + 8003548: ab15 add r3, sp, #84 ; 0x54 + 800354a: 4641 mov r1, r8 + 800354c: 4638 mov r0, r7 + 800354e: f7ff fb89 bl 8002c64 + + return ae_read1(); + 8003552: f7ff fb31 bl 8002bb8 +} + 8003556: b038 add sp, #224 ; 0xe0 + 8003558: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +0800355c : +// ae_encrypted_write() +// + int +ae_encrypted_write(int data_slot, int write_kn, const uint8_t write_key[32], + const uint8_t *data, int len) +{ + 800355c: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8003560: b08a sub sp, #40 ; 0x28 + ASSERT(data_slot >= 0); + ASSERT(data_slot <= 15); + 8003562: 280f cmp r0, #15 +{ + 8003564: 9d12 ldr r5, [sp, #72] ; 0x48 + 8003566: 4606 mov r6, r0 + 8003568: 460f mov r7, r1 + 800356a: 4690 mov r8, r2 + 800356c: 4699 mov r9, r3 + ASSERT(data_slot <= 15); + 800356e: d902 bls.n 8003576 + ASSERT(data_slot >= 0); + 8003570: 4814 ldr r0, [pc, #80] ; (80035c4 ) + 8003572: f7fd fa61 bl 8000a38 + + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 8003576: 2400 movs r4, #0 + int here = MIN(32, len); + + // be nice and don't read past end of input buffer + uint8_t tmp[32] = { 0 }; + 8003578: 46a2 mov sl, r4 + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 800357a: 2d00 cmp r5, #0 + 800357c: dd1d ble.n 80035ba + uint8_t tmp[32] = { 0 }; + 800357e: 221c movs r2, #28 + 8003580: 2100 movs r1, #0 + 8003582: a803 add r0, sp, #12 + 8003584: f8cd a008 str.w sl, [sp, #8] + 8003588: f00a f9cc bl 800d924 + memcpy(tmp, data+(32*blk), here); + 800358c: ab02 add r3, sp, #8 + 800358e: 2d20 cmp r5, #32 + 8003590: 462a mov r2, r5 + 8003592: eb09 1144 add.w r1, r9, r4, lsl #5 + 8003596: bfa8 it ge + 8003598: 2220 movge r2, #32 + 800359a: 4618 mov r0, r3 + 800359c: f00a f9b4 bl 800d908 + + int rv = ae_encrypted_write32(data_slot, blk, write_kn, write_key, tmp); + 80035a0: 4643 mov r3, r8 + 80035a2: 9000 str r0, [sp, #0] + 80035a4: 463a mov r2, r7 + 80035a6: 4621 mov r1, r4 + 80035a8: 4630 mov r0, r6 + 80035aa: f7ff ff71 bl 8003490 + RET_IF_BAD(rv); + 80035ae: b928 cbnz r0, 80035bc + for(int blk=0; blk<3 && len>0; blk++, len-=32) { + 80035b0: 3401 adds r4, #1 + 80035b2: 2c03 cmp r4, #3 + 80035b4: f1a5 0520 sub.w r5, r5, #32 + 80035b8: d1df bne.n 800357a + } + + return 0; + 80035ba: 2000 movs r0, #0 +} + 80035bc: b00a add sp, #40 ; 0x28 + 80035be: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 80035c2: bf00 nop + 80035c4: 0801046c .word 0x0801046c + +080035c8 : + +// ae_read_data_slot() +// + int +ae_read_data_slot(int slot_num, uint8_t *data, int len) +{ + 80035c8: b570 push {r4, r5, r6, lr} + ASSERT((len == 4) || (len == 32) || (len == 72)); + 80035ca: 2a04 cmp r2, #4 +{ + 80035cc: b088 sub sp, #32 + 80035ce: 460d mov r5, r1 + 80035d0: 4616 mov r6, r2 + ASSERT((len == 4) || (len == 32) || (len == 72)); + 80035d2: d006 beq.n 80035e2 + 80035d4: 2a20 cmp r2, #32 + 80035d6: d038 beq.n 800364a + 80035d8: 2a48 cmp r2, #72 ; 0x48 + 80035da: d036 beq.n 800364a + 80035dc: 481c ldr r0, [pc, #112] ; (8003650 ) + 80035de: f7fd fa2b bl 8000a38 + + // zone => data + // only reading first block of 32 bytes. ignore the rest + ae_send(OP_Read, (len == 4 ? 0x00 : 0x80) | 2, (slot_num<<3)); + 80035e2: 2102 movs r1, #2 + 80035e4: 00c4 lsls r4, r0, #3 + 80035e6: b2a2 uxth r2, r4 + 80035e8: 2002 movs r0, #2 + 80035ea: f7ff fb6e bl 8002cca + + int rv = ae_read_n((len == 4) ? 4 : 32, data); + 80035ee: 2e04 cmp r6, #4 + 80035f0: 4629 mov r1, r5 + 80035f2: bf0c ite eq + 80035f4: 2004 moveq r0, #4 + 80035f6: 2020 movne r0, #32 + 80035f8: f7ff fafa bl 8002bf0 + RET_IF_BAD(rv); + 80035fc: 4603 mov r3, r0 + 80035fe: bb08 cbnz r0, 8003644 + + if(len == 72) { + 8003600: 2e48 cmp r6, #72 ; 0x48 + 8003602: d11f bne.n 8003644 + // read second block + ae_send(OP_Read, 0x82, (1<<8) | (slot_num<<3)); + 8003604: b224 sxth r4, r4 + 8003606: f444 7280 orr.w r2, r4, #256 ; 0x100 + 800360a: b292 uxth r2, r2 + 800360c: 2182 movs r1, #130 ; 0x82 + 800360e: 2002 movs r0, #2 + 8003610: f7ff fb5b bl 8002cca + + int rv = ae_read_n(32, data+32); + 8003614: f105 0120 add.w r1, r5, #32 + 8003618: 2020 movs r0, #32 + 800361a: f7ff fae9 bl 8002bf0 + RET_IF_BAD(rv); + 800361e: 4603 mov r3, r0 + 8003620: b980 cbnz r0, 8003644 + + // read third block, but only using part of it + uint8_t tmp[32]; + ae_send(OP_Read, 0x82, (2<<8) | (slot_num<<3)); + 8003622: f444 7400 orr.w r4, r4, #512 ; 0x200 + 8003626: b2a2 uxth r2, r4 + 8003628: 2182 movs r1, #130 ; 0x82 + 800362a: 2002 movs r0, #2 + 800362c: f7ff fb4d bl 8002cca + + rv = ae_read_n(32, tmp); + 8003630: 4669 mov r1, sp + 8003632: 2020 movs r0, #32 + 8003634: f7ff fadc bl 8002bf0 + RET_IF_BAD(rv); + 8003638: 4603 mov r3, r0 + 800363a: b918 cbnz r0, 8003644 + + memcpy(data+64, tmp, 72-64); + 800363c: 466a mov r2, sp + 800363e: ca03 ldmia r2!, {r0, r1} + 8003640: 6428 str r0, [r5, #64] ; 0x40 + 8003642: 6469 str r1, [r5, #68] ; 0x44 + } + + return 0; +} + 8003644: 4618 mov r0, r3 + 8003646: b008 add sp, #32 + 8003648: bd70 pop {r4, r5, r6, pc} + ae_send(OP_Read, (len == 4 ? 0x00 : 0x80) | 2, (slot_num<<3)); + 800364a: 2182 movs r1, #130 ; 0x82 + 800364c: e7ca b.n 80035e4 + 800364e: bf00 nop + 8003650: 0801046c .word 0x0801046c + +08003654 : + +// ae_set_gpio() +// + int +ae_set_gpio(int state) +{ + 8003654: b513 push {r0, r1, r4, lr} + // 1=turn on green, 0=red light (if not yet configured to be secure) + ae_send(OP_Info, 3, 2 | (!!state)); + 8003656: 1e04 subs r4, r0, #0 + 8003658: bf14 ite ne + 800365a: 2203 movne r2, #3 + 800365c: 2202 moveq r2, #2 + 800365e: 2103 movs r1, #3 + 8003660: 2030 movs r0, #48 ; 0x30 + 8003662: f7ff fb32 bl 8002cca + + // "Always return the current state in the first byte followed by three bytes of 0x00" + // - simple 1/0, in LSB. + uint8_t resp[4]; + + int rv = ae_read_n(4, resp); + 8003666: a901 add r1, sp, #4 + 8003668: 2004 movs r0, #4 + 800366a: f7ff fac1 bl 8002bf0 + RET_IF_BAD(rv); + 800366e: b928 cbnz r0, 800367c + + return (resp[0] != state) ? -1 : 0; + 8003670: f89d 0004 ldrb.w r0, [sp, #4] + 8003674: 1b00 subs r0, r0, r4 + 8003676: bf18 it ne + 8003678: f04f 30ff movne.w r0, #4294967295 ; 0xffffffff +} + 800367c: b002 add sp, #8 + 800367e: bd10 pop {r4, pc} + +08003680 : +// +// Set the GPIO using secure hash generated somehow already. +// + int +ae_set_gpio_secure(uint8_t digest[32]) +{ + 8003680: b538 push {r3, r4, r5, lr} + 8003682: 4605 mov r5, r0 + ae_pair_unlock(); + 8003684: f7ff fc5a bl 8002f3c + ae_checkmac(KEYNUM_firmware, digest); + 8003688: 4629 mov r1, r5 + 800368a: 200e movs r0, #14 + 800368c: f7ff fbd4 bl 8002e38 + + int rv = ae_set_gpio(1); + 8003690: 2001 movs r0, #1 + 8003692: f7ff ffdf bl 8003654 + + if(rv == 0) { + 8003696: 4604 mov r4, r0 + 8003698: b940 cbnz r0, 80036ac + // trust that readback, and so do a verify that the chip has + // the digest we think it does. If MitM wanted to turn off the output, + // they can do that anytime regardless. We just don't want them to be + // able to fake it being set, and therefore bypass the + // "unsigned firmware" delay and warning. + ae_pair_unlock(); + 800369a: f7ff fc4f bl 8002f3c + + if(ae_checkmac_hard(KEYNUM_firmware, digest) != 0) { + 800369e: 4629 mov r1, r5 + 80036a0: 200e movs r0, #14 + 80036a2: f7ff fdd9 bl 8003258 + 80036a6: b108 cbz r0, 80036ac + fatal_mitm(); + 80036a8: f7fd f9d0 bl 8000a4c + } + } + + return rv; +} + 80036ac: 4620 mov r0, r4 + 80036ae: bd38 pop {r3, r4, r5, pc} + +080036b0 : +// +// IMPORTANT: do not trust this result, could be MitM'ed. +// + uint8_t +ae_get_gpio(void) +{ + 80036b0: b507 push {r0, r1, r2, lr} + // not doing error checking here + ae_send(OP_Info, 0x3, 0); + 80036b2: 2200 movs r2, #0 + 80036b4: 2103 movs r1, #3 + 80036b6: 2030 movs r0, #48 ; 0x30 + 80036b8: f7ff fb07 bl 8002cca + + // note: always returns 4 bytes, but most are garbage and unused. + uint8_t tmp[4]; + ae_read_n(4, tmp); + 80036bc: a901 add r1, sp, #4 + 80036be: 2004 movs r0, #4 + 80036c0: f7ff fa96 bl 8002bf0 + + return tmp[0]; +} + 80036c4: f89d 0004 ldrb.w r0, [sp, #4] + 80036c8: b003 add sp, #12 + 80036ca: f85d fb04 ldr.w pc, [sp], #4 + +080036ce : +// +// Read a 4-byte area from config area, or -1 if fail. +// + int +ae_read_config_word(int offset, uint8_t *dest) +{ + 80036ce: b510 push {r4, lr} + offset &= 0x7f; + + // read 32 bits (aligned) + ae_send(OP_Read, 0x00, offset/4); + 80036d0: f3c0 0284 ubfx r2, r0, #2, #5 +{ + 80036d4: 460c mov r4, r1 + ae_send(OP_Read, 0x00, offset/4); + 80036d6: 2002 movs r0, #2 + 80036d8: 2100 movs r1, #0 + 80036da: f7ff faf6 bl 8002cca + + int rv = ae_read_n(4, dest); + 80036de: 4621 mov r1, r4 + 80036e0: 2004 movs r0, #4 + 80036e2: f7ff fa85 bl 8002bf0 + if(rv) return -1; + 80036e6: 3800 subs r0, #0 + 80036e8: bf18 it ne + 80036ea: 2001 movne r0, #1 + + return 0; +} + 80036ec: 4240 negs r0, r0 + 80036ee: bd10 pop {r4, pc} + +080036f0 : +{ + 80036f0: b513 push {r0, r1, r4, lr} + 80036f2: 4604 mov r4, r0 + ae_read_config_word(offset, tmp); + 80036f4: a901 add r1, sp, #4 + 80036f6: f7ff ffea bl 80036ce + return tmp[offset % 4]; + 80036fa: 4263 negs r3, r4 + 80036fc: f003 0303 and.w r3, r3, #3 + 8003700: f004 0403 and.w r4, r4, #3 + 8003704: bf58 it pl + 8003706: 425c negpl r4, r3 + 8003708: f104 0308 add.w r3, r4, #8 + 800370c: eb0d 0403 add.w r4, sp, r3 +} + 8003710: f814 0c04 ldrb.w r0, [r4, #-4] + 8003714: b002 add sp, #8 + 8003716: bd10 pop {r4, pc} + +08003718 : + +// ae_destroy_key() +// + int +ae_destroy_key(int keynum) +{ + 8003718: b510 push {r4, lr} + 800371a: b090 sub sp, #64 ; 0x40 + uint8_t numin[20]; + + // Load tempkey with a known (random) nonce value + rng_buffer(numin, sizeof(numin)); + 800371c: 2114 movs r1, #20 +{ + 800371e: 4604 mov r4, r0 + rng_buffer(numin, sizeof(numin)); + 8003720: a803 add r0, sp, #12 + 8003722: f7ff f8db bl 80028dc + ae_send_n(OP_Nonce, 0, 0, numin, 20); + 8003726: 2314 movs r3, #20 + 8003728: 2200 movs r2, #0 + 800372a: 9300 str r3, [sp, #0] + 800372c: 4611 mov r1, r2 + 800372e: 2016 movs r0, #22 + 8003730: ab03 add r3, sp, #12 + 8003732: f7ff fa97 bl 8002c64 + + // Nonce command returns the RNG result, not contents of TempKey, + // but since we are destroying, no need to calculate what it is. + uint8_t randout[32]; + int rv = ae_read_n(32, randout); + 8003736: a908 add r1, sp, #32 + 8003738: 2020 movs r0, #32 + 800373a: f7ff fa59 bl 8002bf0 + RET_IF_BAD(rv); + 800373e: b930 cbnz r0, 800374e + + // do a "DeriveKey" operation, based on that! + ae_send(OP_DeriveKey, 0x00, keynum); + 8003740: 4601 mov r1, r0 + 8003742: b2a2 uxth r2, r4 + 8003744: 201c movs r0, #28 + 8003746: f7ff fac0 bl 8002cca + + return ae_read1(); + 800374a: f7ff fa35 bl 8002bb8 +} + 800374e: b010 add sp, #64 ; 0x40 + 8003750: bd10 pop {r4, pc} + +08003752 : + +// ae_config_read() +// + int +ae_config_read(uint8_t config[128]) +{ + 8003752: b538 push {r3, r4, r5, lr} + 8003754: 4605 mov r5, r0 + for(int blk=0; blk<4; blk++) { + 8003756: 2400 movs r4, #0 + // read 32 bytes (aligned) from config "zone" + ae_send(OP_Read, 0x80, blk<<3); + 8003758: 00e2 lsls r2, r4, #3 + 800375a: 2180 movs r1, #128 ; 0x80 + 800375c: 2002 movs r0, #2 + 800375e: b292 uxth r2, r2 + 8003760: f7ff fab3 bl 8002cca + + int rv = ae_read_n(32, &config[32*blk]); + 8003764: eb05 1144 add.w r1, r5, r4, lsl #5 + 8003768: 2020 movs r0, #32 + 800376a: f7ff fa41 bl 8002bf0 + if(rv) return EIO; + 800376e: b918 cbnz r0, 8003778 + for(int blk=0; blk<4; blk++) { + 8003770: 3401 adds r4, #1 + 8003772: 2c04 cmp r4, #4 + 8003774: d1f0 bne.n 8003758 + } + + return 0; +} + 8003776: bd38 pop {r3, r4, r5, pc} + if(rv) return EIO; + 8003778: 2005 movs r0, #5 + 800377a: e7fc b.n 8003776 + +0800377c : +// us to write the (existing) pairing secret into, they would see the pairing +// secret in cleartext. They could then restore original chip and access freely. +// + int +ae_setup_config(void) +{ + 800377c: b5f0 push {r4, r5, r6, r7, lr} + 800377e: 2405 movs r4, #5 + 8003780: f5ad 7d41 sub.w sp, sp, #772 ; 0x304 + // Need to wake up AE, since many things happen before this point. + for(int retry=0; retry<5; retry++) { + if(!ae_probe()) break; + 8003784: f7ff fc6c bl 8003060 + 8003788: b108 cbz r0, 800378e + for(int retry=0; retry<5; retry++) { + 800378a: 3c01 subs r4, #1 + 800378c: d1fa bne.n 8003784 + // Is data zone is locked? + // Allow rest of function to happen if it's not. + +#if 1 + // 0x55 = unlocked; 0x00 = locked + bool data_locked = (ae_read_config_byte(86) != 0x55); + 800378e: 2056 movs r0, #86 ; 0x56 + 8003790: f7ff ffae bl 80036f0 + if(data_locked) return 0; // basically success + 8003794: 2855 cmp r0, #85 ; 0x55 + 8003796: f040 80df bne.w 8003958 + + // To lock, we need a CRC over whole thing, but we + // only set a few values... plus the serial number is + // in there, so start with some readout. + uint8_t config[128]; + int rv = ae_config_read(config); + 800379a: a838 add r0, sp, #224 ; 0xe0 + 800379c: f7ff ffd9 bl 8003752 + if(rv) return rv; + 80037a0: 4604 mov r4, r0 + 80037a2: 2800 cmp r0, #0 + 80037a4: f040 80d9 bne.w 800395a + uint8_t config[128]; + while(ae_config_read(config)) ; +#endif + + // verify some fixed values + ASSERT(config[0] == 0x01); + 80037a8: f89d 30e0 ldrb.w r3, [sp, #224] ; 0xe0 + 80037ac: 2b01 cmp r3, #1 + 80037ae: d002 beq.n 80037b6 + 80037b0: 486f ldr r0, [pc, #444] ; (8003970 ) + + ae_keep_alive(); + + // lock config zone + if(ae_lock_config_zone(config)) { + INCONSISTENT("conf lock"); + 80037b2: f7fd f941 bl 8000a38 + ASSERT(config[1] == 0x23); + 80037b6: f89d 30e1 ldrb.w r3, [sp, #225] ; 0xe1 + 80037ba: 2b23 cmp r3, #35 ; 0x23 + 80037bc: d1f8 bne.n 80037b0 + ASSERT(config[12] == 0xee); + 80037be: f89d 30ec ldrb.w r3, [sp, #236] ; 0xec + 80037c2: 2bee cmp r3, #238 ; 0xee + 80037c4: d1f4 bne.n 80037b0 + int8_t partno = ((config[6]>>4)&0xf); + 80037c6: f89d 30e6 ldrb.w r3, [sp, #230] ; 0xe6 + ASSERT(partno == 6); + 80037ca: 091b lsrs r3, r3, #4 + 80037cc: 2b06 cmp r3, #6 + 80037ce: d1ef bne.n 80037b0 + memcpy(serial, &config[0], 4); + 80037d0: 9b38 ldr r3, [sp, #224] ; 0xe0 + 80037d2: 9303 str r3, [sp, #12] + memcpy(&serial[4], &config[8], 5); + 80037d4: ab3a add r3, sp, #232 ; 0xe8 + 80037d6: e893 0003 ldmia.w r3, {r0, r1} + 80037da: 9004 str r0, [sp, #16] + 80037dc: f88d 1014 strb.w r1, [sp, #20] + if(check_all_ones(rom_secrets->ae_serial_number, 9)) { + 80037e0: 4864 ldr r0, [pc, #400] ; (8003974 ) + 80037e2: 2109 movs r1, #9 + 80037e4: f7ff f812 bl 800280c + 80037e8: b110 cbz r0, 80037f0 + flash_save_ae_serial(serial); + 80037ea: a803 add r0, sp, #12 + 80037ec: f7fe fd30 bl 8002250 + if(!check_equal(rom_secrets->ae_serial_number, serial, 9)) { + 80037f0: 4860 ldr r0, [pc, #384] ; (8003974 ) + 80037f2: 2209 movs r2, #9 + 80037f4: a903 add r1, sp, #12 + 80037f6: f7ff f822 bl 800283e + 80037fa: 2800 cmp r0, #0 + 80037fc: f000 80b6 beq.w 800396c + if(config[87] == 0x55) { + 8003800: f89d 3137 ldrb.w r3, [sp, #311] ; 0x137 + 8003804: 2b55 cmp r3, #85 ; 0x55 + 8003806: d12b bne.n 8003860 + memcpy(&config[16], config_1, sizeof(config_1)); + 8003808: 495b ldr r1, [pc, #364] ; (8003978 ) + 800380a: 2244 movs r2, #68 ; 0x44 + 800380c: a83c add r0, sp, #240 ; 0xf0 + 800380e: f00a f87b bl 800d908 + memcpy(&config[90], config_2, sizeof(config_2)); + 8003812: 4b5a ldr r3, [pc, #360] ; (800397c ) + 8003814: f50d 729d add.w r2, sp, #314 ; 0x13a + 8003818: f103 0124 add.w r1, r3, #36 ; 0x24 + 800381c: f853 0b04 ldr.w r0, [r3], #4 + 8003820: f842 0b04 str.w r0, [r2], #4 + 8003824: 428b cmp r3, r1 + 8003826: d1f9 bne.n 800381c + 8003828: 881b ldrh r3, [r3, #0] + 800382a: 8013 strh r3, [r2, #0] + for(int n=16; n<128; n+= 4) { + 800382c: 2510 movs r5, #16 + ae_send_n(OP_Write, 0, n/4, &config[n], 4); + 800382e: 2604 movs r6, #4 + if(n == 84) continue; // that word not writable + 8003830: 2d54 cmp r5, #84 ; 0x54 + 8003832: d130 bne.n 8003896 + for(int n=16; n<128; n+= 4) { + 8003834: 3504 adds r5, #4 + 8003836: 2d80 cmp r5, #128 ; 0x80 + 8003838: d1fa bne.n 8003830 + ae_send_idle(); + 800383a: f7ff f90a bl 8002a52 + uint8_t crc[2] = {0, 0}; + 800383e: 2600 movs r6, #0 + crc16_chain(128, config, crc); + 8003840: aa58 add r2, sp, #352 ; 0x160 + 8003842: a938 add r1, sp, #224 ; 0xe0 + 8003844: 4628 mov r0, r5 + uint8_t crc[2] = {0, 0}; + 8003846: f8ad 6160 strh.w r6, [sp, #352] ; 0x160 + crc16_chain(128, config, crc); + 800384a: f7ff f8b5 bl 80029b8 + ae_send(OP_Lock, 0x0, (crc[1]<<8) | crc[0]); + 800384e: f8bd 2160 ldrh.w r2, [sp, #352] ; 0x160 + 8003852: 4631 mov r1, r6 + 8003854: 2017 movs r0, #23 + 8003856: f7ff fa38 bl 8002cca + return ae_read1(); + 800385a: f7ff f9ad bl 8002bb8 + if(ae_lock_config_zone(config)) { + 800385e: bb38 cbnz r0, 80038b0 + // Load data zone with some known values. + // The datazone still unlocked, so no encryption needed (nor possible). + + // will use zeros for all PIN codes, and customer-defined-secret starting values + uint8_t zeros[72]; + memset(zeros, 0, sizeof(zeros)); + 8003860: 2248 movs r2, #72 ; 0x48 + 8003862: 2100 movs r1, #0 + 8003864: a826 add r0, sp, #152 ; 0x98 + 8003866: f00a f85d bl 800d924 + se2_save_auth_pubkey(pubkey); + break; + } + + case 0: + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 800386a: 4e45 ldr r6, [pc, #276] ; (8003980 ) + 800386c: f8bd 5138 ldrh.w r5, [sp, #312] ; 0x138 + if(ae_write_data_slot(kn, rom_secrets->pairing_secret, 32, false)) { + 8003870: 4f44 ldr r7, [pc, #272] ; (8003984 ) + ae_send_idle(); + 8003872: f7ff f8ee bl 8002a52 + if(!(unlocked & (1< + switch(kn) { + 800387e: 2c0e cmp r4, #14 + 8003880: d85c bhi.n 800393c + 8003882: e8df f004 tbb [pc, r4] + 8003886: 176e .short 0x176e + 8003888: 29202920 .word 0x29202920 + 800388c: 2d304c3e .word 0x2d304c3e + 8003890: 2d2d2d2d .word 0x2d2d2d2d + 8003894: 29 .byte 0x29 + 8003895: 00 .byte 0x00 + ae_send_n(OP_Write, 0, n/4, &config[n], 4); + 8003896: ab38 add r3, sp, #224 ; 0xe0 + 8003898: 442b add r3, r5 + 800389a: f3c5 028f ubfx r2, r5, #2, #16 + 800389e: 2100 movs r1, #0 + 80038a0: 2012 movs r0, #18 + 80038a2: 9600 str r6, [sp, #0] + 80038a4: f7ff f9de bl 8002c64 + int rv = ae_read1(); + 80038a8: f7ff f986 bl 8002bb8 + if(rv) return rv; + 80038ac: 2800 cmp r0, #0 + 80038ae: d0c1 beq.n 8003834 + INCONSISTENT("conf lock"); + 80038b0: 4835 ldr r0, [pc, #212] ; (8003988 ) + 80038b2: e77e b.n 80037b2 + if(ae_write_data_slot(kn, rom_secrets->pairing_secret, 32, false)) { + 80038b4: 2300 movs r3, #0 + 80038b6: 2220 movs r2, #32 + 80038b8: 4639 mov r1, r7 + 80038ba: 2001 movs r0, #1 + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 80038bc: f7ff fbf4 bl 80030a8 + 80038c0: 2800 cmp r0, #0 + 80038c2: d03b beq.n 800393c + 80038c4: e7f4 b.n 80038b0 + rng_buffer(tmp, sizeof(tmp)); + 80038c6: 2120 movs r1, #32 + 80038c8: a806 add r0, sp, #24 + 80038ca: f7ff f807 bl 80028dc + if(ae_write_data_slot(kn, tmp, 32, true)) { + 80038ce: 2301 movs r3, #1 + 80038d0: 2220 movs r2, #32 + 80038d2: a906 add r1, sp, #24 + if(ae_write_data_slot(kn, zeros, 32, false)) { + 80038d4: 4620 mov r0, r4 + 80038d6: e7f1 b.n 80038bc + 80038d8: 2300 movs r3, #0 + 80038da: 2220 movs r2, #32 + 80038dc: a926 add r1, sp, #152 ; 0x98 + 80038de: e7f9 b.n 80038d4 + if(ae_write_data_slot(kn, zeros, 72, false)) { + 80038e0: 2300 movs r3, #0 + 80038e2: 2248 movs r2, #72 ; 0x48 + 80038e4: e7fa b.n 80038dc + uint8_t long_zeros[416] = {0}; + 80038e6: 2300 movs r3, #0 + 80038e8: 4619 mov r1, r3 + 80038ea: f44f 72ce mov.w r2, #412 ; 0x19c + 80038ee: a859 add r0, sp, #356 ; 0x164 + 80038f0: 9358 str r3, [sp, #352] ; 0x160 + 80038f2: f00a f817 bl 800d924 + if(ae_write_data_slot(kn, long_zeros, 416, false)) { + 80038f6: 2300 movs r3, #0 + 80038f8: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 80038fc: a958 add r1, sp, #352 ; 0x160 + 80038fe: 2008 movs r0, #8 + 8003900: e7dc b.n 80038bc + uint32_t buf[32/4] = { 1024, 1024 }; + 8003902: 2218 movs r2, #24 + 8003904: 2100 movs r1, #0 + 8003906: a810 add r0, sp, #64 ; 0x40 + 8003908: f00a f80c bl 800d924 + 800390c: f44f 6380 mov.w r3, #1024 ; 0x400 + 8003910: e9cd 330e strd r3, r3, [sp, #56] ; 0x38 + if(ae_write_data_slot(KEYNUM_match_count, (const uint8_t *)buf,sizeof(buf),false)) { + 8003914: 2220 movs r2, #32 + 8003916: 2300 movs r3, #0 + 8003918: a90e add r1, sp, #56 ; 0x38 + 800391a: 2006 movs r0, #6 + 800391c: e7ce b.n 80038bc + if(ae_checkmac_hard(KEYNUM_main_pin, zeros) != 0) { + 800391e: a926 add r1, sp, #152 ; 0x98 + 8003920: 2003 movs r0, #3 + 8003922: f7ff fc99 bl 8003258 + 8003926: 2800 cmp r0, #0 + 8003928: d1c2 bne.n 80038b0 + if(ae_gen_ecc_key(KEYNUM_joiner_key, pubkey)) { + 800392a: a916 add r1, sp, #88 ; 0x58 + 800392c: 2007 movs r0, #7 + 800392e: f7ff fb2b bl 8002f88 + 8003932: 2800 cmp r0, #0 + 8003934: d1bc bne.n 80038b0 + se2_save_auth_pubkey(pubkey); + 8003936: a816 add r0, sp, #88 ; 0x58 + 8003938: f004 f9e4 bl 8007d04 + for(int kn=0; kn<16; kn++) { + 800393c: 3401 adds r4, #1 + 800393e: 2c10 cmp r4, #16 + 8003940: d197 bne.n 8003872 + ae_send_idle(); + 8003942: f7ff f886 bl 8002a52 + ae_send(OP_Lock, 0x81, 0x0000); + 8003946: 2200 movs r2, #0 + 8003948: 2181 movs r1, #129 ; 0x81 + 800394a: 2017 movs r0, #23 + 800394c: f7ff f9bd bl 8002cca + return ae_read1(); + 8003950: f7ff f932 bl 8002bb8 + } + } + + // lock the data zone and effectively enter normal operation. + ae_keep_alive(); + if(ae_lock_data_zone()) { + 8003954: 2800 cmp r0, #0 + 8003956: d1ab bne.n 80038b0 + if(data_locked) return 0; // basically success + 8003958: 2400 movs r4, #0 + INCONSISTENT("data lock"); + } + + return 0; +} + 800395a: 4620 mov r0, r4 + 800395c: f50d 7d41 add.w sp, sp, #772 ; 0x304 + 8003960: bdf0 pop {r4, r5, r6, r7, pc} + if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) { + 8003962: 2301 movs r3, #1 + 8003964: 2220 movs r2, #32 + 8003966: 4631 mov r1, r6 + 8003968: 2000 movs r0, #0 + 800396a: e7a7 b.n 80038bc + return EPERM; + 800396c: 2401 movs r4, #1 + 800396e: e7f4 b.n 800395a + 8003970: 0801046c .word 0x0801046c + 8003974: 0801c040 .word 0x0801c040 + 8003978: 08010706 .word 0x08010706 + 800397c: 0801074a .word 0x0801074a + 8003980: 080106c2 .word 0x080106c2 + 8003984: 0801c000 .word 0x0801c000 + 8003988: 0800d9a0 .word 0x0800d9a0 + +0800398c : +// - but our time to do each iteration is limited by software SHA256 in ae_pair_unlock +// + int +ae_stretch_iter(const uint8_t start[32], uint8_t end[32], int iterations) +{ + ASSERT(start != end); // we can't work inplace + 800398c: 4288 cmp r0, r1 +{ + 800398e: b570 push {r4, r5, r6, lr} + 8003990: 460c mov r4, r1 + 8003992: 4615 mov r5, r2 + ASSERT(start != end); // we can't work inplace + 8003994: d102 bne.n 800399c + 8003996: 4810 ldr r0, [pc, #64] ; (80039d8 ) + 8003998: f7fd f84e bl 8000a38 + memcpy(end, start, 32); + 800399c: 460b mov r3, r1 + 800399e: f100 0220 add.w r2, r0, #32 + 80039a2: f850 1b04 ldr.w r1, [r0], #4 + 80039a6: f843 1b04 str.w r1, [r3], #4 + 80039aa: 4290 cmp r0, r2 + 80039ac: d1f9 bne.n 80039a2 + + for(int i=0; i + + int rv = ae_hmac32(KEYNUM_pin_stretch, end, end); + RET_IF_BAD(rv); + } + + return 0; + 80039b4: 2000 movs r0, #0 +} + 80039b6: bd70 pop {r4, r5, r6, pc} + if(ae_pair_unlock()) return -2; + 80039b8: f7ff fac0 bl 8002f3c + 80039bc: b940 cbnz r0, 80039d0 + int rv = ae_hmac32(KEYNUM_pin_stretch, end, end); + 80039be: 4622 mov r2, r4 + 80039c0: 4621 mov r1, r4 + 80039c2: 2002 movs r0, #2 + 80039c4: f7ff fb02 bl 8002fcc + RET_IF_BAD(rv); + 80039c8: 2800 cmp r0, #0 + 80039ca: d1f4 bne.n 80039b6 + for(int i=0; i + if(ae_pair_unlock()) return -2; + 80039d0: f06f 0001 mvn.w r0, #1 + 80039d4: e7ef b.n 80039b6 + 80039d6: bf00 nop + 80039d8: 0801046c .word 0x0801046c + +080039dc : +// Apply HMAC using secret in chip as a HMAC key, then encrypt +// the result a little because read in clear over bus. +// + int +ae_mixin_key(uint8_t keynum, const uint8_t start[32], uint8_t end[32]) +{ + 80039dc: b570 push {r4, r5, r6, lr} + 80039de: b096 sub sp, #88 ; 0x58 + ASSERT(start != end); // we can't work inplace + 80039e0: 4291 cmp r1, r2 +{ + 80039e2: 460e mov r6, r1 + 80039e4: 4614 mov r4, r2 + 80039e6: f88d 0007 strb.w r0, [sp, #7] + ASSERT(start != end); // we can't work inplace + 80039ea: d102 bne.n 80039f2 + 80039ec: 4818 ldr r0, [pc, #96] ; (8003a50 ) + 80039ee: f7fd f823 bl 8000a38 + + if(ae_pair_unlock()) return -1; + 80039f2: f7ff faa3 bl 8002f3c + 80039f6: bb40 cbnz r0, 8003a4a + + ASSERT(keynum != 0); + 80039f8: f89d 0007 ldrb.w r0, [sp, #7] + 80039fc: 2800 cmp r0, #0 + 80039fe: d0f5 beq.n 80039ec + int rv = ae_hmac32(keynum, start, end); + 8003a00: 4622 mov r2, r4 + 8003a02: 4631 mov r1, r6 + 8003a04: f7ff fae2 bl 8002fcc + RET_IF_BAD(rv); + 8003a08: 4605 mov r5, r0 + 8003a0a: b9d8 cbnz r0, 8003a44 + // use the value provided in cleartext[sic--it's not] write back shortly (to test it). + // Solution: one more SHA256, and to be safe, mixin lots of values! + + SHA256_CTX ctx; + + sha256_init(&ctx); + 8003a0c: a803 add r0, sp, #12 + 8003a0e: f001 feb5 bl 800577c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003a12: 4910 ldr r1, [pc, #64] ; (8003a54 ) + 8003a14: 2220 movs r2, #32 + 8003a16: a803 add r0, sp, #12 + 8003a18: f001 febe bl 8005798 + sha256_update(&ctx, start, 32); + 8003a1c: 2220 movs r2, #32 + 8003a1e: 4631 mov r1, r6 + 8003a20: a803 add r0, sp, #12 + 8003a22: f001 feb9 bl 8005798 + sha256_update(&ctx, &keynum, 1); + 8003a26: 2201 movs r2, #1 + 8003a28: f10d 0107 add.w r1, sp, #7 + 8003a2c: a803 add r0, sp, #12 + 8003a2e: f001 feb3 bl 8005798 + sha256_update(&ctx, end, 32); + 8003a32: 4621 mov r1, r4 + 8003a34: a803 add r0, sp, #12 + 8003a36: 2220 movs r2, #32 + 8003a38: f001 feae bl 8005798 + sha256_final(&ctx, end); + 8003a3c: 4621 mov r1, r4 + 8003a3e: a803 add r0, sp, #12 + 8003a40: f001 fef0 bl 8005824 + + return 0; +} + 8003a44: 4628 mov r0, r5 + 8003a46: b016 add sp, #88 ; 0x58 + 8003a48: bd70 pop {r4, r5, r6, pc} + if(ae_pair_unlock()) return -1; + 8003a4a: f04f 35ff mov.w r5, #4294967295 ; 0xffffffff + 8003a4e: e7f9 b.n 8003a44 + 8003a50: 0801046c .word 0x0801046c + 8003a54: 0801c000 .word 0x0801c000 + +08003a58 : +// Immediately destroy the pairing secret so that we become +// a useless brick. Ignore errors but retry. +// + void +ae_brick_myself(void) +{ + 8003a58: b510 push {r4, lr} + for(int retry=0; retry<10; retry++) { + 8003a5a: 2400 movs r4, #0 + ae_reset_chip(); + 8003a5c: f7ff f86a bl 8002b34 + + if(retry) rng_delay(); + 8003a60: b10c cbz r4, 8003a66 + 8003a62: f7fe ff51 bl 8002908 + + ae_pair_unlock(); + 8003a66: f7ff fa69 bl 8002f3c + + // Concern: MitM could block this by trashing our write + // - but they have to do it without causing CRC or other comm error + // - ten times + int rv = ae_destroy_key(KEYNUM_pairing); + 8003a6a: 2001 movs r0, #1 + 8003a6c: f7ff fe54 bl 8003718 + if(rv == 0) break; + 8003a70: b120 cbz r0, 8003a7c + for(int retry=0; retry<10; retry++) { + 8003a72: 3401 adds r4, #1 + + rng_delay(); + 8003a74: f7fe ff48 bl 8002908 + for(int retry=0; retry<10; retry++) { + 8003a78: 2c0a cmp r4, #10 + 8003a7a: d1ef bne.n 8003a5c + } + + ae_reset_chip(); +} + 8003a7c: e8bd 4010 ldmia.w sp!, {r4, lr} + ae_reset_chip(); + 8003a80: f7ff b858 b.w 8002b34 + +08003a84 : +// + void +delay_ms(int ms) +{ + // Clear the COUNTFLAG and reset value to zero + SysTick->VAL = 0; + 8003a84: f04f 23e0 mov.w r3, #3758153728 ; 0xe000e000 + 8003a88: 2200 movs r2, #0 + 8003a8a: 619a str r2, [r3, #24] + //SysTick->CTRL; + + // Wait for ticks to happen + while(ms > 0) { + 8003a8c: 2800 cmp r0, #0 + 8003a8e: dc00 bgt.n 8003a92 + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + ms--; + } + } +} + 8003a90: 4770 bx lr + if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + 8003a92: 691a ldr r2, [r3, #16] + 8003a94: 03d2 lsls r2, r2, #15 + ms--; + 8003a96: bf48 it mi + 8003a98: f100 30ff addmi.w r0, r0, #4294967295 ; 0xffffffff + 8003a9c: e7f6 b.n 8003a8c + +08003a9e : +// Replace HAL version which needs interrupts +// + void +HAL_Delay(uint32_t Delay) +{ + delay_ms(Delay); + 8003a9e: f7ff bff1 b.w 8003a84 + ... + +08003aa4 : + // NOTES: + // - try not to limit PCB changes for future revs; leave unused unchanged. + // - lcd.c controls some pins as well. + + // enable clock to GPIO's ... we will be using them all at some point + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003aa4: 4b67 ldr r3, [pc, #412] ; (8003c44 ) +{ + 8003aa6: b570 push {r4, r5, r6, lr} + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003aa8: 6cda ldr r2, [r3, #76] ; 0x4c + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); + + { // Onewire bus pins used for ATECC608 comms + GPIO_InitTypeDef setup = { + 8003aaa: 4c67 ldr r4, [pc, #412] ; (8003c48 ) + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003aac: f042 0201 orr.w r2, r2, #1 + 8003ab0: 64da str r2, [r3, #76] ; 0x4c + 8003ab2: 6cda ldr r2, [r3, #76] ; 0x4c +{ + 8003ab4: b08a sub sp, #40 ; 0x28 + __HAL_RCC_GPIOA_CLK_ENABLE(); + 8003ab6: f002 0201 and.w r2, r2, #1 + 8003aba: 9200 str r2, [sp, #0] + 8003abc: 9a00 ldr r2, [sp, #0] + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8003abe: 6cda ldr r2, [r3, #76] ; 0x4c + 8003ac0: f042 0202 orr.w r2, r2, #2 + 8003ac4: 64da str r2, [r3, #76] ; 0x4c + 8003ac6: 6cda ldr r2, [r3, #76] ; 0x4c + 8003ac8: f002 0202 and.w r2, r2, #2 + 8003acc: 9201 str r2, [sp, #4] + 8003ace: 9a01 ldr r2, [sp, #4] + __HAL_RCC_GPIOC_CLK_ENABLE(); + 8003ad0: 6cda ldr r2, [r3, #76] ; 0x4c + 8003ad2: f042 0204 orr.w r2, r2, #4 + 8003ad6: 64da str r2, [r3, #76] ; 0x4c + 8003ad8: 6cda ldr r2, [r3, #76] ; 0x4c + 8003ada: f002 0204 and.w r2, r2, #4 + 8003ade: 9202 str r2, [sp, #8] + 8003ae0: 9a02 ldr r2, [sp, #8] + __HAL_RCC_GPIOD_CLK_ENABLE(); + 8003ae2: 6cda ldr r2, [r3, #76] ; 0x4c + 8003ae4: f042 0208 orr.w r2, r2, #8 + 8003ae8: 64da str r2, [r3, #76] ; 0x4c + 8003aea: 6cda ldr r2, [r3, #76] ; 0x4c + 8003aec: f002 0208 and.w r2, r2, #8 + 8003af0: 9203 str r2, [sp, #12] + 8003af2: 9a03 ldr r2, [sp, #12] + __HAL_RCC_GPIOE_CLK_ENABLE(); + 8003af4: 6cda ldr r2, [r3, #76] ; 0x4c + 8003af6: f042 0210 orr.w r2, r2, #16 + 8003afa: 64da str r2, [r3, #76] ; 0x4c + 8003afc: 6cdb ldr r3, [r3, #76] ; 0x4c + 8003afe: f003 0310 and.w r3, r3, #16 + 8003b02: 9304 str r3, [sp, #16] + 8003b04: 9b04 ldr r3, [sp, #16] + GPIO_InitTypeDef setup = { + 8003b06: cc0f ldmia r4!, {r0, r1, r2, r3} + 8003b08: ad05 add r5, sp, #20 + 8003b0a: c50f stmia r5!, {r0, r1, r2, r3} + 8003b0c: 6823 ldr r3, [r4, #0] + 8003b0e: 602b str r3, [r5, #0] + .Mode = GPIO_MODE_AF_OD, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = GPIO_AF8_UART4, + }; + HAL_GPIO_Init(ONEWIRE_PORT, &setup); + 8003b10: a905 add r1, sp, #20 + 8003b12: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8003b16: f7fd fb6d bl 80011f4 + } + + // Bugfix: re-init of console port pins seems to wreck + // the mpy uart code, so avoid after first time. + if(USART1->BRR == 0) { + 8003b1a: 4b4c ldr r3, [pc, #304] ; (8003c4c ) + 8003b1c: 68de ldr r6, [r3, #12] + 8003b1e: b9ae cbnz r6, 8003b4c + // debug console: USART1 = PA9=Tx & PA10=Rx + GPIO_InitTypeDef setup = { + 8003b20: 3404 adds r4, #4 + 8003b22: cc0f ldmia r4!, {r0, r1, r2, r3} + 8003b24: ad05 add r5, sp, #20 + 8003b26: c50f stmia r5!, {r0, r1, r2, r3} + 8003b28: 6823 ldr r3, [r4, #0] + 8003b2a: 602b str r3, [r5, #0] + .Mode = GPIO_MODE_AF_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_MEDIUM, + .Alternate = GPIO_AF7_USART1, + }; + HAL_GPIO_Init(GPIOA, &setup); + 8003b2c: a905 add r1, sp, #20 + 8003b2e: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + 8003b32: f7fd fb5f bl 80011f4 + + setup.Pin = GPIO_PIN_10; + 8003b36: f44f 6380 mov.w r3, #1024 ; 0x400 + setup.Mode = GPIO_MODE_INPUT; + 8003b3a: e9cd 3605 strd r3, r6, [sp, #20] + setup.Pull = GPIO_PULLUP; + HAL_GPIO_Init(GPIOA, &setup); + 8003b3e: a905 add r1, sp, #20 + setup.Pull = GPIO_PULLUP; + 8003b40: 2301 movs r3, #1 + HAL_GPIO_Init(GPIOA, &setup); + 8003b42: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + setup.Pull = GPIO_PULLUP; + 8003b46: 9307 str r3, [sp, #28] + HAL_GPIO_Init(GPIOA, &setup); + 8003b48: f7fd fb54 bl 80011f4 + } + + { // Port B - mostly unused, but want TEAR input and pwr btn + // TEAR from LCD: PB11 + // PWR_BTN: PB12 + GPIO_InitTypeDef setup = { + 8003b4c: 2400 movs r4, #0 + // Port C - Outputs + // SD1 active LED: PC7 + // USB active LED: PC6 + // TURN OFF: PC0 + // SD mux: PC13 + { GPIO_InitTypeDef setup = { + 8003b4e: 2501 movs r5, #1 + GPIO_InitTypeDef setup = { + 8003b50: f44f 53c0 mov.w r3, #6144 ; 0x1800 + HAL_GPIO_Init(GPIOB, &setup); + 8003b54: a905 add r1, sp, #20 + 8003b56: 483e ldr r0, [pc, #248] ; (8003c50 ) + GPIO_InitTypeDef setup = { + 8003b58: 9409 str r4, [sp, #36] ; 0x24 + 8003b5a: e9cd 3405 strd r3, r4, [sp, #20] + 8003b5e: e9cd 4407 strd r4, r4, [sp, #28] + HAL_GPIO_Init(GPIOB, &setup); + 8003b62: f7fd fb47 bl 80011f4 + { GPIO_InitTypeDef setup = { + 8003b66: 23c1 movs r3, #193 ; 0xc1 + .Pin = GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_0, GPIO_PIN_13, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, 0); // keep power on! + 8003b68: 4622 mov r2, r4 + 8003b6a: 4629 mov r1, r5 + 8003b6c: 4839 ldr r0, [pc, #228] ; (8003c54 ) + { GPIO_InitTypeDef setup = { + 8003b6e: 9409 str r4, [sp, #36] ; 0x24 + 8003b70: e9cd 3505 strd r3, r5, [sp, #20] + 8003b74: e9cd 4407 strd r4, r4, [sp, #28] + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, 0); // keep power on! + 8003b78: f7fd fcb6 bl 80014e8 + HAL_GPIO_Init(GPIOC, &setup); + 8003b7c: a905 add r1, sp, #20 + 8003b7e: 4835 ldr r0, [pc, #212] ; (8003c54 ) + 8003b80: f7fd fb38 bl 80011f4 + + // turn LEDs off, SD mux to A + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7|GPIO_PIN_6|GPIO_PIN_13, 0); + 8003b84: 4622 mov r2, r4 + 8003b86: 4833 ldr r0, [pc, #204] ; (8003c54 ) + 8003b88: f44f 5103 mov.w r1, #8384 ; 0x20c0 + 8003b8c: f7fd fcac bl 80014e8 + } + + // Port C - Inputs + // SD card detect switch: PC1 battery/not + { GPIO_InitTypeDef setup = { + 8003b90: 2210 movs r2, #16 + 8003b92: 4621 mov r1, r4 + 8003b94: a806 add r0, sp, #24 + 8003b96: f009 fec5 bl 800d924 + 8003b9a: f242 0302 movw r3, #8194 ; 0x2002 + .Pin = GPIO_PIN_13 | GPIO_PIN_1, + .Mode = GPIO_MODE_INPUT, + .Pull = GPIO_PULLUP, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOC, &setup); + 8003b9e: a905 add r1, sp, #20 + 8003ba0: 482c ldr r0, [pc, #176] ; (8003c54 ) + { GPIO_InitTypeDef setup = { + 8003ba2: 9305 str r3, [sp, #20] + 8003ba4: 9507 str r5, [sp, #28] + HAL_GPIO_Init(GPIOC, &setup); + 8003ba6: f7fd fb25 bl 80011f4 + .Pin = GPIO_PIN_0, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOD, &setup); + 8003baa: a905 add r1, sp, #20 + 8003bac: 482a ldr r0, [pc, #168] ; (8003c58 ) + { GPIO_InitTypeDef setup = { + 8003bae: 9409 str r4, [sp, #36] ; 0x24 + 8003bb0: e9cd 4407 strd r4, r4, [sp, #28] + 8003bb4: e9cd 5505 strd r5, r5, [sp, #20] + HAL_GPIO_Init(GPIOD, &setup); + 8003bb8: f7fd fb1c bl 80011f4 + + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_0, 0); // turn off + 8003bbc: 4622 mov r2, r4 + 8003bbe: 4629 mov r1, r5 + 8003bc0: 4825 ldr r0, [pc, #148] ; (8003c58 ) + 8003bc2: f7fd fc91 bl 80014e8 + } + + // Port D - Inputs + // SD slots detects: PD3/4 + { GPIO_InitTypeDef setup = { + 8003bc6: 2210 movs r2, #16 + 8003bc8: 4621 mov r1, r4 + 8003bca: a806 add r0, sp, #24 + 8003bcc: f009 feaa bl 800d924 + 8003bd0: 2618 movs r6, #24 + .Pin = GPIO_PIN_3 | GPIO_PIN_4, + .Mode = GPIO_MODE_INPUT, + .Pull = GPIO_PULLUP, // required + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOD, &setup); + 8003bd2: a905 add r1, sp, #20 + 8003bd4: 4820 ldr r0, [pc, #128] ; (8003c58 ) + { GPIO_InitTypeDef setup = { + 8003bd6: 9605 str r6, [sp, #20] + 8003bd8: 9507 str r5, [sp, #28] + HAL_GPIO_Init(GPIOD, &setup); + 8003bda: f7fd fb0b bl 80011f4 + .Pin = GPIO_PIN_3 | GPIO_PIN_4, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(GPIOE, &setup); + 8003bde: a905 add r1, sp, #20 + 8003be0: 481e ldr r0, [pc, #120] ; (8003c5c ) + { GPIO_InitTypeDef setup = { + 8003be2: 9409 str r4, [sp, #36] ; 0x24 + 8003be4: e9cd 4407 strd r4, r4, [sp, #28] + 8003be8: e9cd 6505 strd r6, r5, [sp, #20] + HAL_GPIO_Init(GPIOE, &setup); + 8003bec: f7fd fb02 bl 80011f4 + + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, 0); // turn off NFC LED + 8003bf0: 4622 mov r2, r4 + 8003bf2: 481a ldr r0, [pc, #104] ; (8003c5c ) + 8003bf4: 2110 movs r1, #16 + 8003bf6: f7fd fc77 bl 80014e8 + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, 1); // turn on Backlight: 100% + 8003bfa: 462a mov r2, r5 + 8003bfc: 4817 ldr r0, [pc, #92] ; (8003c5c ) + 8003bfe: 2108 movs r1, #8 + 8003c00: f7fd fc72 bl 80014e8 + + // GPU control: Port E: PE2=G_SWCLK_BOOT0=G_BUSY, PE5=G_CTRL, PE6=G_RESET + // - want open-drain on these outputs, so the SWD debugger can override + // - and PE2 needs to be pull-down input, because active high signal and + // GPU may not be running yet + { GPIO_InitTypeDef setup = { + 8003c04: 2260 movs r2, #96 ; 0x60 + 8003c06: 2311 movs r3, #17 + .Mode = GPIO_MODE_OUTPUT_OD, + .Pull = GPIO_PULLUP, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + + HAL_GPIO_Init(GPIOE, &setup); + 8003c08: a905 add r1, sp, #20 + 8003c0a: 4814 ldr r0, [pc, #80] ; (8003c5c ) + { GPIO_InitTypeDef setup = { + 8003c0c: 9507 str r5, [sp, #28] + 8003c0e: e9cd 2305 strd r2, r3, [sp, #20] + 8003c12: e9cd 4408 strd r4, r4, [sp, #32] + HAL_GPIO_Init(GPIOE, &setup); + 8003c16: f7fd faed bl 80011f4 + + // G_BUSY: input, pull down + setup.Pin = PIN_G_BUSY; + 8003c1a: 2304 movs r3, #4 + 8003c1c: 9305 str r3, [sp, #20] + setup.Pull = GPIO_PULLDOWN; + HAL_GPIO_Init(GPIOE, &setup); + 8003c1e: a905 add r1, sp, #20 + setup.Pull = GPIO_PULLDOWN; + 8003c20: 2302 movs r3, #2 + HAL_GPIO_Init(GPIOE, &setup); + 8003c22: 480e ldr r0, [pc, #56] ; (8003c5c ) + setup.Pull = GPIO_PULLDOWN; + 8003c24: 9307 str r3, [sp, #28] + HAL_GPIO_Init(GPIOE, &setup); + 8003c26: f7fd fae5 bl 80011f4 + + // assert reset, leave others high + HAL_GPIO_WritePin(GPIOE, PIN_G_CTRL, 1); + 8003c2a: 462a mov r2, r5 + 8003c2c: 480b ldr r0, [pc, #44] ; (8003c5c ) + 8003c2e: 2120 movs r1, #32 + 8003c30: f7fd fc5a bl 80014e8 + HAL_GPIO_WritePin(GPIOE, PIN_G_RESET, 0); + 8003c34: 4809 ldr r0, [pc, #36] ; (8003c5c ) + 8003c36: 4622 mov r2, r4 + 8003c38: 2140 movs r1, #64 ; 0x40 + 8003c3a: f7fd fc55 bl 80014e8 + // RCC_MCO1SOURCE_PLLCLK (PLL R output) => (same os SYSCLK) + // RCC_MCO1SOURCE_HSI48 => 48Mhz + // RCC_MCO1SOURCE_HSE => 8Mhz (correct) + __HAL_RCC_MCO1_CONFIG(RCC_MCO1SOURCE_SYSCLK, RCC_MCODIV_1); +#endif +} + 8003c3e: b00a add sp, #40 ; 0x28 + 8003c40: bd70 pop {r4, r5, r6, pc} + 8003c42: bf00 nop + 8003c44: 40021000 .word 0x40021000 + 8003c48: 08010770 .word 0x08010770 + 8003c4c: 40013800 .word 0x40013800 + 8003c50: 48000400 .word 0x48000400 + 8003c54: 48000800 .word 0x48000800 + 8003c58: 48000c00 .word 0x48000c00 + 8003c5c: 48001000 .word 0x48001000 + +08003c60 : +// +// Kill system power; instant. +// + void +turn_power_off(void) +{ + 8003c60: b508 push {r3, lr} + gpio_setup(); + 8003c62: f7ff ff1f bl 8003aa4 + + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, 1); + 8003c66: 2201 movs r2, #1 + 8003c68: 4802 ldr r0, [pc, #8] ; (8003c74 ) + 8003c6a: 4611 mov r1, r2 + 8003c6c: f7fd fc3c bl 80014e8 + + while(1) { + __WFI(); + 8003c70: bf30 wfi + while(1) { + 8003c72: e7fd b.n 8003c70 + 8003c74: 48000800 .word 0x48000800 + +08003c78 : +// Showing a fatal msg to user; power down after a long delay +// or instantly if they touch power btn. Replaces LOCKUP_FOREVER +// + void +q1_wait_powerdown(void) +{ + 8003c78: b508 push {r3, lr} + gpio_setup(); + 8003c7a: f7ff ff13 bl 8003aa4 + + // wait for release (often problem occurs close to power up) + for(uint32_t i=0; i) + gpio_setup(); + 8003c80: 2496 movs r4, #150 ; 0x96 + if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == 1) { + 8003c82: f44f 5180 mov.w r1, #4096 ; 0x1000 + 8003c86: 4628 mov r0, r5 + 8003c88: f7fd fc28 bl 80014dc + 8003c8c: 2801 cmp r0, #1 + 8003c8e: d004 beq.n 8003c9a + break; + } + + delay_ms(100); + 8003c90: 2064 movs r0, #100 ; 0x64 + 8003c92: f7ff fef7 bl 8003a84 + for(uint32_t i=0; i + } + + // wait for press + for(uint32_t i=0; i) + gpio_setup(); + 8003c9c: 2496 movs r4, #150 ; 0x96 + if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == 0) { + 8003c9e: f44f 5180 mov.w r1, #4096 ; 0x1000 + 8003ca2: 4628 mov r0, r5 + 8003ca4: f7fd fc1a bl 80014dc + 8003ca8: b120 cbz r0, 8003cb4 + break; + } + + delay_ms(100); + 8003caa: 2064 movs r0, #100 ; 0x64 + 8003cac: f7ff feea bl 8003a84 + for(uint32_t i=0; i + } + + // turn off power + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, 1); + 8003cb4: 2201 movs r2, #1 + 8003cb6: 4804 ldr r0, [pc, #16] ; (8003cc8 ) + 8003cb8: 4611 mov r1, r2 + 8003cba: f7fd fc15 bl 80014e8 + + // not reached. + while(1) { + __WFI(); + 8003cbe: bf30 wfi + while(1) { + 8003cc0: e7fd b.n 8003cbe + 8003cc2: bf00 nop + 8003cc4: 48000400 .word 0x48000400 + 8003cc8: 48000800 .word 0x48000800 + +08003ccc : + +// reboot_nonce() +// + static inline void +reboot_nonce(SHA256_CTX *ctx) +{ + 8003ccc: b537 push {r0, r1, r2, r4, r5, lr} + uint32_t a = CRC->INIT; + 8003cce: 4d09 ldr r5, [pc, #36] ; (8003cf4 ) + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003cd0: 2204 movs r2, #4 + uint32_t a = CRC->INIT; + 8003cd2: 692b ldr r3, [r5, #16] + 8003cd4: 9301 str r3, [sp, #4] + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003cd6: eb0d 0102 add.w r1, sp, r2 +{ + 8003cda: 4604 mov r4, r0 + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003cdc: f001 fd5c bl 8005798 + + a = CRC->POL; + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003ce0: 2204 movs r2, #4 + a = CRC->POL; + 8003ce2: 696b ldr r3, [r5, #20] + 8003ce4: 9301 str r3, [sp, #4] + sha256_update(ctx, (const uint8_t *)&a, 4); + 8003ce6: eb0d 0102 add.w r1, sp, r2 + 8003cea: 4620 mov r0, r4 + 8003cec: f001 fd54 bl 8005798 +} + 8003cf0: b003 add sp, #12 + 8003cf2: bd30 pop {r4, r5, pc} + 8003cf4: 40023000 .word 0x40023000 + +08003cf8 : +// +// Hash up a string of digits into 32-bytes of goodness. +// + static void +pin_hash(const char *pin, int pin_len, uint8_t result[32], uint32_t purpose) +{ + 8003cf8: b570 push {r4, r5, r6, lr} + 8003cfa: b096 sub sp, #88 ; 0x58 + ASSERT(pin_len <= MAX_PIN_LEN); + 8003cfc: 2920 cmp r1, #32 +{ + 8003cfe: 4606 mov r6, r0 + 8003d00: 460d mov r5, r1 + 8003d02: 4614 mov r4, r2 + 8003d04: 9301 str r3, [sp, #4] + ASSERT(pin_len <= MAX_PIN_LEN); + 8003d06: dd02 ble.n 8003d0e + 8003d08: 4817 ldr r0, [pc, #92] ; (8003d68 ) + 8003d0a: f7fc fe95 bl 8000a38 + + if(pin_len == 0) { + 8003d0e: b929 cbnz r1, 8003d1c + // zero-length PIN is considered the "blank" one: all zero + memset(result, 0, 32); + 8003d10: 2220 movs r2, #32 + 8003d12: 4620 mov r0, r4 + 8003d14: f009 fe06 bl 800d924 + // and run that thru SE2 as well + se2_pin_hash(result, purpose); + + // and a second-sha256 on that, just in case. + sha256_single(result, 32, result); +} + 8003d18: b016 add sp, #88 ; 0x58 + 8003d1a: bd70 pop {r4, r5, r6, pc} + sha256_init(&ctx); + 8003d1c: a803 add r0, sp, #12 + 8003d1e: f001 fd2d bl 800577c + sha256_update(&ctx, rom_secrets->hash_cache_secret, 32); + 8003d22: a803 add r0, sp, #12 + 8003d24: 4911 ldr r1, [pc, #68] ; (8003d6c ) + 8003d26: 2220 movs r2, #32 + 8003d28: f001 fd36 bl 8005798 + sha256_update(&ctx, (uint8_t *)&purpose, 4); + 8003d2c: 2204 movs r2, #4 + 8003d2e: eb0d 0102 add.w r1, sp, r2 + 8003d32: a803 add r0, sp, #12 + 8003d34: f001 fd30 bl 8005798 + sha256_update(&ctx, (uint8_t *)pin, pin_len); + 8003d38: 462a mov r2, r5 + 8003d3a: 4631 mov r1, r6 + 8003d3c: a803 add r0, sp, #12 + 8003d3e: f001 fd2b bl 8005798 + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003d42: 2220 movs r2, #32 + 8003d44: a803 add r0, sp, #12 + 8003d46: 490a ldr r1, [pc, #40] ; (8003d70 ) + 8003d48: f001 fd26 bl 8005798 + sha256_final(&ctx, result); + 8003d4c: 4621 mov r1, r4 + 8003d4e: a803 add r0, sp, #12 + 8003d50: f001 fd68 bl 8005824 + se2_pin_hash(result, purpose); + 8003d54: 9901 ldr r1, [sp, #4] + 8003d56: 4620 mov r0, r4 + 8003d58: f004 fc46 bl 80085e8 + sha256_single(result, 32, result); + 8003d5c: 4622 mov r2, r4 + 8003d5e: 2120 movs r1, #32 + 8003d60: 4620 mov r0, r4 + 8003d62: f001 fd73 bl 800584c + 8003d66: e7d7 b.n 8003d18 + 8003d68: 0801046c .word 0x0801046c + 8003d6c: 0801c070 .word 0x0801c070 + 8003d70: 0801c000 .word 0x0801c000 + +08003d74 <_hmac_attempt>: +// +// Maybe should be proper HMAC from fips std? Can be changed later. +// + static void +_hmac_attempt(const pinAttempt_t *args, uint8_t result[32]) +{ + 8003d74: b530 push {r4, r5, lr} + 8003d76: b095 sub sp, #84 ; 0x54 + 8003d78: 4604 mov r4, r0 + SHA256_CTX ctx; + + sha256_init(&ctx); + 8003d7a: a801 add r0, sp, #4 +{ + 8003d7c: 460d mov r5, r1 + sha256_init(&ctx); + 8003d7e: f001 fcfd bl 800577c + sha256_update(&ctx, rom_secrets->pairing_secret, 32); + 8003d82: 4911 ldr r1, [pc, #68] ; (8003dc8 <_hmac_attempt+0x54>) + 8003d84: 2220 movs r2, #32 + 8003d86: a801 add r0, sp, #4 + 8003d88: f001 fd06 bl 8005798 + reboot_nonce(&ctx); + 8003d8c: a801 add r0, sp, #4 + 8003d8e: f7ff ff9d bl 8003ccc + sha256_update(&ctx, (uint8_t *)args, offsetof(pinAttempt_t, hmac)); + 8003d92: 2244 movs r2, #68 ; 0x44 + 8003d94: 4621 mov r1, r4 + 8003d96: a801 add r0, sp, #4 + 8003d98: f001 fcfe bl 8005798 + + if(args->magic_value == PA_MAGIC_V2) { + 8003d9c: 6822 ldr r2, [r4, #0] + 8003d9e: 4b0b ldr r3, [pc, #44] ; (8003dcc <_hmac_attempt+0x58>) + 8003da0: 429a cmp r2, r3 + 8003da2: d105 bne.n 8003db0 <_hmac_attempt+0x3c> + sha256_update(&ctx, (uint8_t *)args->cached_main_pin, + 8003da4: 2220 movs r2, #32 + 8003da6: f104 01f8 add.w r1, r4, #248 ; 0xf8 + 8003daa: a801 add r0, sp, #4 + 8003dac: f001 fcf4 bl 8005798 + msizeof(pinAttempt_t, cached_main_pin)); + } + + sha256_final(&ctx, result); + 8003db0: 4629 mov r1, r5 + 8003db2: a801 add r0, sp, #4 + 8003db4: f001 fd36 bl 8005824 + + // and a second-sha256 on that, just in case. + sha256_single(result, 32, result); + 8003db8: 462a mov r2, r5 + 8003dba: 2120 movs r1, #32 + 8003dbc: 4628 mov r0, r5 + 8003dbe: f001 fd45 bl 800584c +} + 8003dc2: b015 add sp, #84 ; 0x54 + 8003dc4: bd30 pop {r4, r5, pc} + 8003dc6: bf00 nop + 8003dc8: 0801c000 .word 0x0801c000 + 8003dcc: 2eaf6312 .word 0x2eaf6312 + +08003dd0 <_validate_attempt>: + +// _validate_attempt() +// + static int +_validate_attempt(const pinAttempt_t *args, bool first_time) +{ + 8003dd0: b510 push {r4, lr} + 8003dd2: 4604 mov r4, r0 + 8003dd4: b088 sub sp, #32 + if(first_time) { + 8003dd6: b969 cbnz r1, 8003df4 <_validate_attempt+0x24> + // no hmac needed for setup call + } else { + // if hmac is defined, better be right. + uint8_t actual[32]; + + _hmac_attempt(args, actual); + 8003dd8: 4669 mov r1, sp + 8003dda: f7ff ffcb bl 8003d74 <_hmac_attempt> + + if(!check_equal(actual, args->hmac, 32)) { + 8003dde: 2220 movs r2, #32 + 8003de0: f104 0144 add.w r1, r4, #68 ; 0x44 + 8003de4: 4668 mov r0, sp + 8003de6: f7fe fd2a bl 800283e + 8003dea: b918 cbnz r0, 8003df4 <_validate_attempt+0x24> + // hmac is wrong? + return EPIN_HMAC_FAIL; + 8003dec: f06f 0063 mvn.w r0, #99 ; 0x63 + if((args->change_flags & CHANGE__MASK) != args->change_flags) return EPIN_RANGE_ERR; + + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + + return 0; +} + 8003df0: b008 add sp, #32 + 8003df2: bd10 pop {r4, pc} + if(args->magic_value == PA_MAGIC_V2) { + 8003df4: 6822 ldr r2, [r4, #0] + 8003df6: 4b10 ldr r3, [pc, #64] ; (8003e38 <_validate_attempt+0x68>) + 8003df8: 429a cmp r2, r3 + 8003dfa: d117 bne.n 8003e2c <_validate_attempt+0x5c> + if(args->pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003dfc: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8003dfe: 2b20 cmp r3, #32 + 8003e00: dc17 bgt.n 8003e32 <_validate_attempt+0x62> + if(args->old_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003e02: f8d4 3088 ldr.w r3, [r4, #136] ; 0x88 + 8003e06: 2b20 cmp r3, #32 + 8003e08: dc13 bgt.n 8003e32 <_validate_attempt+0x62> + if(args->new_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR; + 8003e0a: f8d4 30ac ldr.w r3, [r4, #172] ; 0xac + 8003e0e: 2b20 cmp r3, #32 + 8003e10: dc0f bgt.n 8003e32 <_validate_attempt+0x62> + if((args->change_flags & CHANGE__MASK) != args->change_flags) return EPIN_RANGE_ERR; + 8003e12: 6e63 ldr r3, [r4, #100] ; 0x64 + 8003e14: f640 727f movw r2, #3967 ; 0xf7f + 8003e18: 4393 bics r3, r2 + 8003e1a: d10a bne.n 8003e32 <_validate_attempt+0x62> + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + 8003e1c: 6863 ldr r3, [r4, #4] + return 0; + 8003e1e: f033 0301 bics.w r3, r3, #1 + 8003e22: bf14 ite ne + 8003e24: f06f 0066 mvnne.w r0, #102 ; 0x66 + 8003e28: 2000 moveq r0, #0 + 8003e2a: e7e1 b.n 8003df0 <_validate_attempt+0x20> + return EPIN_BAD_MAGIC; + 8003e2c: f06f 0065 mvn.w r0, #101 ; 0x65 + 8003e30: e7de b.n 8003df0 <_validate_attempt+0x20> + if((args->is_secondary & 0x1) != args->is_secondary) return EPIN_RANGE_ERR; + 8003e32: f06f 0066 mvn.w r0, #102 ; 0x66 + 8003e36: e7db b.n 8003df0 <_validate_attempt+0x20> + 8003e38: 2eaf6312 .word 0x2eaf6312 + +08003e3c : + +// warmup_ae() +// + static int +warmup_ae(void) +{ + 8003e3c: b510 push {r4, lr} + ae_setup(); + 8003e3e: f7fe fe87 bl 8002b50 + 8003e42: 2405 movs r4, #5 + + for(int retry=0; retry<5; retry++) { + if(!ae_probe()) break; + 8003e44: f7ff f90c bl 8003060 + 8003e48: b108 cbz r0, 8003e4e + for(int retry=0; retry<5; retry++) { + 8003e4a: 3c01 subs r4, #1 + 8003e4c: d1fa bne.n 8003e44 + } + + if(ae_pair_unlock()) return -1; + 8003e4e: f7ff f875 bl 8002f3c + 8003e52: 4604 mov r4, r0 + 8003e54: b918 cbnz r0, 8003e5e + + // reset watchdog timer + ae_keep_alive(); + 8003e56: f7fe fead bl 8002bb4 + + return 0; +} + 8003e5a: 4620 mov r0, r4 + 8003e5c: bd10 pop {r4, pc} + if(ae_pair_unlock()) return -1; + 8003e5e: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 8003e62: e7fa b.n 8003e5a + +08003e64 <_read_slot_as_counter>: +{ + 8003e64: b530 push {r4, r5, lr} + 8003e66: b091 sub sp, #68 ; 0x44 + uint32_t padded[32/4] = { 0 }; + 8003e68: 2220 movs r2, #32 +{ + 8003e6a: 4604 mov r4, r0 + 8003e6c: 460d mov r5, r1 + uint32_t padded[32/4] = { 0 }; + 8003e6e: 4668 mov r0, sp + 8003e70: 2100 movs r1, #0 + 8003e72: f009 fd57 bl 800d924 + ae_pair_unlock(); + 8003e76: f7ff f861 bl 8002f3c + if(ae_read_data_slot(slot, (uint8_t *)padded, 32)) return -1; + 8003e7a: 2220 movs r2, #32 + 8003e7c: 4669 mov r1, sp + 8003e7e: 4620 mov r0, r4 + 8003e80: f7ff fba2 bl 80035c8 + 8003e84: b120 cbz r0, 8003e90 <_read_slot_as_counter+0x2c> + 8003e86: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff +} + 8003e8a: 4620 mov r0, r4 + 8003e8c: b011 add sp, #68 ; 0x44 + 8003e8e: bd30 pop {r4, r5, pc} + ae_pair_unlock(); + 8003e90: f7ff f854 bl 8002f3c + if(ae_gendig_slot(slot, (const uint8_t *)padded, tempkey)) return -1; + 8003e94: 4620 mov r0, r4 + 8003e96: aa08 add r2, sp, #32 + 8003e98: 4669 mov r1, sp + 8003e9a: f7ff f96f bl 800317c + 8003e9e: 4604 mov r4, r0 + 8003ea0: 2800 cmp r0, #0 + 8003ea2: d1f0 bne.n 8003e86 <_read_slot_as_counter+0x22> + if(!ae_is_correct_tempkey(tempkey)) fatal_mitm(); + 8003ea4: a808 add r0, sp, #32 + 8003ea6: f7fe ff79 bl 8002d9c + 8003eaa: b908 cbnz r0, 8003eb0 <_read_slot_as_counter+0x4c> + 8003eac: f7fc fdce bl 8000a4c + *dest = padded[0]; + 8003eb0: 9b00 ldr r3, [sp, #0] + 8003eb2: 602b str r3, [r5, #0] + return 0; + 8003eb4: e7e9 b.n 8003e8a <_read_slot_as_counter+0x26> + +08003eb6 : +{ + 8003eb6: b530 push {r4, r5, lr} + 8003eb8: b095 sub sp, #84 ; 0x54 + 8003eba: 4605 mov r5, r0 + ae_pair_unlock(); + 8003ebc: f7ff f83e bl 8002f3c + uint32_t padded[32/4] = { 0 }; + 8003ec0: 2220 movs r2, #32 + 8003ec2: 2100 movs r1, #0 + 8003ec4: a804 add r0, sp, #16 + 8003ec6: f009 fd2d bl 800d924 + if(ae_read_data_slot(slot, (uint8_t *)padded, 32)) return -1; + 8003eca: 2220 movs r2, #32 + 8003ecc: a904 add r1, sp, #16 + 8003ece: 2005 movs r0, #5 + 8003ed0: f7ff fb7a bl 80035c8 + 8003ed4: b118 cbz r0, 8003ede + 8003ed6: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff +} + 8003eda: b015 add sp, #84 ; 0x54 + 8003edc: bd30 pop {r4, r5, pc} + ae_pair_unlock(); + 8003ede: f7ff f82d bl 8002f3c + if(ae_gendig_slot(slot, (const uint8_t *)padded, tempkey)) return -1; + 8003ee2: aa0c add r2, sp, #48 ; 0x30 + 8003ee4: a904 add r1, sp, #16 + 8003ee6: 2005 movs r0, #5 + 8003ee8: f7ff f948 bl 800317c + 8003eec: 4604 mov r4, r0 + 8003eee: 2800 cmp r0, #0 + 8003ef0: d1f1 bne.n 8003ed6 + if(!ae_is_correct_tempkey(tempkey)) fatal_mitm(); + 8003ef2: a80c add r0, sp, #48 ; 0x30 + 8003ef4: f7fe ff52 bl 8002d9c + 8003ef8: b908 cbnz r0, 8003efe + 8003efa: f7fc fda7 bl 8000a4c + if(_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -1; + 8003efe: a901 add r1, sp, #4 + 8003f00: 2005 movs r0, #5 + uint32_t lastgood=0, match_count=0, counter=0; + 8003f02: e9cd 4401 strd r4, r4, [sp, #4] + 8003f06: 9403 str r4, [sp, #12] + if(_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -1; + 8003f08: f7ff ffac bl 8003e64 <_read_slot_as_counter> + 8003f0c: 2800 cmp r0, #0 + 8003f0e: d1e2 bne.n 8003ed6 + if(_read_slot_as_counter(KEYNUM_match_count, &match_count)) return -1; + 8003f10: a902 add r1, sp, #8 + 8003f12: 2006 movs r0, #6 + 8003f14: f7ff ffa6 bl 8003e64 <_read_slot_as_counter> + 8003f18: 4601 mov r1, r0 + 8003f1a: 2800 cmp r0, #0 + 8003f1c: d1db bne.n 8003ed6 + if(ae_get_counter(&counter, 0)) return -1; + 8003f1e: a803 add r0, sp, #12 + 8003f20: f7ff fa07 bl 8003332 + 8003f24: 2800 cmp r0, #0 + 8003f26: d1d6 bne.n 8003ed6 + if(lastgood > counter) { + 8003f28: 9a01 ldr r2, [sp, #4] + 8003f2a: 9903 ldr r1, [sp, #12] + match_count &= ~31; + 8003f2c: 9b02 ldr r3, [sp, #8] + if(lastgood > counter) { + 8003f2e: 428a cmp r2, r1 + match_count &= ~31; + 8003f30: f023 031f bic.w r3, r3, #31 + args->num_fails = counter - lastgood; + 8003f34: bf94 ite ls + 8003f36: 1a8a subls r2, r1, r2 + args->num_fails = 99; + 8003f38: 2263 movhi r2, #99 ; 0x63 + if(counter < match_count) { + 8003f3a: 4299 cmp r1, r3 + args->attempts_left = match_count - counter; + 8003f3c: bf34 ite cc + 8003f3e: 1a5b subcc r3, r3, r1 + args->attempts_left = 0; + 8003f40: 2300 movcs r3, #0 + 8003f42: 636a str r2, [r5, #52] ; 0x34 + 8003f44: 63ab str r3, [r5, #56] ; 0x38 + 8003f46: e7c8 b.n 8003eda + +08003f48 : + +// updates_for_good_login() +// + static int +updates_for_good_login(uint8_t digest[32]) +{ + 8003f48: b5f0 push {r4, r5, r6, r7, lr} + 8003f4a: b08d sub sp, #52 ; 0x34 + // User got the main PIN right: update the attempt counters, + // to document this (lastgood) and also bump the match counter if needed + + uint32_t count; + int rv = ae_get_counter(&count, 0); + 8003f4c: 2100 movs r1, #0 +{ + 8003f4e: 4606 mov r6, r0 + int rv = ae_get_counter(&count, 0); + 8003f50: a802 add r0, sp, #8 + 8003f52: f7ff f9ee bl 8003332 + if(rv) goto fail; + 8003f56: 4601 mov r1, r0 + 8003f58: 2800 cmp r0, #0 + 8003f5a: d13b bne.n 8003fd4 + + // Challenge: Have to update both the counter, and the target match value because + // no other way to have exact value. + + uint32_t mc = (count + MAX_TARGET_ATTEMPTS + 32) & ~31; + 8003f5c: 9b02 ldr r3, [sp, #8] + 8003f5e: f103 042d add.w r4, r3, #45 ; 0x2d + 8003f62: f024 041f bic.w r4, r4, #31 + ASSERT(mc >= count); + 8003f66: 42a3 cmp r3, r4 + 8003f68: d902 bls.n 8003f70 + 8003f6a: 481d ldr r0, [pc, #116] ; (8003fe0 ) + 8003f6c: f7fc fd64 bl 8000a38 + + int bump = (mc - MAX_TARGET_ATTEMPTS) - count; + 8003f70: 1ae3 subs r3, r4, r3 + 8003f72: f1a3 050d sub.w r5, r3, #13 + ASSERT(bump >= 1); + 8003f76: 3b0e subs r3, #14 + 8003f78: 2b1f cmp r3, #31 + 8003f7a: d8f6 bhi.n 8003f6a + // Would rather update the counter first, so that a hostile interruption can't increase + // attempts (altho the attacker knows the pin at that point?!) .. but chip won't + // let the counter go past the match value, so that has to be first. + + // set the new "match count" + { uint32_t tmp[32/4] = {mc, mc} ; + 8003f7c: 2218 movs r2, #24 + 8003f7e: eb0d 0002 add.w r0, sp, r2 + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003f82: 2720 movs r7, #32 + { uint32_t tmp[32/4] = {mc, mc} ; + 8003f84: f009 fcce bl 800d924 + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003f88: 2103 movs r1, #3 + 8003f8a: 9700 str r7, [sp, #0] + 8003f8c: ab04 add r3, sp, #16 + 8003f8e: 4632 mov r2, r6 + 8003f90: 2006 movs r0, #6 + { uint32_t tmp[32/4] = {mc, mc} ; + 8003f92: e9cd 4404 strd r4, r4, [sp, #16] + rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003f96: f7ff fae1 bl 800355c + if(rv) goto fail; + 8003f9a: 4601 mov r1, r0 + 8003f9c: b9d0 cbnz r0, 8003fd4 + } + + // incr the counter a bunch to get to that-13 + uint32_t new_count = 0; + 8003f9e: 9003 str r0, [sp, #12] + rv = ae_add_counter(&new_count, 0, bump); + 8003fa0: 462a mov r2, r5 + 8003fa2: a803 add r0, sp, #12 + 8003fa4: f7ff f9e4 bl 8003370 + if(rv) goto fail; + 8003fa8: 4601 mov r1, r0 + 8003faa: b998 cbnz r0, 8003fd4 + + ASSERT(new_count == count + bump); + 8003fac: 9b02 ldr r3, [sp, #8] + 8003fae: 441d add r5, r3 + 8003fb0: 9b03 ldr r3, [sp, #12] + 8003fb2: 429d cmp r5, r3 + 8003fb4: d1d9 bne.n 8003f6a + ASSERT(mc > new_count); + 8003fb6: 42a5 cmp r5, r4 + 8003fb8: d2d7 bcs.n 8003f6a + + // Update the "last good" counter + { uint32_t tmp[32/4] = {new_count, 0 }; + 8003fba: 221c movs r2, #28 + 8003fbc: a805 add r0, sp, #20 + 8003fbe: f009 fcb1 bl 800d924 + rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003fc2: 9700 str r7, [sp, #0] + 8003fc4: ab04 add r3, sp, #16 + 8003fc6: 4632 mov r2, r6 + 8003fc8: 2103 movs r1, #3 + 8003fca: 2005 movs r0, #5 + { uint32_t tmp[32/4] = {new_count, 0 }; + 8003fcc: 9504 str r5, [sp, #16] + rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32); + 8003fce: f7ff fac5 bl 800355c + if(rv) goto fail; + 8003fd2: b118 cbz r0, 8003fdc + // just be reducing attempts. + + return 0; + +fail: + ae_reset_chip(); + 8003fd4: f7fe fdae bl 8002b34 + return EPIN_AE_FAIL; + 8003fd8: f06f 0069 mvn.w r0, #105 ; 0x69 +} + 8003fdc: b00d add sp, #52 ; 0x34 + 8003fde: bdf0 pop {r4, r5, r6, r7, pc} + 8003fe0: 0801046c .word 0x0801046c + +08003fe4 : +{ + 8003fe4: b5f0 push {r4, r5, r6, r7, lr} + 8003fe6: 4615 mov r5, r2 + 8003fe8: b089 sub sp, #36 ; 0x24 + if(pin_len == 0) { + 8003fea: 460c mov r4, r1 + 8003fec: b931 cbnz r1, 8003ffc + memset(result, 0, 32); + 8003fee: 2220 movs r2, #32 + 8003ff0: 4628 mov r0, r5 + 8003ff2: f009 fc97 bl 800d924 +} + 8003ff6: 4620 mov r0, r4 + 8003ff8: b009 add sp, #36 ; 0x24 + 8003ffa: bdf0 pop {r4, r5, r6, r7, pc} + pin_hash(pin, pin_len, tmp, PIN_PURPOSE_NORMAL); + 8003ffc: 4b0f ldr r3, [pc, #60] ; (800403c ) + 8003ffe: 466a mov r2, sp + 8004000: f7ff fe7a bl 8003cf8 + int rv = ae_stretch_iter(tmp, result, KDF_ITER_PIN); + 8004004: 2208 movs r2, #8 + 8004006: 4629 mov r1, r5 + 8004008: 4668 mov r0, sp + 800400a: f7ff fcbf bl 800398c + if(rv) return EPIN_AE_FAIL; + 800400e: 4604 mov r4, r0 + 8004010: b988 cbnz r0, 8004036 + memcpy(tmp, result, 32); + 8004012: 462b mov r3, r5 + 8004014: 466e mov r6, sp + 8004016: f105 0720 add.w r7, r5, #32 + 800401a: 6818 ldr r0, [r3, #0] + 800401c: 6859 ldr r1, [r3, #4] + 800401e: 4632 mov r2, r6 + 8004020: c203 stmia r2!, {r0, r1} + 8004022: 3308 adds r3, #8 + 8004024: 42bb cmp r3, r7 + 8004026: 4616 mov r6, r2 + 8004028: d1f7 bne.n 800401a + ae_mixin_key(KEYNUM_pin_attempt, tmp, result); + 800402a: 462a mov r2, r5 + 800402c: 4669 mov r1, sp + 800402e: 2004 movs r0, #4 + 8004030: f7ff fcd4 bl 80039dc + return 0; + 8004034: e7df b.n 8003ff6 + if(rv) return EPIN_AE_FAIL; + 8004036: f06f 0469 mvn.w r4, #105 ; 0x69 + 800403a: e7dc b.n 8003ff6 + 800403c: 334d1858 .word 0x334d1858 + +08004040 : +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8004040: b5f0 push {r4, r5, r6, r7, lr} + args->delay_achieved = slot->tc_arg; + 8004042: 88cb ldrh r3, [r1, #6] + 8004044: 62c3 str r3, [r0, #44] ; 0x2c +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8004046: f5ad 7d0d sub.w sp, sp, #564 ; 0x234 + memcpy(key, &args->private_state, sizeof(args->private_state)); + 800404a: 6c03 ldr r3, [r0, #64] ; 0x40 + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 800404c: 4d0f ldr r5, [pc, #60] ; (800408c ) + memcpy(key, &args->private_state, sizeof(args->private_state)); + 800404e: 9303 str r3, [sp, #12] +set_is_trick(pinAttempt_t *args, const trick_slot_t *slot) + 8004050: 4606 mov r6, r0 + 8004052: 460f mov r7, r1 + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8004054: cd0f ldmia r5!, {r0, r1, r2, r3} + 8004056: ac04 add r4, sp, #16 + 8004058: c40f stmia r4!, {r0, r1, r2, r3} + 800405a: e895 0007 ldmia.w r5, {r0, r1, r2} + 800405e: e884 0007 stmia.w r4, {r0, r1, r2} + aes_init(&ctx); + 8004062: a80b add r0, sp, #44 ; 0x2c + 8004064: f004 fb6e bl 8008744 + aes_add(&ctx, (uint8_t *)slot, 32); + 8004068: 4639 mov r1, r7 + 800406a: a80b add r0, sp, #44 ; 0x2c + 800406c: 2220 movs r2, #32 + 800406e: f004 fb6f bl 8008750 + aes_done(&ctx, args->cached_main_pin, 32, key, NULL); + 8004072: 2300 movs r3, #0 + 8004074: 9300 str r3, [sp, #0] + 8004076: 2220 movs r2, #32 + 8004078: ab03 add r3, sp, #12 + 800407a: f106 01f8 add.w r1, r6, #248 ; 0xf8 + 800407e: a80b add r0, sp, #44 ; 0x2c + 8004080: f004 fb7c bl 800877c +} + 8004084: f50d 7d0d add.w sp, sp, #564 ; 0x234 + 8004088: bdf0 pop {r4, r5, r6, r7, pc} + 800408a: bf00 nop + 800408c: 0801c074 .word 0x0801c074 + +08004090 : + __HAL_RCC_CRC_CLK_ENABLE(); + 8004090: 4b09 ldr r3, [pc, #36] ; (80040b8 ) + 8004092: 6c9a ldr r2, [r3, #72] ; 0x48 +{ + 8004094: b513 push {r0, r1, r4, lr} + __HAL_RCC_CRC_CLK_ENABLE(); + 8004096: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 800409a: 649a str r2, [r3, #72] ; 0x48 + 800409c: 6c9b ldr r3, [r3, #72] ; 0x48 + CRC->INIT = rng_sample(); + 800409e: 4c07 ldr r4, [pc, #28] ; (80040bc ) + __HAL_RCC_CRC_CLK_ENABLE(); + 80040a0: f403 5380 and.w r3, r3, #4096 ; 0x1000 + 80040a4: 9301 str r3, [sp, #4] + 80040a6: 9b01 ldr r3, [sp, #4] + CRC->INIT = rng_sample(); + 80040a8: f7fe fbda bl 8002860 + 80040ac: 6120 str r0, [r4, #16] + CRC->POL = rng_sample(); + 80040ae: f7fe fbd7 bl 8002860 + 80040b2: 6160 str r0, [r4, #20] +} + 80040b4: b002 add sp, #8 + 80040b6: bd10 pop {r4, pc} + 80040b8: 40021000 .word 0x40021000 + 80040bc: 40023000 .word 0x40023000 + +080040c0 : +{ + 80040c0: b510 push {r4, lr} + 80040c2: b094 sub sp, #80 ; 0x50 + 80040c4: 4604 mov r4, r0 + sha256_init(&ctx); + 80040c6: a801 add r0, sp, #4 + 80040c8: f001 fb58 bl 800577c + reboot_nonce(&ctx); + 80040cc: a801 add r0, sp, #4 + 80040ce: f7ff fdfd bl 8003ccc + sha256_update(&ctx, rom_secrets->hash_cache_secret, 32); + 80040d2: 2220 movs r2, #32 + 80040d4: a801 add r0, sp, #4 + 80040d6: 4904 ldr r1, [pc, #16] ; (80040e8 ) + 80040d8: f001 fb5e bl 8005798 + sha256_final(&ctx, key); + 80040dc: 4621 mov r1, r4 + 80040de: a801 add r0, sp, #4 + 80040e0: f001 fba0 bl 8005824 +} + 80040e4: b014 add sp, #80 ; 0x50 + 80040e6: bd10 pop {r4, pc} + 80040e8: 0801c070 .word 0x0801c070 + +080040ec : +{ + 80040ec: b530 push {r4, r5, lr} + 80040ee: 460d mov r5, r1 + 80040f0: b089 sub sp, #36 ; 0x24 + 80040f2: 4604 mov r4, r0 + if(!check_all_zeros(digest, 32)) { + 80040f4: 2120 movs r1, #32 + 80040f6: 4628 mov r0, r5 + 80040f8: f7fe fb92 bl 8002820 + 80040fc: b9a0 cbnz r0, 8004128 + pin_cache_get_key(value); + 80040fe: 4668 mov r0, sp + 8004100: f7ff ffde bl 80040c0 + 8004104: 466b mov r3, sp + 8004106: f105 0120 add.w r1, r5, #32 + *(acc) ^= *(more); + 800410a: 781a ldrb r2, [r3, #0] + 800410c: f815 0b01 ldrb.w r0, [r5], #1 + 8004110: 4042 eors r2, r0 + for(; len; len--, more++, acc++) { + 8004112: 428d cmp r5, r1 + *(acc) ^= *(more); + 8004114: f803 2b01 strb.w r2, [r3], #1 + for(; len; len--, more++, acc++) { + 8004118: d1f7 bne.n 800410a + ASSERT(args->magic_value == PA_MAGIC_V2); + 800411a: 6822 ldr r2, [r4, #0] + 800411c: 4b0d ldr r3, [pc, #52] ; (8004154 ) + 800411e: 429a cmp r2, r3 + 8004120: d008 beq.n 8004134 + 8004122: 480d ldr r0, [pc, #52] ; (8004158 ) + 8004124: f7fc fc88 bl 8000a38 + memset(value, 0, 32); + 8004128: 2220 movs r2, #32 + 800412a: 2100 movs r1, #0 + 800412c: 4668 mov r0, sp + 800412e: f009 fbf9 bl 800d924 + 8004132: e7f2 b.n 800411a + memcpy(args->cached_main_pin, value, 32); + 8004134: 466b mov r3, sp + 8004136: f104 02f8 add.w r2, r4, #248 ; 0xf8 + 800413a: ad08 add r5, sp, #32 + 800413c: 461c mov r4, r3 + 800413e: cc03 ldmia r4!, {r0, r1} + 8004140: 42ac cmp r4, r5 + 8004142: 6010 str r0, [r2, #0] + 8004144: 6051 str r1, [r2, #4] + 8004146: 4623 mov r3, r4 + 8004148: f102 0208 add.w r2, r2, #8 + 800414c: d1f6 bne.n 800413c +} + 800414e: b009 add sp, #36 ; 0x24 + 8004150: bd30 pop {r4, r5, pc} + 8004152: bf00 nop + 8004154: 2eaf6312 .word 0x2eaf6312 + 8004158: 0801046c .word 0x0801046c + +0800415c : +{ + 800415c: b510 push {r4, lr} + ASSERT(args->magic_value == PA_MAGIC_V2); + 800415e: 6802 ldr r2, [r0, #0] + 8004160: 4b14 ldr r3, [pc, #80] ; (80041b4 ) + 8004162: 429a cmp r2, r3 +{ + 8004164: b088 sub sp, #32 + 8004166: 460c mov r4, r1 + ASSERT(args->magic_value == PA_MAGIC_V2); + 8004168: d002 beq.n 8004170 + 800416a: 4813 ldr r0, [pc, #76] ; (80041b8 ) + 800416c: f7fc fc64 bl 8000a38 + memcpy(digest, args->cached_main_pin, 32); + 8004170: f100 03f8 add.w r3, r0, #248 ; 0xf8 + 8004174: 460a mov r2, r1 + 8004176: f500 708c add.w r0, r0, #280 ; 0x118 + 800417a: f853 1b04 ldr.w r1, [r3], #4 + 800417e: f842 1b04 str.w r1, [r2], #4 + 8004182: 4283 cmp r3, r0 + 8004184: d1f9 bne.n 800417a + if(!check_all_zeros(digest, 32)) { + 8004186: 2120 movs r1, #32 + 8004188: 4620 mov r0, r4 + 800418a: f7fe fb49 bl 8002820 + 800418e: b970 cbnz r0, 80041ae + pin_cache_get_key(key); + 8004190: 4668 mov r0, sp + 8004192: f7ff ff95 bl 80040c0 + 8004196: 1e62 subs r2, r4, #1 + 8004198: 466b mov r3, sp + 800419a: 341f adds r4, #31 + *(acc) ^= *(more); + 800419c: f812 1f01 ldrb.w r1, [r2, #1]! + 80041a0: f813 0b01 ldrb.w r0, [r3], #1 + for(; len; len--, more++, acc++) { + 80041a4: 42a2 cmp r2, r4 + *(acc) ^= *(more); + 80041a6: ea81 0100 eor.w r1, r1, r0 + 80041aa: 7011 strb r1, [r2, #0] + for(; len; len--, more++, acc++) { + 80041ac: d1f6 bne.n 800419c +} + 80041ae: b008 add sp, #32 + 80041b0: bd10 pop {r4, pc} + 80041b2: bf00 nop + 80041b4: 2eaf6312 .word 0x2eaf6312 + 80041b8: 0801046c .word 0x0801046c + +080041bc : +{ + 80041bc: b530 push {r4, r5, lr} + 80041be: b091 sub sp, #68 ; 0x44 + pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_WORDS); + 80041c0: 4b0b ldr r3, [pc, #44] ; (80041f0 ) +{ + 80041c2: 4615 mov r5, r2 + pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_WORDS); + 80041c4: 466a mov r2, sp + 80041c6: f7ff fd97 bl 8003cf8 + ae_setup(); + 80041ca: f7fe fcc1 bl 8002b50 + int rv = ae_stretch_iter(tmp, digest, KDF_ITER_WORDS); + 80041ce: 2206 movs r2, #6 + 80041d0: a908 add r1, sp, #32 + 80041d2: 4668 mov r0, sp + 80041d4: f7ff fbda bl 800398c + 80041d8: 4604 mov r4, r0 + ae_reset_chip(); + 80041da: f7fe fcab bl 8002b34 + if(rv) return -1; + 80041de: b924 cbnz r4, 80041ea + memcpy(result, digest, 4); + 80041e0: 9b08 ldr r3, [sp, #32] + 80041e2: 602b str r3, [r5, #0] +} + 80041e4: 4620 mov r0, r4 + 80041e6: b011 add sp, #68 ; 0x44 + 80041e8: bd30 pop {r4, r5, pc} + if(rv) return -1; + 80041ea: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff + 80041ee: e7f9 b.n 80041e4 + 80041f0: 2e6d6773 .word 0x2e6d6773 + +080041f4 : +} + 80041f4: 2000 movs r0, #0 + 80041f6: 4770 bx lr + +080041f8 : +{ + 80041f8: b5f0 push {r4, r5, r6, r7, lr} + int rv = _validate_attempt(args, true); + 80041fa: 2101 movs r1, #1 +{ + 80041fc: b091 sub sp, #68 ; 0x44 + 80041fe: 4605 mov r5, r0 + int rv = _validate_attempt(args, true); + 8004200: f7ff fde6 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 8004204: 4604 mov r4, r0 + 8004206: bb28 cbnz r0, 8004254 + if(args->is_secondary) { + 8004208: 686b ldr r3, [r5, #4] + 800420a: 2b00 cmp r3, #0 + 800420c: d158 bne.n 80042c0 + int pin_len = args->pin_len; + 800420e: 6aaf ldr r7, [r5, #40] ; 0x28 + memcpy(pin_copy, args->pin, pin_len); + 8004210: f105 0608 add.w r6, r5, #8 + 8004214: 463a mov r2, r7 + 8004216: 4631 mov r1, r6 + 8004218: 4668 mov r0, sp + 800421a: f009 fb75 bl 800d908 + memset(args, 0, PIN_ATTEMPT_SIZE_V2); + 800421e: f44f 728c mov.w r2, #280 ; 0x118 + 8004222: 4621 mov r1, r4 + 8004224: 4628 mov r0, r5 + 8004226: f009 fb7d bl 800d924 + args->magic_value = PA_MAGIC_V2; + 800422a: 4b28 ldr r3, [pc, #160] ; (80042cc ) + 800422c: 602b str r3, [r5, #0] + memcpy(args->pin, pin_copy, pin_len); + 800422e: 463a mov r2, r7 + 8004230: 4669 mov r1, sp + args->pin_len = pin_len; + 8004232: 62af str r7, [r5, #40] ; 0x28 + memcpy(args->pin, pin_copy, pin_len); + 8004234: 4630 mov r0, r6 + 8004236: f009 fb67 bl 800d908 + if(warmup_ae()) { + 800423a: f7ff fdff bl 8003e3c + 800423e: 2800 cmp r0, #0 + 8004240: d141 bne.n 80042c6 + if(get_last_success(args)) { + 8004242: 4628 mov r0, r5 + 8004244: f7ff fe37 bl 8003eb6 + 8004248: 4604 mov r4, r0 + 800424a: b130 cbz r0, 800425a + ae_reset_chip(); + 800424c: f7fe fc72 bl 8002b34 + return EPIN_AE_FAIL; + 8004250: f06f 0469 mvn.w r4, #105 ; 0x69 +} + 8004254: 4620 mov r0, r4 + 8004256: b011 add sp, #68 ; 0x44 + 8004258: bdf0 pop {r4, r5, r6, r7, pc} + uint8_t blank[32] = {0}; + 800425a: 4601 mov r1, r0 + 800425c: 221c movs r2, #28 + args->delay_achieved = 0; + 800425e: e9c5 000b strd r0, r0, [r5, #44] ; 0x2c + uint8_t blank[32] = {0}; + 8004262: 9008 str r0, [sp, #32] + 8004264: a809 add r0, sp, #36 ; 0x24 + 8004266: f009 fb5d bl 800d924 + ae_reset_chip(); + 800426a: f7fe fc63 bl 8002b34 + ae_pair_unlock(); + 800426e: f7fe fe65 bl 8002f3c + int is_blank = (ae_checkmac_hard(keynum, blank) == 0); + 8004272: a908 add r1, sp, #32 + 8004274: 2003 movs r0, #3 + 8004276: f7fe ffef bl 8003258 + 800427a: 4606 mov r6, r0 + ae_reset_chip(); + 800427c: f7fe fc5a bl 8002b34 + if(pin_is_blank(KEYNUM_main_pin)) { + 8004280: b9c6 cbnz r6, 80042b4 + args->state_flags |= PA_SUCCESSFUL | PA_IS_BLANK; + 8004282: 6beb ldr r3, [r5, #60] ; 0x3c + const uint8_t zeros[32] = {0}; + 8004284: 9408 str r4, [sp, #32] + args->state_flags |= PA_SUCCESSFUL | PA_IS_BLANK; + 8004286: f043 0303 orr.w r3, r3, #3 + 800428a: 63eb str r3, [r5, #60] ; 0x3c + const uint8_t zeros[32] = {0}; + 800428c: 221c movs r2, #28 + 800428e: 4621 mov r1, r4 + 8004290: a809 add r0, sp, #36 ; 0x24 + 8004292: f009 fb47 bl 800d924 + pin_cache_save(args, zeros); + 8004296: a908 add r1, sp, #32 + 8004298: 4628 mov r0, r5 + 800429a: f7ff ff27 bl 80040ec + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 800429e: f7fe fadf bl 8002860 + 80042a2: 4b0b ldr r3, [pc, #44] ; (80042d0 ) + 80042a4: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80042a8: f020 0001 bic.w r0, r0, #1 + args->delay_achieved = 0; + 80042ac: e9c5 440b strd r4, r4, [r5, #44] ; 0x2c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 80042b0: 4058 eors r0, r3 + 80042b2: 6428 str r0, [r5, #64] ; 0x40 + _hmac_attempt(args, args->hmac); + 80042b4: f105 0144 add.w r1, r5, #68 ; 0x44 + 80042b8: 4628 mov r0, r5 + 80042ba: f7ff fd5b bl 8003d74 <_hmac_attempt> +} + 80042be: e7c9 b.n 8004254 + return EPIN_PRIMARY_ONLY; + 80042c0: f06f 0471 mvn.w r4, #113 ; 0x71 + 80042c4: e7c6 b.n 8004254 + return EPIN_I_AM_BRICK; + 80042c6: f06f 0468 mvn.w r4, #104 ; 0x68 + 80042ca: e7c3 b.n 8004254 + 80042cc: 2eaf6312 .word 0x2eaf6312 + 80042d0: 0801c000 .word 0x0801c000 + +080042d4 : +} + 80042d4: 2000 movs r0, #0 + 80042d6: 4770 bx lr + +080042d8 : +// +// Do the PIN check, and return a value. Or fail. +// + int +pin_login_attempt(pinAttempt_t *args) +{ + 80042d8: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + bool deltamode = false; + char tmp_pin[32]; + + int rv = _validate_attempt(args, false); + 80042dc: 2100 movs r1, #0 +{ + 80042de: b0c7 sub sp, #284 ; 0x11c + 80042e0: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 80042e2: f7ff fd75 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 80042e6: 4605 mov r5, r0 + 80042e8: 2800 cmp r0, #0 + 80042ea: d179 bne.n 80043e0 + + if(args->state_flags & PA_SUCCESSFUL) { + 80042ec: 6be3 ldr r3, [r4, #60] ; 0x3c + 80042ee: 07d9 lsls r1, r3, #31 + 80042f0: f100 80c5 bmi.w 800447e + } + + // Mk4: Check SE2 first to see if this is a "trick" pin. + // - this call may have side-effects, like wiping keys, bricking, etc. + trick_slot_t slot; + bool is_trick = se2_test_trick_pin(args->pin, args->pin_len, &slot, false); + 80042f4: f104 0808 add.w r8, r4, #8 + 80042f8: 4603 mov r3, r0 + 80042fa: 6aa1 ldr r1, [r4, #40] ; 0x28 + 80042fc: aa26 add r2, sp, #152 ; 0x98 + 80042fe: 4640 mov r0, r8 + 8004300: f003 ff02 bl 8008108 + + if(is_trick) { + 8004304: 4606 mov r6, r0 + 8004306: 2800 cmp r0, #0 + 8004308: d04b beq.n 80043a2 + // Mark as success + args->state_flags = PA_SUCCESSFUL; + args->num_fails = 0; + args->attempts_left = MAX_TARGET_ATTEMPTS; + + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 800430a: f9bd 209c ldrsh.w r2, [sp, #156] ; 0x9c + args->num_fails = 0; + 800430e: 6365 str r5, [r4, #52] ; 0x34 + args->state_flags = PA_SUCCESSFUL; + 8004310: 2301 movs r3, #1 + 8004312: 63e3 str r3, [r4, #60] ; 0x3c + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 8004314: 2a00 cmp r2, #0 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 8004316: f04f 030d mov.w r3, #13 + 800431a: 63a3 str r3, [r4, #56] ; 0x38 + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 800431c: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + 8004320: da4f bge.n 80043c2 + 8004322: f413 5fc0 tst.w r3, #6144 ; 0x1800 + 8004326: bf0c ite eq + 8004328: 2701 moveq r7, #1 + 800432a: 2700 movne r7, #0 + if(check_all_zeros(slot.xdata, 32) || wipe) { + 800432c: 2120 movs r1, #32 + 800432e: a828 add r0, sp, #160 ; 0xa0 + 8004330: f7fe fa76 bl 8002820 + 8004334: b900 cbnz r0, 8004338 + 8004336: b11f cbz r7, 8004340 + args->state_flags |= PA_ZERO_SECRET; + 8004338: 6be3 ldr r3, [r4, #60] ; 0x3c + 800433a: f043 0310 orr.w r3, r3, #16 + 800433e: 63e3 str r3, [r4, #60] ; 0x3c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8004340: f7fe fa8e bl 8002860 + 8004344: 4b51 ldr r3, [pc, #324] ; (800448c ) + 8004346: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 800434a: f040 0001 orr.w r0, r0, #1 + 800434e: 4058 eors r0, r3 + args->delay_required = (slot->tc_flags & ~TC_HIDDEN_MASK); + 8004350: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8004354: 6420 str r0, [r4, #64] ; 0x40 + args->delay_required = (slot->tc_flags & ~TC_HIDDEN_MASK); + 8004356: f423 4278 bic.w r2, r3, #63488 ; 0xf800 + 800435a: 6322 str r2, [r4, #48] ; 0x30 + if(slot->tc_flags & TC_DELTA_MODE) { + 800435c: 055a lsls r2, r3, #21 + 800435e: d532 bpl.n 80043c6 + args->delay_achieved = 0; + 8004360: 2300 movs r3, #0 + 8004362: 62e3 str r3, [r4, #44] ; 0x2c + memcpy(tmp_pin, pin, pin_len); + 8004364: 6aa7 ldr r7, [r4, #40] ; 0x28 + // Thug gave wrong PIN, but we are going to let them + // past (by calculating correct PIN, up to 4 digits different), + // and the mpy firmware can do tricky stuff to protect funds + // even though the private key is known at that point. + deltamode = true; + apply_pin_delta(args->pin, args->pin_len, slot.tc_arg, tmp_pin); + 8004366: f8bd 909e ldrh.w r9, [sp, #158] ; 0x9e + memcpy(tmp_pin, pin, pin_len); + 800436a: ab04 add r3, sp, #16 + 800436c: 463a mov r2, r7 + 800436e: 4641 mov r1, r8 + 8004370: 4618 mov r0, r3 + 8004372: f009 fac9 bl 800d908 + tmp_pin[pin_len] = 0; + 8004376: 2200 movs r2, #0 + 8004378: 55c2 strb r2, [r0, r7] + char *p = &tmp_pin[pin_len-1]; + 800437a: 1e7a subs r2, r7, #1 + 800437c: 4402 add r2, r0 + 800437e: 2104 movs r1, #4 + if(*p == '-') p--; + 8004380: 7813 ldrb r3, [r2, #0] + 8004382: 2b2d cmp r3, #45 ; 0x2d + 8004384: f009 030f and.w r3, r9, #15 + 8004388: bf08 it eq + 800438a: f102 32ff addeq.w r2, r2, #4294967295 ; 0xffffffff + if((here >= 0) && (here <= 9)) { + 800438e: 2b09 cmp r3, #9 + *p = '0' + here; + 8004390: bf9c itt ls + 8004392: 3330 addls r3, #48 ; 0x30 + 8004394: 7013 strbls r3, [r2, #0] + for(int i=0; i<4; i++, p--) { + 8004396: 3901 subs r1, #1 + replacement >>= 4; + 8004398: ea4f 1919 mov.w r9, r9, lsr #4 + for(int i=0; i<4; i++, p--) { + 800439c: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 80043a0: d1ee bne.n 8004380 + return 0; + } + +real_login: + // unlock the AE chip + if(warmup_ae()) return EPIN_I_AM_BRICK; + 80043a2: f7ff fd4b bl 8003e3c + 80043a6: 2800 cmp r0, #0 + 80043a8: d16c bne.n 8004484 + + // hash up the pin now, assuming we'll use it on main PIN + uint8_t digest[32]; + rv = pin_hash_attempt(deltamode ? tmp_pin : args->pin, args->pin_len, digest); + 80043aa: b10e cbz r6, 80043b0 + 80043ac: f10d 0810 add.w r8, sp, #16 + 80043b0: 6aa1 ldr r1, [r4, #40] ; 0x28 + 80043b2: aa0c add r2, sp, #48 ; 0x30 + 80043b4: 4640 mov r0, r8 + 80043b6: f7ff fe15 bl 8003fe4 + if(rv) return EPIN_AE_FAIL; + 80043ba: b1a8 cbz r0, 80043e8 + + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + if(rv) { + ae_reset_chip(); + + return EPIN_AE_FAIL; + 80043bc: f06f 0569 mvn.w r5, #105 ; 0x69 + 80043c0: e00e b.n 80043e0 + bool wipe = (slot.tc_flags & TC_WIPE) && !(slot.tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)); + 80043c2: 462f mov r7, r5 + 80043c4: e7b2 b.n 800432c + 80043c6: a926 add r1, sp, #152 ; 0x98 + 80043c8: 4620 mov r0, r4 + 80043ca: f7ff fe39 bl 8004040 + if(slot.tc_flags & TC_DELTA_MODE) { + 80043ce: f8bd 309c ldrh.w r3, [sp, #156] ; 0x9c + 80043d2: 055b lsls r3, r3, #21 + 80043d4: d4c6 bmi.n 8004364 + _hmac_attempt(args, args->hmac); + 80043d6: f104 0144 add.w r1, r4, #68 ; 0x44 + 80043da: 4620 mov r0, r4 + 80043dc: f7ff fcca bl 8003d74 <_hmac_attempt> + } + + _sign_attempt(args); + + return 0; +} + 80043e0: 4628 mov r0, r5 + 80043e2: b047 add sp, #284 ; 0x11c + 80043e4: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + ae_reset_chip(); + 80043e8: f7fe fba4 bl 8002b34 + ae_pair_unlock(); + 80043ec: f7fe fda6 bl 8002f3c + return (ae_checkmac_hard(KEYNUM_main_pin, digest) == 0); + 80043f0: a90c add r1, sp, #48 ; 0x30 + 80043f2: 2003 movs r0, #3 + 80043f4: f7fe ff30 bl 8003258 + if(!is_main_pin(digest)) { + 80043f8: b130 cbz r0, 8004408 + se2_handle_bad_pin(args->num_fails + 1); + 80043fa: 6b60 ldr r0, [r4, #52] ; 0x34 + 80043fc: 3001 adds r0, #1 + 80043fe: f003 ff5f bl 80082c0 + return EPIN_AUTH_FAIL; + 8004402: f06f 056f mvn.w r5, #111 ; 0x6f + 8004406: e7eb b.n 80043e0 + rv = updates_for_good_login(digest); + 8004408: a80c add r0, sp, #48 ; 0x30 + 800440a: f7ff fd9d bl 8003f48 + if(rv) return EPIN_AE_FAIL; + 800440e: 4607 mov r7, r0 + 8004410: 2800 cmp r0, #0 + 8004412: d1d3 bne.n 80043bc + pin_cache_save(args, digest); + 8004414: a90c add r1, sp, #48 ; 0x30 + 8004416: 4620 mov r0, r4 + 8004418: f7ff fe68 bl 80040ec + args->state_flags = PA_SUCCESSFUL; + 800441c: 2301 movs r3, #1 + 800441e: 63e3 str r3, [r4, #60] ; 0x3c + args->num_fails = 0; + 8004420: 6367 str r7, [r4, #52] ; 0x34 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 8004422: 230d movs r3, #13 + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + 8004424: 2748 movs r7, #72 ; 0x48 + args->attempts_left = MAX_TARGET_ATTEMPTS; + 8004426: 63a3 str r3, [r4, #56] ; 0x38 + rv = ae_encrypted_read(KEYNUM_secret, KEYNUM_main_pin, digest, ts, AE_SECRET_LEN); + 8004428: 9700 str r7, [sp, #0] + 800442a: ab14 add r3, sp, #80 ; 0x50 + 800442c: aa0c add r2, sp, #48 ; 0x30 + 800442e: 2103 movs r1, #3 + 8004430: 2009 movs r0, #9 + 8004432: f7fe fff3 bl 800341c + if(rv) { + 8004436: b110 cbz r0, 800443e + ae_reset_chip(); + 8004438: f7fe fb7c bl 8002b34 + 800443c: e7be b.n 80043bc + ae_reset_chip(); + 800443e: f7fe fb79 bl 8002b34 + mcu_key_get(&mcu_key_valid); + 8004442: f10d 000f add.w r0, sp, #15 + 8004446: f7fe f8ad bl 80025a4 + if(check_all_zeros(ts, AE_SECRET_LEN) || !mcu_key_valid) { + 800444a: 4639 mov r1, r7 + 800444c: a814 add r0, sp, #80 ; 0x50 + 800444e: f7fe f9e7 bl 8002820 + 8004452: b910 cbnz r0, 800445a + 8004454: f89d 300f ldrb.w r3, [sp, #15] + 8004458: b91b cbnz r3, 8004462 + args->state_flags |= PA_ZERO_SECRET; + 800445a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800445c: f043 0310 orr.w r3, r3, #16 + 8004460: 63e3 str r3, [r4, #60] ; 0x3c + if(!deltamode) { + 8004462: 2e00 cmp r6, #0 + 8004464: d1b7 bne.n 80043d6 + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 8004466: f7fe f9fb bl 8002860 + 800446a: 4b08 ldr r3, [pc, #32] ; (800448c ) + 800446c: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8004470: f020 0001 bic.w r0, r0, #1 + 8004474: 4058 eors r0, r3 + args->delay_achieved = 0; + 8004476: e9c4 660b strd r6, r6, [r4, #44] ; 0x2c + args->private_state = ((rng_sample() & ~1) | is_trick_pin) ^ rom_secrets->hash_cache_secret[0]; + 800447a: 6420 str r0, [r4, #64] ; 0x40 + return; + 800447c: e7ab b.n 80043d6 + return EPIN_WRONG_SUCCESS; + 800447e: f06f 056c mvn.w r5, #108 ; 0x6c + 8004482: e7ad b.n 80043e0 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004484: f06f 0568 mvn.w r5, #104 ; 0x68 + 8004488: e7aa b.n 80043e0 + 800448a: bf00 nop + 800448c: 0801c000 .word 0x0801c000 + +08004490 : +// +// Verify we know the main PIN, but don't do anything with it. +// + int +pin_check_logged_in(const pinAttempt_t *args, bool *is_trick) +{ + 8004490: b570 push {r4, r5, r6, lr} + 8004492: 460e mov r6, r1 + 8004494: b088 sub sp, #32 + int rv = _validate_attempt(args, false); + 8004496: 2100 movs r1, #0 +{ + 8004498: 4605 mov r5, r0 + int rv = _validate_attempt(args, false); + 800449a: f7ff fc99 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 800449e: 4604 mov r4, r0 + 80044a0: b980 cbnz r0, 80044c4 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80044a2: 6beb ldr r3, [r5, #60] ; 0x3c + 80044a4: 07da lsls r2, r3, #31 + 80044a6: d520 bpl.n 80044ea + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 80044a8: 4b11 ldr r3, [pc, #68] ; (80044f0 ) + 80044aa: 6c2a ldr r2, [r5, #64] ; 0x40 + 80044ac: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80044b0: 4053 eors r3, r2 + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(get_is_trick(args, NULL)) { + 80044b2: 07db lsls r3, r3, #31 + 80044b4: d509 bpl.n 80044ca + // they used a trick pin to get this far. Amuse them more. + *is_trick = true; + 80044b6: 2301 movs r3, #1 + 80044b8: 7033 strb r3, [r6, #0] + + // should calibrate this, but smart money will just look at the bus + delay_ms(10); + 80044ba: 200a movs r0, #10 + 80044bc: f7ff fae2 bl 8003a84 + rng_delay(); + 80044c0: f7fe fa22 bl 8002908 + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + if(rv) return EPIN_AUTH_FAIL; + } + + return 0; +} + 80044c4: 4620 mov r0, r4 + 80044c6: b008 add sp, #32 + 80044c8: bd70 pop {r4, r5, r6, pc} + pin_cache_restore(args, auth_digest); + 80044ca: 4669 mov r1, sp + *is_trick = false; + 80044cc: 7030 strb r0, [r6, #0] + pin_cache_restore(args, auth_digest); + 80044ce: 4628 mov r0, r5 + 80044d0: f7ff fe44 bl 800415c + ae_pair_unlock(); + 80044d4: f7fe fd32 bl 8002f3c + int rv = ae_checkmac(KEYNUM_main_pin, auth_digest); + 80044d8: 4669 mov r1, sp + 80044da: 2003 movs r0, #3 + 80044dc: f7fe fcac bl 8002e38 + if(rv) return EPIN_AUTH_FAIL; + 80044e0: 1e04 subs r4, r0, #0 + 80044e2: bf18 it ne + 80044e4: f06f 046f mvnne.w r4, #111 ; 0x6f + 80044e8: e7ec b.n 80044c4 + return EPIN_WRONG_SUCCESS; + 80044ea: f06f 046c mvn.w r4, #108 ; 0x6c + 80044ee: e7e9 b.n 80044c4 + 80044f0: 0801c000 .word 0x0801c000 + +080044f4 : +// +// Change the PIN and/or the secret. (Must also know the previous value, or it must be blank) +// + int +pin_change(pinAttempt_t *args) +{ + 80044f4: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 80044f8: 2100 movs r1, #0 +{ + 80044fa: b0a4 sub sp, #144 ; 0x90 + 80044fc: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 80044fe: f7ff fc67 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 8004502: 4605 mov r5, r0 + 8004504: 2800 cmp r0, #0 + 8004506: f040 8094 bne.w 8004632 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 800450a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800450c: 07d9 lsls r1, r3, #31 + 800450e: f140 809c bpl.w 800464a + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(args->state_flags & PA_IS_BLANK) { + 8004512: 079a lsls r2, r3, #30 + 8004514: d502 bpl.n 800451c + // if blank, must provide blank value + if(args->pin_len) return EPIN_RANGE_ERR; + 8004516: 6aa3 ldr r3, [r4, #40] ; 0x28 + 8004518: 2b00 cmp r3, #0 + 800451a: d158 bne.n 80045ce + } + + // Look at change flags. + const uint32_t cf = args->change_flags; + + ASSERT(!args->is_secondary); + 800451c: 6863 ldr r3, [r4, #4] + const uint32_t cf = args->change_flags; + 800451e: f8d4 9064 ldr.w r9, [r4, #100] ; 0x64 + ASSERT(!args->is_secondary); + 8004522: b113 cbz r3, 800452a + 8004524: 484c ldr r0, [pc, #304] ; (8004658 ) + 8004526: f7fc fa87 bl 8000a38 + if(cf & CHANGE_SECONDARY_WALLET_PIN) { + // obsolete secondary support, can't support. + return EPIN_BAD_REQUEST; + } + if(cf & (CHANGE_DURESS_PIN | CHANGE_DURESS_SECRET | CHANGE_BRICKME_PIN)) { + 800452a: f019 0f36 tst.w r9, #54 ; 0x36 + 800452e: d10b bne.n 8004548 + // we need some new API for trick PIN lookup/changes. + return EPIN_BAD_REQUEST; + } + if(!(cf & (CHANGE_WALLET_PIN | CHANGE_SECRET))) { + 8004530: f019 0f09 tst.w r9, #9 + 8004534: d04b beq.n 80045ce + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004536: 4b49 ldr r3, [pc, #292] ; (800465c ) + 8004538: 6c22 ldr r2, [r4, #64] ; 0x40 + 800453a: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 800453e: 4053 eors r3, r2 + // If they authorized w/ a trick PIN, new policy is to wipe ourselves if + // they try to change PIN code or the secret. + // - it's hard to fake them out here, and they may be onto us. + // - this protects the seed, but does end the game somewhat + // - all trick PINs will still be in effect, and looks like random reset + if(get_is_trick(args, NULL)) { + 8004540: 07db lsls r3, r3, #31 + 8004542: d504 bpl.n 800454e + // User is a thug.. kill secret and reboot w/o any notice + fast_wipe(); + 8004544: f7fe f940 bl 80027c8 + return EPIN_BAD_REQUEST; + 8004548: f06f 0567 mvn.w r5, #103 ; 0x67 + 800454c: e071 b.n 8004632 + // NOT-REACHED + return EPIN_BAD_REQUEST; + } + + // unlock the AE chip + if(warmup_ae()) return EPIN_I_AM_BRICK; + 800454e: f7ff fc75 bl 8003e3c + 8004552: 4605 mov r5, r0 + 8004554: 2800 cmp r0, #0 + 8004556: d17b bne.n 8004650 + // If they tricked us to get to this point, doesn't matter as + // below SE1 validates it all again. + + // Restore cached version of PIN digest: fast + uint8_t required_digest[32]; + pin_cache_restore(args, required_digest); + 8004558: f10d 0808 add.w r8, sp, #8 + 800455c: 4641 mov r1, r8 + 800455e: 4620 mov r0, r4 + 8004560: f7ff fdfc bl 800415c + + // Calculate new PIN hashed value: will be slow to do + if(cf & CHANGE_WALLET_PIN) { + 8004564: f019 0f01 tst.w r9, #1 + 8004568: d021 beq.n 80045ae + uint8_t new_digest[32]; + rv = pin_hash_attempt(args->new_pin, args->new_pin_len, new_digest); + 800456a: f8d4 10ac ldr.w r1, [r4, #172] ; 0xac + 800456e: aa12 add r2, sp, #72 ; 0x48 + 8004570: f104 008c add.w r0, r4, #140 ; 0x8c + 8004574: f7ff fd36 bl 8003fe4 + if(rv) goto ae_fail; + 8004578: 2800 cmp r0, #0 + 800457a: d161 bne.n 8004640 + + if(ae_encrypted_write(KEYNUM_main_pin, KEYNUM_main_pin, required_digest, new_digest, 32)) { + 800457c: 2320 movs r3, #32 + 800457e: 2103 movs r1, #3 + 8004580: 9300 str r3, [sp, #0] + 8004582: 4642 mov r2, r8 + 8004584: ab12 add r3, sp, #72 ; 0x48 + 8004586: 4608 mov r0, r1 + 8004588: f7fe ffe8 bl 800355c + 800458c: 2800 cmp r0, #0 + 800458e: d157 bne.n 8004640 + goto ae_fail; + } + + memcpy(required_digest, new_digest, 32); + 8004590: af12 add r7, sp, #72 ; 0x48 + 8004592: cf0f ldmia r7!, {r0, r1, r2, r3} + 8004594: 4646 mov r6, r8 + 8004596: c60f stmia r6!, {r0, r1, r2, r3} + 8004598: e897 000f ldmia.w r7, {r0, r1, r2, r3} + 800459c: e886 000f stmia.w r6, {r0, r1, r2, r3} + + // main pin is changing; reset counter to zero (good login) and our cache + pin_cache_save(args, new_digest); + 80045a0: 4620 mov r0, r4 + 80045a2: a912 add r1, sp, #72 ; 0x48 + 80045a4: f7ff fda2 bl 80040ec + + updates_for_good_login(new_digest); + 80045a8: a812 add r0, sp, #72 ; 0x48 + 80045aa: f7ff fccd bl 8003f48 + } + + // Recording new secret. + // Note the required_digest might have just changed above. + if(cf & CHANGE_SECRET) { + 80045ae: f019 0f08 tst.w r9, #8 + 80045b2: d037 beq.n 8004624 + int which = (args->change_flags >> 8) & 0xf; + 80045b4: 6e63 ldr r3, [r4, #100] ; 0x64 + 80045b6: 121b asrs r3, r3, #8 + switch(which) { + 80045b8: f013 020c ands.w r2, r3, #12 + 80045bc: d107 bne.n 80045ce + 80045be: 4928 ldr r1, [pc, #160] ; (8004660 ) + int which = (args->change_flags >> 8) & 0xf; + 80045c0: f003 030f and.w r3, r3, #15 + 80045c4: f911 a003 ldrsb.w sl, [r1, r3] + uint8_t tmp[AE_SECRET_LEN]; + uint8_t check[32]; + + // what slot (key number) are updating? (probably: KEYNUM_secret) + int target_slot = keynum_for_secret(args); + if(target_slot < 0) return EPIN_RANGE_ERR; + 80045c8: f1ba 0f00 cmp.w sl, #0 + 80045cc: da02 bge.n 80045d4 + if(args->pin_len) return EPIN_RANGE_ERR; + 80045ce: f06f 0566 mvn.w r5, #102 ; 0x66 + 80045d2: e02e b.n 8004632 + + se2_encrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, required_digest); + 80045d4: f104 07b0 add.w r7, r4, #176 ; 0xb0 + 80045d8: ae0a add r6, sp, #40 ; 0x28 + 80045da: ab12 add r3, sp, #72 ; 0x48 + 80045dc: 2148 movs r1, #72 ; 0x48 + + // write into two slots + if(ae_encrypted_write(target_slot, KEYNUM_main_pin, + 80045de: f04f 0948 mov.w r9, #72 ; 0x48 + se2_encrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, required_digest); + 80045e2: f8cd 8004 str.w r8, [sp, #4] + 80045e6: 9600 str r6, [sp, #0] + 80045e8: 4638 mov r0, r7 + 80045ea: f003 ff47 bl 800847c + if(ae_encrypted_write(target_slot, KEYNUM_main_pin, + 80045ee: 2103 movs r1, #3 + 80045f0: f8cd 9000 str.w r9, [sp] + 80045f4: eb0d 0309 add.w r3, sp, r9 + 80045f8: 4642 mov r2, r8 + 80045fa: 4650 mov r0, sl + 80045fc: f7fe ffae bl 800355c + 8004600: 4601 mov r1, r0 + 8004602: b9e8 cbnz r0, 8004640 + required_digest, tmp, AE_SECRET_LEN)){ + goto ae_fail; + } + if(ae_encrypted_write32(KEYNUM_check_secret, 0, KEYNUM_main_pin, required_digest, check)){ + 8004604: 9600 str r6, [sp, #0] + 8004606: 4643 mov r3, r8 + 8004608: 2203 movs r2, #3 + 800460a: 200a movs r0, #10 + 800460c: f7fe ff40 bl 8003490 + 8004610: b9b0 cbnz r0, 8004640 + goto ae_fail; + } + + // update the zero-secret flag to be correct. + if(cf & CHANGE_SECRET) { + if(check_all_zeros(args->secret, AE_SECRET_LEN)) { + 8004612: 4649 mov r1, r9 + 8004614: 4638 mov r0, r7 + 8004616: f7fe f903 bl 8002820 + 800461a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800461c: b168 cbz r0, 800463a + args->state_flags |= PA_ZERO_SECRET; + 800461e: f043 0310 orr.w r3, r3, #16 + 8004622: 63e3 str r3, [r4, #60] ; 0x3c + args->state_flags &= ~PA_ZERO_SECRET; + } + } + } + + ae_reset_chip(); + 8004624: f7fe fa86 bl 8002b34 + _hmac_attempt(args, args->hmac); + 8004628: f104 0144 add.w r1, r4, #68 ; 0x44 + 800462c: 4620 mov r0, r4 + 800462e: f7ff fba1 bl 8003d74 <_hmac_attempt> + +ae_fail: + ae_reset_chip(); + + return EPIN_AE_FAIL; +} + 8004632: 4628 mov r0, r5 + 8004634: b024 add sp, #144 ; 0x90 + 8004636: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + args->state_flags &= ~PA_ZERO_SECRET; + 800463a: f023 0310 bic.w r3, r3, #16 + 800463e: e7f0 b.n 8004622 + ae_reset_chip(); + 8004640: f7fe fa78 bl 8002b34 + return EPIN_AE_FAIL; + 8004644: f06f 0569 mvn.w r5, #105 ; 0x69 + 8004648: e7f3 b.n 8004632 + return EPIN_WRONG_SUCCESS; + 800464a: f06f 056c mvn.w r5, #108 ; 0x6c + 800464e: e7f0 b.n 8004632 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004650: f06f 0568 mvn.w r5, #104 ; 0x68 + 8004654: e7ed b.n 8004632 + 8004656: bf00 nop + 8004658: 0801046c .word 0x0801046c + 800465c: 0801c000 .word 0x0801c000 + 8004660: 08010798 .word 0x08010798 + +08004664 : +// To encourage not keeping the secret in memory, a way to fetch it after you've already +// proven you know the PIN. +// + int +pin_fetch_secret(pinAttempt_t *args) +{ + 8004664: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 8004668: 2100 movs r1, #0 +{ + 800466a: f5ad 7d38 sub.w sp, sp, #736 ; 0x2e0 + 800466e: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 8004670: f7ff fbae bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 8004674: 4605 mov r5, r0 + 8004676: 2800 cmp r0, #0 + 8004678: d144 bne.n 8004704 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 800467a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800467c: 07db lsls r3, r3, #31 + 800467e: f140 80e3 bpl.w 8004848 + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + if(args->change_flags & CHANGE_DURESS_SECRET) { + 8004682: 6e65 ldr r5, [r4, #100] ; 0x64 + 8004684: f015 0510 ands.w r5, r5, #16 + 8004688: f040 80e1 bne.w 800484e + + // fetch the already-hashed pin + // - no real need to re-prove PIN knowledge. + // - if they tricked us, doesn't matter as below the SE validates it all again + uint8_t digest[32]; + pin_cache_restore(args, digest); + 800468c: f10d 081c add.w r8, sp, #28 + 8004690: 4641 mov r1, r8 + 8004692: 4620 mov r0, r4 + 8004694: f7ff fd62 bl 800415c + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004698: 4b70 ldr r3, [pc, #448] ; (800485c ) + 800469a: 6c26 ldr r6, [r4, #64] ; 0x40 + 800469c: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 80046a0: 4073 eors r3, r6 + if(!slot || !is_trick) return is_trick; + 80046a2: 07df lsls r7, r3, #31 + 80046a4: d577 bpl.n 8004796 + memset(slot, 0, sizeof(trick_slot_t)); + 80046a6: 2280 movs r2, #128 ; 0x80 + 80046a8: 4629 mov r1, r5 + 80046aa: a817 add r0, sp, #92 ; 0x5c + 80046ac: f009 f93a bl 800d924 + if(args->delay_required & TC_DELTA_MODE) { + 80046b0: 6b23 ldr r3, [r4, #48] ; 0x30 + 80046b2: 0558 lsls r0, r3, #21 + 80046b4: d52b bpl.n 800470e + slot->tc_flags = args->delay_required; + 80046b6: f8ad 3060 strh.w r3, [sp, #96] ; 0x60 + slot->slot_num = -1; // unknown + 80046ba: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 80046be: 9317 str r3, [sp, #92] ; 0x5c + + // determine if we should proceed under duress + trick_slot_t slot; + bool is_trick = get_is_trick(args, &slot); + + if(is_trick && !(slot.tc_flags & TC_DELTA_MODE)) { + 80046c0: f8bd 6060 ldrh.w r6, [sp, #96] ; 0x60 + 80046c4: f416 6180 ands.w r1, r6, #1024 ; 0x400 + 80046c8: d165 bne.n 8004796 + // emulate a 24-word wallet, or xprv based wallet + // see stash.py for encoding details + memset(args->secret, 0, AE_SECRET_LEN); + 80046ca: 2248 movs r2, #72 ; 0x48 + 80046cc: f104 00b0 add.w r0, r4, #176 ; 0xb0 + 80046d0: f009 f928 bl 800d924 + + if(slot.tc_flags & TC_WORD_WALLET) { + 80046d4: 04f1 lsls r1, r6, #19 + 80046d6: d54c bpl.n 8004772 + if(check_all_zeros(&slot.xdata[16], 16)) { + 80046d8: ae1d add r6, sp, #116 ; 0x74 + 80046da: 2110 movs r1, #16 + 80046dc: 4630 mov r0, r6 + 80046de: f7fe f89f bl 8002820 + // 2nd half is zeros, must be 12-word wallet + args->secret[0] = 0x80; // 12 word phrase + memcpy(&args->secret[1], slot.xdata, 16); + 80046e2: f104 03b1 add.w r3, r4, #177 ; 0xb1 + if(check_all_zeros(&slot.xdata[16], 16)) { + 80046e6: 2800 cmp r0, #0 + 80046e8: d034 beq.n 8004754 + args->secret[0] = 0x80; // 12 word phrase + 80046ea: 2280 movs r2, #128 ; 0x80 + 80046ec: f884 20b0 strb.w r2, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 16); + 80046f0: ac19 add r4, sp, #100 ; 0x64 + 80046f2: 4622 mov r2, r4 + 80046f4: ca03 ldmia r2!, {r0, r1} + 80046f6: 42b2 cmp r2, r6 + 80046f8: 6018 str r0, [r3, #0] + 80046fa: 6059 str r1, [r3, #4] + 80046fc: 4614 mov r4, r2 + 80046fe: f103 0308 add.w r3, r3, #8 + 8004702: d1f6 bne.n 80046f2 + ae_reset_chip(); + + if(rv) return EPIN_AE_FAIL; + + return 0; +} + 8004704: 4628 mov r0, r5 + 8004706: f50d 7d38 add.w sp, sp, #736 ; 0x2e0 + 800470a: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 800470e: 4f54 ldr r7, [pc, #336] ; (8004860 ) + memcpy(key, &args->private_state, sizeof(args->private_state)); + 8004710: 960f str r6, [sp, #60] ; 0x3c + memcpy(key+4, rom_secrets->hash_cache_secret+4, sizeof(rom_secrets->hash_cache_secret)-4); + 8004712: cf0f ldmia r7!, {r0, r1, r2, r3} + 8004714: ae10 add r6, sp, #64 ; 0x40 + 8004716: c60f stmia r6!, {r0, r1, r2, r3} + 8004718: e897 0007 ldmia.w r7, {r0, r1, r2} + 800471c: e886 0007 stmia.w r6, {r0, r1, r2} + aes_init(&ctx); + 8004720: a837 add r0, sp, #220 ; 0xdc + 8004722: f004 f80f bl 8008744 + aes_add(&ctx, args->cached_main_pin, 32); + 8004726: 2220 movs r2, #32 + 8004728: f104 01f8 add.w r1, r4, #248 ; 0xf8 + 800472c: a837 add r0, sp, #220 ; 0xdc + 800472e: f004 f80f bl 8008750 + aes_done(&ctx, (uint8_t *)slot, 32, key, NULL); + 8004732: a917 add r1, sp, #92 ; 0x5c + 8004734: 9500 str r5, [sp, #0] + 8004736: ab0f add r3, sp, #60 ; 0x3c + 8004738: 2220 movs r2, #32 + 800473a: a837 add r0, sp, #220 ; 0xdc + 800473c: f004 f81e bl 800877c + if(slot->tc_flags & (TC_WORD_WALLET|TC_XPRV_WALLET)) { + 8004740: f8bd 1060 ldrh.w r1, [sp, #96] ; 0x60 + 8004744: f411 5fc0 tst.w r1, #6144 ; 0x1800 + 8004748: d0ba beq.n 80046c0 + se2_read_trick_data(slot->slot_num, slot->tc_flags, slot->xdata); + 800474a: 9817 ldr r0, [sp, #92] ; 0x5c + 800474c: aa19 add r2, sp, #100 ; 0x64 + 800474e: f003 fca1 bl 8008094 + if(is_trick && !(slot.tc_flags & TC_DELTA_MODE)) { + 8004752: e7b5 b.n 80046c0 + args->secret[0] = 0x82; // 24 word phrase + 8004754: 2282 movs r2, #130 ; 0x82 + 8004756: f884 20b0 strb.w r2, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 32); + 800475a: ae21 add r6, sp, #132 ; 0x84 + 800475c: aa19 add r2, sp, #100 ; 0x64 + 800475e: 4614 mov r4, r2 + 8004760: cc03 ldmia r4!, {r0, r1} + 8004762: 42b4 cmp r4, r6 + 8004764: 6018 str r0, [r3, #0] + 8004766: 6059 str r1, [r3, #4] + 8004768: 4622 mov r2, r4 + 800476a: f103 0308 add.w r3, r3, #8 + 800476e: d1f6 bne.n 800475e + 8004770: e7c8 b.n 8004704 + } else if(slot.tc_flags & TC_XPRV_WALLET) { + 8004772: 0532 lsls r2, r6, #20 + 8004774: d5c6 bpl.n 8004704 + args->secret[0] = 0x01; // XPRV mode + 8004776: 2301 movs r3, #1 + 8004778: f884 30b0 strb.w r3, [r4, #176] ; 0xb0 + memcpy(&args->secret[1], slot.xdata, 64); + 800477c: aa19 add r2, sp, #100 ; 0x64 + 800477e: 34b1 adds r4, #177 ; 0xb1 + 8004780: ae29 add r6, sp, #164 ; 0xa4 + 8004782: 4613 mov r3, r2 + 8004784: cb03 ldmia r3!, {r0, r1} + 8004786: 42b3 cmp r3, r6 + 8004788: 6020 str r0, [r4, #0] + 800478a: 6061 str r1, [r4, #4] + 800478c: 461a mov r2, r3 + 800478e: f104 0408 add.w r4, r4, #8 + 8004792: d1f6 bne.n 8004782 + 8004794: e7b6 b.n 8004704 + int which = (args->change_flags >> 8) & 0xf; + 8004796: 6e63 ldr r3, [r4, #100] ; 0x64 + 8004798: 121b asrs r3, r3, #8 + switch(which) { + 800479a: f013 0f0c tst.w r3, #12 + 800479e: d159 bne.n 8004854 + 80047a0: 4a30 ldr r2, [pc, #192] ; (8004864 ) + int which = (args->change_flags >> 8) & 0xf; + 80047a2: f003 030f and.w r3, r3, #15 + 80047a6: f912 9003 ldrsb.w r9, [r2, r3] + if(kn < 0) return EPIN_RANGE_ERR; + 80047aa: f1b9 0f00 cmp.w r9, #0 + 80047ae: db51 blt.n 8004854 + 80047b0: 2703 movs r7, #3 + rv = ae_encrypted_read(kn, KEYNUM_main_pin, digest, tmp, AE_SECRET_LEN); + 80047b2: f04f 0a48 mov.w sl, #72 ; 0x48 + 80047b6: 2103 movs r1, #3 + 80047b8: f8cd a000 str.w sl, [sp] + 80047bc: ab37 add r3, sp, #220 ; 0xdc + 80047be: 4642 mov r2, r8 + 80047c0: 4648 mov r0, r9 + 80047c2: f7fe fe2b bl 800341c + if(rv) continue; + 80047c6: 4601 mov r1, r0 + 80047c8: b130 cbz r0, 80047d8 + for(int retry=0; retry<3; retry++) { + 80047ca: 3f01 subs r7, #1 + 80047cc: d1f3 bne.n 80047b6 + ae_reset_chip(); + 80047ce: f7fe f9b1 bl 8002b34 + if(rv) return EPIN_AE_FAIL; + 80047d2: f06f 0569 mvn.w r5, #105 ; 0x69 + 80047d6: e795 b.n 8004704 + rv = ae_encrypted_read32(KEYNUM_check_secret, 0, KEYNUM_main_pin, digest, check); + 80047d8: ae0f add r6, sp, #60 ; 0x3c + 80047da: 9600 str r6, [sp, #0] + 80047dc: 4643 mov r3, r8 + 80047de: 2203 movs r2, #3 + 80047e0: 200a movs r0, #10 + 80047e2: f7fe fdf0 bl 80033c6 + if(rv) continue; + 80047e6: 4605 mov r5, r0 + 80047e8: 2800 cmp r0, #0 + 80047ea: d1ee bne.n 80047ca + se2_decrypt_secret(args->secret, AE_SECRET_LEN, 0, tmp, check, digest, &is_valid); + 80047ec: f10d 071b add.w r7, sp, #27 + 80047f0: f104 00b0 add.w r0, r4, #176 ; 0xb0 + 80047f4: ab37 add r3, sp, #220 ; 0xdc + 80047f6: e9cd 8701 strd r8, r7, [sp, #4] + 80047fa: 9600 str r6, [sp, #0] + 80047fc: 462a mov r2, r5 + 80047fe: 2148 movs r1, #72 ; 0x48 + 8004800: 9005 str r0, [sp, #20] + 8004802: f003 fe91 bl 8008528 + if(!is_valid) { + 8004806: f89d 301b ldrb.w r3, [sp, #27] + 800480a: 9805 ldr r0, [sp, #20] + 800480c: b993 cbnz r3, 8004834 + memset(args->secret, 0, AE_SECRET_LEN); + 800480e: 2248 movs r2, #72 ; 0x48 + 8004810: 4629 mov r1, r5 + 8004812: f009 f887 bl 800d924 + if(!(args->state_flags & PA_ZERO_SECRET)) { + 8004816: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004818: 06db lsls r3, r3, #27 + 800481a: d408 bmi.n 800482e + args->state_flags |= PA_ZERO_SECRET; + 800481c: 6be3 ldr r3, [r4, #60] ; 0x3c + 800481e: f043 0310 orr.w r3, r3, #16 + 8004822: 63e3 str r3, [r4, #60] ; 0x3c + _hmac_attempt(args, args->hmac); + 8004824: f104 0144 add.w r1, r4, #68 ; 0x44 + 8004828: 4620 mov r0, r4 + 800482a: f7ff faa3 bl 8003d74 <_hmac_attempt> + ae_reset_chip(); + 800482e: f7fe f981 bl 8002b34 + if(rv) return EPIN_AE_FAIL; + 8004832: e767 b.n 8004704 + if(!args->secret[0] && check_all_zeros(args->secret, AE_SECRET_LEN)) { + 8004834: f894 30b0 ldrb.w r3, [r4, #176] ; 0xb0 + 8004838: 2b00 cmp r3, #0 + 800483a: d1f8 bne.n 800482e + 800483c: 2148 movs r1, #72 ; 0x48 + 800483e: f7fd ffef bl 8002820 + 8004842: 2800 cmp r0, #0 + 8004844: d0f3 beq.n 800482e + 8004846: e7e9 b.n 800481c + return EPIN_WRONG_SUCCESS; + 8004848: f06f 056c mvn.w r5, #108 ; 0x6c + 800484c: e75a b.n 8004704 + return EPIN_BAD_REQUEST; + 800484e: f06f 0567 mvn.w r5, #103 ; 0x67 + 8004852: e757 b.n 8004704 + if(kn < 0) return EPIN_RANGE_ERR; + 8004854: f06f 0566 mvn.w r5, #102 ; 0x66 + 8004858: e754 b.n 8004704 + 800485a: bf00 nop + 800485c: 0801c000 .word 0x0801c000 + 8004860: 0801c074 .word 0x0801c074 + 8004864: 08010798 .word 0x08010798 + +08004868 : +// - new API so whole thing provided in one shot? encryption issues: provide +// "dest" and all 416 bytes end up there (read case only). +// + int +pin_long_secret(pinAttempt_t *args, uint8_t *dest) +{ + 8004868: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 800486c: 460f mov r7, r1 + 800486e: b099 sub sp, #100 ; 0x64 + // Validate args and signature + int rv = _validate_attempt(args, false); + 8004870: 2100 movs r1, #0 +{ + 8004872: 4606 mov r6, r0 + int rv = _validate_attempt(args, false); + 8004874: f7ff faac bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 8004878: 4604 mov r4, r0 + 800487a: b9b8 cbnz r0, 80048ac + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 800487c: 6bf3 ldr r3, [r6, #60] ; 0x3c + 800487e: 07da lsls r2, r3, #31 + 8004880: f140 80a5 bpl.w 80049ce + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004884: 4b55 ldr r3, [pc, #340] ; (80049dc ) + 8004886: 6c32 ldr r2, [r6, #64] ; 0x40 + 8004888: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 800488c: 4053 eors r3, r2 + } + + // determine if we should proceed under duress/in some trick way + bool is_trick = get_is_trick(args, NULL); + + if(is_trick) { + 800488e: 07db lsls r3, r3, #31 + 8004890: d510 bpl.n 80048b4 + // Not supported in trick mode. Pretend it's all zeros. Accept all writes. + memset(args->secret, 0, 32); + 8004892: 4601 mov r1, r0 + 8004894: 2220 movs r2, #32 + 8004896: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 800489a: f009 f843 bl 800d924 + if(dest) memset(dest, 0, AE_LONG_SECRET_LEN); + 800489e: b12f cbz r7, 80048ac + 80048a0: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 80048a4: 4621 mov r1, r4 + 80048a6: 4638 mov r0, r7 + 80048a8: f009 f83c bl 800d924 + +se2_fail: + ae_reset_chip(); + + return EPIN_SE2_FAIL; +} + 80048ac: 4620 mov r0, r4 + 80048ae: b019 add sp, #100 ; 0x64 + 80048b0: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + int blk = (args->change_flags >> 8) & 0xf; + 80048b4: 6e73 ldr r3, [r6, #100] ; 0x64 + 80048b6: f3c3 2803 ubfx r8, r3, #8, #4 + if(blk > 13) return EPIN_RANGE_ERR; + 80048ba: f1b8 0f0d cmp.w r8, #13 + 80048be: f300 8089 bgt.w 80049d4 + pin_cache_restore(args, digest); + 80048c2: a908 add r1, sp, #32 + 80048c4: 4630 mov r0, r6 + 80048c6: f7ff fc49 bl 800415c + if(!(args->change_flags & CHANGE_SECRET)) { + 80048ca: 6e71 ldr r1, [r6, #100] ; 0x64 + 80048cc: f011 0908 ands.w r9, r1, #8 + 80048d0: d156 bne.n 8004980 + if(!dest) { + 80048d2: bb27 cbnz r7, 800491e + rv = ae_encrypted_read32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, tmp); + 80048d4: af10 add r7, sp, #64 ; 0x40 + 80048d6: 9700 str r7, [sp, #0] + 80048d8: ab08 add r3, sp, #32 + 80048da: 2203 movs r2, #3 + 80048dc: 4641 mov r1, r8 + 80048de: 2008 movs r0, #8 + 80048e0: f7fe fd71 bl 80033c6 + if(rv) goto fail; + 80048e4: 4605 mov r5, r0 + 80048e6: 2800 cmp r0, #0 + 80048e8: d16a bne.n 80049c0 + se2_decrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest, &is_valid); + 80048ea: f10d 031f add.w r3, sp, #31 + 80048ee: 9302 str r3, [sp, #8] + 80048f0: ab08 add r3, sp, #32 + 80048f2: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 80048f6: e9cd 4300 strd r4, r3, [sp] + 80048fa: ea4f 1248 mov.w r2, r8, lsl #5 + 80048fe: 463b mov r3, r7 + 8004900: 2120 movs r1, #32 + 8004902: 9005 str r0, [sp, #20] + 8004904: f003 fe10 bl 8008528 + if(!is_valid) { + 8004908: f89d 301f ldrb.w r3, [sp, #31] + 800490c: 9805 ldr r0, [sp, #20] + 800490e: b91b cbnz r3, 8004918 + memset(args->secret, 0, 32); + 8004910: 2220 movs r2, #32 + 8004912: 4621 mov r1, r4 + memset(dest, 0, AE_LONG_SECRET_LEN); + 8004914: f009 f806 bl 800d924 + ae_reset_chip(); + 8004918: f7fe f90c bl 8002b34 + if(rv) return EPIN_AE_FAIL; + 800491c: e7c6 b.n 80048ac + 800491e: 463e mov r6, r7 + rv = ae_encrypted_read32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, p); + 8004920: 9600 str r6, [sp, #0] + 8004922: ab08 add r3, sp, #32 + 8004924: 2203 movs r2, #3 + 8004926: 4649 mov r1, r9 + 8004928: 2008 movs r0, #8 + 800492a: f7fe fd4c bl 80033c6 + if(rv) goto fail; + 800492e: 4605 mov r5, r0 + 8004930: 2800 cmp r0, #0 + 8004932: d145 bne.n 80049c0 + for(blk=0; blk<13; blk++, p += 32) { + 8004934: f109 0901 add.w r9, r9, #1 + 8004938: f1b9 0f0d cmp.w r9, #13 + 800493c: f106 0620 add.w r6, r6, #32 + 8004940: d1ee bne.n 8004920 + ASSERT(p == dest+AE_LONG_SECRET_LEN); + 8004942: f507 73d0 add.w r3, r7, #416 ; 0x1a0 + 8004946: 429e cmp r6, r3 + 8004948: d002 beq.n 8004950 + 800494a: 4825 ldr r0, [pc, #148] ; (80049e0 ) + 800494c: f7fc f874 bl 8000a38 + se2_decrypt_secret(dest, AE_LONG_SECRET_LEN, 0, dest, NULL, digest, &is_valid); + 8004950: ab10 add r3, sp, #64 ; 0x40 + 8004952: 9302 str r3, [sp, #8] + 8004954: ab08 add r3, sp, #32 + 8004956: e9cd 0300 strd r0, r3, [sp] + 800495a: 4602 mov r2, r0 + 800495c: 463b mov r3, r7 + 800495e: f44f 71d0 mov.w r1, #416 ; 0x1a0 + 8004962: 4638 mov r0, r7 + 8004964: f003 fde0 bl 8008528 + if(!is_valid) { + 8004968: f89d 4040 ldrb.w r4, [sp, #64] ; 0x40 + 800496c: b924 cbnz r4, 8004978 + memset(dest, 0, AE_LONG_SECRET_LEN); + 800496e: f44f 72d0 mov.w r2, #416 ; 0x1a0 + 8004972: 4621 mov r1, r4 + 8004974: 4638 mov r0, r7 + 8004976: e7cd b.n 8004914 + ae_reset_chip(); + 8004978: f7fe f8dc bl 8002b34 + return 0; + 800497c: 462c mov r4, r5 + 800497e: e795 b.n 80048ac + uint8_t tmp[32] = {0}; + 8004980: 221c movs r2, #28 + 8004982: 4621 mov r1, r4 + 8004984: a811 add r0, sp, #68 ; 0x44 + 8004986: 9410 str r4, [sp, #64] ; 0x40 + if(se2_encrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest)) { + 8004988: ad10 add r5, sp, #64 ; 0x40 + uint8_t tmp[32] = {0}; + 800498a: f008 ffcb bl 800d924 + if(se2_encrypt_secret(args->secret, 32, blk*32, tmp, NULL, digest)) { + 800498e: ab08 add r3, sp, #32 + 8004990: e9cd 4300 strd r4, r3, [sp] + 8004994: ea4f 1248 mov.w r2, r8, lsl #5 + 8004998: 462b mov r3, r5 + 800499a: 2120 movs r1, #32 + 800499c: f106 00b0 add.w r0, r6, #176 ; 0xb0 + 80049a0: f003 fd6c bl 800847c + 80049a4: b120 cbz r0, 80049b0 + ae_reset_chip(); + 80049a6: f7fe f8c5 bl 8002b34 + return EPIN_SE2_FAIL; + 80049aa: f06f 0472 mvn.w r4, #114 ; 0x72 + 80049ae: e77d b.n 80048ac + rv = ae_encrypted_write32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, tmp); + 80049b0: 9500 str r5, [sp, #0] + 80049b2: ab08 add r3, sp, #32 + 80049b4: 2203 movs r2, #3 + 80049b6: 4641 mov r1, r8 + 80049b8: 2008 movs r0, #8 + 80049ba: f7fe fd69 bl 8003490 + 80049be: 4605 mov r5, r0 + ae_reset_chip(); + 80049c0: f7fe f8b8 bl 8002b34 + if(rv) return EPIN_AE_FAIL; + 80049c4: 2d00 cmp r5, #0 + 80049c6: bf18 it ne + 80049c8: f06f 0469 mvnne.w r4, #105 ; 0x69 + 80049cc: e76e b.n 80048ac + return EPIN_WRONG_SUCCESS; + 80049ce: f06f 046c mvn.w r4, #108 ; 0x6c + 80049d2: e76b b.n 80048ac + if(blk > 13) return EPIN_RANGE_ERR; + 80049d4: f06f 0466 mvn.w r4, #102 ; 0x66 + 80049d8: e768 b.n 80048ac + 80049da: bf00 nop + 80049dc: 0801c000 .word 0x0801c000 + 80049e0: 0801046c .word 0x0801046c + +080049e4 : +// +// Record current flash checksum and make green light go on. +// + int +pin_firmware_greenlight(pinAttempt_t *args) +{ + 80049e4: b530 push {r4, r5, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 80049e6: 2100 movs r1, #0 +{ + 80049e8: b09b sub sp, #108 ; 0x6c + 80049ea: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 80049ec: f7ff f9f0 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 80049f0: bb20 cbnz r0, 8004a3c + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 80049f2: 6be3 ldr r3, [r4, #60] ; 0x3c + 80049f4: 07da lsls r2, r3, #31 + 80049f6: d529 bpl.n 8004a4c + // must come here with a successful PIN login (so it's rate limited nicely) + return EPIN_WRONG_SUCCESS; + } + + if(args->is_secondary) { + 80049f8: 6865 ldr r5, [r4, #4] + 80049fa: bb55 cbnz r5, 8004a52 + return EPIN_PRIMARY_ONLY; + } + + // load existing PIN's hash + uint8_t digest[32]; + pin_cache_restore(args, digest); + 80049fc: a902 add r1, sp, #8 + 80049fe: 4620 mov r0, r4 + 8004a00: f7ff fbac bl 800415c + + // step 1: calc the value to use + uint8_t fw_check[32], world_check[32]; + checksum_flash(fw_check, world_check, 0); + 8004a04: 462a mov r2, r5 + 8004a06: a912 add r1, sp, #72 ; 0x48 + 8004a08: a80a add r0, sp, #40 ; 0x28 + 8004a0a: f7fd f907 bl 8001c1c + + // step 2: write it out to chip. + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004a0e: f7ff fa15 bl 8003e3c + 8004a12: bb08 cbnz r0, 8004a58 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004a14: 4b12 ldr r3, [pc, #72] ; (8004a60 ) + 8004a16: 6c22 ldr r2, [r4, #64] ; 0x40 + 8004a18: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8004a1c: 4053 eors r3, r2 + + // under duress, we can't fake this, but we go through the motions anyway + if(!get_is_trick(args, NULL)) { + 8004a1e: 07db lsls r3, r3, #31 + 8004a20: d40e bmi.n 8004a40 + rv = ae_encrypted_write(KEYNUM_firmware, KEYNUM_main_pin, digest, world_check, 32); + 8004a22: 2320 movs r3, #32 + 8004a24: 9300 str r3, [sp, #0] + 8004a26: aa02 add r2, sp, #8 + 8004a28: ab12 add r3, sp, #72 ; 0x48 + 8004a2a: 2103 movs r1, #3 + 8004a2c: 200e movs r0, #14 + 8004a2e: f7fe fd95 bl 800355c + + if(rv) { + 8004a32: b128 cbz r0, 8004a40 + ae_reset_chip(); + 8004a34: f7fe f87e bl 8002b34 + + return EPIN_AE_FAIL; + 8004a38: f06f 0069 mvn.w r0, #105 ; 0x69 + + return EPIN_AE_FAIL; + } + + return 0; +} + 8004a3c: b01b add sp, #108 ; 0x6c + 8004a3e: bd30 pop {r4, r5, pc} + rv = ae_set_gpio_secure(world_check); + 8004a40: a812 add r0, sp, #72 ; 0x48 + 8004a42: f7fe fe1d bl 8003680 + if(rv) { + 8004a46: 2800 cmp r0, #0 + 8004a48: d0f8 beq.n 8004a3c + 8004a4a: e7f3 b.n 8004a34 + return EPIN_WRONG_SUCCESS; + 8004a4c: f06f 006c mvn.w r0, #108 ; 0x6c + 8004a50: e7f4 b.n 8004a3c + return EPIN_PRIMARY_ONLY; + 8004a52: f06f 0071 mvn.w r0, #113 ; 0x71 + 8004a56: e7f1 b.n 8004a3c + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004a58: f06f 0068 mvn.w r0, #104 ; 0x68 + 8004a5c: e7ee b.n 8004a3c + 8004a5e: bf00 nop + 8004a60: 0801c000 .word 0x0801c000 + +08004a64 : +// Update the system firmware via file in PSRAM. Arrange for +// light to stay green through out process. +// + int +pin_firmware_upgrade(pinAttempt_t *args) +{ + 8004a64: b570 push {r4, r5, r6, lr} + // Validate args and signature + int rv = _validate_attempt(args, false); + 8004a66: 2100 movs r1, #0 +{ + 8004a68: b092 sub sp, #72 ; 0x48 + 8004a6a: 4604 mov r4, r0 + int rv = _validate_attempt(args, false); + 8004a6c: f7ff f9b0 bl 8003dd0 <_validate_attempt> + if(rv) return rv; + 8004a70: 2800 cmp r0, #0 + 8004a72: d14e bne.n 8004b12 + + if((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { + 8004a74: 6be3 ldr r3, [r4, #60] ; 0x3c + 8004a76: 07da lsls r2, r3, #31 + 8004a78: d54d bpl.n 8004b16 + // must come here with a successful PIN login + return EPIN_WRONG_SUCCESS; + } + + if(args->change_flags != CHANGE_FIRMWARE) { + 8004a7a: 6e63 ldr r3, [r4, #100] ; 0x64 + 8004a7c: 2b40 cmp r3, #64 ; 0x40 + 8004a7e: d11c bne.n 8004aba + } + + // expecting start/length relative to psram start + uint32_t *about = (uint32_t *)args->secret; + uint32_t start = about[0]; + uint32_t len = about[1]; + 8004a80: e9d4 562c ldrd r5, r6, [r4, #176] ; 0xb0 + + if(len < 32768) return EPIN_RANGE_ERR; + 8004a84: f5a6 4300 sub.w r3, r6, #32768 ; 0x8000 + 8004a88: f5b3 1ffc cmp.w r3, #2064384 ; 0x1f8000 + 8004a8c: d846 bhi.n 8004b1c + if(len > 2<<20) return EPIN_RANGE_ERR; + if(start+len > PSRAM_SIZE) return EPIN_RANGE_ERR; + 8004a8e: 19ab adds r3, r5, r6 + 8004a90: f5b3 0f00 cmp.w r3, #8388608 ; 0x800000 + 8004a94: d842 bhi.n 8004b1c + + const uint8_t *data = (const uint8_t *)PSRAM_BASE+start; + 8004a96: f105 4510 add.w r5, r5, #2415919104 ; 0x90000000 + + // verify a firmware image that's in RAM, and calc its digest + // - also applies watermark policy, etc + uint8_t world_check[32]; + bool ok = verify_firmware_in_ram(data, len, world_check); + 8004a9a: aa02 add r2, sp, #8 + 8004a9c: 4631 mov r1, r6 + 8004a9e: 4628 mov r0, r5 + 8004aa0: f7fd f9c2 bl 8001e28 + if(!ok) { + 8004aa4: 2800 cmp r0, #0 + 8004aa6: d03c beq.n 8004b22 + bool is_trick = ((args->private_state ^ rom_secrets->hash_cache_secret[0]) & 0x1); + 8004aa8: 4b21 ldr r3, [pc, #132] ; (8004b30 ) + 8004aaa: 6c22 ldr r2, [r4, #64] ; 0x40 + 8004aac: f893 3070 ldrb.w r3, [r3, #112] ; 0x70 + 8004ab0: 4053 eors r3, r2 + return EPIN_AUTH_FAIL; + } + + // under duress, we can't fake this, so kill ourselves. + if(get_is_trick(args, NULL)) { + 8004ab2: 07db lsls r3, r3, #31 + 8004ab4: d504 bpl.n 8004ac0 + // User is a thug.. kill secret and reboot w/o any notice + fast_wipe(); + 8004ab6: f7fd fe87 bl 80027c8 + return EPIN_BAD_REQUEST; + 8004aba: f06f 0067 mvn.w r0, #103 ; 0x67 + 8004abe: e028 b.n 8004b12 + return EPIN_BAD_REQUEST; + } + + // load existing PIN's hash + uint8_t digest[32]; + pin_cache_restore(args, digest); + 8004ac0: a90a add r1, sp, #40 ; 0x28 + 8004ac2: 4620 mov r0, r4 + 8004ac4: f7ff fb4a bl 800415c + + // step 1: calc the value to use, see above + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004ac8: f7ff f9b8 bl 8003e3c + 8004acc: bb60 cbnz r0, 8004b28 + + // step 2: write it out to chip. + rv = ae_encrypted_write(KEYNUM_firmware, KEYNUM_main_pin, digest, world_check, 32); + 8004ace: 2320 movs r3, #32 + 8004ad0: 9300 str r3, [sp, #0] + 8004ad2: aa0a add r2, sp, #40 ; 0x28 + 8004ad4: ab02 add r3, sp, #8 + 8004ad6: 2103 movs r1, #3 + 8004ad8: 200e movs r0, #14 + 8004ada: f7fe fd3f bl 800355c + if(rv) goto fail; + 8004ade: b9a0 cbnz r0, 8004b0a + + // this turns on green light + rv = ae_set_gpio_secure(world_check); + 8004ae0: a802 add r0, sp, #8 + 8004ae2: f7fe fdcd bl 8003680 + if(rv) goto fail; + 8004ae6: b980 cbnz r0, 8004b0a + + // -- point of no return -- + + // burn it, shows progress + psram_do_upgrade(data, len); + 8004ae8: 4631 mov r1, r6 + 8004aea: 4628 mov r0, r5 + 8004aec: f000 fbf4 bl 80052d8 + 8004af0: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8004af4: 490f ldr r1, [pc, #60] ; (8004b34 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8004af6: 4b10 ldr r3, [pc, #64] ; (8004b38 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8004af8: 68ca ldr r2, [r1, #12] + 8004afa: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8004afe: 4313 orrs r3, r2 + 8004b00: 60cb str r3, [r1, #12] + 8004b02: f3bf 8f4f dsb sy + __NOP(); + 8004b06: bf00 nop + for(;;) /* wait until reset */ + 8004b08: e7fd b.n 8004b06 + NVIC_SystemReset(); + + return 0; + +fail: + ae_reset_chip(); + 8004b0a: f7fe f813 bl 8002b34 + + return EPIN_AE_FAIL; + 8004b0e: f06f 0069 mvn.w r0, #105 ; 0x69 +} + 8004b12: b012 add sp, #72 ; 0x48 + 8004b14: bd70 pop {r4, r5, r6, pc} + return EPIN_WRONG_SUCCESS; + 8004b16: f06f 006c mvn.w r0, #108 ; 0x6c + 8004b1a: e7fa b.n 8004b12 + if(len < 32768) return EPIN_RANGE_ERR; + 8004b1c: f06f 0066 mvn.w r0, #102 ; 0x66 + 8004b20: e7f7 b.n 8004b12 + return EPIN_AUTH_FAIL; + 8004b22: f06f 006f mvn.w r0, #111 ; 0x6f + 8004b26: e7f4 b.n 8004b12 + if(warmup_ae()) return EPIN_I_AM_BRICK; + 8004b28: f06f 0068 mvn.w r0, #104 ; 0x68 + 8004b2c: e7f1 b.n 8004b12 + 8004b2e: bf00 nop + 8004b30: 0801c000 .word 0x0801c000 + 8004b34: e000ed00 .word 0xe000ed00 + 8004b38: 05fa0004 .word 0x05fa0004 + +08004b3c : + +// strcat_hex() +// + void +strcat_hex(char *msg, const void *d, int len) +{ + 8004b3c: b570 push {r4, r5, r6, lr} + 8004b3e: 4616 mov r6, r2 + 8004b40: 4604 mov r4, r0 + 8004b42: 460d mov r5, r1 + char *p = msg+strlen(msg); + 8004b44: f008 ff19 bl 800d97a + const uint8_t *h = (const uint8_t *)d; + + for(; len; len--, h++) { + *(p++) = hexmap[(*h>>4) & 0xf]; + 8004b48: 4a0b ldr r2, [pc, #44] ; (8004b78 ) + char *p = msg+strlen(msg); + 8004b4a: 4420 add r0, r4 + for(; len; len--, h++) { + 8004b4c: 1e69 subs r1, r5, #1 + 8004b4e: eb00 0646 add.w r6, r0, r6, lsl #1 + 8004b52: 42b0 cmp r0, r6 + 8004b54: d102 bne.n 8004b5c + *(p++) = hexmap[(*h>>0) & 0xf]; + } + + *(p++) = 0; + 8004b56: 2300 movs r3, #0 + 8004b58: 7003 strb r3, [r0, #0] +} + 8004b5a: bd70 pop {r4, r5, r6, pc} + *(p++) = hexmap[(*h>>4) & 0xf]; + 8004b5c: f811 3f01 ldrb.w r3, [r1, #1]! + 8004b60: 091b lsrs r3, r3, #4 + 8004b62: 5cd3 ldrb r3, [r2, r3] + 8004b64: f800 3b02 strb.w r3, [r0], #2 + *(p++) = hexmap[(*h>>0) & 0xf]; + 8004b68: 780b ldrb r3, [r1, #0] + 8004b6a: f003 030f and.w r3, r3, #15 + 8004b6e: 5cd3 ldrb r3, [r2, r3] + 8004b70: f800 3c01 strb.w r3, [r0, #-1] + for(; len; len--, h++) { + 8004b74: e7ed b.n 8004b52 + 8004b76: bf00 nop + 8004b78: 080107ce .word 0x080107ce + +08004b7c : + * parameters in the USART_InitTypeDef and initialize the associated handle. + * @param husart USART handle. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_USART_Init(USART_HandleTypeDef *husart) +{ + 8004b7c: b5f8 push {r3, r4, r5, r6, r7, lr} + /* Check the USART handle allocation */ + if (husart == NULL) + 8004b7e: 4604 mov r4, r0 + 8004b80: b910 cbnz r0, 8004b88 + { + return HAL_ERROR; + 8004b82: 2501 movs r5, #1 + /* Enable the Peripheral */ + __HAL_USART_ENABLE(husart); + + /* TEACK and/or REACK to check before moving husart->State to Ready */ + return (USART_CheckIdleState(husart)); +} + 8004b84: 4628 mov r0, r5 + 8004b86: bdf8 pop {r3, r4, r5, r6, r7, pc} + if (husart->State == HAL_USART_STATE_RESET) + 8004b88: f890 3059 ldrb.w r3, [r0, #89] ; 0x59 + 8004b8c: f003 02ff and.w r2, r3, #255 ; 0xff + 8004b90: b90b cbnz r3, 8004b96 + husart->Lock = HAL_UNLOCKED; + 8004b92: f880 2058 strb.w r2, [r0, #88] ; 0x58 + __HAL_USART_DISABLE(husart); + 8004b96: 6823 ldr r3, [r4, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 8004b98: 6921 ldr r1, [r4, #16] + husart->State = HAL_USART_STATE_BUSY; + 8004b9a: 2502 movs r5, #2 + 8004b9c: f884 5059 strb.w r5, [r4, #89] ; 0x59 + __HAL_USART_DISABLE(husart); + 8004ba0: 681a ldr r2, [r3, #0] + 8004ba2: f022 0201 bic.w r2, r2, #1 + 8004ba6: 601a str r2, [r3, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 8004ba8: 68a2 ldr r2, [r4, #8] + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 8004baa: 6818 ldr r0, [r3, #0] + tmpreg = (uint32_t)husart->Init.WordLength | husart->Init.Parity | husart->Init.Mode | USART_CR1_OVER8; + 8004bac: 430a orrs r2, r1 + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 8004bae: 49a9 ldr r1, [pc, #676] ; (8004e54 ) + 8004bb0: 4001 ands r1, r0 + 8004bb2: 430a orrs r2, r1 + 8004bb4: 6961 ldr r1, [r4, #20] + MODIFY_REG(husart->Instance->CR2, USART_CR2_FIELDS, tmpreg); + 8004bb6: 69a0 ldr r0, [r4, #24] + MODIFY_REG(husart->Instance->CR1, USART_CR1_FIELDS, tmpreg); + 8004bb8: 430a orrs r2, r1 + 8004bba: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 8004bbe: 601a str r2, [r3, #0] + MODIFY_REG(husart->Instance->CR2, USART_CR2_FIELDS, tmpreg); + 8004bc0: 6859 ldr r1, [r3, #4] + 8004bc2: 6a22 ldr r2, [r4, #32] + 8004bc4: f421 517c bic.w r1, r1, #16128 ; 0x3f00 + 8004bc8: f021 0109 bic.w r1, r1, #9 + 8004bcc: 4302 orrs r2, r0 + 8004bce: 430a orrs r2, r1 + 8004bd0: 69e1 ldr r1, [r4, #28] + 8004bd2: 430a orrs r2, r1 + 8004bd4: 68e1 ldr r1, [r4, #12] + 8004bd6: 430a orrs r2, r1 + 8004bd8: f442 6200 orr.w r2, r2, #2048 ; 0x800 + 8004bdc: 605a str r2, [r3, #4] + MODIFY_REG(husart->Instance->PRESC, USART_PRESC_PRESCALER, husart->Init.ClockPrescaler); + 8004bde: 6ad9 ldr r1, [r3, #44] ; 0x2c + 8004be0: 6a62 ldr r2, [r4, #36] ; 0x24 + 8004be2: f021 010f bic.w r1, r1, #15 + 8004be6: 4311 orrs r1, r2 + 8004be8: 62d9 str r1, [r3, #44] ; 0x2c + USART_GETCLOCKSOURCE(husart, clocksource); + 8004bea: 499b ldr r1, [pc, #620] ; (8004e58 ) + 8004bec: 428b cmp r3, r1 + 8004bee: d10e bne.n 8004c0e + 8004bf0: 4b9a ldr r3, [pc, #616] ; (8004e5c ) + 8004bf2: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 8004bf6: f003 0303 and.w r3, r3, #3 + 8004bfa: 42ab cmp r3, r5 + 8004bfc: f000 80cd beq.w 8004d9a + 8004c00: 2b03 cmp r3, #3 + 8004c02: d01a beq.n 8004c3a + 8004c04: 2b01 cmp r3, #1 + 8004c06: d153 bne.n 8004cb0 + pclk = HAL_RCC_GetSysClockFreq(); + 8004c08: f003 ff52 bl 8008ab0 + 8004c0c: e052 b.n 8004cb4 + USART_GETCLOCKSOURCE(husart, clocksource); + 8004c0e: 4994 ldr r1, [pc, #592] ; (8004e60 ) + 8004c10: 428b cmp r3, r1 + 8004c12: d13c bne.n 8004c8e + 8004c14: 4b91 ldr r3, [pc, #580] ; (8004e5c ) + 8004c16: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 8004c1a: f003 030c and.w r3, r3, #12 + 8004c1e: 2b08 cmp r3, #8 + 8004c20: f000 80bb beq.w 8004d9a + 8004c24: d807 bhi.n 8004c36 + 8004c26: 2b00 cmp r3, #0 + 8004c28: f000 80b4 beq.w 8004d94 + 8004c2c: 2b04 cmp r3, #4 + 8004c2e: d0eb beq.n 8004c08 + uint32_t usartdiv = 0x00000000; + 8004c30: 2300 movs r3, #0 + ret = HAL_ERROR; + 8004c32: 2501 movs r5, #1 + 8004c34: e06e b.n 8004d14 + USART_GETCLOCKSOURCE(husart, clocksource); + 8004c36: 2b0c cmp r3, #12 + 8004c38: d1fa bne.n 8004c30 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004c3a: 2a00 cmp r2, #0 + 8004c3c: f000 80fb beq.w 8004e36 + 8004c40: 2a01 cmp r2, #1 + 8004c42: f000 80fa beq.w 8004e3a + 8004c46: 2a02 cmp r2, #2 + 8004c48: f000 80f9 beq.w 8004e3e + 8004c4c: 2a03 cmp r2, #3 + 8004c4e: f000 80f8 beq.w 8004e42 + 8004c52: 2a04 cmp r2, #4 + 8004c54: f000 80f7 beq.w 8004e46 + 8004c58: 2a05 cmp r2, #5 + 8004c5a: f000 80f6 beq.w 8004e4a + 8004c5e: 2a06 cmp r2, #6 + 8004c60: f000 80f5 beq.w 8004e4e + 8004c64: 2a07 cmp r2, #7 + 8004c66: f000 8101 beq.w 8004e6c + 8004c6a: 2a08 cmp r2, #8 + 8004c6c: f000 8100 beq.w 8004e70 + 8004c70: 2a09 cmp r2, #9 + 8004c72: f000 80ff beq.w 8004e74 + 8004c76: 2a0a cmp r2, #10 + 8004c78: f000 80fe beq.w 8004e78 + 8004c7c: 2a0b cmp r2, #11 + 8004c7e: bf14 ite ne + 8004c80: 2201 movne r2, #1 + 8004c82: f44f 7280 moveq.w r2, #256 ; 0x100 + 8004c86: 6861 ldr r1, [r4, #4] + 8004c88: f44f 4300 mov.w r3, #32768 ; 0x8000 + 8004c8c: e0a1 b.n 8004dd2 + USART_GETCLOCKSOURCE(husart, clocksource); + 8004c8e: 4975 ldr r1, [pc, #468] ; (8004e64 ) + 8004c90: 428b cmp r3, r1 + 8004c92: d1cd bne.n 8004c30 + 8004c94: 4b71 ldr r3, [pc, #452] ; (8004e5c ) + 8004c96: f8d3 3088 ldr.w r3, [r3, #136] ; 0x88 + 8004c9a: f003 0330 and.w r3, r3, #48 ; 0x30 + 8004c9e: 2b20 cmp r3, #32 + 8004ca0: d07b beq.n 8004d9a + 8004ca2: d803 bhi.n 8004cac + 8004ca4: 2b00 cmp r3, #0 + 8004ca6: d075 beq.n 8004d94 + 8004ca8: 2b10 cmp r3, #16 + 8004caa: e7c0 b.n 8004c2e + 8004cac: 2b30 cmp r3, #48 ; 0x30 + 8004cae: e7c3 b.n 8004c38 + pclk = HAL_RCC_GetPCLK2Freq(); + 8004cb0: f004 fb0c bl 80092cc + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004cb4: 6a62 ldr r2, [r4, #36] ; 0x24 + 8004cb6: 2a00 cmp r2, #0 + 8004cb8: f000 80a7 beq.w 8004e0a + 8004cbc: 2a01 cmp r2, #1 + 8004cbe: f000 80a6 beq.w 8004e0e + 8004cc2: 2a02 cmp r2, #2 + 8004cc4: f000 80a5 beq.w 8004e12 + 8004cc8: 2a03 cmp r2, #3 + 8004cca: f000 80a4 beq.w 8004e16 + 8004cce: 2a04 cmp r2, #4 + 8004cd0: f000 80a3 beq.w 8004e1a + 8004cd4: 2a05 cmp r2, #5 + 8004cd6: f000 80a2 beq.w 8004e1e + 8004cda: 2a06 cmp r2, #6 + 8004cdc: f000 80a1 beq.w 8004e22 + 8004ce0: 2a07 cmp r2, #7 + 8004ce2: f000 80a0 beq.w 8004e26 + 8004ce6: 2a08 cmp r2, #8 + 8004ce8: f000 809f beq.w 8004e2a + 8004cec: 2a09 cmp r2, #9 + 8004cee: f000 809e beq.w 8004e2e + 8004cf2: 2a0a cmp r2, #10 + 8004cf4: f000 809d beq.w 8004e32 + 8004cf8: 2a0b cmp r2, #11 + 8004cfa: bf14 ite ne + 8004cfc: 2201 movne r2, #1 + 8004cfe: f44f 7280 moveq.w r2, #256 ; 0x100 + 8004d02: 6861 ldr r1, [r4, #4] + 8004d04: fbb0 f0f2 udiv r0, r0, r2 + 8004d08: 084b lsrs r3, r1, #1 + 8004d0a: eb03 0340 add.w r3, r3, r0, lsl #1 + HAL_StatusTypeDef ret = HAL_OK; + 8004d0e: 2500 movs r5, #0 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004d10: fbb3 f3f1 udiv r3, r3, r1 + if ((usartdiv >= USART_BRR_MIN) && (usartdiv <= USART_BRR_MAX)) + 8004d14: f1a3 0110 sub.w r1, r3, #16 + 8004d18: f64f 72ef movw r2, #65519 ; 0xffef + 8004d1c: 4291 cmp r1, r2 + brrtemp = (uint16_t)(usartdiv & 0xFFF0U); + 8004d1e: bf9f itttt ls + 8004d20: f023 020f bicls.w r2, r3, #15 + 8004d24: b292 uxthls r2, r2 + brrtemp |= (uint16_t)((usartdiv & (uint16_t)0x000FU) >> 1U); + 8004d26: f3c3 0342 ubfxls r3, r3, #1, #3 + husart->Instance->BRR = brrtemp; + 8004d2a: 6821 ldrls r1, [r4, #0] + 8004d2c: bf9a itte ls + 8004d2e: 4313 orrls r3, r2 + 8004d30: 60cb strls r3, [r1, #12] + ret = HAL_ERROR; + 8004d32: 2501 movhi r5, #1 + husart->NbTxDataToProcess = 1U; + 8004d34: 2301 movs r3, #1 + husart->RxISR = NULL; + 8004d36: 2200 movs r2, #0 + if (USART_SetConfig(husart) == HAL_ERROR) + 8004d38: 429d cmp r5, r3 + husart->TxISR = NULL; + 8004d3a: e9c4 2212 strd r2, r2, [r4, #72] ; 0x48 + husart->NbTxDataToProcess = 1U; + 8004d3e: 87a3 strh r3, [r4, #60] ; 0x3c + husart->NbRxDataToProcess = 1U; + 8004d40: 8763 strh r3, [r4, #58] ; 0x3a + if (USART_SetConfig(husart) == HAL_ERROR) + 8004d42: f43f af1e beq.w 8004b82 + husart->Instance->CR2 &= ~USART_CR2_LINEN; + 8004d46: 6823 ldr r3, [r4, #0] + 8004d48: 6859 ldr r1, [r3, #4] + 8004d4a: f421 4180 bic.w r1, r1, #16384 ; 0x4000 + 8004d4e: 6059 str r1, [r3, #4] + husart->Instance->CR3 &= ~(USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN); + 8004d50: 6899 ldr r1, [r3, #8] + 8004d52: f021 012a bic.w r1, r1, #42 ; 0x2a + 8004d56: 6099 str r1, [r3, #8] + __HAL_USART_ENABLE(husart); + 8004d58: 6819 ldr r1, [r3, #0] + 8004d5a: f041 0101 orr.w r1, r1, #1 + 8004d5e: 6019 str r1, [r3, #0] + husart->ErrorCode = HAL_USART_ERROR_NONE; + 8004d60: 65e2 str r2, [r4, #92] ; 0x5c + tickstart = HAL_GetTick(); + 8004d62: f002 fb3b bl 80073dc + if ((husart->Instance->CR1 & USART_CR1_TE) == USART_CR1_TE) + 8004d66: 6823 ldr r3, [r4, #0] + 8004d68: 681b ldr r3, [r3, #0] + 8004d6a: 071a lsls r2, r3, #28 + tickstart = HAL_GetTick(); + 8004d6c: 4607 mov r7, r0 + if ((husart->Instance->CR1 & USART_CR1_TE) == USART_CR1_TE) + 8004d6e: f100 8085 bmi.w 8004e7c + if ((husart->Instance->CR1 & USART_CR1_RE) == USART_CR1_RE) + 8004d72: 6823 ldr r3, [r4, #0] + 8004d74: 681b ldr r3, [r3, #0] + 8004d76: 075b lsls r3, r3, #29 + 8004d78: d505 bpl.n 8004d86 + while ((__HAL_USART_GET_FLAG(husart, Flag) ? SET : RESET) == Status) + 8004d7a: 6823 ldr r3, [r4, #0] + 8004d7c: 69de ldr r6, [r3, #28] + 8004d7e: f416 0680 ands.w r6, r6, #4194304 ; 0x400000 + 8004d82: f000 808e beq.w 8004ea2 + husart->State = HAL_USART_STATE_READY; + 8004d86: 2301 movs r3, #1 + 8004d88: f884 3059 strb.w r3, [r4, #89] ; 0x59 + __HAL_UNLOCK(husart); + 8004d8c: 2300 movs r3, #0 + 8004d8e: f884 3058 strb.w r3, [r4, #88] ; 0x58 + return HAL_OK; + 8004d92: e6f7 b.n 8004b84 + pclk = HAL_RCC_GetPCLK1Freq(); + 8004d94: f004 fa88 bl 80092a8 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004d98: e78c b.n 8004cb4 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(HSI_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004d9a: b302 cbz r2, 8004dde + 8004d9c: 2a01 cmp r2, #1 + 8004d9e: d020 beq.n 8004de2 + 8004da0: 2a02 cmp r2, #2 + 8004da2: d020 beq.n 8004de6 + 8004da4: 2a03 cmp r2, #3 + 8004da6: d020 beq.n 8004dea + 8004da8: 2a04 cmp r2, #4 + 8004daa: d020 beq.n 8004dee + 8004dac: 2a05 cmp r2, #5 + 8004dae: d020 beq.n 8004df2 + 8004db0: 2a06 cmp r2, #6 + 8004db2: d020 beq.n 8004df6 + 8004db4: 2a07 cmp r2, #7 + 8004db6: d020 beq.n 8004dfa + 8004db8: 2a08 cmp r2, #8 + 8004dba: d020 beq.n 8004dfe + 8004dbc: 2a09 cmp r2, #9 + 8004dbe: d020 beq.n 8004e02 + 8004dc0: 2a0a cmp r2, #10 + 8004dc2: d020 beq.n 8004e06 + 8004dc4: 2a0b cmp r2, #11 + 8004dc6: bf14 ite ne + 8004dc8: 2201 movne r2, #1 + 8004dca: f44f 7280 moveq.w r2, #256 ; 0x100 + 8004dce: 6861 ldr r1, [r4, #4] + 8004dd0: 4b25 ldr r3, [pc, #148] ; (8004e68 ) + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004dd2: fbb3 f2f2 udiv r2, r3, r2 + 8004dd6: 084b lsrs r3, r1, #1 + 8004dd8: eb03 0342 add.w r3, r3, r2, lsl #1 + 8004ddc: e797 b.n 8004d0e + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(HSI_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004dde: 2201 movs r2, #1 + 8004de0: e7f5 b.n 8004dce + 8004de2: 2202 movs r2, #2 + 8004de4: e7f3 b.n 8004dce + 8004de6: 2204 movs r2, #4 + 8004de8: e7f1 b.n 8004dce + 8004dea: 2206 movs r2, #6 + 8004dec: e7ef b.n 8004dce + 8004dee: 2208 movs r2, #8 + 8004df0: e7ed b.n 8004dce + 8004df2: 220a movs r2, #10 + 8004df4: e7eb b.n 8004dce + 8004df6: 220c movs r2, #12 + 8004df8: e7e9 b.n 8004dce + 8004dfa: 2210 movs r2, #16 + 8004dfc: e7e7 b.n 8004dce + 8004dfe: 2220 movs r2, #32 + 8004e00: e7e5 b.n 8004dce + 8004e02: 2240 movs r2, #64 ; 0x40 + 8004e04: e7e3 b.n 8004dce + 8004e06: 2280 movs r2, #128 ; 0x80 + 8004e08: e7e1 b.n 8004dce + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(pclk, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004e0a: 2201 movs r2, #1 + 8004e0c: e779 b.n 8004d02 + 8004e0e: 2202 movs r2, #2 + 8004e10: e777 b.n 8004d02 + 8004e12: 2204 movs r2, #4 + 8004e14: e775 b.n 8004d02 + 8004e16: 2206 movs r2, #6 + 8004e18: e773 b.n 8004d02 + 8004e1a: 2208 movs r2, #8 + 8004e1c: e771 b.n 8004d02 + 8004e1e: 220a movs r2, #10 + 8004e20: e76f b.n 8004d02 + 8004e22: 220c movs r2, #12 + 8004e24: e76d b.n 8004d02 + 8004e26: 2210 movs r2, #16 + 8004e28: e76b b.n 8004d02 + 8004e2a: 2220 movs r2, #32 + 8004e2c: e769 b.n 8004d02 + 8004e2e: 2240 movs r2, #64 ; 0x40 + 8004e30: e767 b.n 8004d02 + 8004e32: 2280 movs r2, #128 ; 0x80 + 8004e34: e765 b.n 8004d02 + usartdiv = (uint32_t)(USART_DIV_SAMPLING8(LSE_VALUE, husart->Init.BaudRate, husart->Init.ClockPrescaler)); + 8004e36: 2201 movs r2, #1 + 8004e38: e725 b.n 8004c86 + 8004e3a: 2202 movs r2, #2 + 8004e3c: e723 b.n 8004c86 + 8004e3e: 2204 movs r2, #4 + 8004e40: e721 b.n 8004c86 + 8004e42: 2206 movs r2, #6 + 8004e44: e71f b.n 8004c86 + 8004e46: 2208 movs r2, #8 + 8004e48: e71d b.n 8004c86 + 8004e4a: 220a movs r2, #10 + 8004e4c: e71b b.n 8004c86 + 8004e4e: 220c movs r2, #12 + 8004e50: e719 b.n 8004c86 + 8004e52: bf00 nop + 8004e54: cfff69f3 .word 0xcfff69f3 + 8004e58: 40013800 .word 0x40013800 + 8004e5c: 40021000 .word 0x40021000 + 8004e60: 40004400 .word 0x40004400 + 8004e64: 40004800 .word 0x40004800 + 8004e68: 00f42400 .word 0x00f42400 + 8004e6c: 2210 movs r2, #16 + 8004e6e: e70a b.n 8004c86 + 8004e70: 2220 movs r2, #32 + 8004e72: e708 b.n 8004c86 + 8004e74: 2240 movs r2, #64 ; 0x40 + 8004e76: e706 b.n 8004c86 + 8004e78: 2280 movs r2, #128 ; 0x80 + 8004e7a: e704 b.n 8004c86 + while ((__HAL_USART_GET_FLAG(husart, Flag) ? SET : RESET) == Status) + 8004e7c: 6823 ldr r3, [r4, #0] + 8004e7e: 69de ldr r6, [r3, #28] + 8004e80: f416 1600 ands.w r6, r6, #2097152 ; 0x200000 + 8004e84: f47f af75 bne.w 8004d72 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 8004e88: f002 faa8 bl 80073dc + 8004e8c: 1bc0 subs r0, r0, r7 + 8004e8e: f5b0 7f7a cmp.w r0, #1000 ; 0x3e8 + 8004e92: d9f3 bls.n 8004e7c + husart->State = HAL_USART_STATE_READY; + 8004e94: 2301 movs r3, #1 + 8004e96: f884 3059 strb.w r3, [r4, #89] ; 0x59 + __HAL_UNLOCK(husart); + 8004e9a: f884 6058 strb.w r6, [r4, #88] ; 0x58 + return HAL_TIMEOUT; + 8004e9e: 2503 movs r5, #3 + 8004ea0: e670 b.n 8004b84 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 8004ea2: f002 fa9b bl 80073dc + 8004ea6: 1bc0 subs r0, r0, r7 + 8004ea8: f5b0 7f7a cmp.w r0, #1000 ; 0x3e8 + 8004eac: f67f af65 bls.w 8004d7a + 8004eb0: e7f0 b.n 8004e94 + 8004eb2: bf00 nop + +08004eb4 : + __HAL_RCC_USART1_CONFIG(RCC_USART1CLKSOURCE_SYSCLK); + 8004eb4: 4b14 ldr r3, [pc, #80] ; (8004f08 ) + 8004eb6: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8004eba: f022 0203 bic.w r2, r2, #3 + 8004ebe: f042 0201 orr.w r2, r2, #1 +{ + 8004ec2: b513 push {r0, r1, r4, lr} + __HAL_RCC_USART1_CONFIG(RCC_USART1CLKSOURCE_SYSCLK); + 8004ec4: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + __HAL_RCC_USART1_CLK_ENABLE(); + 8004ec8: 6e1a ldr r2, [r3, #96] ; 0x60 + memset(&con, 0, sizeof(con)); + 8004eca: 4c10 ldr r4, [pc, #64] ; (8004f0c ) + __HAL_RCC_USART1_CLK_ENABLE(); + 8004ecc: f442 4280 orr.w r2, r2, #16384 ; 0x4000 + 8004ed0: 661a str r2, [r3, #96] ; 0x60 + 8004ed2: 6e1b ldr r3, [r3, #96] ; 0x60 + 8004ed4: f403 4380 and.w r3, r3, #16384 ; 0x4000 + 8004ed8: 9301 str r3, [sp, #4] + memset(&con, 0, sizeof(con)); + 8004eda: 2258 movs r2, #88 ; 0x58 + 8004edc: 2100 movs r1, #0 + 8004ede: f104 0008 add.w r0, r4, #8 + __HAL_RCC_USART1_CLK_ENABLE(); + 8004ee2: 9b01 ldr r3, [sp, #4] + memset(&con, 0, sizeof(con)); + 8004ee4: f008 fd1e bl 800d924 + con.Init.BaudRate = 115200; + 8004ee8: 4a09 ldr r2, [pc, #36] ; (8004f10 ) + 8004eea: f44f 33e1 mov.w r3, #115200 ; 0x1c200 + 8004eee: e9c4 2300 strd r2, r3, [r4] + HAL_StatusTypeDef rv = HAL_USART_Init(&con); + 8004ef2: 4620 mov r0, r4 + con.Init.Mode = USART_MODE_TX_RX; + 8004ef4: 230c movs r3, #12 + 8004ef6: 6163 str r3, [r4, #20] + HAL_StatusTypeDef rv = HAL_USART_Init(&con); + 8004ef8: f7ff fe40 bl 8004b7c + ASSERT(rv == HAL_OK); + 8004efc: b110 cbz r0, 8004f04 + 8004efe: 4805 ldr r0, [pc, #20] ; (8004f14 ) + 8004f00: f7fb fd9a bl 8000a38 +} + 8004f04: b002 add sp, #8 + 8004f06: bd10 pop {r4, pc} + 8004f08: 40021000 .word 0x40021000 + 8004f0c: 2009e1c4 .word 0x2009e1c4 + 8004f10: 40013800 .word 0x40013800 + 8004f14: 0801046c .word 0x0801046c + +08004f18 : + * @param Timeout Timeout duration. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_USART_Transmit(USART_HandleTypeDef *husart, uint8_t *pTxData, uint16_t Size, uint32_t Timeout) +{ + while(Size > 0U) { + 8004f18: 4b0b ldr r3, [pc, #44] ; (8004f48 ) + 8004f1a: 440a add r2, r1 + 8004f1c: 4291 cmp r1, r2 + 8004f1e: d10b bne.n 8004f38 + MY_UART->TDR = *pTxData; + pTxData++; + Size --; + } + + while(!(MY_UART->ISR & UART_FLAG_TC)) { + 8004f20: 69da ldr r2, [r3, #28] + 8004f22: 0652 lsls r2, r2, #25 + 8004f24: d5fc bpl.n 8004f20 + // wait for final byte to be sent + } + + // Clear Transmission Complete Flag + MY_UART->ICR = USART_CLEAR_TCF; + 8004f26: 2240 movs r2, #64 ; 0x40 + 8004f28: 621a str r2, [r3, #32] + + // Clear overrun flag and discard the received data + MY_UART->ICR = USART_CLEAR_OREF; + 8004f2a: 2208 movs r2, #8 + 8004f2c: 621a str r2, [r3, #32] + MY_UART->RQR = USART_RXDATA_FLUSH_REQUEST; + 8004f2e: 831a strh r2, [r3, #24] + MY_UART->RQR = USART_TXDATA_FLUSH_REQUEST; + 8004f30: 2210 movs r2, #16 + 8004f32: 831a strh r2, [r3, #24] + + return HAL_OK; +} + 8004f34: 2000 movs r0, #0 + 8004f36: 4770 bx lr + while(!(MY_UART->ISR & UART_FLAG_TXE)) { + 8004f38: 69d8 ldr r0, [r3, #28] + 8004f3a: 0600 lsls r0, r0, #24 + 8004f3c: d5fc bpl.n 8004f38 + MY_UART->TDR = *pTxData; + 8004f3e: f811 0b01 ldrb.w r0, [r1], #1 + 8004f42: 8518 strh r0, [r3, #40] ; 0x28 + Size --; + 8004f44: e7ea b.n 8004f1c + 8004f46: bf00 nop + 8004f48: 40013800 .word 0x40013800 + +08004f4c : +{ + 8004f4c: b510 push {r4, lr} + 8004f4e: 4604 mov r4, r0 + rng_delay(); + 8004f50: f7fd fcda bl 8002908 + HAL_USART_Transmit(&con, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY); + 8004f54: 4620 mov r0, r4 + 8004f56: f008 fd10 bl 800d97a + 8004f5a: 4621 mov r1, r4 + 8004f5c: b282 uxth r2, r0 + 8004f5e: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004f62: 4803 ldr r0, [pc, #12] ; (8004f70 ) + 8004f64: f7ff ffd8 bl 8004f18 +} + 8004f68: e8bd 4010 ldmia.w sp!, {r4, lr} + rng_delay(); + 8004f6c: f7fd bccc b.w 8002908 + 8004f70: 2009e1c4 .word 0x2009e1c4 + +08004f74 : +{ + 8004f74: b513 push {r0, r1, r4, lr} + 8004f76: 4604 mov r4, r0 + uint8_t cb = c; + 8004f78: f88d 0007 strb.w r0, [sp, #7] + rng_delay(); + 8004f7c: f7fd fcc4 bl 8002908 + if(cb != '\n') { + 8004f80: f89d 3007 ldrb.w r3, [sp, #7] + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004f84: 4808 ldr r0, [pc, #32] ; (8004fa8 ) + if(cb != '\n') { + 8004f86: 2b0a cmp r3, #10 + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004f88: bf08 it eq + 8004f8a: 4908 ldreq r1, [pc, #32] ; (8004fac ) + HAL_USART_Transmit(&con, &cb, 1, HAL_MAX_DELAY); + 8004f8c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8004f90: bf1a itte ne + 8004f92: 2201 movne r2, #1 + 8004f94: f10d 0107 addne.w r1, sp, #7 + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8004f98: 2202 moveq r2, #2 + 8004f9a: f7ff ffbd bl 8004f18 + rng_delay(); + 8004f9e: f7fd fcb3 bl 8002908 +} + 8004fa2: 4620 mov r0, r4 + 8004fa4: b002 add sp, #8 + 8004fa6: bd10 pop {r4, pc} + 8004fa8: 2009e1c4 .word 0x2009e1c4 + 8004fac: 080107cb .word 0x080107cb + +08004fb0 : +{ + 8004fb0: b538 push {r3, r4, r5, lr} + putchar(hexmap[(b>>4) & 0xf]); + 8004fb2: 4d06 ldr r5, [pc, #24] ; (8004fcc ) + 8004fb4: 0903 lsrs r3, r0, #4 +{ + 8004fb6: 4604 mov r4, r0 + putchar(hexmap[(b>>0) & 0xf]); + 8004fb8: f004 040f and.w r4, r4, #15 + putchar(hexmap[(b>>4) & 0xf]); + 8004fbc: 5ce8 ldrb r0, [r5, r3] + 8004fbe: f7ff ffd9 bl 8004f74 + putchar(hexmap[(b>>0) & 0xf]); + 8004fc2: 5d28 ldrb r0, [r5, r4] +} + 8004fc4: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + putchar(hexmap[(b>>0) & 0xf]); + 8004fc8: f7ff bfd4 b.w 8004f74 + 8004fcc: 080107ce .word 0x080107ce + +08004fd0 : +{ + 8004fd0: b538 push {r3, r4, r5, lr} + putchar(hexmap[(w>>12) & 0xf]); + 8004fd2: 4d0b ldr r5, [pc, #44] ; (8005000 ) + 8004fd4: 0b03 lsrs r3, r0, #12 +{ + 8004fd6: 4604 mov r4, r0 + putchar(hexmap[(w>>12) & 0xf]); + 8004fd8: 5ce8 ldrb r0, [r5, r3] + 8004fda: f7ff ffcb bl 8004f74 + putchar(hexmap[(w>>8) & 0xf]); + 8004fde: f3c4 2303 ubfx r3, r4, #8, #4 + 8004fe2: 5ce8 ldrb r0, [r5, r3] + 8004fe4: f7ff ffc6 bl 8004f74 + putchar(hexmap[(w>>4) & 0xf]); + 8004fe8: f3c4 1303 ubfx r3, r4, #4, #4 + putchar(hexmap[(w>>0) & 0xf]); + 8004fec: f004 040f and.w r4, r4, #15 + putchar(hexmap[(w>>4) & 0xf]); + 8004ff0: 5ce8 ldrb r0, [r5, r3] + 8004ff2: f7ff ffbf bl 8004f74 + putchar(hexmap[(w>>0) & 0xf]); + 8004ff6: 5d28 ldrb r0, [r5, r4] +} + 8004ff8: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + putchar(hexmap[(w>>0) & 0xf]); + 8004ffc: f7ff bfba b.w 8004f74 + 8005000: 080107ce .word 0x080107ce + +08005004 : +{ + 8005004: b510 push {r4, lr} + 8005006: 4604 mov r4, r0 + puthex4(w >> 16); + 8005008: 0c00 lsrs r0, r0, #16 + 800500a: f7ff ffe1 bl 8004fd0 + puthex4(w & 0xffff); + 800500e: b2a0 uxth r0, r4 +} + 8005010: e8bd 4010 ldmia.w sp!, {r4, lr} + puthex4(w & 0xffff); + 8005014: f7ff bfdc b.w 8004fd0 + +08005018 : +{ + 8005018: b5f8 push {r3, r4, r5, r6, r7, lr} + 800501a: 4605 mov r5, r0 + 800501c: 2604 movs r6, #4 + for(int m=1000; m; m /= 10) { + 800501e: f44f 747a mov.w r4, #1000 ; 0x3e8 + char n = '0' + ((w / m) % 10); + 8005022: 270a movs r7, #10 + if(w >= m) { + 8005024: 42a5 cmp r5, r4 + 8005026: db09 blt.n 800503c + char n = '0' + ((w / m) % 10); + 8005028: fb95 f3f4 sdiv r3, r5, r4 + 800502c: fb93 f0f7 sdiv r0, r3, r7 + 8005030: fb07 3310 mls r3, r7, r0, r3 + 8005034: 3330 adds r3, #48 ; 0x30 + putchar(n); + 8005036: b2d8 uxtb r0, r3 + 8005038: f7ff ff9c bl 8004f74 + for(int m=1000; m; m /= 10) { + 800503c: fb94 f4f7 sdiv r4, r4, r7 + 8005040: 3e01 subs r6, #1 + 8005042: d1ef bne.n 8005024 +} + 8005044: bdf8 pop {r3, r4, r5, r6, r7, pc} + +08005046 : +{ + 8005046: b570 push {r4, r5, r6, lr} + 8005048: 4606 mov r6, r0 + 800504a: 460d mov r5, r1 + for(int i=0; i +} + 8005052: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + putchar('\n'); + 8005056: 200a movs r0, #10 + 8005058: f7ff bf8c b.w 8004f74 + puthex2(data[i]); + 800505c: 5d30 ldrb r0, [r6, r4] + 800505e: f7ff ffa7 bl 8004fb0 + for(int i=0; i + ... + +08005068 : +{ + 8005068: b513 push {r0, r1, r4, lr} + 800506a: 9001 str r0, [sp, #4] + int ln = strlen(msg); + 800506c: f008 fc85 bl 800d97a + 8005070: 4604 mov r4, r0 + rng_delay(); + 8005072: f7fd fc49 bl 8002908 + if(ln) HAL_USART_Transmit(&con, (uint8_t *)msg, ln, HAL_MAX_DELAY); + 8005076: 9901 ldr r1, [sp, #4] + 8005078: b12c cbz r4, 8005086 + 800507a: 4809 ldr r0, [pc, #36] ; (80050a0 ) + 800507c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8005080: b2a2 uxth r2, r4 + 8005082: f7ff ff49 bl 8004f18 + HAL_USART_Transmit(&con, (uint8_t *)CRLF, 2, HAL_MAX_DELAY); + 8005086: 4907 ldr r1, [pc, #28] ; (80050a4 ) + 8005088: 4805 ldr r0, [pc, #20] ; (80050a0 ) + 800508a: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800508e: 2202 movs r2, #2 + 8005090: f7ff ff42 bl 8004f18 + rng_delay(); + 8005094: f7fd fc38 bl 8002908 +} + 8005098: 2001 movs r0, #1 + 800509a: b002 add sp, #8 + 800509c: bd10 pop {r4, pc} + 800509e: bf00 nop + 80050a0: 2009e1c4 .word 0x2009e1c4 + 80050a4: 080107cb .word 0x080107cb + +080050a8 : + +// psram_send_byte() +// + void +psram_send_byte(OSPI_HandleTypeDef *qh, uint8_t cmd_byte, bool is_quad) +{ + 80050a8: b570 push {r4, r5, r6, lr} + 80050aa: b094 sub sp, #80 ; 0x50 + 80050ac: 4604 mov r4, r0 + 80050ae: 460e mov r6, r1 + 80050b0: 4615 mov r5, r2 + // Send single-byte commands to the PSRAM chip. Quad mode or normal SPI. + + OSPI_RegularCmdTypeDef cmd = { + 80050b2: 2100 movs r1, #0 + 80050b4: 2250 movs r2, #80 ; 0x50 + 80050b6: 4668 mov r0, sp + 80050b8: f008 fc34 bl 800d924 + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .Instruction = cmd_byte, // Exit Quad Mode + .InstructionMode = is_quad ? HAL_OSPI_INSTRUCTION_4_LINES : HAL_OSPI_INSTRUCTION_1_LINE, + 80050bc: 2d00 cmp r5, #0 + 80050be: bf14 ite ne + 80050c0: 2303 movne r3, #3 + 80050c2: 2301 moveq r3, #1 + .DataMode = HAL_OSPI_DATA_NONE, + .NbData = 0, // how much to read in bytes + }; + + // Start and finish a "Indirection functional mode" request + HAL_OSPI_Command(qh, &cmd, HAL_MAX_DELAY); + 80050c4: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 80050c8: 4669 mov r1, sp + 80050ca: 4620 mov r0, r4 + OSPI_RegularCmdTypeDef cmd = { + 80050cc: 9602 str r6, [sp, #8] + 80050ce: 9303 str r3, [sp, #12] + HAL_OSPI_Command(qh, &cmd, HAL_MAX_DELAY); + 80050d0: f006 f898 bl 800b204 +} + 80050d4: b014 add sp, #80 ; 0x50 + 80050d6: bd70 pop {r4, r5, r6, pc} + +080050d8 : + +// psram_setup() +// + void +psram_setup(void) +{ + 80050d8: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80050dc: b0c6 sub sp, #280 ; 0x118 + // Using OSPI1 block + OSPI_HandleTypeDef qh = { 0 }; + 80050de: 2250 movs r2, #80 ; 0x50 + 80050e0: 2100 movs r1, #0 + 80050e2: a80a add r0, sp, #40 ; 0x28 + 80050e4: f008 fc1e bl 800d924 + + // enable clocks + __HAL_RCC_OSPI1_CLK_ENABLE(); + 80050e8: 4b6a ldr r3, [pc, #424] ; (8005294 ) + // reset module + __HAL_RCC_OSPI1_FORCE_RESET(); + __HAL_RCC_OSPI1_RELEASE_RESET(); + + // configure pins: Port E PE10-PE15 + GPIO_InitTypeDef setup = { + 80050ea: 4c6b ldr r4, [pc, #428] ; (8005298 ) + __HAL_RCC_OSPI1_CLK_ENABLE(); + 80050ec: 6d1a ldr r2, [r3, #80] ; 0x50 + 80050ee: f442 7280 orr.w r2, r2, #256 ; 0x100 + 80050f2: 651a str r2, [r3, #80] ; 0x50 + 80050f4: 6d1a ldr r2, [r3, #80] ; 0x50 + 80050f6: f402 7280 and.w r2, r2, #256 ; 0x100 + 80050fa: 9201 str r2, [sp, #4] + 80050fc: 9a01 ldr r2, [sp, #4] + __HAL_RCC_GPIOE_CLK_ENABLE(); + 80050fe: 6cda ldr r2, [r3, #76] ; 0x4c + 8005100: f042 0210 orr.w r2, r2, #16 + 8005104: 64da str r2, [r3, #76] ; 0x4c + 8005106: 6cda ldr r2, [r3, #76] ; 0x4c + 8005108: f002 0210 and.w r2, r2, #16 + 800510c: 9202 str r2, [sp, #8] + 800510e: 9a02 ldr r2, [sp, #8] + __HAL_RCC_OSPI1_FORCE_RESET(); + 8005110: 6b1a ldr r2, [r3, #48] ; 0x30 + 8005112: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8005116: 631a str r2, [r3, #48] ; 0x30 + __HAL_RCC_OSPI1_RELEASE_RESET(); + 8005118: 6b1a ldr r2, [r3, #48] ; 0x30 + 800511a: f422 7280 bic.w r2, r2, #256 ; 0x100 + 800511e: 631a str r2, [r3, #48] ; 0x30 + GPIO_InitTypeDef setup = { + 8005120: cc0f ldmia r4!, {r0, r1, r2, r3} + 8005122: ad05 add r5, sp, #20 + 8005124: c50f stmia r5!, {r0, r1, r2, r3} + 8005126: 6823 ldr r3, [r4, #0] + .Mode = GPIO_MODE_AF_PP, // not sure + .Pull = GPIO_NOPULL, // not sure + .Speed = GPIO_SPEED_FREQ_VERY_HIGH, + .Alternate = GPIO_AF10_OCTOSPIM_P1, + }; + HAL_GPIO_Init(GPIOE, &setup); + 8005128: 485c ldr r0, [pc, #368] ; (800529c ) + GPIO_InitTypeDef setup = { + 800512a: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOE, &setup); + 800512c: a905 add r1, sp, #20 + 800512e: f7fc f861 bl 80011f4 + + + // Config operational values + qh.Instance = OCTOSPI1; + qh.Init.FifoThreshold = 1; // ?? unused + 8005132: 4b5b ldr r3, [pc, #364] ; (80052a0 ) + 8005134: 2701 movs r7, #1 + qh.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; + qh.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // want standard mode (but octo only?) + qh.Init.DeviceSize = 24; // assume max size, actual is 8Mbyte + 8005136: 2218 movs r2, #24 + qh.Init.FifoThreshold = 1; // ?? unused + 8005138: e9cd 370a strd r3, r7, [sp, #40] ; 0x28 + qh.Init.ChipSelectHighTime = 1; // 1, maxed out, seems to work + 800513c: e9cd 270e strd r2, r7, [sp, #56] ; 0x38 + qh.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; + 8005140: 2300 movs r3, #0 + qh.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; // maybe? + 8005142: f04f 5280 mov.w r2, #268435456 ; 0x10000000 + qh.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; // required! + qh.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; // low clock between ops (required, see errata) +#if HCLK_FREQUENCY == 80000000 + qh.Init.ClockPrescaler = 1; // prescaler (1=>80Mhz, 2=>40Mhz, etc) +#elif HCLK_FREQUENCY == 120000000 + qh.Init.ClockPrescaler = 2; // prescaler (1=>120Mhz, 2=>60Mhz, etc) + 8005146: f04f 0802 mov.w r8, #2 +#else +# error "testing needed" +#endif + qh.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; // dont need it? + 800514a: f04f 0908 mov.w r9, #8 + // - (during reads) 3 => 400ns 4 => 660ns 5+ => 1us + // - LATER: Errata 2.8.1 => says shall not use + qh.Init.ChipSelectBoundary = 0; + + // module init + HAL_StatusTypeDef rv = HAL_OSPI_Init(&qh); + 800514e: a80a add r0, sp, #40 ; 0x28 + qh.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // want standard mode (but octo only?) + 8005150: e9cd 330c strd r3, r3, [sp, #48] ; 0x30 + qh.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; // low clock between ops (required, see errata) + 8005154: e9cd 3310 strd r3, r3, [sp, #64] ; 0x40 + qh.Init.ChipSelectBoundary = 0; + 8005158: e9cd 3915 strd r3, r9, [sp, #84] ; 0x54 + qh.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; // maybe? + 800515c: 9214 str r2, [sp, #80] ; 0x50 + qh.Init.ClockPrescaler = 2; // prescaler (1=>120Mhz, 2=>60Mhz, etc) + 800515e: f8cd 8048 str.w r8, [sp, #72] ; 0x48 + HAL_StatusTypeDef rv = HAL_OSPI_Init(&qh); + 8005162: f005 ffe5 bl 800b130 + ASSERT(rv == HAL_OK); + 8005166: 4606 mov r6, r0 + 8005168: b110 cbz r0, 8005170 + 800516a: 484e ldr r0, [pc, #312] ; (80052a4 ) + 800516c: f7fb fc64 bl 8000a38 + + // do some SPI commands first + + // Exit Quad mode, to get to a known state, after first power-up + psram_send_byte(&qh, 0xf5, true); + 8005170: 463a mov r2, r7 + 8005172: 21f5 movs r1, #245 ; 0xf5 + 8005174: a80a add r0, sp, #40 ; 0x28 + 8005176: f7ff ff97 bl 80050a8 + + // Chip Reset sequence + psram_send_byte(&qh, 0x66, false); // reset enable + 800517a: 4632 mov r2, r6 + 800517c: 2166 movs r1, #102 ; 0x66 + 800517e: a80a add r0, sp, #40 ; 0x28 + 8005180: f7ff ff92 bl 80050a8 + + // Read Electronic ID + // - length not clear from datasheet, but repeats after 8 bytes + uint8_t psram_chip_eid[8]; + + { OSPI_RegularCmdTypeDef cmd = { + 8005184: ad32 add r5, sp, #200 ; 0xc8 + psram_send_byte(&qh, 0x99, false); // reset + 8005186: 4632 mov r2, r6 + 8005188: 2199 movs r1, #153 ; 0x99 + 800518a: a80a add r0, sp, #40 ; 0x28 + 800518c: f7ff ff8c bl 80050a8 + { OSPI_RegularCmdTypeDef cmd = { + 8005190: 2250 movs r2, #80 ; 0x50 + 8005192: 4631 mov r1, r6 + 8005194: 4628 mov r0, r5 + 8005196: f008 fbc5 bl 800d924 + 800519a: 239f movs r3, #159 ; 0x9f + 800519c: e9cd 3734 strd r3, r7, [sp, #208] ; 0xd0 + 80051a0: f44f 5a00 mov.w sl, #8192 ; 0x2000 + 80051a4: f44f 7380 mov.w r3, #256 ; 0x100 + 80051a8: e9cd 3a39 strd r3, sl, [sp, #228] ; 0xe4 + .DataMode = HAL_OSPI_DATA_1_LINE, + .NbData = sizeof(psram_chip_eid), // how much to read in bytes + }; + + // Start a "Indirection functional mode" request + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 80051ac: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + { OSPI_RegularCmdTypeDef cmd = { + 80051b0: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 80051b4: 4629 mov r1, r5 + 80051b6: a80a add r0, sp, #40 ; 0x28 + { OSPI_RegularCmdTypeDef cmd = { + 80051b8: e9cd 3940 strd r3, r9, [sp, #256] ; 0x100 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 80051bc: f006 f822 bl 800b204 + if(rv != HAL_OK) goto fail; + 80051c0: 2800 cmp r0, #0 + 80051c2: d15d bne.n 8005280 + + rv = HAL_OSPI_Receive(&qh, psram_chip_eid, HAL_MAX_DELAY); + 80051c4: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 80051c8: a903 add r1, sp, #12 + 80051ca: a80a add r0, sp, #40 ; 0x28 + 80051cc: f006 f94c bl 800b468 + if(rv != HAL_OK) goto fail; + 80051d0: 4606 mov r6, r0 + 80051d2: 2800 cmp r0, #0 + 80051d4: d154 bne.n 8005280 + } + + //puts2("PSRAM EID: "); + //hex_dump(psram_chip_eid, sizeof(psram_chip_eid)); + ASSERT(psram_chip_eid[0] == 0x0d); + 80051d6: f89d 300c ldrb.w r3, [sp, #12] + 80051da: 2b0d cmp r3, #13 + 80051dc: d1c5 bne.n 800516a + ASSERT(psram_chip_eid[1] == 0x5d); + 80051de: f89d 300d ldrb.w r3, [sp, #13] + 80051e2: 2b5d cmp r3, #93 ; 0x5d + 80051e4: d1c1 bne.n 800516a + // .. other bits seem pretty similar between devices, they don't claim they are UUID + + // Put into Quad mode + psram_send_byte(&qh, 0x35, false); // 0x35 = Enter Quad Mode + 80051e6: 4602 mov r2, r0 + 80051e8: 2135 movs r1, #53 ; 0x35 + 80051ea: a80a add r0, sp, #40 ; 0x28 + 80051ec: f7ff ff5c bl 80050a8 + + // Configure read/write cycles for mem-mapped mode + { OSPI_RegularCmdTypeDef cmd = { + 80051f0: 4631 mov r1, r6 + 80051f2: 224c movs r2, #76 ; 0x4c + 80051f4: a81f add r0, sp, #124 ; 0x7c + 80051f6: f008 fb95 bl 800d924 + 80051fa: f04f 0903 mov.w r9, #3 + 80051fe: f8cd 8078 str.w r8, [sp, #120] ; 0x78 + 8005202: f8cd 8080 str.w r8, [sp, #128] ; 0x80 + .DataMode = HAL_OSPI_DATA_4_LINES, + .NbData = 0, // don't care / TBD? + }; + + // Config for write + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8005206: a91e add r1, sp, #120 ; 0x78 + { OSPI_RegularCmdTypeDef cmd = { + 8005208: f44f 7840 mov.w r8, #768 ; 0x300 + 800520c: f04f 7640 mov.w r6, #50331648 ; 0x3000000 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8005210: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 8005214: a80a add r0, sp, #40 ; 0x28 + { OSPI_RegularCmdTypeDef cmd = { + 8005216: e9cd 8a25 strd r8, sl, [sp, #148] ; 0x94 + 800521a: f8cd 9084 str.w r9, [sp, #132] ; 0x84 + 800521e: 962c str r6, [sp, #176] ; 0xb0 + rv = HAL_OSPI_Command(&qh, &cmd, HAL_MAX_DELAY); + 8005220: f005 fff0 bl 800b204 + if(rv != HAL_OK) goto fail; + 8005224: 4601 mov r1, r0 + 8005226: bb58 cbnz r0, 8005280 + + // .. for read + OSPI_RegularCmdTypeDef cmd2 = { + 8005228: 224c movs r2, #76 ; 0x4c + 800522a: a833 add r0, sp, #204 ; 0xcc + 800522c: f008 fb7a bl 800d924 + 8005230: 23eb movs r3, #235 ; 0xeb + 8005232: e9cd 3934 strd r3, r9, [sp, #208] ; 0xd0 + .DataMode = HAL_OSPI_DATA_4_LINES, + .NbData = 0, // don't care / TBD? + }; + + // Config for read + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 8005236: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + OSPI_RegularCmdTypeDef cmd2 = { + 800523a: 2306 movs r3, #6 + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 800523c: 4629 mov r1, r5 + 800523e: a80a add r0, sp, #40 ; 0x28 + OSPI_RegularCmdTypeDef cmd2 = { + 8005240: e9cd 8a39 strd r8, sl, [sp, #228] ; 0xe4 + 8005244: 9732 str r7, [sp, #200] ; 0xc8 + 8005246: 9640 str r6, [sp, #256] ; 0x100 + 8005248: 9343 str r3, [sp, #268] ; 0x10c + rv = HAL_OSPI_Command(&qh, &cmd2, HAL_MAX_DELAY); + 800524a: f005 ffdb bl 800b204 + if(rv != HAL_OK) goto fail; + 800524e: b9b8 cbnz r0, 8005280 + } + + // config for memmap + { OSPI_MemoryMappedTypeDef mmap = { + 8005250: e9d4 0101 ldrd r0, r1, [r4, #4] + 8005254: e885 0003 stmia.w r5, {r0, r1} + // Need this so that CS lines returns to inactive sometimes. + .TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_ENABLE, + .TimeOutPeriod = 16, // no idea, max value 0xffff + }; + + rv = HAL_OSPI_MemoryMapped(&qh, &mmap); + 8005258: 4629 mov r1, r5 + 800525a: a80a add r0, sp, #40 ; 0x28 + 800525c: f006 f9ea bl 800b634 + if(rv != HAL_OK) goto fail; + 8005260: b970 cbnz r0, 8005280 +#else + // Only a quick operational check only here. Non-destructive. + { __IO uint32_t *ptr = (uint32_t *)(PSRAM_BASE+PSRAM_SIZE-4); + uint32_t tmp; + + tmp = *ptr; + 8005262: 4b11 ldr r3, [pc, #68] ; (80052a8 ) + *ptr = 0x55aa1234; + 8005264: 4a11 ldr r2, [pc, #68] ; (80052ac ) + tmp = *ptr; + 8005266: f8d3 1ffc ldr.w r1, [r3, #4092] ; 0xffc + *ptr = 0x55aa1234; + 800526a: f8c3 2ffc str.w r2, [r3, #4092] ; 0xffc + if(*ptr != 0x55aa1234) goto fail; + 800526e: f8d3 0ffc ldr.w r0, [r3, #4092] ; 0xffc + 8005272: 4290 cmp r0, r2 + 8005274: d104 bne.n 8005280 + *ptr = tmp; + 8005276: f8c3 1ffc str.w r1, [r3, #4092] ; 0xffc + + oled_setup(); + oled_show(screen_fatal); + + LOCKUP_FOREVER(); +} + 800527a: b046 add sp, #280 ; 0x118 + 800527c: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + puts("PSRAM fail"); + 8005280: 480b ldr r0, [pc, #44] ; (80052b0 ) + 8005282: f7ff fef1 bl 8005068 + oled_setup(); + 8005286: f7fb fd75 bl 8000d74 + oled_show(screen_fatal); + 800528a: 480a ldr r0, [pc, #40] ; (80052b4 ) + 800528c: f7fb fef2 bl 8001074 + LOCKUP_FOREVER(); + 8005290: f7fe fcf2 bl 8003c78 + 8005294: 40021000 .word 0x40021000 + 8005298: 0801080c .word 0x0801080c + 800529c: 48001000 .word 0x48001000 + 80052a0: a0001000 .word 0xa0001000 + 80052a4: 0801046c .word 0x0801046c + 80052a8: 907ff000 .word 0x907ff000 + 80052ac: 55aa1234 .word 0x55aa1234 + 80052b0: 080107de .word 0x080107de + 80052b4: 0800e58d .word 0x0800e58d + +080052b8 : + +// psram_wipe() +// + void +psram_wipe(void) +{ + 80052b8: b508 push {r3, lr} + if(OCTOSPI1->CR == 0) return; // PSRAM not enabled (yet?) + 80052ba: 4b06 ldr r3, [pc, #24] ; (80052d4 ) + 80052bc: 681b ldr r3, [r3, #0] + 80052be: b143 cbz r3, 80052d2 + + // Fast! But real; maybe 150ms + //puts2("PSRAM Wipe: "); + memset4((uint32_t *)PSRAM_BASE, rng_sample(), PSRAM_SIZE); + 80052c0: f7fd face bl 8002860 + 80052c4: f04f 4310 mov.w r3, #2415919104 ; 0x90000000 + *dest = value; + 80052c8: f843 0b04 str.w r0, [r3], #4 + for(; byte_len; byte_len-=4, dest++) { + 80052cc: f113 4fdf cmn.w r3, #1870659584 ; 0x6f800000 + 80052d0: d1fa bne.n 80052c8 + //puts("done"); +} + 80052d2: bd08 pop {r3, pc} + 80052d4: a0001000 .word 0xa0001000 + +080052d8 : +// NOTE: Incoming start address is typically not aligned. +// + void +psram_do_upgrade(const uint8_t *start, uint32_t size) +{ + ASSERT(size >= FW_MIN_LENGTH); + 80052d8: f5b1 2f80 cmp.w r1, #262144 ; 0x40000 +{ + 80052dc: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + 80052e0: 4606 mov r6, r0 + 80052e2: 460d mov r5, r1 + ASSERT(size >= FW_MIN_LENGTH); + 80052e4: d202 bcs.n 80052ec + 80052e6: 481e ldr r0, [pc, #120] ; (8005360 ) + 80052e8: f7fb fba6 bl 8000a38 + + // In case of reset/crash, we can recover, so save + // what we need for that -- yes, we will re-verify signatures + volatile recovery_header_t *h = RECHDR_POS; + h->start = start; + 80052ec: 4b1d ldr r3, [pc, #116] ; (8005364 ) + h->size = size; + h->magic1 = RECHDR_MAGIC1; + 80052ee: 4a1e ldr r2, [pc, #120] ; (8005368 ) + h->start = start; + 80052f0: 6058 str r0, [r3, #4] + h->size = size; + 80052f2: 6099 str r1, [r3, #8] + h->magic1 = RECHDR_MAGIC1; + 80052f4: 601a str r2, [r3, #0] + h->magic2 = RECHDR_MAGIC2; + 80052f6: 4a1d ldr r2, [pc, #116] ; (800536c ) + 80052f8: 60da str r2, [r3, #12] + + flash_setup0(); + 80052fa: f7fc ff4f bl 800219c + flash_unlock(); + 80052fe: f7fc ff71 bl 80021e4 + for(uint32_t pos=0; pos < size; pos += 8) { + uint32_t dest = FIRMWARE_START+pos; + + if(dest % (4*FLASH_ERASE_SIZE) == 0) { + // show some progress + oled_show_progress(screen_upgrading, pos*100/size); + 8005302: f8df 906c ldr.w r9, [pc, #108] ; 8005370 + for(uint32_t pos=0; pos < size; pos += 8) { + 8005306: 2400 movs r4, #0 + oled_show_progress(screen_upgrading, pos*100/size); + 8005308: f04f 0864 mov.w r8, #100 ; 0x64 + uint32_t dest = FIRMWARE_START+pos; + 800530c: f104 6700 add.w r7, r4, #134217728 ; 0x8000000 + if(dest % (4*FLASH_ERASE_SIZE) == 0) { + 8005310: f3c4 030d ubfx r3, r4, #0, #14 + 8005314: f507 3700 add.w r7, r7, #131072 ; 0x20000 + 8005318: b933 cbnz r3, 8005328 + oled_show_progress(screen_upgrading, pos*100/size); + 800531a: fb08 f104 mul.w r1, r8, r4 + 800531e: 4648 mov r0, r9 + 8005320: fbb1 f1f5 udiv r1, r1, r5 + 8005324: f7fb ff20 bl 8001168 + } + + if(dest % FLASH_ERASE_SIZE == 0) { + 8005328: f3c7 030b ubfx r3, r7, #0, #12 + 800532c: b923 cbnz r3, 8005338 + // page erase as we go + rv = flash_page_erase(dest); + 800532e: 4638 mov r0, r7 + 8005330: f008 fb32 bl 800d998 <__flash_page_erase_veneer> + puts2("erase rv="); + puthex2(rv); + putchar('\n'); + } +#endif + ASSERT(rv == 0); + 8005334: 2800 cmp r0, #0 + 8005336: d1d6 bne.n 80052e6 + } + + memcpy(&tmp, start+pos, 8); + 8005338: 1932 adds r2, r6, r4 + 800533a: 5930 ldr r0, [r6, r4] + 800533c: 6851 ldr r1, [r2, #4] + 800533e: 466b mov r3, sp + 8005340: c303 stmia r3!, {r0, r1} + rv = flash_burn(dest, tmp); + 8005342: 4638 mov r0, r7 + 8005344: e9dd 2300 ldrd r2, r3, [sp] + 8005348: f008 fb22 bl 800d990 <__flash_burn_veneer> + puts2(" addr="); + puthex8(dest); + putchar('\n'); + } +#endif + ASSERT(rv == 0); + 800534c: 2800 cmp r0, #0 + 800534e: d1ca bne.n 80052e6 + for(uint32_t pos=0; pos < size; pos += 8) { + 8005350: 3408 adds r4, #8 + 8005352: 42a5 cmp r5, r4 + 8005354: d8da bhi.n 800530c + } + + flash_lock(); +} + 8005356: b003 add sp, #12 + 8005358: e8bd 43f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, lr} + flash_lock(); + 800535c: f7fc bf3a b.w 80021d4 + 8005360: 0801046c .word 0x0801046c + 8005364: 907ff800 .word 0x907ff800 + 8005368: dbcc8350 .word 0xdbcc8350 + 800536c: bafcfba3 .word 0xbafcfba3 + 8005370: 0800ff35 .word 0x0800ff35 + +08005374 : +{ + 8005374: b510 push {r4, lr} + if( (h->magic1 != RECHDR_MAGIC1) + 8005376: 4c1f ldr r4, [pc, #124] ; (80053f4 ) + 8005378: 4b1f ldr r3, [pc, #124] ; (80053f8 ) + 800537a: 6822 ldr r2, [r4, #0] + 800537c: 429a cmp r2, r3 +{ + 800537e: b088 sub sp, #32 + if( (h->magic1 != RECHDR_MAGIC1) + 8005380: d113 bne.n 80053aa + || (h->magic2 != RECHDR_MAGIC2) + 8005382: 68e2 ldr r2, [r4, #12] + 8005384: 4b1d ldr r3, [pc, #116] ; (80053fc ) + 8005386: 429a cmp r2, r3 + 8005388: d10f bne.n 80053aa + || ((uint32_t)h->start < PSRAM_BASE) + 800538a: 6863 ldr r3, [r4, #4] + 800538c: f1b3 4f10 cmp.w r3, #2415919104 ; 0x90000000 + 8005390: d30b bcc.n 80053aa + || ((uint32_t)h->start >= PSRAM_BASE+(PSRAM_SIZE/2)) + 8005392: 6862 ldr r2, [r4, #4] + 8005394: 4b1a ldr r3, [pc, #104] ; (8005400 ) + 8005396: 429a cmp r2, r3 + 8005398: d807 bhi.n 80053aa + || (h->size > FW_MAX_LENGTH_MK4) + 800539a: 68a3 ldr r3, [r4, #8] + 800539c: f5b3 1ff0 cmp.w r3, #1966080 ; 0x1e0000 + 80053a0: d803 bhi.n 80053aa + || (h->size < FW_MIN_LENGTH) + 80053a2: 68a3 ldr r3, [r4, #8] + 80053a4: f5b3 2f80 cmp.w r3, #262144 ; 0x40000 + 80053a8: d205 bcs.n 80053b6 + puts("PSR: nada"); + 80053aa: 4816 ldr r0, [pc, #88] ; (8005404 ) + puts("PSR: version"); + 80053ac: f7ff fe5c bl 8005068 +} + 80053b0: 2000 movs r0, #0 + 80053b2: b008 add sp, #32 + 80053b4: bd10 pop {r4, pc} + bool ok = verify_firmware_in_ram(h->start, h->size, world_check); + 80053b6: 6860 ldr r0, [r4, #4] + 80053b8: 68a1 ldr r1, [r4, #8] + 80053ba: 466a mov r2, sp + 80053bc: f7fc fd34 bl 8001e28 + if(!ok) { + 80053c0: b908 cbnz r0, 80053c6 + puts("PSR: !check"); + 80053c2: 4811 ldr r0, [pc, #68] ; (8005408 ) + 80053c4: e7f2 b.n 80053ac + if(!verify_world_checksum(world_check)) { + 80053c6: 4668 mov r0, sp + 80053c8: f7fc fd82 bl 8001ed0 + 80053cc: b908 cbnz r0, 80053d2 + puts("PSR: version"); + 80053ce: 480f ldr r0, [pc, #60] ; (800540c ) + 80053d0: e7ec b.n 80053ac + psram_do_upgrade(h->start, h->size); + 80053d2: 6860 ldr r0, [r4, #4] + 80053d4: 68a1 ldr r1, [r4, #8] + 80053d6: f7ff ff7f bl 80052d8 + 80053da: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80053de: 490c ldr r1, [pc, #48] ; (8005410 ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80053e0: 4b0c ldr r3, [pc, #48] ; (8005414 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80053e2: 68ca ldr r2, [r1, #12] + 80053e4: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80053e8: 4313 orrs r3, r2 + 80053ea: 60cb str r3, [r1, #12] + 80053ec: f3bf 8f4f dsb sy + __NOP(); + 80053f0: bf00 nop + for(;;) /* wait until reset */ + 80053f2: e7fd b.n 80053f0 + 80053f4: 907ff800 .word 0x907ff800 + 80053f8: dbcc8350 .word 0xdbcc8350 + 80053fc: bafcfba3 .word 0xbafcfba3 + 8005400: 903fffff .word 0x903fffff + 8005404: 080107e9 .word 0x080107e9 + 8005408: 080107f3 .word 0x080107f3 + 800540c: 080107ff .word 0x080107ff + 8005410: e000ed00 .word 0xe000ed00 + 8005414: 05fa0004 .word 0x05fa0004 + +08005418 : + +// sdcard_light() +// + void inline +sdcard_light(bool on) +{ + 8005418: 4602 mov r2, r0 + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, !!on); // turn LED off + 800541a: 2180 movs r1, #128 ; 0x80 + 800541c: 4801 ldr r0, [pc, #4] ; (8005424 ) + 800541e: f7fc b863 b.w 80014e8 + 8005422: bf00 nop + 8005424: 48000800 .word 0x48000800 + +08005428 : + +// sdcard_is_inserted() +// + bool +sdcard_is_inserted(void) +{ + 8005428: b508 push {r3, lr} +#ifdef FOR_Q1_ONLY + return !HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_3); // PD3 - inserted when low (Q) + 800542a: 2108 movs r1, #8 + 800542c: 4803 ldr r0, [pc, #12] ; (800543c ) + 800542e: f7fc f855 bl 80014dc +#else + return !!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13); // PC13 - inserted when high (Mk4) +#endif +} + 8005432: fab0 f080 clz r0, r0 + 8005436: 0940 lsrs r0, r0, #5 + 8005438: bd08 pop {r3, pc} + 800543a: bf00 nop + 800543c: 48000c00 .word 0x48000c00 + +08005440 : + +// sdcard_try_file() +// + void +sdcard_try_file(uint32_t blk_pos) +{ + 8005440: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8005444: 4606 mov r6, r0 + 8005446: f5ad 7d0a sub.w sp, sp, #552 ; 0x228 + oled_show(screen_verify); + 800544a: 4832 ldr r0, [pc, #200] ; (8005514 ) + uint8_t *ps = (uint8_t *)PSRAM_BASE; + //uint8_t buf[512*8]; // half of all our SRAM 0x00002000 + uint8_t buf[512]; // slower, but works. + + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + int rv = HAL_SD_ReadBlocks(&hsd, buf, blk_pos+(off/512), sizeof(buf)/512, 60000); + 800544c: f8df 80e4 ldr.w r8, [pc, #228] ; 8005534 + oled_show(screen_verify); + 8005450: f7fb fe10 bl 8001074 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 8005454: 2500 movs r5, #0 + int rv = HAL_SD_ReadBlocks(&hsd, buf, blk_pos+(off/512), sizeof(buf)/512, 60000); + 8005456: f64e 2760 movw r7, #60000 ; 0xea60 + 800545a: 9700 str r7, [sp, #0] + 800545c: 2301 movs r3, #1 + 800545e: eb06 2255 add.w r2, r6, r5, lsr #9 + 8005462: a90a add r1, sp, #40 ; 0x28 + 8005464: 4640 mov r0, r8 + 8005466: f006 fe61 bl 800c12c + if(rv != HAL_OK) { + 800546a: 4604 mov r4, r0 + 800546c: b130 cbz r0, 800547c + puts("long read fail"); + 800546e: 482a ldr r0, [pc, #168] ; (8005518 ) + + // Check we have the **right** firmware, based on the world check sum + // but don't set the light at this point. + // - this includes check over bootrom (ourselves) + if(!verify_world_checksum(world_check)) { + puts("wrong world"); + 8005470: f7ff fdfa bl 8005068 + // Do the upgrade, using PSRAM data. + psram_do_upgrade(start, len); + + // done + NVIC_SystemReset(); +} + 8005474: f50d 7d0a add.w sp, sp, #552 ; 0x228 + 8005478: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + memcpy(ps + off, buf, sizeof(buf)); + 800547c: f105 4010 add.w r0, r5, #2415919104 ; 0x90000000 + 8005480: f44f 7200 mov.w r2, #512 ; 0x200 + 8005484: a90a add r1, sp, #40 ; 0x28 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 8005486: f505 7500 add.w r5, r5, #512 ; 0x200 + memcpy(ps + off, buf, sizeof(buf)); + 800548a: f008 fa3d bl 800d908 + for(uint32_t off = 0; off < FW_MAX_LENGTH_MK4; off += sizeof(buf)) { + 800548e: f5b5 1ff0 cmp.w r5, #1966080 ; 0x1e0000 + 8005492: d1e2 bne.n 800545a + for(int idx=0; idxtargets; idx++) { + 8005494: f04f 4310 mov.w r3, #2415919104 ; 0x90000000 + if(elem->addr == FIRMWARE_START) { + 8005498: 4d20 ldr r5, [pc, #128] ; (800551c ) + for(int idx=0; idxtargets; idx++) { + 800549a: 7a99 ldrb r1, [r3, #10] + 800549c: 4620 mov r0, r4 + ptr += sizeof(DFUFile_t); + 800549e: 330b adds r3, #11 + for(int idx=0; idxtargets; idx++) { + 80054a0: 4288 cmp r0, r1 + 80054a2: db01 blt.n 80054a8 + puts("DFU parse fail"); + 80054a4: 481e ldr r0, [pc, #120] ; (8005520 ) + 80054a6: e7e3 b.n 8005470 + for(int ei=0; eielements; ei++) { + 80054a8: f8d3 610e ldr.w r6, [r3, #270] ; 0x10e + 80054ac: 2200 movs r2, #0 + ptr += sizeof(DFUTarget_t); + 80054ae: f503 7389 add.w r3, r3, #274 ; 0x112 + for(int ei=0; eielements; ei++) { + 80054b2: 42b2 cmp r2, r6 + 80054b4: d101 bne.n 80054ba + for(int idx=0; idxtargets; idx++) { + 80054b6: 3001 adds r0, #1 + 80054b8: e7f2 b.n 80054a0 + ptr += sizeof(DFUElement_t); + 80054ba: 461c mov r4, r3 + if(elem->addr == FIRMWARE_START) { + 80054bc: f854 7b08 ldr.w r7, [r4], #8 + 80054c0: 42af cmp r7, r5 + 80054c2: d110 bne.n 80054e6 + *target_size = elem->size; + 80054c4: 685d ldr r5, [r3, #4] + bool ok = verify_firmware_in_ram(start, len, world_check); + 80054c6: aa02 add r2, sp, #8 + 80054c8: 4629 mov r1, r5 + 80054ca: 4620 mov r0, r4 + 80054cc: f7fc fcac bl 8001e28 + if(!ok) return; + 80054d0: 2800 cmp r0, #0 + 80054d2: d0cf beq.n 8005474 + puts("good firmware"); + 80054d4: 4813 ldr r0, [pc, #76] ; (8005524 ) + 80054d6: f7ff fdc7 bl 8005068 + if(!verify_world_checksum(world_check)) { + 80054da: a802 add r0, sp, #8 + 80054dc: f7fc fcf8 bl 8001ed0 + 80054e0: b920 cbnz r0, 80054ec + puts("wrong world"); + 80054e2: 4811 ldr r0, [pc, #68] ; (8005528 ) + 80054e4: e7c4 b.n 8005470 + for(int ei=0; eielements; ei++) { + 80054e6: 3201 adds r2, #1 + ptr += sizeof(DFUElement_t); + 80054e8: 4623 mov r3, r4 + 80054ea: e7e2 b.n 80054b2 + sdcard_light(false); + 80054ec: 2000 movs r0, #0 + 80054ee: f7ff ff93 bl 8005418 + psram_do_upgrade(start, len); + 80054f2: 4629 mov r1, r5 + 80054f4: 4620 mov r0, r4 + 80054f6: f7ff feef bl 80052d8 + 80054fa: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80054fe: 490b ldr r1, [pc, #44] ; (800552c ) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8005500: 4b0b ldr r3, [pc, #44] ; (8005530 ) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 8005502: 68ca ldr r2, [r1, #12] + 8005504: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 8005508: 4313 orrs r3, r2 + 800550a: 60cb str r3, [r1, #12] + 800550c: f3bf 8f4f dsb sy + __NOP(); + 8005510: bf00 nop + for(;;) /* wait until reset */ + 8005512: e7fd b.n 8005510 + 8005514: 0801004d .word 0x0801004d + 8005518: 08010828 .word 0x08010828 + 800551c: 08020000 .word 0x08020000 + 8005520: 08010837 .word 0x08010837 + 8005524: 08010846 .word 0x08010846 + 8005528: 08010854 .word 0x08010854 + 800552c: e000ed00 .word 0xe000ed00 + 8005530: 05fa0004 .word 0x05fa0004 + 8005534: 2009e224 .word 0x2009e224 + +08005538 : + +// sdcard_search() +// + void +sdcard_search(void) +{ + 8005538: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + oled_show(screen_search); + 800553c: 4861 ldr r0, [pc, #388] ; (80056c4 ) +{ + 800553e: f5ad 7d05 sub.w sp, sp, #532 ; 0x214 + oled_show(screen_search); + 8005542: f7fb fd97 bl 8001074 + + if(!sdcard_is_inserted()) return; + 8005546: f7ff ff6f bl 8005428 + 800554a: 2800 cmp r0, #0 + 800554c: f000 8095 beq.w 800567a + __HAL_RCC_SDMMC1_CLK_ENABLE(); + 8005550: f8df 81b0 ldr.w r8, [pc, #432] ; 8005704 + + uint32_t num_blocks; + + // open card (power it) and get details, do setup + puts2("sdcard_search: "); + 8005554: 485c ldr r0, [pc, #368] ; (80056c8 ) + { GPIO_InitTypeDef setup = { + 8005556: 4c5d ldr r4, [pc, #372] ; (80056cc ) + puts2("sdcard_search: "); + 8005558: f7ff fcf8 bl 8004f4c + __HAL_RCC_SDMMC1_CLK_ENABLE(); + 800555c: f8d8 304c ldr.w r3, [r8, #76] ; 0x4c + 8005560: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 8005564: f8c8 304c str.w r3, [r8, #76] ; 0x4c + 8005568: f8d8 304c ldr.w r3, [r8, #76] ; 0x4c + 800556c: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 8005570: 9303 str r3, [sp, #12] + 8005572: 9b03 ldr r3, [sp, #12] + { GPIO_InitTypeDef setup = { + 8005574: cc0f ldmia r4!, {r0, r1, r2, r3} + 8005576: ad04 add r5, sp, #16 + 8005578: c50f stmia r5!, {r0, r1, r2, r3} + 800557a: f854 3b04 ldr.w r3, [r4], #4 + 800557e: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOC, &setup); + 8005580: 4853 ldr r0, [pc, #332] ; (80056d0 ) + 8005582: a904 add r1, sp, #16 + 8005584: f7fb fe36 bl 80011f4 + GPIO_InitTypeDef setup = { + 8005588: 2700 movs r7, #0 + 800558a: f44f 5600 mov.w r6, #8192 ; 0x2000 + HAL_GPIO_Init(GPIOC, &setup); + 800558e: 4850 ldr r0, [pc, #320] ; (80056d0 ) + GPIO_InitTypeDef setup = { + 8005590: 9708 str r7, [sp, #32] + HAL_GPIO_Init(GPIOC, &setup); + 8005592: a904 add r1, sp, #16 + GPIO_InitTypeDef setup = { + 8005594: f04f 0901 mov.w r9, #1 + 8005598: e9cd 6904 strd r6, r9, [sp, #16] + 800559c: e9cd 7706 strd r7, r7, [sp, #24] + HAL_GPIO_Init(GPIOC, &setup); + 80055a0: f7fb fe28 bl 80011f4 + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0); // select A + 80055a4: 4631 mov r1, r6 + 80055a6: 484a ldr r0, [pc, #296] ; (80056d0 ) + 80055a8: 463a mov r2, r7 + 80055aa: f7fb ff9d bl 80014e8 + { GPIO_InitTypeDef setup = { + 80055ae: cc0f ldmia r4!, {r0, r1, r2, r3} + 80055b0: ae04 add r6, sp, #16 + 80055b2: c60f stmia r6!, {r0, r1, r2, r3} + 80055b4: 6823 ldr r3, [r4, #0] + 80055b6: 602b str r3, [r5, #0] + HAL_GPIO_Init(GPIOD, &setup); + 80055b8: a904 add r1, sp, #16 + 80055ba: 4846 ldr r0, [pc, #280] ; (80056d4 ) + memset(&hsd, 0, sizeof(SD_HandleTypeDef)); + 80055bc: 4d46 ldr r5, [pc, #280] ; (80056d8 ) + HAL_GPIO_Init(GPIOD, &setup); + 80055be: f7fb fe19 bl 80011f4 + __HAL_RCC_SDMMC1_FORCE_RESET(); + 80055c2: f8d8 302c ldr.w r3, [r8, #44] ; 0x2c + 80055c6: f443 0380 orr.w r3, r3, #4194304 ; 0x400000 + 80055ca: f8c8 302c str.w r3, [r8, #44] ; 0x2c + __HAL_RCC_SDMMC1_RELEASE_RESET(); + 80055ce: f8d8 302c ldr.w r3, [r8, #44] ; 0x2c + 80055d2: f423 0380 bic.w r3, r3, #4194304 ; 0x400000 + 80055d6: f8c8 302c str.w r3, [r8, #44] ; 0x2c + sdcard_setup(); + delay_ms(100); + 80055da: 2064 movs r0, #100 ; 0x64 + 80055dc: f7fe fa52 bl 8003a84 + memset(&hsd, 0, sizeof(SD_HandleTypeDef)); + 80055e0: 2280 movs r2, #128 ; 0x80 + 80055e2: 4639 mov r1, r7 + 80055e4: 4628 mov r0, r5 + 80055e6: f008 f99d bl 800d924 + puts2("sdcard_probe: "); + 80055ea: 483c ldr r0, [pc, #240] ; (80056dc ) + 80055ec: f7ff fcae bl 8004f4c + hsd.Instance = SDMMC1; + 80055f0: 4b3b ldr r3, [pc, #236] ; (80056e0 ) + hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + 80055f2: 612f str r7, [r5, #16] + hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + 80055f4: e9c5 3700 strd r3, r7, [r5] + hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE; + 80055f8: f44f 5380 mov.w r3, #4096 ; 0x1000 + hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; + 80055fc: e9c5 3702 strd r3, r7, [r5, #8] + int rv = HAL_SD_Init(&hsd); + 8005600: 4628 mov r0, r5 + hsd.Init.ClockDiv = SDMMC_TRANSFER_CLK_DIV; + 8005602: 2303 movs r3, #3 + 8005604: 616b str r3, [r5, #20] + int rv = HAL_SD_Init(&hsd); + 8005606: f007 fb0b bl 800cc20 + if(rv != HAL_OK) { + 800560a: 4604 mov r4, r0 + 800560c: b130 cbz r0, 800561c + puts("init fail"); + 800560e: 4835 ldr r0, [pc, #212] ; (80056e4 ) + oled_show_progress(screen_search, pos*100 / num_blocks); + sdcard_light(true); + } + } + +} + 8005610: f50d 7d05 add.w sp, sp, #532 ; 0x214 + 8005614: e8bd 43f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, lr} + puts("bsize?"); + 8005618: f7ff bd26 b.w 8005068 + sdcard_light(true); + 800561c: 4648 mov r0, r9 + 800561e: f7ff fefb bl 8005418 + rv = HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO); + 8005622: 4621 mov r1, r4 + 8005624: 4628 mov r0, r5 + 8005626: f007 fbd3 bl 800cdd0 + if(rv != HAL_OK) { + 800562a: b108 cbz r0, 8005630 + puts("speed"); + 800562c: 482e ldr r0, [pc, #184] ; (80056e8 ) + 800562e: e7ef b.n 8005610 + rv = HAL_SD_ConfigWideBusOperation(&hsd, SDMMC_BUS_WIDE_4B); + 8005630: f44f 4180 mov.w r1, #16384 ; 0x4000 + 8005634: 4628 mov r0, r5 + 8005636: f007 fa1d bl 800ca74 + if(rv != HAL_OK) { + 800563a: 4604 mov r4, r0 + 800563c: b108 cbz r0, 8005642 + puts("wide"); + 800563e: 482b ldr r0, [pc, #172] ; (80056ec ) + 8005640: e7e6 b.n 8005610 + if(hsd.SdCard.BlockSize != 512) { + 8005642: 6d2b ldr r3, [r5, #80] ; 0x50 + 8005644: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 8005648: d001 beq.n 800564e + puts("bsize?"); + 800564a: 4829 ldr r0, [pc, #164] ; (80056f0 ) + 800564c: e7e0 b.n 8005610 + puts("ok"); + 800564e: 4829 ldr r0, [pc, #164] ; (80056f4 ) + *num_blocks = hsd.SdCard.BlockNbr; + 8005650: 6cee ldr r6, [r5, #76] ; 0x4c + if(memcmp(blk, "DfuSe", 5) == 0) { + 8005652: 4f29 ldr r7, [pc, #164] ; (80056f8 ) + oled_show_progress(screen_search, pos*100 / num_blocks); + 8005654: f8df 806c ldr.w r8, [pc, #108] ; 80056c4 + puts("ok"); + 8005658: f7ff fd06 bl 8005068 + for(int pos=0; pos + int rv = HAL_SD_ReadBlocks(&hsd, blk, pos, 1, 60000); + 8005660: f64e 2360 movw r3, #60000 ; 0xea60 + 8005664: 9300 str r3, [sp, #0] + 8005666: 4622 mov r2, r4 + 8005668: 2301 movs r3, #1 + 800566a: a904 add r1, sp, #16 + 800566c: 4628 mov r0, r5 + 800566e: f006 fd5d bl 800c12c + if(rv != HAL_OK) { + 8005672: b130 cbz r0, 8005682 + puts("fail read"); + 8005674: 4821 ldr r0, [pc, #132] ; (80056fc ) + 8005676: f7ff fcf7 bl 8005068 +} + 800567a: f50d 7d05 add.w sp, sp, #532 ; 0x214 + 800567e: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + if(memcmp(blk, "DfuSe", 5) == 0) { + 8005682: 2205 movs r2, #5 + 8005684: 4639 mov r1, r7 + 8005686: a804 add r0, sp, #16 + 8005688: f008 f92e bl 800d8e8 + 800568c: b9b0 cbnz r0, 80056bc + puts2("found @ "); + 800568e: 481c ldr r0, [pc, #112] ; (8005700 ) + 8005690: f7ff fc5c bl 8004f4c + puthex8(pos); + 8005694: 4620 mov r0, r4 + 8005696: f7ff fcb5 bl 8005004 + putchar('\n'); + 800569a: 200a movs r0, #10 + 800569c: f7ff fc6a bl 8004f74 + sdcard_try_file(pos); + 80056a0: 4620 mov r0, r4 + 80056a2: f7ff fecd bl 8005440 + oled_show_progress(screen_search, pos*100 / num_blocks); + 80056a6: 2164 movs r1, #100 ; 0x64 + 80056a8: 4640 mov r0, r8 + 80056aa: 4361 muls r1, r4 + 80056ac: fbb1 f1f6 udiv r1, r1, r6 + 80056b0: f7fb fd5a bl 8001168 + sdcard_light(true); + 80056b4: 2001 movs r0, #1 + 80056b6: f7ff feaf bl 8005418 + 80056ba: e001 b.n 80056c0 + if(pos % 128 == 0) { + 80056bc: 0663 lsls r3, r4, #25 + 80056be: d0f2 beq.n 80056a6 + for(int pos=0; pos + 80056c4: 0800fa95 .word 0x0800fa95 + 80056c8: 08010860 .word 0x08010860 + 80056cc: 080108c8 .word 0x080108c8 + 80056d0: 48000800 .word 0x48000800 + 80056d4: 48000c00 .word 0x48000c00 + 80056d8: 2009e224 .word 0x2009e224 + 80056dc: 08010870 .word 0x08010870 + 80056e0: 50062400 .word 0x50062400 + 80056e4: 0801087f .word 0x0801087f + 80056e8: 08010889 .word 0x08010889 + 80056ec: 0801088f .word 0x0801088f + 80056f0: 08010894 .word 0x08010894 + 80056f4: 0801089b .word 0x0801089b + 80056f8: 080108a8 .word 0x080108a8 + 80056fc: 0801089e .word 0x0801089e + 8005700: 080108ae .word 0x080108ae + 8005704: 40021000 .word 0x40021000 + +08005708 : + +// sdcard_recovery() +// + void +sdcard_recovery(void) +{ + 8005708: b508 push {r3, lr} + // Use SDCard to recover. Must be precise version they tried to + // install before, and will be slow AF. + + puts("Recovery mode."); + 800570a: 480b ldr r0, [pc, #44] ; (8005738 ) + while(1) { + // .. need them to insert a card + + sdcard_light(false); + while(!sdcard_is_inserted()) { + oled_show(screen_recovery); + 800570c: 4c0b ldr r4, [pc, #44] ; (800573c ) + puts("Recovery mode."); + 800570e: f7ff fcab bl 8005068 + sdcard_light(false); + 8005712: 2000 movs r0, #0 + 8005714: f7ff fe80 bl 8005418 + while(!sdcard_is_inserted()) { + 8005718: f7ff fe86 bl 8005428 + 800571c: b128 cbz r0, 800572a + delay_ms(200); + } + + // look for binary, will reset system if successful + sdcard_light(true); + 800571e: 2001 movs r0, #1 + 8005720: f7ff fe7a bl 8005418 + sdcard_search(); + 8005724: f7ff ff08 bl 8005538 + sdcard_light(false); + 8005728: e7f3 b.n 8005712 + oled_show(screen_recovery); + 800572a: 4620 mov r0, r4 + 800572c: f7fb fca2 bl 8001074 + delay_ms(200); + 8005730: 20c8 movs r0, #200 ; 0xc8 + 8005732: f7fe f9a7 bl 8003a84 + 8005736: e7ef b.n 8005718 + 8005738: 080108b7 .word 0x080108b7 + 800573c: 0800e85f .word 0x0800e85f + +08005740 : +#include + +// so we don't need stm32l4xx_hal_hash_ex.c +HAL_StatusTypeDef HAL_HASHEx_SHA256_Accmlt(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size) +{ + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA256); + 8005740: 4b01 ldr r3, [pc, #4] ; (8005748 ) + 8005742: f005 ba3b b.w 800abbc + 8005746: bf00 nop + 8005748: 00040080 .word 0x00040080 + +0800574c : +} + +HAL_StatusTypeDef HAL_HASHEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 800574c: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256); + 800574e: 4c04 ldr r4, [pc, #16] ; (8005760 ) + 8005750: 9401 str r4, [sp, #4] + 8005752: 9c04 ldr r4, [sp, #16] + 8005754: 9400 str r4, [sp, #0] + 8005756: f005 f98d bl 800aa74 +} + 800575a: b002 add sp, #8 + 800575c: bd10 pop {r4, pc} + 800575e: bf00 nop + 8005760: 00040080 .word 0x00040080 + +08005764 : + +HAL_StatusTypeDef HAL_HMACEx_SHA256_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 8005764: b513 push {r0, r1, r4, lr} + return HMAC_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA256); + 8005766: 4c04 ldr r4, [pc, #16] ; (8005778 ) + 8005768: 9401 str r4, [sp, #4] + 800576a: 9c04 ldr r4, [sp, #16] + 800576c: 9400 str r4, [sp, #0] + 800576e: f005 fbc3 bl 800aef8 +} + 8005772: b002 add sp, #8 + 8005774: bd10 pop {r4, pc} + 8005776: bf00 nop + 8005778: 00040080 .word 0x00040080 + +0800577c : + +void sha256_init(SHA256_CTX *ctx) +{ + 800577c: b510 push {r4, lr} + memset(ctx, 0, sizeof(SHA256_CTX)); + 800577e: 2248 movs r2, #72 ; 0x48 +{ + 8005780: 4604 mov r4, r0 + memset(ctx, 0, sizeof(SHA256_CTX)); + 8005782: 2100 movs r1, #0 + 8005784: 3004 adds r0, #4 + 8005786: f008 f8cd bl 800d924 + +#if 1 + ctx->num_pending = 0; + ctx->hh.Init.DataType = HASH_DATATYPE_8B; + 800578a: 2320 movs r3, #32 + 800578c: 6023 str r3, [r4, #0] + HAL_HASH_Init(&ctx->hh); + 800578e: 4620 mov r0, r4 + __HAL_HASH_RESET_MDMAT(); + + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, + HASH_ALGOSELECTION_SHA256 | HASH_CR_INIT); +#endif +} + 8005790: e8bd 4010 ldmia.w sp!, {r4, lr} + HAL_HASH_Init(&ctx->hh); + 8005794: f004 bffc b.w 800a790 + +08005798 : + +void sha256_update(SHA256_CTX *ctx, const uint8_t data[], uint32_t len) +{ + 8005798: b5f8 push {r3, r4, r5, r6, r7, lr} + HAL_StatusTypeDef rv; + + // clear out any pending bytes + if(ctx->num_pending + len >= 4) { + 800579a: f890 3048 ldrb.w r3, [r0, #72] ; 0x48 + 800579e: 4413 add r3, r2 + 80057a0: 2b03 cmp r3, #3 +{ + 80057a2: 4605 mov r5, r0 + 80057a4: 460e mov r6, r1 + 80057a6: 4614 mov r4, r2 + if(ctx->num_pending + len >= 4) { + 80057a8: d818 bhi.n 80057dc + } + } + + // write full blocks + uint32_t blocks = len / 4; + if(blocks) { + 80057aa: 2c03 cmp r4, #3 + 80057ac: d926 bls.n 80057fc +#if 1 + rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, (uint8_t *)data, blocks*4); + 80057ae: f024 0703 bic.w r7, r4, #3 + 80057b2: 463a mov r2, r7 + 80057b4: 4631 mov r1, r6 + 80057b6: 4628 mov r0, r5 + 80057b8: f7ff ffc2 bl 8005740 + ASSERT(rv == HAL_OK); + 80057bc: b9c8 cbnz r0, 80057f2 + uint32_t tmp; + memcpy(&tmp, data, 4); + HASH->DIN = tmp; + } +#endif + len -= blocks*4; + 80057be: f004 0403 and.w r4, r4, #3 + data += blocks*4; + 80057c2: 443e add r6, r7 + 80057c4: e01a b.n 80057fc + ctx->pending[ctx->num_pending++] = *data; + 80057c6: 1c5a adds r2, r3, #1 + 80057c8: b2d2 uxtb r2, r2 + 80057ca: f885 2048 strb.w r2, [r5, #72] ; 0x48 + 80057ce: 442b add r3, r5 + 80057d0: f816 1b01 ldrb.w r1, [r6], #1 + 80057d4: f883 1044 strb.w r1, [r3, #68] ; 0x44 + if(!len) break; + 80057d8: 3c01 subs r4, #1 + 80057da: d00d beq.n 80057f8 + while(ctx->num_pending != 4) { + 80057dc: f895 3048 ldrb.w r3, [r5, #72] ; 0x48 + 80057e0: 2b04 cmp r3, #4 + 80057e2: d1f0 bne.n 80057c6 + rv = HAL_HASHEx_SHA256_Accumulate(&ctx->hh, ctx->pending, 4); + 80057e4: 2204 movs r2, #4 + 80057e6: f105 0144 add.w r1, r5, #68 ; 0x44 + 80057ea: 4628 mov r0, r5 + 80057ec: f7ff ffa8 bl 8005740 + ASSERT(rv == HAL_OK); + 80057f0: b140 cbz r0, 8005804 + 80057f2: 480b ldr r0, [pc, #44] ; (8005820 ) + 80057f4: f7fb f920 bl 8000a38 + if(ctx->num_pending == 4) { + 80057f8: 2a04 cmp r2, #4 + 80057fa: d0f3 beq.n 80057e4 + 80057fc: 4434 add r4, r6 + } + + // save runt for later + ASSERT(len <= 3); + while(len) { + 80057fe: 42b4 cmp r4, r6 + 8005800: d103 bne.n 800580a + ctx->pending[ctx->num_pending++] = *data; + data++; + len--; + } +} + 8005802: bdf8 pop {r3, r4, r5, r6, r7, pc} + ctx->num_pending = 0; + 8005804: f885 0048 strb.w r0, [r5, #72] ; 0x48 + 8005808: e7cf b.n 80057aa + ctx->pending[ctx->num_pending++] = *data; + 800580a: f895 3048 ldrb.w r3, [r5, #72] ; 0x48 + 800580e: 1c5a adds r2, r3, #1 + 8005810: f885 2048 strb.w r2, [r5, #72] ; 0x48 + 8005814: 442b add r3, r5 + 8005816: f816 2b01 ldrb.w r2, [r6], #1 + 800581a: f883 2044 strb.w r2, [r3, #68] ; 0x44 + len--; + 800581e: e7ee b.n 80057fe + 8005820: 0801046c .word 0x0801046c + +08005824 : + +void sha256_final(SHA256_CTX *ctx, uint8_t digest[32]) +{ + 8005824: b513 push {r0, r1, r4, lr} + // Do final 0-3 bytes, pad and return digest. +#if 1 + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&ctx->hh, + 8005826: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800582a: 9200 str r2, [sp, #0] +{ + 800582c: 460b mov r3, r1 + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&ctx->hh, + 800582e: f890 2048 ldrb.w r2, [r0, #72] ; 0x48 + 8005832: f100 0144 add.w r1, r0, #68 ; 0x44 + 8005836: f7ff ff89 bl 800574c + ctx->pending, ctx->num_pending, digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 800583a: b110 cbz r0, 8005842 + 800583c: 4802 ldr r0, [pc, #8] ; (8005848 ) + 800583e: f7fb f8fb bl 8000a38 + tmp = __REV(HASH_DIGEST->HR[6]); + memcpy(out, &tmp, 4); out += 4; + tmp = __REV(HASH_DIGEST->HR[7]); + memcpy(out, &tmp, 4); +#endif +} + 8005842: b002 add sp, #8 + 8005844: bd10 pop {r4, pc} + 8005846: bf00 nop + 8005848: 0801046c .word 0x0801046c + +0800584c : +// +// single-shot version (best) +// + void +sha256_single(const uint8_t data[], uint32_t len, uint8_t digest[32]) +{ + 800584c: b530 push {r4, r5, lr} + 800584e: b097 sub sp, #92 ; 0x5c + 8005850: 4604 mov r4, r0 + 8005852: 460d mov r5, r1 + 8005854: 9203 str r2, [sp, #12] + HASH_HandleTypeDef hh = {0}; + 8005856: 2100 movs r1, #0 + 8005858: 2240 movs r2, #64 ; 0x40 + 800585a: a806 add r0, sp, #24 + 800585c: f008 f862 bl 800d924 + + hh.Init.DataType = HASH_DATATYPE_8B; + 8005860: 2220 movs r2, #32 + + HAL_HASH_Init(&hh); + 8005862: a805 add r0, sp, #20 + hh.Init.DataType = HASH_DATATYPE_8B; + 8005864: 9205 str r2, [sp, #20] + HAL_HASH_Init(&hh); + 8005866: f004 ff93 bl 800a790 + + // It's called "Start" but it handles the runt packet, so really can only + // be used once at end of message, or for whole message. + HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&hh, (uint8_t *)data, len, + 800586a: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800586e: 9200 str r2, [sp, #0] + 8005870: 9b03 ldr r3, [sp, #12] + 8005872: 462a mov r2, r5 + 8005874: 4621 mov r1, r4 + 8005876: a805 add r0, sp, #20 + 8005878: f7ff ff68 bl 800574c + digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 800587c: b110 cbz r0, 8005884 + 800587e: 4802 ldr r0, [pc, #8] ; (8005888 ) + 8005880: f7fb f8da bl 8000a38 +} + 8005884: b017 add sp, #92 ; 0x5c + 8005886: bd30 pop {r4, r5, pc} + 8005888: 0801046c .word 0x0801046c + +0800588c : +// hmac_sha256_init() +// + void +hmac_sha256_init(HMAC_CTX *ctx) +{ + memset(ctx, 0, sizeof(HMAC_CTX)); + 800588c: f44f 7282 mov.w r2, #260 ; 0x104 + 8005890: 2100 movs r1, #0 + 8005892: f008 b847 b.w 800d924 + ... + +08005898 : + +// hmac_sha256_update() +// + void +hmac_sha256_update(HMAC_CTX *ctx, const uint8_t data[], uint32_t len) +{ + 8005898: b538 push {r3, r4, r5, lr} + 800589a: 4604 mov r4, r0 + // simple append + ASSERT(ctx->num_pending + len < sizeof(ctx->pending)); + 800589c: f8d0 0100 ldr.w r0, [r0, #256] ; 0x100 + 80058a0: 1883 adds r3, r0, r2 + 80058a2: 2bff cmp r3, #255 ; 0xff +{ + 80058a4: 4615 mov r5, r2 + ASSERT(ctx->num_pending + len < sizeof(ctx->pending)); + 80058a6: d902 bls.n 80058ae + 80058a8: 4805 ldr r0, [pc, #20] ; (80058c0 ) + 80058aa: f7fb f8c5 bl 8000a38 + + memcpy(ctx->pending+ctx->num_pending, data, len); + 80058ae: 4420 add r0, r4 + 80058b0: f008 f82a bl 800d908 + + ctx->num_pending += len; + 80058b4: f8d4 2100 ldr.w r2, [r4, #256] ; 0x100 + 80058b8: 442a add r2, r5 + 80058ba: f8c4 2100 str.w r2, [r4, #256] ; 0x100 +} + 80058be: bd38 pop {r3, r4, r5, pc} + 80058c0: 0801046c .word 0x0801046c + +080058c4 : + +// hmac_sha256_final() +// + void +hmac_sha256_final(HMAC_CTX *ctx, const uint8_t key[32], uint8_t digest[32]) +{ + 80058c4: b530 push {r4, r5, lr} + 80058c6: b097 sub sp, #92 ; 0x5c + 80058c8: 4604 mov r4, r0 + 80058ca: 460d mov r5, r1 + 80058cc: 9203 str r2, [sp, #12] + HASH_HandleTypeDef hh = {0}; + 80058ce: 2100 movs r1, #0 + 80058d0: 2238 movs r2, #56 ; 0x38 + 80058d2: a808 add r0, sp, #32 + 80058d4: f008 f826 bl 800d924 + + hh.Init.DataType = HASH_DATATYPE_8B; + 80058d8: 2220 movs r2, #32 + hh.Init.pKey = (uint8_t *)key; // const viol due to API dumbness + hh.Init.KeySize = 32; + + HAL_HASH_Init(&hh); + 80058da: a805 add r0, sp, #20 + hh.Init.KeySize = 32; + 80058dc: e9cd 2506 strd r2, r5, [sp, #24] + hh.Init.DataType = HASH_DATATYPE_8B; + 80058e0: 9205 str r2, [sp, #20] + HAL_HASH_Init(&hh); + 80058e2: f004 ff55 bl 800a790 + + HAL_StatusTypeDef rv = HAL_HMACEx_SHA256_Start(&hh, + 80058e6: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 80058ea: 9200 str r2, [sp, #0] + 80058ec: 9b03 ldr r3, [sp, #12] + 80058ee: f8d4 2100 ldr.w r2, [r4, #256] ; 0x100 + 80058f2: 4621 mov r1, r4 + 80058f4: a805 add r0, sp, #20 + 80058f6: f7ff ff35 bl 8005764 + ctx->pending, ctx->num_pending, digest, HAL_MAX_DELAY); + ASSERT(rv == HAL_OK); + 80058fa: b110 cbz r0, 8005902 + 80058fc: 4802 ldr r0, [pc, #8] ; (8005908 ) + 80058fe: f7fb f89b bl 8000a38 +} + 8005902: b017 add sp, #92 ; 0x5c + 8005904: bd30 pop {r4, r5, pc} + 8005906: bf00 nop + 8005908: 0801046c .word 0x0801046c + +0800590c : + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + 800590c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + ); + +#else /* Thumb-1 */ + uint32_t r4, r5, r6, r7; + + __asm__ volatile ( + 8005910: 3b01 subs r3, #1 + 8005912: 009b lsls r3, r3, #2 + 8005914: 4698 mov r8, r3 + 8005916: 005b lsls r3, r3, #1 + 8005918: 4699 mov r9, r3 + 800591a: 2300 movs r3, #0 + 800591c: 2400 movs r4, #0 + 800591e: 2500 movs r5, #0 + 8005920: 2600 movs r6, #0 + 8005922: b401 push {r0} + 8005924: 2700 movs r7, #0 + 8005926: e002 b.n 800592e + 8005928: 0037 movs r7, r6 + 800592a: 4640 mov r0, r8 + 800592c: 1a3f subs r7, r7, r0 + 800592e: b478 push {r3, r4, r5, r6} + 8005930: 1bf0 subs r0, r6, r7 + 8005932: 5814 ldr r4, [r2, r0] + 8005934: 59c8 ldr r0, [r1, r7] + 8005936: 0c03 lsrs r3, r0, #16 + 8005938: b280 uxth r0, r0 + 800593a: 0c25 lsrs r5, r4, #16 + 800593c: b2a4 uxth r4, r4 + 800593e: 001e movs r6, r3 + 8005940: 436e muls r6, r5 + 8005942: 4363 muls r3, r4 + 8005944: 4345 muls r5, r0 + 8005946: 4360 muls r0, r4 + 8005948: 2400 movs r4, #0 + 800594a: 195b adds r3, r3, r5 + 800594c: 4164 adcs r4, r4 + 800594e: 0424 lsls r4, r4, #16 + 8005950: 1936 adds r6, r6, r4 + 8005952: 041c lsls r4, r3, #16 + 8005954: 0c1b lsrs r3, r3, #16 + 8005956: 1900 adds r0, r0, r4 + 8005958: 415e adcs r6, r3 + 800595a: bc38 pop {r3, r4, r5} + 800595c: 181b adds r3, r3, r0 + 800595e: 4174 adcs r4, r6 + 8005960: 2000 movs r0, #0 + 8005962: 4145 adcs r5, r0 + 8005964: bc40 pop {r6} + 8005966: 3704 adds r7, #4 + 8005968: 4547 cmp r7, r8 + 800596a: dc01 bgt.n 8005970 + 800596c: 42b7 cmp r7, r6 + 800596e: ddde ble.n 800592e + 8005970: 9800 ldr r0, [sp, #0] + 8005972: 5183 str r3, [r0, r6] + 8005974: 4623 mov r3, r4 + 8005976: 462c mov r4, r5 + 8005978: 2500 movs r5, #0 + 800597a: 3604 adds r6, #4 + 800597c: 4546 cmp r6, r8 + 800597e: ddd1 ble.n 8005924 + 8005980: 454e cmp r6, r9 + 8005982: ddd1 ble.n 8005928 + 8005984: 5183 str r3, [r0, r6] + 8005986: bc01 pop {r0} + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) + : "r8", "r9", "cc", "memory" + ); +#endif +} + 8005988: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + +0800598c : + +#if !asm_clear +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + 800598c: ea21 71e1 bic.w r1, r1, r1, asr #31 + 8005990: 008a lsls r2, r1, #2 + 8005992: 2100 movs r1, #0 + 8005994: f007 bfc6 b.w 800d924 + +08005998 : +} +#endif /* !asm_clear */ + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { + 8005998: b510 push {r4, lr} + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + 800599a: 2300 movs r3, #0 + uECC_word_t bits = 0; + 800599c: 461a mov r2, r3 + for (i = 0; i < num_words; ++i) { + 800599e: b25c sxtb r4, r3 + 80059a0: 42a1 cmp r1, r4 + 80059a2: dc03 bgt.n 80059ac + bits |= vli[i]; + } + return (bits == 0); +} + 80059a4: fab2 f082 clz r0, r2 + 80059a8: 0940 lsrs r0, r0, #5 + 80059aa: bd10 pop {r4, pc} + bits |= vli[i]; + 80059ac: f850 4023 ldr.w r4, [r0, r3, lsl #2] + 80059b0: 3301 adds r3, #1 + 80059b2: 4322 orrs r2, r4 + for (i = 0; i < num_words; ++i) { + 80059b4: e7f3 b.n 800599e + +080059b6 : + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 80059b6: 114a asrs r2, r1, #5 + 80059b8: 2301 movs r3, #1 + 80059ba: f850 0022 ldr.w r0, [r0, r2, lsl #2] + 80059be: f001 011f and.w r1, r1, #31 + 80059c2: fa03 f101 lsl.w r1, r3, r1 +} + 80059c6: 4008 ands r0, r1 + 80059c8: 4770 bx lr + +080059ca : +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { + wordcount_t i; + /* Search from the end until we find a non-zero digit. + We do it in reverse because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + 80059ca: 3901 subs r1, #1 + + return (i + 1); +} + +/* Counts the number of bits required to represent vli. */ +uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { + 80059cc: b510 push {r4, lr} + 80059ce: b249 sxtb r1, r1 + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + 80059d0: 1d04 adds r4, r0, #4 + 80059d2: 060a lsls r2, r1, #24 + 80059d4: b2cb uxtb r3, r1 + 80059d6: d404 bmi.n 80059e2 + 80059d8: 3901 subs r1, #1 + 80059da: f854 2021 ldr.w r2, [r4, r1, lsl #2] + 80059de: 2a00 cmp r2, #0 + 80059e0: d0f7 beq.n 80059d2 + return (i + 1); + 80059e2: 3301 adds r3, #1 + 80059e4: b25b sxtb r3, r3 + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + 80059e6: b173 cbz r3, 8005a06 + return 0; + } + + digit = vli[num_digits - 1]; + 80059e8: f103 4280 add.w r2, r3, #1073741824 ; 0x40000000 + 80059ec: 3a01 subs r2, #1 + 80059ee: f850 2022 ldr.w r2, [r0, r2, lsl #2] + for (i = 0; digit; ++i) { + 80059f2: 2000 movs r0, #0 + 80059f4: b922 cbnz r2, 8005a00 + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); + 80059f6: 3b01 subs r3, #1 + 80059f8: eb00 1343 add.w r3, r0, r3, lsl #5 + 80059fc: b218 sxth r0, r3 +} + 80059fe: bd10 pop {r4, pc} + digit >>= 1; + 8005a00: 0852 lsrs r2, r2, #1 + for (i = 0; digit; ++i) { + 8005a02: 3001 adds r0, #1 + 8005a04: e7f6 b.n 80059f4 + return 0; + 8005a06: 4618 mov r0, r3 + 8005a08: e7f9 b.n 80059fe + +08005a0a : + +/* Sets dest = src. */ +#if !asm_set +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + 8005a0a: b510 push {r4, lr} + wordcount_t i; + for (i = 0; i < num_words; ++i) { + 8005a0c: 2300 movs r3, #0 + 8005a0e: b25c sxtb r4, r3 + 8005a10: 42a2 cmp r2, r4 + 8005a12: dc00 bgt.n 8005a16 + dest[i] = src[i]; + } +} + 8005a14: bd10 pop {r4, pc} + dest[i] = src[i]; + 8005a16: f851 4023 ldr.w r4, [r1, r3, lsl #2] + 8005a1a: f840 4023 str.w r4, [r0, r3, lsl #2] + for (i = 0; i < num_words; ++i) { + 8005a1e: 3301 adds r3, #1 + 8005a20: e7f5 b.n 8005a0e + +08005a22 : +#endif /* !asm_set */ + +/* Returns sign of left - right. */ +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + 8005a22: b510 push {r4, lr} + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + 8005a24: 3a01 subs r2, #1 + 8005a26: b252 sxtb r2, r2 + 8005a28: 0613 lsls r3, r2, #24 + 8005a2a: d501 bpl.n 8005a30 + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; + 8005a2c: 2000 movs r0, #0 +} + 8005a2e: bd10 pop {r4, pc} + if (left[i] > right[i]) { + 8005a30: f850 4022 ldr.w r4, [r0, r2, lsl #2] + 8005a34: f851 3022 ldr.w r3, [r1, r2, lsl #2] + 8005a38: 429c cmp r4, r3 + 8005a3a: d805 bhi.n 8005a48 + } else if (left[i] < right[i]) { + 8005a3c: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 8005a40: d2f2 bcs.n 8005a28 + return -1; + 8005a42: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 8005a46: e7f2 b.n 8005a2e + return 1; + 8005a48: 2001 movs r0, #1 + 8005a4a: e7f0 b.n 8005a2e + +08005a4c : +#if !asm_rshift1 +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + 8005a4c: eb00 0181 add.w r1, r0, r1, lsl #2 + uECC_word_t carry = 0; + 8005a50: 2300 movs r3, #0 + while (vli-- > end) { + 8005a52: 4288 cmp r0, r1 + 8005a54: d300 bcc.n 8005a58 + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} + 8005a56: 4770 bx lr + uECC_word_t temp = *vli; + 8005a58: f851 2d04 ldr.w r2, [r1, #-4]! + *vli = (temp >> 1) | carry; + 8005a5c: ea43 0352 orr.w r3, r3, r2, lsr #1 + 8005a60: 600b str r3, [r1, #0] + carry = temp << (uECC_WORD_BITS - 1); + 8005a62: 07d3 lsls r3, r2, #31 + 8005a64: e7f5 b.n 8005a52 + +08005a66 : +/* Computes result = (left * right) % mod. */ +uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + 8005a66: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8005a6a: b0b5 sub sp, #212 ; 0xd4 + 8005a6c: 461f mov r7, r3 + 8005a6e: f99d 50f8 ldrsb.w r5, [sp, #248] ; 0xf8 + 8005a72: 4680 mov r8, r0 + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, num_words); + 8005a74: 462b mov r3, r5 + 8005a76: a804 add r0, sp, #16 + 8005a78: f7ff ff48 bl 800590c + uECC_word_t *v[2] = {tmp, product}; + 8005a7c: ab24 add r3, sp, #144 ; 0x90 + 8005a7e: e9cd 3002 strd r3, r0, [sp, #8] + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); + 8005a82: 4629 mov r1, r5 + 8005a84: 4638 mov r0, r7 + 8005a86: f7ff ffa0 bl 80059ca + 8005a8a: ebc0 1085 rsb r0, r0, r5, lsl #6 + 8005a8e: b204 sxth r4, r0 + wordcount_t word_shift = shift / uECC_WORD_BITS; + 8005a90: 2c00 cmp r4, #0 + 8005a92: 4626 mov r6, r4 + 8005a94: bfb8 it lt + 8005a96: f104 061f addlt.w r6, r4, #31 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 8005a9a: 4263 negs r3, r4 + wordcount_t word_shift = shift / uECC_WORD_BITS; + 8005a9c: f346 1647 sbfx r6, r6, #5, #8 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 8005aa0: f003 031f and.w r3, r3, #31 + 8005aa4: f004 091f and.w r9, r4, #31 + uECC_vli_clear(mod_multiple, word_shift); + 8005aa8: 4631 mov r1, r6 + wordcount_t bit_shift = shift % uECC_WORD_BITS; + 8005aaa: bf58 it pl + 8005aac: f1c3 0900 rsbpl r9, r3, #0 + uECC_vli_clear(mod_multiple, word_shift); + 8005ab0: a814 add r0, sp, #80 ; 0x50 + 8005ab2: f7ff ff6b bl 800598c + if (bit_shift > 0) { + 8005ab6: f1b9 0f00 cmp.w r9, #0 + 8005aba: b236 sxth r6, r6 + 8005abc: dd2b ble.n 8005b16 + 8005abe: ab14 add r3, sp, #80 ; 0x50 + uECC_word_t carry = 0; + 8005ac0: 2200 movs r2, #0 + 8005ac2: eb03 0686 add.w r6, r3, r6, lsl #2 + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + 8005ac6: f1c9 0c20 rsb ip, r9, #32 + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 8005aca: 4613 mov r3, r2 + 8005acc: 42ab cmp r3, r5 + 8005ace: d317 bcc.n 8005b00 + for (i = 0; i < num_words * 2; ++i) { + 8005ad0: 006b lsls r3, r5, #1 + 8005ad2: 9301 str r3, [sp, #4] + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005ad4: ab14 add r3, sp, #80 ; 0x50 + 8005ad6: eb03 0985 add.w r9, r3, r5, lsl #2 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 8005ada: 1e6f subs r7, r5, #1 + 8005adc: ab34 add r3, sp, #208 ; 0xd0 + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005ade: 2601 movs r6, #1 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 8005ae0: eb03 0787 add.w r7, r3, r7, lsl #2 + for (index = 1; shift >= 0; --shift) { + 8005ae4: 2c00 cmp r4, #0 + 8005ae6: da54 bge.n 8005b92 + uECC_vli_set(result, v[index], num_words); + 8005ae8: ab34 add r3, sp, #208 ; 0xd0 + 8005aea: eb03 0686 add.w r6, r3, r6, lsl #2 + 8005aee: 462a mov r2, r5 + 8005af0: f856 1cc8 ldr.w r1, [r6, #-200] + 8005af4: 4640 mov r0, r8 + 8005af6: f7ff ff88 bl 8005a0a + uECC_vli_mmod(result, product, mod, num_words); +} + 8005afa: b035 add sp, #212 ; 0xd4 + 8005afc: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + 8005b00: f857 0023 ldr.w r0, [r7, r3, lsl #2] + 8005b04: fa00 f109 lsl.w r1, r0, r9 + 8005b08: 430a orrs r2, r1 + 8005b0a: f846 2b04 str.w r2, [r6], #4 + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 8005b0e: 3301 adds r3, #1 + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + 8005b10: fa20 f20c lsr.w r2, r0, ip + for(index = 0; index < (uECC_word_t)num_words; ++index) { + 8005b14: e7da b.n 8005acc + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + 8005b16: ab14 add r3, sp, #80 ; 0x50 + 8005b18: 462a mov r2, r5 + 8005b1a: 4639 mov r1, r7 + 8005b1c: eb03 0086 add.w r0, r3, r6, lsl #2 + 8005b20: f7ff ff73 bl 8005a0a + 8005b24: e7d4 b.n 8005ad0 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 8005b26: fa0f fe82 sxth.w lr, r2 + 8005b2a: f85a 3cc8 ldr.w r3, [sl, #-200] + 8005b2e: f853 b02e ldr.w fp, [r3, lr, lsl #2] + 8005b32: ab34 add r3, sp, #208 ; 0xd0 + 8005b34: eb03 0282 add.w r2, r3, r2, lsl #2 + 8005b38: 3001 adds r0, #1 + 8005b3a: f852 3c80 ldr.w r3, [r2, #-128] + 8005b3e: 440b add r3, r1 + 8005b40: ebbb 0303 subs.w r3, fp, r3 + 8005b44: bf34 ite cc + 8005b46: 2201 movcc r2, #1 + 8005b48: 2200 movcs r2, #0 + if (diff != v[index][i]) { + 8005b4a: 459b cmp fp, r3 + borrow = (diff > v[index][i]); + 8005b4c: bf18 it ne + 8005b4e: 4611 movne r1, r2 + v[1 - index][i] = diff; + 8005b50: f85c 2cc8 ldr.w r2, [ip, #-200] + 8005b54: f842 302e str.w r3, [r2, lr, lsl #2] + for (i = 0; i < num_words * 2; ++i) { + 8005b58: 9b01 ldr r3, [sp, #4] + 8005b5a: b242 sxtb r2, r0 + 8005b5c: 429a cmp r2, r3 + 8005b5e: dbe2 blt.n 8005b26 + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + 8005b60: 1a73 subs r3, r6, r1 + 8005b62: 425e negs r6, r3 + uECC_vli_rshift1(mod_multiple, num_words); + 8005b64: 4629 mov r1, r5 + 8005b66: a814 add r0, sp, #80 ; 0x50 + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + 8005b68: 415e adcs r6, r3 + uECC_vli_rshift1(mod_multiple, num_words); + 8005b6a: f7ff ff6f bl 8005a4c + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 8005b6e: ab34 add r3, sp, #208 ; 0xd0 + 8005b70: eb03 0385 add.w r3, r3, r5, lsl #2 + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005b74: 4629 mov r1, r5 + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + 8005b76: f853 2c80 ldr.w r2, [r3, #-128] + 8005b7a: f857 3c80 ldr.w r3, [r7, #-128] + 8005b7e: ea43 73c2 orr.w r3, r3, r2, lsl #31 + 8005b82: f847 3c80 str.w r3, [r7, #-128] + uECC_vli_rshift1(mod_multiple + num_words, num_words); + 8005b86: 4648 mov r0, r9 + 8005b88: 3c01 subs r4, #1 + 8005b8a: f7ff ff5f bl 8005a4c + for (index = 1; shift >= 0; --shift) { + 8005b8e: b224 sxth r4, r4 + 8005b90: e7a8 b.n 8005ae4 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 8005b92: ab34 add r3, sp, #208 ; 0xd0 + 8005b94: 2000 movs r0, #0 + v[1 - index][i] = diff; + 8005b96: f1c6 0c01 rsb ip, r6, #1 + uECC_word_t borrow = 0; + 8005b9a: 4601 mov r1, r0 + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + 8005b9c: eb03 0a86 add.w sl, r3, r6, lsl #2 + v[1 - index][i] = diff; + 8005ba0: eb03 0c8c add.w ip, r3, ip, lsl #2 + 8005ba4: e7d8 b.n 8005b58 + +08005ba6 : + +uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve) { + 8005ba6: b530 push {r4, r5, lr} + 8005ba8: 461c mov r4, r3 + 8005baa: b091 sub sp, #68 ; 0x44 + 8005bac: 4605 mov r5, r0 + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + 8005bae: f993 3000 ldrsb.w r3, [r3] + 8005bb2: 4668 mov r0, sp + 8005bb4: f7ff feaa bl 800590c +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); + 8005bb8: 4601 mov r1, r0 + 8005bba: f8d4 30b0 ldr.w r3, [r4, #176] ; 0xb0 + 8005bbe: 4628 mov r0, r5 + 8005bc0: 4798 blx r3 +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + 8005bc2: b011 add sp, #68 ; 0x44 + 8005bc4: bd30 pop {r4, r5, pc} + +08005bc6 : +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + 8005bc6: 4613 mov r3, r2 + uECC_vli_modMult_fast(result, left, left, curve); + 8005bc8: 460a mov r2, r1 + 8005bca: f7ff bfec b.w 8005ba6 + +08005bce : + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(uECC_word_t * X1, + uECC_word_t * Y1, + const uECC_word_t * const Z, + uECC_Curve curve) { + 8005bce: b570 push {r4, r5, r6, lr} + 8005bd0: 4614 mov r4, r2 + 8005bd2: b08a sub sp, #40 ; 0x28 + 8005bd4: 4606 mov r6, r0 + 8005bd6: 460d mov r5, r1 + uECC_word_t t1[uECC_MAX_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + 8005bd8: 461a mov r2, r3 + 8005bda: 4621 mov r1, r4 + 8005bdc: a802 add r0, sp, #8 + 8005bde: 9301 str r3, [sp, #4] + 8005be0: f7ff fff1 bl 8005bc6 + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + 8005be4: 9b01 ldr r3, [sp, #4] + 8005be6: aa02 add r2, sp, #8 + 8005be8: 4631 mov r1, r6 + 8005bea: 4630 mov r0, r6 + 8005bec: f7ff ffdb bl 8005ba6 + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + 8005bf0: a902 add r1, sp, #8 + 8005bf2: 9b01 ldr r3, [sp, #4] + 8005bf4: 4622 mov r2, r4 + 8005bf6: 4608 mov r0, r1 + 8005bf8: f7ff ffd5 bl 8005ba6 + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ + 8005bfc: 9b01 ldr r3, [sp, #4] + 8005bfe: aa02 add r2, sp, #8 + 8005c00: 4629 mov r1, r5 + 8005c02: 4628 mov r0, r5 + 8005c04: f7ff ffcf bl 8005ba6 +} + 8005c08: b00a add sp, #40 ; 0x28 + 8005c0a: bd70 pop {r4, r5, r6, pc} + +08005c0c : + +#else + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uECC_word_t *native) { + 8005c0c: b5f0 push {r4, r5, r6, r7, lr} + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + 8005c0e: 2500 movs r5, #0 + unsigned b = num_bytes - 1 - i; + 8005c10: 1e4f subs r7, r1, #1 + 8005c12: b26c sxtb r4, r5 + for (i = 0; i < num_bytes; ++i) { + 8005c14: 428c cmp r4, r1 + 8005c16: f105 0501 add.w r5, r5, #1 + 8005c1a: db00 blt.n 8005c1e + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + 8005c1c: bdf0 pop {r4, r5, r6, r7, pc} + unsigned b = num_bytes - 1 - i; + 8005c1e: 1b3b subs r3, r7, r4 + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + 8005c20: f023 0603 bic.w r6, r3, #3 + 8005c24: f003 0303 and.w r3, r3, #3 + 8005c28: 5996 ldr r6, [r2, r6] + 8005c2a: 00db lsls r3, r3, #3 + 8005c2c: fa26 f303 lsr.w r3, r6, r3 + 8005c30: 5503 strb r3, [r0, r4] + for (i = 0; i < num_bytes; ++i) { + 8005c32: e7ee b.n 8005c12 + +08005c34 : + +uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, + const uint8_t *bytes, + int num_bytes) { + 8005c34: b5f8 push {r3, r4, r5, r6, r7, lr} + 8005c36: 460e mov r6, r1 + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005c38: 1cd1 adds r1, r2, #3 + 8005c3a: bf48 it mi + 8005c3c: 1d91 addmi r1, r2, #6 + int num_bytes) { + 8005c3e: 4614 mov r4, r2 + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005c40: f341 0187 sbfx r1, r1, #2, #8 + int num_bytes) { + 8005c44: 4605 mov r5, r0 + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + 8005c46: 1e67 subs r7, r4, #1 + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + 8005c48: f7ff fea0 bl 800598c + for (i = 0; i < num_bytes; ++i) { + 8005c4c: 2000 movs r0, #0 + 8005c4e: b242 sxtb r2, r0 + 8005c50: 42a2 cmp r2, r4 + 8005c52: f100 0001 add.w r0, r0, #1 + 8005c56: db00 blt.n 8005c5a + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + 8005c58: bdf8 pop {r3, r4, r5, r6, r7, pc} + unsigned b = num_bytes - 1 - i; + 8005c5a: 1abb subs r3, r7, r2 + native[b / uECC_WORD_SIZE] |= + 8005c5c: f023 0103 bic.w r1, r3, #3 + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + 8005c60: 5cb2 ldrb r2, [r6, r2] + 8005c62: f003 0303 and.w r3, r3, #3 + 8005c66: 00db lsls r3, r3, #3 + 8005c68: fa02 f303 lsl.w r3, r2, r3 + native[b / uECC_WORD_SIZE] |= + 8005c6c: 586a ldr r2, [r5, r1] + 8005c6e: 431a orrs r2, r3 + 8005c70: 506a str r2, [r5, r1] + for (i = 0; i < num_bytes; ++i) { + 8005c72: e7ec b.n 8005c4e + +08005c74 : + return 0; +} + +/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always + the same size as the hash result size. */ +static void HMAC_init(uECC_HashContext *hash_context, const uint8_t *K) { + 8005c74: b570 push {r4, r5, r6, lr} + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 8005c76: e9d0 3504 ldrd r3, r5, [r0, #16] +static void HMAC_init(uECC_HashContext *hash_context, const uint8_t *K) { + 8005c7a: 4604 mov r4, r0 + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 8005c7c: eb05 0543 add.w r5, r5, r3, lsl #1 + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + 8005c80: 2300 movs r3, #0 + 8005c82: 6922 ldr r2, [r4, #16] + 8005c84: 429a cmp r2, r3 + 8005c86: d80d bhi.n 8005ca4 + pad[i] = K[i] ^ 0x36; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x36; + 8005c88: 2136 movs r1, #54 ; 0x36 + for (; i < hash_context->block_size; ++i) + 8005c8a: 68e2 ldr r2, [r4, #12] + 8005c8c: 429a cmp r2, r3 + 8005c8e: d80f bhi.n 8005cb0 + + hash_context->init_hash(hash_context); + 8005c90: 6823 ldr r3, [r4, #0] + 8005c92: 4620 mov r0, r4 + 8005c94: 4798 blx r3 + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 8005c96: 6863 ldr r3, [r4, #4] + 8005c98: 68e2 ldr r2, [r4, #12] + 8005c9a: 4629 mov r1, r5 + 8005c9c: 4620 mov r0, r4 +} + 8005c9e: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 8005ca2: 4718 bx r3 + pad[i] = K[i] ^ 0x36; + 8005ca4: 5cca ldrb r2, [r1, r3] + 8005ca6: f082 0236 eor.w r2, r2, #54 ; 0x36 + 8005caa: 54ea strb r2, [r5, r3] + for (i = 0; i < hash_context->result_size; ++i) + 8005cac: 3301 adds r3, #1 + 8005cae: e7e8 b.n 8005c82 + pad[i] = 0x36; + 8005cb0: 54e9 strb r1, [r5, r3] + for (; i < hash_context->block_size; ++i) + 8005cb2: 3301 adds r3, #1 + 8005cb4: e7e9 b.n 8005c8a + +08005cb6 : + +static void HMAC_update(uECC_HashContext *hash_context, + const uint8_t *message, + unsigned message_size) { + hash_context->update_hash(hash_context, message, message_size); + 8005cb6: 6843 ldr r3, [r0, #4] + 8005cb8: 4718 bx r3 + +08005cba : +} + +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 8005cba: b570 push {r4, r5, r6, lr} + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 8005cbc: e9d0 3604 ldrd r3, r6, [r0, #16] +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 8005cc0: 4604 mov r4, r0 + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + 8005cc2: eb06 0643 add.w r6, r6, r3, lsl #1 +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + 8005cc6: 4615 mov r5, r2 + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + 8005cc8: 2300 movs r3, #0 + 8005cca: 6922 ldr r2, [r4, #16] + 8005ccc: 429a cmp r2, r3 + 8005cce: d81a bhi.n 8005d06 + pad[i] = K[i] ^ 0x5c; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x5c; + 8005cd0: 215c movs r1, #92 ; 0x5c + for (; i < hash_context->block_size; ++i) + 8005cd2: 68e2 ldr r2, [r4, #12] + 8005cd4: 429a cmp r2, r3 + 8005cd6: d81c bhi.n 8005d12 + + hash_context->finish_hash(hash_context, result); + 8005cd8: 4629 mov r1, r5 + 8005cda: 68a3 ldr r3, [r4, #8] + 8005cdc: 4620 mov r0, r4 + 8005cde: 4798 blx r3 + + hash_context->init_hash(hash_context); + 8005ce0: 6823 ldr r3, [r4, #0] + 8005ce2: 4620 mov r0, r4 + 8005ce4: 4798 blx r3 + hash_context->update_hash(hash_context, pad, hash_context->block_size); + 8005ce6: 6863 ldr r3, [r4, #4] + 8005ce8: 68e2 ldr r2, [r4, #12] + 8005cea: 4631 mov r1, r6 + 8005cec: 4620 mov r0, r4 + 8005cee: 4798 blx r3 + hash_context->update_hash(hash_context, result, hash_context->result_size); + 8005cf0: 6863 ldr r3, [r4, #4] + 8005cf2: 6922 ldr r2, [r4, #16] + 8005cf4: 4629 mov r1, r5 + 8005cf6: 4620 mov r0, r4 + 8005cf8: 4798 blx r3 + hash_context->finish_hash(hash_context, result); + 8005cfa: 68a3 ldr r3, [r4, #8] + 8005cfc: 4629 mov r1, r5 + 8005cfe: 4620 mov r0, r4 +} + 8005d00: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + hash_context->finish_hash(hash_context, result); + 8005d04: 4718 bx r3 + pad[i] = K[i] ^ 0x5c; + 8005d06: 5cca ldrb r2, [r1, r3] + 8005d08: f082 025c eor.w r2, r2, #92 ; 0x5c + 8005d0c: 54f2 strb r2, [r6, r3] + for (i = 0; i < hash_context->result_size; ++i) + 8005d0e: 3301 adds r3, #1 + 8005d10: e7db b.n 8005cca + pad[i] = 0x5c; + 8005d12: 54f1 strb r1, [r6, r3] + for (; i < hash_context->block_size; ++i) + 8005d14: 3301 adds r3, #1 + 8005d16: e7dc b.n 8005cd2 + +08005d18 : + +/* V = HMAC_K(V) */ +static void update_V(uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { + 8005d18: b570 push {r4, r5, r6, lr} + 8005d1a: 4604 mov r4, r0 + 8005d1c: 4615 mov r5, r2 + 8005d1e: 460e mov r6, r1 + HMAC_init(hash_context, K); + 8005d20: f7ff ffa8 bl 8005c74 + HMAC_update(hash_context, V, hash_context->result_size); + 8005d24: 6922 ldr r2, [r4, #16] + 8005d26: 4629 mov r1, r5 + 8005d28: 4620 mov r0, r4 + 8005d2a: f7ff ffc4 bl 8005cb6 + HMAC_finish(hash_context, K, V); + 8005d2e: 462a mov r2, r5 + 8005d30: 4631 mov r1, r6 + 8005d32: 4620 mov r0, r4 +} + 8005d34: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + HMAC_finish(hash_context, K, V); + 8005d38: f7ff bfbf b.w 8005cba + +08005d3c : +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + 8005d3c: b530 push {r4, r5, lr} + __asm__ volatile ( + 8005d3e: 2300 movs r3, #0 + 8005d40: c910 ldmia r1!, {r4} + 8005d42: ca20 ldmia r2!, {r5} + 8005d44: 1b64 subs r4, r4, r5 + 8005d46: c010 stmia r0!, {r4} + 8005d48: c910 ldmia r1!, {r4} + 8005d4a: ca20 ldmia r2!, {r5} + 8005d4c: 41ac sbcs r4, r5 + 8005d4e: c010 stmia r0!, {r4} + 8005d50: c910 ldmia r1!, {r4} + 8005d52: ca20 ldmia r2!, {r5} + 8005d54: 41ac sbcs r4, r5 + 8005d56: c010 stmia r0!, {r4} + 8005d58: c910 ldmia r1!, {r4} + 8005d5a: ca20 ldmia r2!, {r5} + 8005d5c: 41ac sbcs r4, r5 + 8005d5e: c010 stmia r0!, {r4} + 8005d60: c910 ldmia r1!, {r4} + 8005d62: ca20 ldmia r2!, {r5} + 8005d64: 41ac sbcs r4, r5 + 8005d66: c010 stmia r0!, {r4} + 8005d68: c910 ldmia r1!, {r4} + 8005d6a: ca20 ldmia r2!, {r5} + 8005d6c: 41ac sbcs r4, r5 + 8005d6e: c010 stmia r0!, {r4} + 8005d70: c910 ldmia r1!, {r4} + 8005d72: ca20 ldmia r2!, {r5} + 8005d74: 41ac sbcs r4, r5 + 8005d76: c010 stmia r0!, {r4} + 8005d78: c910 ldmia r1!, {r4} + 8005d7a: ca20 ldmia r2!, {r5} + 8005d7c: 41ac sbcs r4, r5 + 8005d7e: c010 stmia r0!, {r4} + 8005d80: 415b adcs r3, r3 +} + 8005d82: fab3 f083 clz r0, r3 + 8005d86: 0940 lsrs r0, r0, #5 + 8005d88: bd30 pop {r4, r5, pc} + +08005d8a : + uECC_Curve curve) { + 8005d8a: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 8005d8e: 4698 mov r8, r3 + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005d90: f9b3 3002 ldrsh.w r3, [r3, #2] + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005d94: f113 041f adds.w r4, r3, #31 + 8005d98: bf48 it mi + 8005d9a: f103 043e addmi.w r4, r3, #62 ; 0x3e + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005d9e: 1ddd adds r5, r3, #7 + 8005da0: bf48 it mi + 8005da2: f103 050e addmi.w r5, r3, #14 + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005da6: 1166 asrs r6, r4, #5 + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + 8005da8: 10ec asrs r4, r5, #3 + 8005daa: 4294 cmp r4, r2 + uECC_vli_clear(native, num_n_words); + 8005dac: b275 sxtb r5, r6 + 8005dae: bf28 it cs + 8005db0: 4614 movcs r4, r2 + uECC_Curve curve) { + 8005db2: 4607 mov r7, r0 + 8005db4: 4689 mov r9, r1 + uECC_vli_clear(native, num_n_words); + 8005db6: 4629 mov r1, r5 + 8005db8: f7ff fde8 bl 800598c + uECC_vli_bytesToNative(native, bits, bits_size); + 8005dbc: 4622 mov r2, r4 + 8005dbe: 4649 mov r1, r9 + 8005dc0: 4638 mov r0, r7 + 8005dc2: f7ff ff37 bl 8005c34 + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + 8005dc6: f9b8 2002 ldrsh.w r2, [r8, #2] + 8005dca: ebb2 0fc4 cmp.w r2, r4, lsl #3 + 8005dce: ea4f 03c4 mov.w r3, r4, lsl #3 + 8005dd2: d21f bcs.n 8005e14 + int shift = bits_size * 8 - curve->num_n_bits; + 8005dd4: 1a9b subs r3, r3, r2 + uECC_word_t *ptr = native + num_n_words; + 8005dd6: eb07 0486 add.w r4, r7, r6, lsl #2 + uECC_word_t carry = 0; + 8005dda: 2100 movs r1, #0 + carry = temp << (uECC_WORD_BITS - shift); + 8005ddc: f1c3 0620 rsb r6, r3, #32 + while (ptr-- > native) { + 8005de0: 42a7 cmp r7, r4 + 8005de2: d30e bcc.n 8005e02 + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + 8005de4: f108 0824 add.w r8, r8, #36 ; 0x24 + 8005de8: 462a mov r2, r5 + 8005dea: 4639 mov r1, r7 + 8005dec: 4640 mov r0, r8 + 8005dee: f7ff fe18 bl 8005a22 + 8005df2: 2801 cmp r0, #1 + 8005df4: d00e beq.n 8005e14 + uECC_vli_sub(native, native, curve->n, num_n_words); + 8005df6: 4642 mov r2, r8 + 8005df8: 4638 mov r0, r7 +} + 8005dfa: e8bd 43f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + uECC_vli_sub(native, native, curve->n, num_n_words); + 8005dfe: f7ff bf9d b.w 8005d3c + uECC_word_t temp = *ptr; + 8005e02: f854 0d04 ldr.w r0, [r4, #-4]! + *ptr = (temp >> shift) | carry; + 8005e06: fa20 f203 lsr.w r2, r0, r3 + 8005e0a: 430a orrs r2, r1 + 8005e0c: 6022 str r2, [r4, #0] + carry = temp << (uECC_WORD_BITS - shift); + 8005e0e: fa00 f106 lsl.w r1, r0, r6 + 8005e12: e7e5 b.n 8005de0 +} + 8005e14: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + +08005e18 : + wordcount_t num_words) { + 8005e18: b530 push {r4, r5, lr} + 8005e1a: b089 sub sp, #36 ; 0x24 + 8005e1c: 4615 mov r5, r2 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005e1e: 460a mov r2, r1 + 8005e20: 4601 mov r1, r0 + 8005e22: 4668 mov r0, sp + 8005e24: f7ff ff8a bl 8005d3c + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + 8005e28: 4629 mov r1, r5 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005e2a: 4604 mov r4, r0 + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + 8005e2c: 4668 mov r0, sp + 8005e2e: f7ff fdb3 bl 8005998 + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + 8005e32: 3c00 subs r4, #0 + 8005e34: bf18 it ne + 8005e36: 2401 movne r4, #1 + return (!equal - 2 * neg); + 8005e38: 0064 lsls r4, r4, #1 +} + 8005e3a: 2800 cmp r0, #0 + 8005e3c: bf14 ite ne + 8005e3e: 4260 negne r0, r4 + 8005e40: f1c4 0001 rsbeq r0, r4, #1 + 8005e44: b009 add sp, #36 ; 0x24 + 8005e46: bd30 pop {r4, r5, pc} + +08005e48 : + wordcount_t num_words) { + 8005e48: e92d 4ff8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8005e4c: 460f mov r7, r1 + if (!g_rng_function) { + 8005e4e: f8df a06c ldr.w sl, [pc, #108] ; 8005ebc + wordcount_t num_words) { + 8005e52: 4606 mov r6, r0 + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + 8005e54: 4611 mov r1, r2 + 8005e56: 4638 mov r0, r7 + wordcount_t num_words) { + 8005e58: 4614 mov r4, r2 + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + 8005e5a: f7ff fdb6 bl 80059ca + if (!g_rng_function) { + 8005e5e: f8da 3000 ldr.w r3, [sl] + 8005e62: b303 cbz r3, 8005ea6 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005e64: 2504 movs r5, #4 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005e66: ebc0 1044 rsb r0, r0, r4, lsl #5 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005e6a: fb14 fb05 smulbb fp, r4, r5 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005e6e: b200 sxth r0, r0 + 8005e70: fb05 6504 mla r5, r5, r4, r6 + 8005e74: f04f 38ff mov.w r8, #4294967295 ; 0xffffffff + 8005e78: 3d04 subs r5, #4 + 8005e7a: fa28 f800 lsr.w r8, r8, r0 + 8005e7e: f04f 0940 mov.w r9, #64 ; 0x40 + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + 8005e82: f8da 3000 ldr.w r3, [sl] + 8005e86: 4659 mov r1, fp + 8005e88: 4630 mov r0, r6 + 8005e8a: 4798 blx r3 + 8005e8c: b158 cbz r0, 8005ea6 + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + 8005e8e: 682b ldr r3, [r5, #0] + 8005e90: ea03 0308 and.w r3, r3, r8 + 8005e94: 602b str r3, [r5, #0] + if (!uECC_vli_isZero(random, num_words) && + 8005e96: 4621 mov r1, r4 + 8005e98: 4630 mov r0, r6 + 8005e9a: f7ff fd7d bl 8005998 + 8005e9e: b120 cbz r0, 8005eaa + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8005ea0: f1b9 0901 subs.w r9, r9, #1 + 8005ea4: d1ed bne.n 8005e82 + return 0; + 8005ea6: 2000 movs r0, #0 + 8005ea8: e006 b.n 8005eb8 + uECC_vli_cmp(top, random, num_words) == 1) { + 8005eaa: 4622 mov r2, r4 + 8005eac: 4631 mov r1, r6 + 8005eae: 4638 mov r0, r7 + 8005eb0: f7ff ffb2 bl 8005e18 + if (!uECC_vli_isZero(random, num_words) && + 8005eb4: 2801 cmp r0, #1 + 8005eb6: d1f3 bne.n 8005ea0 +} + 8005eb8: e8bd 8ff8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, pc} + 8005ebc: 2009e2a4 .word 0x2009e2a4 + +08005ec0 : +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + 8005ec0: b530 push {r4, r5, lr} + __asm__ volatile ( + 8005ec2: 4603 mov r3, r0 + 8005ec4: 2000 movs r0, #0 + 8005ec6: c910 ldmia r1!, {r4} + 8005ec8: ca20 ldmia r2!, {r5} + 8005eca: 1964 adds r4, r4, r5 + 8005ecc: c310 stmia r3!, {r4} + 8005ece: c910 ldmia r1!, {r4} + 8005ed0: ca20 ldmia r2!, {r5} + 8005ed2: 416c adcs r4, r5 + 8005ed4: c310 stmia r3!, {r4} + 8005ed6: c910 ldmia r1!, {r4} + 8005ed8: ca20 ldmia r2!, {r5} + 8005eda: 416c adcs r4, r5 + 8005edc: c310 stmia r3!, {r4} + 8005ede: c910 ldmia r1!, {r4} + 8005ee0: ca20 ldmia r2!, {r5} + 8005ee2: 416c adcs r4, r5 + 8005ee4: c310 stmia r3!, {r4} + 8005ee6: c910 ldmia r1!, {r4} + 8005ee8: ca20 ldmia r2!, {r5} + 8005eea: 416c adcs r4, r5 + 8005eec: c310 stmia r3!, {r4} + 8005eee: c910 ldmia r1!, {r4} + 8005ef0: ca20 ldmia r2!, {r5} + 8005ef2: 416c adcs r4, r5 + 8005ef4: c310 stmia r3!, {r4} + 8005ef6: c910 ldmia r1!, {r4} + 8005ef8: ca20 ldmia r2!, {r5} + 8005efa: 416c adcs r4, r5 + 8005efc: c310 stmia r3!, {r4} + 8005efe: c910 ldmia r1!, {r4} + 8005f00: ca20 ldmia r2!, {r5} + 8005f02: 416c adcs r4, r5 + 8005f04: c310 stmia r3!, {r4} + 8005f06: 4140 adcs r0, r0 +} + 8005f08: bd30 pop {r4, r5, pc} + +08005f0a : + uECC_Curve curve) { + 8005f0a: b573 push {r0, r1, r4, r5, r6, lr} + 8005f0c: 460d mov r5, r1 + 8005f0e: 4616 mov r6, r2 + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005f10: 4601 mov r1, r0 + 8005f12: f103 0224 add.w r2, r3, #36 ; 0x24 + 8005f16: 4628 mov r0, r5 + 8005f18: 9201 str r2, [sp, #4] + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005f1a: f9b3 4002 ldrsh.w r4, [r3, #2] + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005f1e: f7ff ffcf bl 8005ec0 + 8005f22: 9a01 ldr r2, [sp, #4] + 8005f24: b9c8 cbnz r0, 8005f5a + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8005f26: f114 031f adds.w r3, r4, #31 + 8005f2a: bf48 it mi + 8005f2c: f104 033e addmi.w r3, r4, #62 ; 0x3e + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + 8005f30: f343 1347 sbfx r3, r3, #5, #8 + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005f34: ebb4 1f43 cmp.w r4, r3, lsl #5 + 8005f38: da11 bge.n 8005f5e + uECC_vli_testBit(k0, num_n_bits)); + 8005f3a: 4621 mov r1, r4 + 8005f3c: 4628 mov r0, r5 + 8005f3e: 9201 str r2, [sp, #4] + 8005f40: f7ff fd39 bl 80059b6 + 8005f44: 9a01 ldr r2, [sp, #4] + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + 8005f46: 1e04 subs r4, r0, #0 + 8005f48: bf18 it ne + 8005f4a: 2401 movne r4, #1 + uECC_vli_add(k1, k0, curve->n, num_n_words); + 8005f4c: 4629 mov r1, r5 + 8005f4e: 4630 mov r0, r6 + 8005f50: f7ff ffb6 bl 8005ec0 +} + 8005f54: 4620 mov r0, r4 + 8005f56: b002 add sp, #8 + 8005f58: bd70 pop {r4, r5, r6, pc} + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + 8005f5a: 2401 movs r4, #1 + 8005f5c: e7f6 b.n 8005f4c + 8005f5e: 2400 movs r4, #0 + 8005f60: e7f4 b.n 8005f4c + +08005f62 : + /* add the 2^32 multiple */ + result[4 + num_words_secp256k1] = + uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + 8005f62: b5f8 push {r3, r4, r5, r6, r7, lr} + 8005f64: 460a mov r2, r1 + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uint32_t carry = 0; + 8005f66: 2300 movs r3, #0 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + 8005f68: 4604 mov r4, r0 + 8005f6a: 3904 subs r1, #4 + 8005f6c: 3804 subs r0, #4 + 8005f6e: f102 071c add.w r7, r2, #28 + wordcount_t k; + + for (k = 0; k < num_words_secp256k1; ++k) { + uint64_t p = (uint64_t)0x3D1 * right[k] + carry; + 8005f72: 469e mov lr, r3 + 8005f74: f240 35d1 movw r5, #977 ; 0x3d1 + 8005f78: f851 6f04 ldr.w r6, [r1, #4]! + 8005f7c: 46f4 mov ip, lr + 8005f7e: fbe6 3c05 umlal r3, ip, r6, r5 + for (k = 0; k < num_words_secp256k1; ++k) { + 8005f82: 428f cmp r7, r1 + result[k] = p; + 8005f84: f840 3f04 str.w r3, [r0, #4]! + carry = p >> 32; + 8005f88: 4663 mov r3, ip + for (k = 0; k < num_words_secp256k1; ++k) { + 8005f8a: d1f5 bne.n 8005f78 + } + result[num_words_secp256k1] = carry; + /* add the 2^32 multiple */ + result[1 + num_words_secp256k1] = + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); + 8005f8c: 1d21 adds r1, r4, #4 + result[num_words_secp256k1] = carry; + 8005f8e: f8c4 c020 str.w ip, [r4, #32] + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); + 8005f92: 4608 mov r0, r1 + 8005f94: f7ff ff94 bl 8005ec0 + result[1 + num_words_secp256k1] = + 8005f98: 6260 str r0, [r4, #36] ; 0x24 +} + 8005f9a: bdf8 pop {r3, r4, r5, r6, r7, pc} + +08005f9c : +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + 8005f9c: b570 push {r4, r5, r6, lr} + 8005f9e: b090 sub sp, #64 ; 0x40 + 8005fa0: 460e mov r6, r1 + 8005fa2: 4604 mov r4, r0 + uECC_vli_clear(tmp, num_words_secp256k1); + 8005fa4: 2108 movs r1, #8 + 8005fa6: 4668 mov r0, sp + 8005fa8: f7ff fcf0 bl 800598c + uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); + 8005fac: 2108 movs r1, #8 + 8005fae: a808 add r0, sp, #32 + 8005fb0: f7ff fcec bl 800598c + omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ + 8005fb4: f106 0120 add.w r1, r6, #32 + 8005fb8: 4668 mov r0, sp + 8005fba: f7ff ffd2 bl 8005f62 + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + 8005fbe: 466a mov r2, sp + 8005fc0: 4631 mov r1, r6 + 8005fc2: 4620 mov r0, r4 + 8005fc4: f7ff ff7c bl 8005ec0 + uECC_vli_clear(product, num_words_secp256k1); + 8005fc8: 2108 movs r1, #8 + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + 8005fca: 4605 mov r5, r0 + uECC_vli_clear(product, num_words_secp256k1); + 8005fcc: 4630 mov r0, r6 + 8005fce: f7ff fcdd bl 800598c + omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ + 8005fd2: 4630 mov r0, r6 + 8005fd4: a908 add r1, sp, #32 + 8005fd6: f7ff ffc4 bl 8005f62 + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + 8005fda: 4632 mov r2, r6 + 8005fdc: 4621 mov r1, r4 + 8005fde: 4620 mov r0, r4 + 8005fe0: f7ff ff6e bl 8005ec0 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005fe4: 4e0b ldr r6, [pc, #44] ; (8006014 ) + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + 8005fe6: 4405 add r5, r0 + while (carry > 0) { + 8005fe8: b96d cbnz r5, 8006006 + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { + 8005fea: 490a ldr r1, [pc, #40] ; (8006014 ) + 8005fec: 2208 movs r2, #8 + 8005fee: 4620 mov r0, r4 + 8005ff0: f7ff fd17 bl 8005a22 + 8005ff4: 2800 cmp r0, #0 + 8005ff6: dd04 ble.n 8006002 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8005ff8: 460a mov r2, r1 + 8005ffa: 4620 mov r0, r4 + 8005ffc: 4621 mov r1, r4 + 8005ffe: f7ff fe9d bl 8005d3c +} + 8006002: b010 add sp, #64 ; 0x40 + 8006004: bd70 pop {r4, r5, r6, pc} + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 8006006: 4632 mov r2, r6 + 8006008: 4621 mov r1, r4 + 800600a: 4620 mov r0, r4 + --carry; + 800600c: 3d01 subs r5, #1 + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + 800600e: f7ff fe95 bl 8005d3c + 8006012: e7e9 b.n 8005fe8 + 8006014: 080108f4 .word 0x080108f4 + +08006018 : +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 8006018: e92d 44f0 stmdb sp!, {r4, r5, r6, r7, sl, lr} + uECC_vli_set(result, product, num_words_secp256r1); + 800601c: 2208 movs r2, #8 +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 800601e: b088 sub sp, #32 + uECC_vli_set(result, product, num_words_secp256r1); + 8006020: f7ff fcf3 bl 8005a0a + tmp[3] = product[11]; + 8006024: 6acb ldr r3, [r1, #44] ; 0x2c + 8006026: 9303 str r3, [sp, #12] + tmp[4] = product[12]; + 8006028: 6b0b ldr r3, [r1, #48] ; 0x30 + 800602a: 9304 str r3, [sp, #16] + tmp[5] = product[13]; + 800602c: 6b4b ldr r3, [r1, #52] ; 0x34 + 800602e: 9305 str r3, [sp, #20] + tmp[6] = product[14]; + 8006030: 6b8b ldr r3, [r1, #56] ; 0x38 + 8006032: 9306 str r3, [sp, #24] +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + 8006034: 460c mov r4, r1 + 8006036: 4682 mov sl, r0 + tmp[0] = tmp[1] = tmp[2] = 0; + 8006038: 2700 movs r7, #0 + tmp[7] = product[15]; + 800603a: 6bcb ldr r3, [r1, #60] ; 0x3c + 800603c: 9307 str r3, [sp, #28] + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 800603e: 466a mov r2, sp + 8006040: 4669 mov r1, sp + 8006042: 4668 mov r0, sp + tmp[0] = tmp[1] = tmp[2] = 0; + 8006044: e9cd 7701 strd r7, r7, [sp, #4] + 8006048: 9700 str r7, [sp, #0] + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 800604a: f7ff ff39 bl 8005ec0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 800604e: 466a mov r2, sp + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8006050: 4605 mov r5, r0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8006052: 4651 mov r1, sl + 8006054: 4650 mov r0, sl + 8006056: f7ff ff33 bl 8005ec0 + tmp[3] = product[12]; + 800605a: 6b23 ldr r3, [r4, #48] ; 0x30 + 800605c: 9303 str r3, [sp, #12] + tmp[4] = product[13]; + 800605e: 6b63 ldr r3, [r4, #52] ; 0x34 + 8006060: 9304 str r3, [sp, #16] + tmp[5] = product[14]; + 8006062: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8006064: 9305 str r3, [sp, #20] + tmp[6] = product[15]; + 8006066: 6be3 ldr r3, [r4, #60] ; 0x3c + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8006068: 4405 add r5, r0 + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 800606a: 466a mov r2, sp + 800606c: 4669 mov r1, sp + 800606e: 4668 mov r0, sp + tmp[7] = 0; + 8006070: e9cd 3706 strd r3, r7, [sp, #24] + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 8006074: f7ff ff24 bl 8005ec0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8006078: 466a mov r2, sp + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + 800607a: 4405 add r5, r0 + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 800607c: 4651 mov r1, sl + 800607e: 4650 mov r0, sl + 8006080: f7ff ff1e bl 8005ec0 + tmp[0] = product[8]; + 8006084: 6a23 ldr r3, [r4, #32] + 8006086: 9300 str r3, [sp, #0] + tmp[1] = product[9]; + 8006088: 6a63 ldr r3, [r4, #36] ; 0x24 + 800608a: 9301 str r3, [sp, #4] + tmp[2] = product[10]; + 800608c: 6aa3 ldr r3, [r4, #40] ; 0x28 + 800608e: 9302 str r3, [sp, #8] + tmp[6] = product[14]; + 8006090: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8006092: 9306 str r3, [sp, #24] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 8006094: 4405 add r5, r0 + tmp[7] = product[15]; + 8006096: 6be3 ldr r3, [r4, #60] ; 0x3c + 8006098: 9307 str r3, [sp, #28] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 800609a: 466a mov r2, sp + 800609c: 4651 mov r1, sl + 800609e: 4650 mov r0, sl + tmp[3] = tmp[4] = tmp[5] = 0; + 80060a0: e9cd 7704 strd r7, r7, [sp, #16] + 80060a4: 9703 str r7, [sp, #12] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 80060a6: f7ff ff0b bl 8005ec0 + tmp[0] = product[9]; + 80060aa: 6a63 ldr r3, [r4, #36] ; 0x24 + 80060ac: 9300 str r3, [sp, #0] + tmp[1] = product[10]; + 80060ae: 6aa3 ldr r3, [r4, #40] ; 0x28 + tmp[4] = product[14]; + 80060b0: 6ba2 ldr r2, [r4, #56] ; 0x38 + tmp[1] = product[10]; + 80060b2: 9301 str r3, [sp, #4] + tmp[2] = product[11]; + 80060b4: 6ae3 ldr r3, [r4, #44] ; 0x2c + 80060b6: 9302 str r3, [sp, #8] + tmp[4] = product[14]; + 80060b8: 9204 str r2, [sp, #16] + tmp[3] = product[13]; + 80060ba: 6b63 ldr r3, [r4, #52] ; 0x34 + tmp[5] = product[15]; + 80060bc: 6be2 ldr r2, [r4, #60] ; 0x3c + tmp[3] = product[13]; + 80060be: 9303 str r3, [sp, #12] + tmp[6] = product[13]; + 80060c0: e9cd 2305 strd r2, r3, [sp, #20] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 80060c4: 182e adds r6, r5, r0 + tmp[7] = product[8]; + 80060c6: 6a23 ldr r3, [r4, #32] + 80060c8: 9307 str r3, [sp, #28] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 80060ca: 466a mov r2, sp + 80060cc: 4651 mov r1, sl + 80060ce: 4650 mov r0, sl + 80060d0: f7ff fef6 bl 8005ec0 + tmp[0] = product[11]; + 80060d4: 6ae3 ldr r3, [r4, #44] ; 0x2c + 80060d6: 9300 str r3, [sp, #0] + tmp[1] = product[12]; + 80060d8: 6b23 ldr r3, [r4, #48] ; 0x30 + 80060da: 9301 str r3, [sp, #4] + tmp[2] = product[13]; + 80060dc: 6b63 ldr r3, [r4, #52] ; 0x34 + 80060de: 9302 str r3, [sp, #8] + tmp[6] = product[8]; + 80060e0: 6a23 ldr r3, [r4, #32] + 80060e2: 9306 str r3, [sp, #24] + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + 80060e4: 1835 adds r5, r6, r0 + tmp[7] = product[10]; + 80060e6: 6aa3 ldr r3, [r4, #40] ; 0x28 + 80060e8: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 80060ea: 466a mov r2, sp + 80060ec: 4651 mov r1, sl + 80060ee: 4650 mov r0, sl + tmp[3] = tmp[4] = tmp[5] = 0; + 80060f0: e9cd 7704 strd r7, r7, [sp, #16] + 80060f4: 9703 str r7, [sp, #12] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 80060f6: f7ff fe21 bl 8005d3c + tmp[0] = product[12]; + 80060fa: 6b23 ldr r3, [r4, #48] ; 0x30 + 80060fc: 9300 str r3, [sp, #0] + tmp[1] = product[13]; + 80060fe: 6b63 ldr r3, [r4, #52] ; 0x34 + 8006100: 9301 str r3, [sp, #4] + tmp[2] = product[14]; + 8006102: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8006104: 9302 str r3, [sp, #8] + tmp[3] = product[15]; + 8006106: 6be3 ldr r3, [r4, #60] ; 0x3c + 8006108: 9303 str r3, [sp, #12] + tmp[6] = product[9]; + 800610a: 6a63 ldr r3, [r4, #36] ; 0x24 + 800610c: 9306 str r3, [sp, #24] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 800610e: 1a2e subs r6, r5, r0 + tmp[7] = product[11]; + 8006110: 6ae3 ldr r3, [r4, #44] ; 0x2c + 8006112: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8006114: 466a mov r2, sp + 8006116: 4651 mov r1, sl + 8006118: 4650 mov r0, sl + tmp[4] = tmp[5] = 0; + 800611a: e9cd 7704 strd r7, r7, [sp, #16] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 800611e: f7ff fe0d bl 8005d3c + tmp[0] = product[13]; + 8006122: 6b63 ldr r3, [r4, #52] ; 0x34 + 8006124: 9300 str r3, [sp, #0] + tmp[1] = product[14]; + 8006126: 6ba3 ldr r3, [r4, #56] ; 0x38 + 8006128: 9301 str r3, [sp, #4] + tmp[2] = product[15]; + 800612a: 6be3 ldr r3, [r4, #60] ; 0x3c + 800612c: 9302 str r3, [sp, #8] + tmp[3] = product[8]; + 800612e: 6a23 ldr r3, [r4, #32] + 8006130: 9303 str r3, [sp, #12] + tmp[4] = product[9]; + 8006132: 6a63 ldr r3, [r4, #36] ; 0x24 + 8006134: 9304 str r3, [sp, #16] + tmp[5] = product[10]; + 8006136: 6aa3 ldr r3, [r4, #40] ; 0x28 + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8006138: 1a36 subs r6, r6, r0 + tmp[6] = 0; + 800613a: e9cd 3705 strd r3, r7, [sp, #20] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 800613e: 466a mov r2, sp + tmp[7] = product[12]; + 8006140: 6b23 ldr r3, [r4, #48] ; 0x30 + 8006142: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8006144: 4651 mov r1, sl + 8006146: 4650 mov r0, sl + 8006148: f7ff fdf8 bl 8005d3c + tmp[0] = product[14]; + 800614c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800614e: 9300 str r3, [sp, #0] + tmp[1] = product[15]; + 8006150: 6be3 ldr r3, [r4, #60] ; 0x3c + tmp[2] = 0; + 8006152: e9cd 3701 strd r3, r7, [sp, #4] + tmp[3] = product[9]; + 8006156: 6a63 ldr r3, [r4, #36] ; 0x24 + 8006158: 9303 str r3, [sp, #12] + tmp[4] = product[10]; + 800615a: 6aa3 ldr r3, [r4, #40] ; 0x28 + 800615c: 9304 str r3, [sp, #16] + tmp[5] = product[11]; + 800615e: 6ae3 ldr r3, [r4, #44] ; 0x2c + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8006160: 1a36 subs r6, r6, r0 + tmp[6] = 0; + 8006162: e9cd 3705 strd r3, r7, [sp, #20] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 8006166: 466a mov r2, sp + tmp[7] = product[13]; + 8006168: 6b63 ldr r3, [r4, #52] ; 0x34 + 800616a: 9307 str r3, [sp, #28] + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + 800616c: 4651 mov r1, sl + 800616e: 4650 mov r0, sl + 8006170: f7ff fde4 bl 8005d3c + if (carry < 0) { + 8006174: 1a36 subs r6, r6, r0 + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + 8006176: 4c0d ldr r4, [pc, #52] ; (80061ac ) + if (carry < 0) { + 8006178: d40e bmi.n 8006198 + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + 800617a: b936 cbnz r6, 800618a + 800617c: 2208 movs r2, #8 + 800617e: 4651 mov r1, sl + 8006180: 4620 mov r0, r4 + 8006182: f7ff fc4e bl 8005a22 + 8006186: 2801 cmp r0, #1 + 8006188: d00d beq.n 80061a6 + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + 800618a: 4622 mov r2, r4 + 800618c: 4651 mov r1, sl + 800618e: 4650 mov r0, sl + 8006190: f7ff fdd4 bl 8005d3c + 8006194: 1a36 subs r6, r6, r0 + 8006196: e7f0 b.n 800617a + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + 8006198: 4622 mov r2, r4 + 800619a: 4651 mov r1, sl + 800619c: 4650 mov r0, sl + 800619e: f7ff fe8f bl 8005ec0 + } while (carry < 0); + 80061a2: 1836 adds r6, r6, r0 + 80061a4: d4f8 bmi.n 8006198 +} + 80061a6: b008 add sp, #32 + 80061a8: e8bd 84f0 ldmia.w sp!, {r4, r5, r6, r7, sl, pc} + 80061ac: 080109a8 .word 0x080109a8 + +080061b0 : +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + 80061b0: b5f0 push {r4, r5, r6, r7, lr} + 80061b2: b091 sub sp, #68 ; 0x44 + 80061b4: 460d mov r5, r1 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 80061b6: 221c movs r2, #28 + 80061b8: 2100 movs r1, #0 +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + 80061ba: 4606 mov r6, r0 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 80061bc: a801 add r0, sp, #4 + 80061be: f007 fbb1 bl 800d924 + 80061c2: 2401 movs r4, #1 + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 80061c4: 221c movs r2, #28 + 80061c6: 2100 movs r1, #0 + 80061c8: a809 add r0, sp, #36 ; 0x24 + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + 80061ca: 9400 str r4, [sp, #0] + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 80061cc: f007 fbaa bl 800d924 + wordcount_t num_words = curve->num_words; + 80061d0: 4629 mov r1, r5 + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + 80061d2: 466a mov r2, sp + wordcount_t num_words = curve->num_words; + 80061d4: f911 7b04 ldrsb.w r7, [r1], #4 + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + 80061d8: 9408 str r4, [sp, #32] + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + 80061da: 4668 mov r0, sp + 80061dc: f7ff fe70 bl 8005ec0 + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + 80061e0: 4639 mov r1, r7 + 80061e2: 4668 mov r0, sp + 80061e4: f7ff fbf1 bl 80059ca + 80061e8: 1e44 subs r4, r0, #1 + 80061ea: b224 sxth r4, r4 + 80061ec: 2c01 cmp r4, #1 + 80061ee: dc06 bgt.n 80061fe + uECC_vli_set(a, l_result, num_words); + 80061f0: 463a mov r2, r7 + 80061f2: a908 add r1, sp, #32 + 80061f4: 4630 mov r0, r6 + 80061f6: f7ff fc08 bl 8005a0a +} + 80061fa: b011 add sp, #68 ; 0x44 + 80061fc: bdf0 pop {r4, r5, r6, r7, pc} + uECC_vli_modSquare_fast(l_result, l_result, curve); + 80061fe: a908 add r1, sp, #32 + 8006200: 4608 mov r0, r1 + 8006202: 462a mov r2, r5 + 8006204: f7ff fcdf bl 8005bc6 + if (uECC_vli_testBit(p1, i)) { + 8006208: 4621 mov r1, r4 + 800620a: 4668 mov r0, sp + 800620c: f7ff fbd3 bl 80059b6 + 8006210: b128 cbz r0, 800621e + uECC_vli_modMult_fast(l_result, l_result, a, curve); + 8006212: a908 add r1, sp, #32 + 8006214: 462b mov r3, r5 + 8006216: 4632 mov r2, r6 + 8006218: 4608 mov r0, r1 + 800621a: f7ff fcc4 bl 8005ba6 + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + 800621e: 3c01 subs r4, #1 + 8006220: e7e3 b.n 80061ea + +08006222 : + if (!EVEN(uv)) { + 8006222: 6803 ldr r3, [r0, #0] + wordcount_t num_words) { + 8006224: b570 push {r4, r5, r6, lr} + if (!EVEN(uv)) { + 8006226: f013 0601 ands.w r6, r3, #1 + wordcount_t num_words) { + 800622a: 4605 mov r5, r0 + 800622c: 4614 mov r4, r2 + if (!EVEN(uv)) { + 800622e: d004 beq.n 800623a + carry = uECC_vli_add(uv, uv, mod, num_words); + 8006230: 460a mov r2, r1 + 8006232: 4601 mov r1, r0 + 8006234: f7ff fe44 bl 8005ec0 + 8006238: 4606 mov r6, r0 + uECC_vli_rshift1(uv, num_words); + 800623a: 4621 mov r1, r4 + 800623c: 4628 mov r0, r5 + 800623e: f7ff fc05 bl 8005a4c + if (carry) { + 8006242: b146 cbz r6, 8006256 + uv[num_words - 1] |= HIGH_BIT_SET; + 8006244: f104 4280 add.w r2, r4, #1073741824 ; 0x40000000 + 8006248: 3a01 subs r2, #1 + 800624a: f855 3022 ldr.w r3, [r5, r2, lsl #2] + 800624e: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 8006252: f845 3022 str.w r3, [r5, r2, lsl #2] +} + 8006256: bd70 pop {r4, r5, r6, pc} + +08006258 : + wordcount_t num_words) { + 8006258: b5f0 push {r4, r5, r6, r7, lr} + 800625a: 460f mov r7, r1 + 800625c: b0a1 sub sp, #132 ; 0x84 + 800625e: 4606 mov r6, r0 + if (uECC_vli_isZero(input, num_words)) { + 8006260: 4619 mov r1, r3 + 8006262: 4638 mov r0, r7 + wordcount_t num_words) { + 8006264: 4615 mov r5, r2 + 8006266: 461c mov r4, r3 + if (uECC_vli_isZero(input, num_words)) { + 8006268: f7ff fb96 bl 8005998 + 800626c: b128 cbz r0, 800627a + uECC_vli_clear(result, num_words); + 800626e: 4630 mov r0, r6 +} + 8006270: b021 add sp, #132 ; 0x84 + 8006272: e8bd 40f0 ldmia.w sp!, {r4, r5, r6, r7, lr} + uECC_vli_clear(result, num_words); + 8006276: f7ff bb89 b.w 800598c + uECC_vli_set(a, input, num_words); + 800627a: 4622 mov r2, r4 + 800627c: 4639 mov r1, r7 + 800627e: 4668 mov r0, sp + 8006280: f7ff fbc3 bl 8005a0a + uECC_vli_set(b, mod, num_words); + 8006284: 4629 mov r1, r5 + 8006286: a808 add r0, sp, #32 + 8006288: f7ff fbbf bl 8005a0a + uECC_vli_clear(u, num_words); + 800628c: 4621 mov r1, r4 + 800628e: a810 add r0, sp, #64 ; 0x40 + 8006290: f7ff fb7c bl 800598c + u[0] = 1; + 8006294: 2301 movs r3, #1 + uECC_vli_clear(v, num_words); + 8006296: 4621 mov r1, r4 + 8006298: a818 add r0, sp, #96 ; 0x60 + u[0] = 1; + 800629a: 9310 str r3, [sp, #64] ; 0x40 + uECC_vli_clear(v, num_words); + 800629c: f7ff fb76 bl 800598c + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + 80062a0: 4622 mov r2, r4 + 80062a2: a908 add r1, sp, #32 + 80062a4: 4668 mov r0, sp + 80062a6: f7ff fbbc bl 8005a22 + 80062aa: b930 cbnz r0, 80062ba + uECC_vli_set(result, u, num_words); + 80062ac: 4622 mov r2, r4 + 80062ae: a910 add r1, sp, #64 ; 0x40 + 80062b0: 4630 mov r0, r6 + 80062b2: f7ff fbaa bl 8005a0a +} + 80062b6: b021 add sp, #132 ; 0x84 + 80062b8: bdf0 pop {r4, r5, r6, r7, pc} + if (EVEN(a)) { + 80062ba: 9b00 ldr r3, [sp, #0] + 80062bc: 07da lsls r2, r3, #31 + 80062be: d409 bmi.n 80062d4 + uECC_vli_rshift1(a, num_words); + 80062c0: 4621 mov r1, r4 + 80062c2: 4668 mov r0, sp + 80062c4: f7ff fbc2 bl 8005a4c + vli_modInv_update(u, mod, num_words); + 80062c8: 4622 mov r2, r4 + 80062ca: 4629 mov r1, r5 + 80062cc: a810 add r0, sp, #64 ; 0x40 + vli_modInv_update(v, mod, num_words); + 80062ce: f7ff ffa8 bl 8006222 + 80062d2: e7e5 b.n 80062a0 + } else if (EVEN(b)) { + 80062d4: 9b08 ldr r3, [sp, #32] + 80062d6: 07db lsls r3, r3, #31 + 80062d8: d407 bmi.n 80062ea + uECC_vli_rshift1(b, num_words); + 80062da: 4621 mov r1, r4 + 80062dc: a808 add r0, sp, #32 + 80062de: f7ff fbb5 bl 8005a4c + vli_modInv_update(v, mod, num_words); + 80062e2: 4622 mov r2, r4 + 80062e4: 4629 mov r1, r5 + 80062e6: a818 add r0, sp, #96 ; 0x60 + 80062e8: e7f1 b.n 80062ce + } else if (cmpResult > 0) { + 80062ea: 2800 cmp r0, #0 + 80062ec: dd1a ble.n 8006324 + uECC_vli_sub(a, a, b, num_words); + 80062ee: aa08 add r2, sp, #32 + 80062f0: 4669 mov r1, sp + 80062f2: 4668 mov r0, sp + 80062f4: f7ff fd22 bl 8005d3c + uECC_vli_rshift1(a, num_words); + 80062f8: 4621 mov r1, r4 + 80062fa: 4668 mov r0, sp + 80062fc: f7ff fba6 bl 8005a4c + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + 8006300: 4622 mov r2, r4 + 8006302: a918 add r1, sp, #96 ; 0x60 + 8006304: a810 add r0, sp, #64 ; 0x40 + 8006306: f7ff fb8c bl 8005a22 + 800630a: 2800 cmp r0, #0 + 800630c: da04 bge.n 8006318 + uECC_vli_add(u, u, mod, num_words); + 800630e: a910 add r1, sp, #64 ; 0x40 + 8006310: 462a mov r2, r5 + 8006312: 4608 mov r0, r1 + 8006314: f7ff fdd4 bl 8005ec0 + uECC_vli_sub(u, u, v, num_words); + 8006318: a910 add r1, sp, #64 ; 0x40 + 800631a: aa18 add r2, sp, #96 ; 0x60 + 800631c: 4608 mov r0, r1 + 800631e: f7ff fd0d bl 8005d3c + 8006322: e7d1 b.n 80062c8 + uECC_vli_sub(b, b, a, num_words); + 8006324: 466a mov r2, sp + 8006326: a808 add r0, sp, #32 + 8006328: f7ff fd08 bl 8005d3c + uECC_vli_rshift1(b, num_words); + 800632c: 4621 mov r1, r4 + 800632e: a808 add r0, sp, #32 + 8006330: f7ff fb8c bl 8005a4c + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + 8006334: 4622 mov r2, r4 + 8006336: a910 add r1, sp, #64 ; 0x40 + 8006338: a818 add r0, sp, #96 ; 0x60 + 800633a: f7ff fb72 bl 8005a22 + 800633e: 2800 cmp r0, #0 + 8006340: da04 bge.n 800634c + uECC_vli_add(v, v, mod, num_words); + 8006342: a918 add r1, sp, #96 ; 0x60 + 8006344: 462a mov r2, r5 + 8006346: 4608 mov r0, r1 + 8006348: f7ff fdba bl 8005ec0 + uECC_vli_sub(v, v, u, num_words); + 800634c: a918 add r1, sp, #96 ; 0x60 + 800634e: aa10 add r2, sp, #64 ; 0x40 + 8006350: 4608 mov r0, r1 + 8006352: f7ff fcf3 bl 8005d3c + 8006356: e7c4 b.n 80062e2 + +08006358 : + wordcount_t num_words) { + 8006358: b570 push {r4, r5, r6, lr} + 800635a: 4604 mov r4, r0 + 800635c: f99d 6010 ldrsb.w r6, [sp, #16] + 8006360: 461d mov r5, r3 + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + 8006362: f7ff fdad bl 8005ec0 + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + 8006366: b930 cbnz r0, 8006376 + 8006368: 4632 mov r2, r6 + 800636a: 4621 mov r1, r4 + 800636c: 4628 mov r0, r5 + 800636e: f7ff fb58 bl 8005a22 + 8006372: 2801 cmp r0, #1 + 8006374: d006 beq.n 8006384 + uECC_vli_sub(result, result, mod, num_words); + 8006376: 462a mov r2, r5 + 8006378: 4621 mov r1, r4 + 800637a: 4620 mov r0, r4 +} + 800637c: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr} + uECC_vli_sub(result, result, mod, num_words); + 8006380: f7ff bcdc b.w 8005d3c +} + 8006384: bd70 pop {r4, r5, r6, pc} + +08006386 : +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + 8006386: b573 push {r0, r1, r4, r5, r6, lr} + 8006388: 4604 mov r4, r0 + 800638a: 4615 mov r5, r2 + 800638c: 460e mov r6, r1 + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 800638e: f7ff fc1a bl 8005bc6 + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ + 8006392: 462b mov r3, r5 + 8006394: 4632 mov r2, r6 + 8006396: 4621 mov r1, r4 + 8006398: 4620 mov r0, r4 + 800639a: f7ff fc04 bl 8005ba6 + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ + 800639e: 2308 movs r3, #8 + 80063a0: 9300 str r3, [sp, #0] + 80063a2: f105 0284 add.w r2, r5, #132 ; 0x84 + 80063a6: 1d2b adds r3, r5, #4 + 80063a8: 4621 mov r1, r4 + 80063aa: 4620 mov r0, r4 + 80063ac: f7ff ffd4 bl 8006358 +} + 80063b0: b002 add sp, #8 + 80063b2: bd70 pop {r4, r5, r6, pc} + +080063b4 : +uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, + 80063b4: b538 push {r3, r4, r5, lr} + 80063b6: 4604 mov r4, r0 + 80063b8: 461d mov r5, r3 + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + 80063ba: f7ff fcbf bl 8005d3c + if (l_borrow) { + 80063be: b130 cbz r0, 80063ce + uECC_vli_add(result, result, mod, num_words); + 80063c0: 462a mov r2, r5 + 80063c2: 4621 mov r1, r4 + 80063c4: 4620 mov r0, r4 +} + 80063c6: e8bd 4038 ldmia.w sp!, {r3, r4, r5, lr} + uECC_vli_add(result, result, mod, num_words); + 80063ca: f7ff bd79 b.w 8005ec0 +} + 80063ce: bd38 pop {r3, r4, r5, pc} + +080063d0 : + uECC_Curve curve) { + 80063d0: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80063d4: b09a sub sp, #104 ; 0x68 + 80063d6: 4615 mov r5, r2 + 80063d8: 9f22 ldr r7, [sp, #136] ; 0x88 + wordcount_t num_words = curve->num_words; + 80063da: 463c mov r4, r7 + uECC_Curve curve) { + 80063dc: 4698 mov r8, r3 + wordcount_t num_words = curve->num_words; + 80063de: f914 ab04 ldrsb.w sl, [r4], #4 + uECC_Curve curve) { + 80063e2: 4606 mov r6, r0 + 80063e4: 4689 mov r9, r1 + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + 80063e6: 4623 mov r3, r4 + 80063e8: 4602 mov r2, r0 + 80063ea: 4629 mov r1, r5 + 80063ec: a802 add r0, sp, #8 + 80063ee: f7ff ffe1 bl 80063b4 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + 80063f2: a902 add r1, sp, #8 + 80063f4: 463a mov r2, r7 + 80063f6: 4608 mov r0, r1 + 80063f8: f7ff fbe5 bl 8005bc6 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + 80063fc: 463b mov r3, r7 + 80063fe: aa02 add r2, sp, #8 + 8006400: 4631 mov r1, r6 + 8006402: 4630 mov r0, r6 + 8006404: f7ff fbcf bl 8005ba6 + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + 8006408: 463b mov r3, r7 + 800640a: aa02 add r2, sp, #8 + 800640c: 4629 mov r1, r5 + 800640e: 4628 mov r0, r5 + 8006410: f7ff fbc9 bl 8005ba6 + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + 8006414: 4623 mov r3, r4 + 8006416: 464a mov r2, r9 + 8006418: 4641 mov r1, r8 + 800641a: a802 add r0, sp, #8 + 800641c: f8cd a000 str.w sl, [sp] + 8006420: f7ff ff9a bl 8006358 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + 8006424: 4623 mov r3, r4 + 8006426: 464a mov r2, r9 + 8006428: 4641 mov r1, r8 + 800642a: 4640 mov r0, r8 + 800642c: f7ff ffc2 bl 80063b4 + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + 8006430: 4623 mov r3, r4 + 8006432: 4632 mov r2, r6 + 8006434: 4629 mov r1, r5 + 8006436: a80a add r0, sp, #40 ; 0x28 + 8006438: f7ff ffbc bl 80063b4 + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + 800643c: 463b mov r3, r7 + 800643e: aa0a add r2, sp, #40 ; 0x28 + 8006440: 4649 mov r1, r9 + 8006442: 4648 mov r0, r9 + 8006444: f7ff fbaf bl 8005ba6 + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + 8006448: 4623 mov r3, r4 + 800644a: 462a mov r2, r5 + 800644c: 4631 mov r1, r6 + 800644e: a80a add r0, sp, #40 ; 0x28 + 8006450: f8cd a000 str.w sl, [sp] + 8006454: f7ff ff80 bl 8006358 + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + 8006458: 463a mov r2, r7 + 800645a: 4641 mov r1, r8 + 800645c: 4628 mov r0, r5 + 800645e: f7ff fbb2 bl 8005bc6 + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + 8006462: 4623 mov r3, r4 + 8006464: aa0a add r2, sp, #40 ; 0x28 + 8006466: 4629 mov r1, r5 + 8006468: 4628 mov r0, r5 + 800646a: f7ff ffa3 bl 80063b4 + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + 800646e: 4623 mov r3, r4 + 8006470: 462a mov r2, r5 + 8006472: 4631 mov r1, r6 + 8006474: a812 add r0, sp, #72 ; 0x48 + 8006476: f7ff ff9d bl 80063b4 + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + 800647a: 463b mov r3, r7 + 800647c: aa12 add r2, sp, #72 ; 0x48 + 800647e: 4641 mov r1, r8 + 8006480: 4640 mov r0, r8 + 8006482: f7ff fb90 bl 8005ba6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ + 8006486: 4623 mov r3, r4 + 8006488: 464a mov r2, r9 + 800648a: 4641 mov r1, r8 + 800648c: 4640 mov r0, r8 + 800648e: f7ff ff91 bl 80063b4 + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + 8006492: 463a mov r2, r7 + 8006494: a902 add r1, sp, #8 + 8006496: a812 add r0, sp, #72 ; 0x48 + 8006498: f7ff fb95 bl 8005bc6 + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + 800649c: a912 add r1, sp, #72 ; 0x48 + 800649e: 4623 mov r3, r4 + 80064a0: aa0a add r2, sp, #40 ; 0x28 + 80064a2: 4608 mov r0, r1 + 80064a4: f7ff ff86 bl 80063b4 + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + 80064a8: 4623 mov r3, r4 + 80064aa: 4632 mov r2, r6 + 80064ac: a912 add r1, sp, #72 ; 0x48 + 80064ae: a80a add r0, sp, #40 ; 0x28 + 80064b0: f7ff ff80 bl 80063b4 + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + 80064b4: a90a add r1, sp, #40 ; 0x28 + 80064b6: 463b mov r3, r7 + 80064b8: aa02 add r2, sp, #8 + 80064ba: 4608 mov r0, r1 + 80064bc: f7ff fb73 bl 8005ba6 + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ + 80064c0: 4623 mov r3, r4 + 80064c2: 464a mov r2, r9 + 80064c4: a90a add r1, sp, #40 ; 0x28 + 80064c6: 4648 mov r0, r9 + 80064c8: f7ff ff74 bl 80063b4 + uECC_vli_set(X1, t7, num_words); + 80064cc: 4652 mov r2, sl + 80064ce: a912 add r1, sp, #72 ; 0x48 + 80064d0: 4630 mov r0, r6 + 80064d2: f7ff fa9a bl 8005a0a +} + 80064d6: b01a add sp, #104 ; 0x68 + 80064d8: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + +080064dc : + uECC_Curve curve) { + 80064dc: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 80064e0: b088 sub sp, #32 + 80064e2: 4614 mov r4, r2 + 80064e4: f8dd 8040 ldr.w r8, [sp, #64] ; 0x40 + wordcount_t num_words = curve->num_words; + 80064e8: 4645 mov r5, r8 + uECC_Curve curve) { + 80064ea: 461e mov r6, r3 + wordcount_t num_words = curve->num_words; + 80064ec: f915 ab04 ldrsb.w sl, [r5], #4 + uECC_Curve curve) { + 80064f0: 4607 mov r7, r0 + 80064f2: 4689 mov r9, r1 + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + 80064f4: 462b mov r3, r5 + 80064f6: 4602 mov r2, r0 + 80064f8: 4621 mov r1, r4 + 80064fa: 4668 mov r0, sp + 80064fc: f7ff ff5a bl 80063b4 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + 8006500: 4642 mov r2, r8 + 8006502: 4669 mov r1, sp + 8006504: 4668 mov r0, sp + 8006506: f7ff fb5e bl 8005bc6 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + 800650a: 4643 mov r3, r8 + 800650c: 466a mov r2, sp + 800650e: 4639 mov r1, r7 + 8006510: 4638 mov r0, r7 + 8006512: f7ff fb48 bl 8005ba6 + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + 8006516: 4643 mov r3, r8 + 8006518: 466a mov r2, sp + 800651a: 4621 mov r1, r4 + 800651c: 4620 mov r0, r4 + 800651e: f7ff fb42 bl 8005ba6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + 8006522: 462b mov r3, r5 + 8006524: 464a mov r2, r9 + 8006526: 4631 mov r1, r6 + 8006528: 4630 mov r0, r6 + 800652a: f7ff ff43 bl 80063b4 + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + 800652e: 4642 mov r2, r8 + 8006530: 4631 mov r1, r6 + 8006532: 4668 mov r0, sp + 8006534: f7ff fb47 bl 8005bc6 + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + 8006538: 462b mov r3, r5 + 800653a: 463a mov r2, r7 + 800653c: 4669 mov r1, sp + 800653e: 4668 mov r0, sp + 8006540: f7ff ff38 bl 80063b4 + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + 8006544: 462b mov r3, r5 + 8006546: 4622 mov r2, r4 + 8006548: 4669 mov r1, sp + 800654a: 4668 mov r0, sp + 800654c: f7ff ff32 bl 80063b4 + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + 8006550: 462b mov r3, r5 + 8006552: 463a mov r2, r7 + 8006554: 4621 mov r1, r4 + 8006556: 4620 mov r0, r4 + 8006558: f7ff ff2c bl 80063b4 + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + 800655c: 4643 mov r3, r8 + 800655e: 4622 mov r2, r4 + 8006560: 4649 mov r1, r9 + 8006562: 4648 mov r0, r9 + 8006564: f7ff fb1f bl 8005ba6 + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + 8006568: 462b mov r3, r5 + 800656a: 466a mov r2, sp + 800656c: 4639 mov r1, r7 + 800656e: 4620 mov r0, r4 + 8006570: f7ff ff20 bl 80063b4 + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + 8006574: 4643 mov r3, r8 + 8006576: 4622 mov r2, r4 + 8006578: 4631 mov r1, r6 + 800657a: 4630 mov r0, r6 + 800657c: f7ff fb13 bl 8005ba6 + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + 8006580: 462b mov r3, r5 + 8006582: 464a mov r2, r9 + 8006584: 4631 mov r1, r6 + 8006586: 4630 mov r0, r6 + 8006588: f7ff ff14 bl 80063b4 + uECC_vli_set(X2, t5, num_words); + 800658c: 4652 mov r2, sl + 800658e: 4669 mov r1, sp + 8006590: 4620 mov r0, r4 + 8006592: f7ff fa3a bl 8005a0a +} + 8006596: b008 add sp, #32 + 8006598: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + +0800659c : + uECC_Curve curve) { + 800659c: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 80065a0: b0b1 sub sp, #196 ; 0xc4 + 80065a2: e9cd 0102 strd r0, r1, [sp, #8] + 80065a6: 9c3b ldr r4, [sp, #236] ; 0xec + 80065a8: 9204 str r2, [sp, #16] + wordcount_t num_words = curve->num_words; + 80065aa: f994 9000 ldrsb.w r9, [r4] + uECC_vli_set(Rx[1], point, num_words); + 80065ae: a818 add r0, sp, #96 ; 0x60 + 80065b0: 464a mov r2, r9 + uECC_Curve curve) { + 80065b2: 461d mov r5, r3 + uECC_vli_set(Rx[1], point, num_words); + 80065b4: f7ff fa29 bl 8005a0a + uECC_vli_set(Ry[1], point + num_words, num_words); + 80065b8: ea4f 0389 mov.w r3, r9, lsl #2 + 80065bc: 9305 str r3, [sp, #20] + 80065be: 9b03 ldr r3, [sp, #12] + 80065c0: eb03 0a89 add.w sl, r3, r9, lsl #2 + 80065c4: 4651 mov r1, sl + 80065c6: a828 add r0, sp, #160 ; 0xa0 + 80065c8: f7ff fa1f bl 8005a0a + wordcount_t num_words = curve->num_words; + 80065cc: f994 2000 ldrsb.w r2, [r4] + if (initial_Z) { + 80065d0: 2d00 cmp r5, #0 + 80065d2: f000 8082 beq.w 80066da + uECC_vli_set(z, initial_Z, num_words); + 80065d6: 4629 mov r1, r5 + 80065d8: a808 add r0, sp, #32 + 80065da: f7ff fa16 bl 8005a0a + uECC_vli_set(X2, X1, num_words); + 80065de: af10 add r7, sp, #64 ; 0x40 + 80065e0: a918 add r1, sp, #96 ; 0x60 + 80065e2: 4638 mov r0, r7 + uECC_vli_set(Y2, Y1, num_words); + 80065e4: f10d 0880 add.w r8, sp, #128 ; 0x80 + uECC_vli_set(X2, X1, num_words); + 80065e8: f7ff fa0f bl 8005a0a + uECC_vli_set(Y2, Y1, num_words); + 80065ec: a928 add r1, sp, #160 ; 0xa0 + 80065ee: 4640 mov r0, r8 + 80065f0: f7ff fa0b bl 8005a0a + apply_z(X1, Y1, z, curve); + 80065f4: 4623 mov r3, r4 + 80065f6: aa08 add r2, sp, #32 + 80065f8: a818 add r0, sp, #96 ; 0x60 + 80065fa: f7ff fae8 bl 8005bce + curve->double_jacobian(X1, Y1, z, curve); + 80065fe: f8d4 50a4 ldr.w r5, [r4, #164] ; 0xa4 + 8006602: 4623 mov r3, r4 + 8006604: aa08 add r2, sp, #32 + 8006606: a928 add r1, sp, #160 ; 0xa0 + 8006608: a818 add r0, sp, #96 ; 0x60 + 800660a: 47a8 blx r5 + apply_z(X2, Y2, z, curve); + 800660c: 4623 mov r3, r4 + 800660e: aa08 add r2, sp, #32 + 8006610: 4641 mov r1, r8 + 8006612: 4638 mov r0, r7 + 8006614: f7ff fadb bl 8005bce + for (i = num_bits - 2; i > 0; --i) { + 8006618: f9bd 50e8 ldrsh.w r5, [sp, #232] ; 0xe8 + 800661c: 3d02 subs r5, #2 + 800661e: b22d sxth r5, r5 + 8006620: 2d00 cmp r5, #0 + 8006622: dc63 bgt.n 80066ec + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 8006624: 9b04 ldr r3, [sp, #16] + 8006626: 681d ldr r5, [r3, #0] + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 8006628: 9400 str r4, [sp, #0] + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 800662a: f005 0601 and.w r6, r5, #1 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 800662e: ab10 add r3, sp, #64 ; 0x40 + 8006630: eb03 1746 add.w r7, r3, r6, lsl #5 + 8006634: 43ed mvns r5, r5 + 8006636: ab20 add r3, sp, #128 ; 0x80 + 8006638: eb03 1646 add.w r6, r3, r6, lsl #5 + 800663c: f005 0501 and.w r5, r5, #1 + 8006640: ab10 add r3, sp, #64 ; 0x40 + 8006642: eb03 1845 add.w r8, r3, r5, lsl #5 + 8006646: ab20 add r3, sp, #128 ; 0x80 + 8006648: eb03 1545 add.w r5, r3, r5, lsl #5 + 800664c: 462b mov r3, r5 + 800664e: 4642 mov r2, r8 + 8006650: 4631 mov r1, r6 + 8006652: 4638 mov r0, r7 + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + 8006654: f104 0b04 add.w fp, r4, #4 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 8006658: f7ff feba bl 80063d0 + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + 800665c: 465b mov r3, fp + 800665e: aa10 add r2, sp, #64 ; 0x40 + 8006660: a918 add r1, sp, #96 ; 0x60 + 8006662: a808 add r0, sp, #32 + 8006664: f7ff fea6 bl 80063b4 + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + 8006668: a908 add r1, sp, #32 + 800666a: 4623 mov r3, r4 + 800666c: 4632 mov r2, r6 + 800666e: 4608 mov r0, r1 + 8006670: f7ff fa99 bl 8005ba6 + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + 8006674: a908 add r1, sp, #32 + 8006676: 9a03 ldr r2, [sp, #12] + 8006678: 4623 mov r3, r4 + 800667a: 4608 mov r0, r1 + 800667c: f7ff fa93 bl 8005ba6 + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ + 8006680: a908 add r1, sp, #32 + 8006682: 464b mov r3, r9 + 8006684: 465a mov r2, fp + 8006686: 4608 mov r0, r1 + 8006688: f7ff fde6 bl 8006258 + uECC_vli_modMult_fast(z, z, point + num_words, curve); + 800668c: a908 add r1, sp, #32 + 800668e: 4623 mov r3, r4 + 8006690: 4652 mov r2, sl + 8006692: 4608 mov r0, r1 + 8006694: f7ff fa87 bl 8005ba6 + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + 8006698: a908 add r1, sp, #32 + 800669a: 4623 mov r3, r4 + 800669c: 463a mov r2, r7 + 800669e: 4608 mov r0, r1 + 80066a0: f7ff fa81 bl 8005ba6 + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + 80066a4: 4633 mov r3, r6 + 80066a6: 463a mov r2, r7 + 80066a8: 4629 mov r1, r5 + 80066aa: 4640 mov r0, r8 + 80066ac: 9400 str r4, [sp, #0] + 80066ae: f7ff ff15 bl 80064dc + apply_z(Rx[0], Ry[0], z, curve); + 80066b2: 4623 mov r3, r4 + 80066b4: aa08 add r2, sp, #32 + 80066b6: a920 add r1, sp, #128 ; 0x80 + 80066b8: a810 add r0, sp, #64 ; 0x40 + 80066ba: f7ff fa88 bl 8005bce + uECC_vli_set(result, Rx[0], num_words); + 80066be: 9802 ldr r0, [sp, #8] + 80066c0: 464a mov r2, r9 + 80066c2: a910 add r1, sp, #64 ; 0x40 + 80066c4: f7ff f9a1 bl 8005a0a + uECC_vli_set(result + num_words, Ry[0], num_words); + 80066c8: 9802 ldr r0, [sp, #8] + 80066ca: 9b05 ldr r3, [sp, #20] + 80066cc: a920 add r1, sp, #128 ; 0x80 + 80066ce: 4418 add r0, r3 + 80066d0: f7ff f99b bl 8005a0a +} + 80066d4: b031 add sp, #196 ; 0xc4 + 80066d6: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + uECC_vli_clear(z, num_words); + 80066da: 4611 mov r1, r2 + 80066dc: a808 add r0, sp, #32 + 80066de: 9206 str r2, [sp, #24] + 80066e0: f7ff f954 bl 800598c + z[0] = 1; + 80066e4: 2301 movs r3, #1 + 80066e6: 9a06 ldr r2, [sp, #24] + 80066e8: 9308 str r3, [sp, #32] + 80066ea: e778 b.n 80065de + nb = !uECC_vli_testBit(scalar, i); + 80066ec: 4629 mov r1, r5 + 80066ee: 9804 ldr r0, [sp, #16] + 80066f0: f7ff f961 bl 80059b6 + 80066f4: fab0 f680 clz r6, r0 + 80066f8: 0976 lsrs r6, r6, #5 + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + 80066fa: f1c6 0101 rsb r1, r6, #1 + 80066fe: eb07 1b46 add.w fp, r7, r6, lsl #5 + 8006702: eb08 1646 add.w r6, r8, r6, lsl #5 + 8006706: eb07 1041 add.w r0, r7, r1, lsl #5 + 800670a: 4633 mov r3, r6 + 800670c: eb08 1141 add.w r1, r8, r1, lsl #5 + 8006710: 465a mov r2, fp + 8006712: 9400 str r4, [sp, #0] + 8006714: e9cd 0106 strd r0, r1, [sp, #24] + 8006718: f7ff fe5a bl 80063d0 + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + 800671c: 9907 ldr r1, [sp, #28] + 800671e: 9806 ldr r0, [sp, #24] + 8006720: 9400 str r4, [sp, #0] + 8006722: 460b mov r3, r1 + 8006724: 4602 mov r2, r0 + 8006726: 4631 mov r1, r6 + 8006728: 4658 mov r0, fp + 800672a: f7ff fed7 bl 80064dc + for (i = num_bits - 2; i > 0; --i) { + 800672e: 3d01 subs r5, #1 + 8006730: e775 b.n 800661e + +08006732 : + uECC_Curve curve) { + 8006732: b530 push {r4, r5, lr} + 8006734: 4614 mov r4, r2 + 8006736: b095 sub sp, #84 ; 0x54 + 8006738: 4605 mov r5, r0 + uECC_word_t *p2[2] = {tmp1, tmp2}; + 800673a: aa0c add r2, sp, #48 ; 0x30 + carry = regularize_k(private, tmp1, tmp2, curve); + 800673c: 4623 mov r3, r4 + uECC_Curve curve) { + 800673e: 4608 mov r0, r1 + uECC_word_t *p2[2] = {tmp1, tmp2}; + 8006740: a904 add r1, sp, #16 + 8006742: 9102 str r1, [sp, #8] + 8006744: 9203 str r2, [sp, #12] + carry = regularize_k(private, tmp1, tmp2, curve); + 8006746: f7ff fbe0 bl 8005f0a + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + 800674a: fab0 f380 clz r3, r0 + 800674e: 095b lsrs r3, r3, #5 + 8006750: aa14 add r2, sp, #80 ; 0x50 + 8006752: eb02 0283 add.w r2, r2, r3, lsl #2 + 8006756: 8863 ldrh r3, [r4, #2] + 8006758: 9401 str r4, [sp, #4] + 800675a: 3301 adds r3, #1 + 800675c: b21b sxth r3, r3 + 800675e: 9300 str r3, [sp, #0] + 8006760: f852 2c48 ldr.w r2, [r2, #-72] + 8006764: 2300 movs r3, #0 + 8006766: f104 0144 add.w r1, r4, #68 ; 0x44 + 800676a: 4628 mov r0, r5 + 800676c: f7ff ff16 bl 800659c + if (EccPoint_isZero(result, curve)) { + 8006770: 7821 ldrb r1, [r4, #0] + 8006772: 0049 lsls r1, r1, #1 + 8006774: b249 sxtb r1, r1 + 8006776: 4628 mov r0, r5 + 8006778: f7ff f90e bl 8005998 +} + 800677c: fab0 f080 clz r0, r0 + 8006780: 0940 lsrs r0, r0, #5 + 8006782: b015 add sp, #84 ; 0x54 + 8006784: bd30 pop {r4, r5, pc} + ... + +08006788 : + uECC_Curve curve) { + 8006788: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800678c: ed2d 8b02 vpush {d8} + 8006790: b0a7 sub sp, #156 ; 0x9c + 8006792: 461e mov r6, r3 + 8006794: 9d33 ldr r5, [sp, #204] ; 0xcc + wordcount_t num_words = curve->num_words; + 8006796: f995 a000 ldrsb.w sl, [r5] + uECC_Curve curve) { + 800679a: ee08 1a10 vmov s16, r1 + 800679e: 4683 mov fp, r0 + uECC_word_t *k2[2] = {tmp, s}; + 80067a0: f10d 0918 add.w r9, sp, #24 + 80067a4: ab0e add r3, sp, #56 ; 0x38 + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80067a6: 4651 mov r1, sl + 80067a8: 4630 mov r0, r6 + uECC_Curve curve) { + 80067aa: ee08 2a90 vmov s17, r2 + uECC_word_t *k2[2] = {tmp, s}; + 80067ae: f8cd 9010 str.w r9, [sp, #16] + 80067b2: 9305 str r3, [sp, #20] + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80067b4: f7ff f8f0 bl 8005998 + 80067b8: b128 cbz r0, 80067c6 + return 0; + 80067ba: 2000 movs r0, #0 +} + 80067bc: b027 add sp, #156 ; 0x9c + 80067be: ecbd 8b02 vpop {d8} + 80067c2: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 80067c6: f9b5 8002 ldrsh.w r8, [r5, #2] + 80067ca: f118 041f adds.w r4, r8, #31 + 80067ce: bf48 it mi + 80067d0: f108 043e addmi.w r4, r8, #62 ; 0x3e + 80067d4: f344 1447 sbfx r4, r4, #5, #8 + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + 80067d8: f105 0724 add.w r7, r5, #36 ; 0x24 + 80067dc: 4622 mov r2, r4 + 80067de: 4631 mov r1, r6 + 80067e0: 4638 mov r0, r7 + 80067e2: f7ff fb19 bl 8005e18 + 80067e6: 2801 cmp r0, #1 + 80067e8: 9003 str r0, [sp, #12] + 80067ea: d1e6 bne.n 80067ba + carry = regularize_k(k, tmp, s, curve); + 80067ec: 462b mov r3, r5 + 80067ee: aa0e add r2, sp, #56 ; 0x38 + 80067f0: 4649 mov r1, r9 + 80067f2: 4630 mov r0, r6 + 80067f4: f7ff fb89 bl 8005f0a + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + 80067f8: fab0 f080 clz r0, r0 + 80067fc: ab26 add r3, sp, #152 ; 0x98 + 80067fe: 0940 lsrs r0, r0, #5 + 8006800: f108 0801 add.w r8, r8, #1 + 8006804: eb03 0080 add.w r0, r3, r0, lsl #2 + 8006808: fa0f f388 sxth.w r3, r8 + 800680c: 9300 str r3, [sp, #0] + 800680e: 9501 str r5, [sp, #4] + 8006810: f850 2c88 ldr.w r2, [r0, #-136] + 8006814: f105 0144 add.w r1, r5, #68 ; 0x44 + 8006818: a816 add r0, sp, #88 ; 0x58 + 800681a: 2300 movs r3, #0 + 800681c: f7ff febe bl 800659c + if (uECC_vli_isZero(p, num_words)) { + 8006820: 4651 mov r1, sl + 8006822: a816 add r0, sp, #88 ; 0x58 + 8006824: f7ff f8b8 bl 8005998 + 8006828: 2800 cmp r0, #0 + 800682a: d1c6 bne.n 80067ba + uECC_recid = (p[curve->num_words] & 0x01); + 800682c: f995 3000 ldrsb.w r3, [r5] + 8006830: aa26 add r2, sp, #152 ; 0x98 + 8006832: eb02 0383 add.w r3, r2, r3, lsl #2 + 8006836: 4a3b ldr r2, [pc, #236] ; (8006924 ) + 8006838: f853 3c40 ldr.w r3, [r3, #-64] + 800683c: f003 0301 and.w r3, r3, #1 + 8006840: 7013 strb r3, [r2, #0] + if (!g_rng_function) { + 8006842: 4b39 ldr r3, [pc, #228] ; (8006928 ) + 8006844: 681b ldr r3, [r3, #0] + 8006846: 2b00 cmp r3, #0 + 8006848: d163 bne.n 8006912 + uECC_vli_clear(tmp, num_n_words); + 800684a: 4621 mov r1, r4 + 800684c: 4648 mov r0, r9 + 800684e: f7ff f89d bl 800598c + tmp[0] = 1; + 8006852: 9b03 ldr r3, [sp, #12] + 8006854: 9306 str r3, [sp, #24] + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + 8006856: 463b mov r3, r7 + 8006858: aa06 add r2, sp, #24 + 800685a: 4631 mov r1, r6 + 800685c: 4630 mov r0, r6 + 800685e: 9400 str r4, [sp, #0] + 8006860: f7ff f901 bl 8005a66 + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + 8006864: 4623 mov r3, r4 + 8006866: 463a mov r2, r7 + 8006868: 4631 mov r1, r6 + 800686a: 4630 mov r0, r6 + 800686c: f7ff fcf4 bl 8006258 + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + 8006870: 463b mov r3, r7 + 8006872: aa06 add r2, sp, #24 + 8006874: 4631 mov r1, r6 + 8006876: 4630 mov r0, r6 + 8006878: 9400 str r4, [sp, #0] + 800687a: f7ff f8f4 bl 8005a66 + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + 800687e: f995 1001 ldrsb.w r1, [r5, #1] + 8006882: 9832 ldr r0, [sp, #200] ; 0xc8 + 8006884: aa16 add r2, sp, #88 ; 0x58 + 8006886: f7ff f9c1 bl 8005c0c + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ + 800688a: f9b5 3002 ldrsh.w r3, [r5, #2] + 800688e: 1dda adds r2, r3, #7 + 8006890: bf48 it mi + 8006892: f103 020e addmi.w r2, r3, #14 + 8006896: 10d2 asrs r2, r2, #3 + 8006898: 4659 mov r1, fp + 800689a: a806 add r0, sp, #24 + 800689c: f7ff f9ca bl 8005c34 + s[num_n_words - 1] = 0; + 80068a0: aa26 add r2, sp, #152 ; 0x98 + 80068a2: 1e63 subs r3, r4, #1 + 80068a4: eb02 0383 add.w r3, r2, r3, lsl #2 + 80068a8: 2200 movs r2, #0 + uECC_vli_set(s, p, num_words); + 80068aa: a80e add r0, sp, #56 ; 0x38 + s[num_n_words - 1] = 0; + 80068ac: f843 2c60 str.w r2, [r3, #-96] + uECC_vli_set(s, p, num_words); + 80068b0: a916 add r1, sp, #88 ; 0x58 + 80068b2: 4652 mov r2, sl + 80068b4: f7ff f8a9 bl 8005a0a + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + 80068b8: 4602 mov r2, r0 + 80068ba: 463b mov r3, r7 + 80068bc: a906 add r1, sp, #24 + 80068be: 9400 str r4, [sp, #0] + 80068c0: f7ff f8d1 bl 8005a66 + bits2int(tmp, message_hash, hash_size, curve); + 80068c4: ee18 2a90 vmov r2, s17 + 80068c8: ee18 1a10 vmov r1, s16 + 80068cc: 462b mov r3, r5 + 80068ce: a806 add r0, sp, #24 + 80068d0: f7ff fa5b bl 8005d8a + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + 80068d4: aa0e add r2, sp, #56 ; 0x38 + 80068d6: 4610 mov r0, r2 + 80068d8: 463b mov r3, r7 + 80068da: a906 add r1, sp, #24 + 80068dc: 9400 str r4, [sp, #0] + 80068de: f7ff fd3b bl 8006358 + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + 80068e2: a90e add r1, sp, #56 ; 0x38 + 80068e4: 4608 mov r0, r1 + 80068e6: 463b mov r3, r7 + 80068e8: 4632 mov r2, r6 + 80068ea: 9400 str r4, [sp, #0] + 80068ec: f7ff f8bb bl 8005a66 + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + 80068f0: 4621 mov r1, r4 + 80068f2: a80e add r0, sp, #56 ; 0x38 + 80068f4: f7ff f869 bl 80059ca + 80068f8: f995 1001 ldrsb.w r1, [r5, #1] + 80068fc: ebb0 0fc1 cmp.w r0, r1, lsl #3 + 8006900: f73f af5b bgt.w 80067ba + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + 8006904: 9b32 ldr r3, [sp, #200] ; 0xc8 + 8006906: aa0e add r2, sp, #56 ; 0x38 + 8006908: 1858 adds r0, r3, r1 + 800690a: f7ff f97f bl 8005c0c + return 1; + 800690e: 2001 movs r0, #1 + 8006910: e754 b.n 80067bc + } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + 8006912: 4622 mov r2, r4 + 8006914: 4639 mov r1, r7 + 8006916: 4648 mov r0, r9 + 8006918: f7ff fa96 bl 8005e48 + 800691c: 2800 cmp r0, #0 + 800691e: d19a bne.n 8006856 + 8006920: e74b b.n 80067ba + 8006922: bf00 nop + 8006924: 2009e2a8 .word 0x2009e2a8 + 8006928: 2009e2a4 .word 0x2009e2a4 + +0800692c : + uECC_Curve curve) { + 800692c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8006930: 4605 mov r5, r0 + 8006932: b093 sub sp, #76 ; 0x4c + 8006934: 460c mov r4, r1 + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + 8006936: 4610 mov r0, r2 + 8006938: 2108 movs r1, #8 + uECC_Curve curve) { + 800693a: 4617 mov r7, r2 + 800693c: 461e mov r6, r3 + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + 800693e: f7ff f82b bl 8005998 + 8006942: 2800 cmp r0, #0 + 8006944: d161 bne.n 8006a0a + uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ + 8006946: 4632 mov r2, r6 + 8006948: 4621 mov r1, r4 + 800694a: a80a add r0, sp, #40 ; 0x28 + 800694c: f7ff f93b bl 8005bc6 + uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ + 8006950: 4633 mov r3, r6 + 8006952: aa0a add r2, sp, #40 ; 0x28 + 8006954: 4629 mov r1, r5 + 8006956: a802 add r0, sp, #8 + 8006958: f7ff f925 bl 8005ba6 + uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ + 800695c: 4632 mov r2, r6 + 800695e: 4629 mov r1, r5 + 8006960: 4628 mov r0, r5 + 8006962: f7ff f930 bl 8005bc6 + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ + 8006966: a90a add r1, sp, #40 ; 0x28 + 8006968: 4608 mov r0, r1 + 800696a: 4632 mov r2, r6 + 800696c: f7ff f92b bl 8005bc6 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 8006970: f04f 0808 mov.w r8, #8 + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + 8006974: 463a mov r2, r7 + 8006976: 4638 mov r0, r7 + 8006978: 4633 mov r3, r6 + 800697a: 4621 mov r1, r4 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 800697c: 1d37 adds r7, r6, #4 + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + 800697e: f7ff f912 bl 8005ba6 + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + 8006982: 463b mov r3, r7 + 8006984: 462a mov r2, r5 + 8006986: 4629 mov r1, r5 + 8006988: 4620 mov r0, r4 + 800698a: f8cd 8000 str.w r8, [sp] + 800698e: f7ff fce3 bl 8006358 + uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ + 8006992: 463b mov r3, r7 + 8006994: f8cd 8000 str.w r8, [sp] + 8006998: 462a mov r2, r5 + 800699a: 4621 mov r1, r4 + 800699c: 4620 mov r0, r4 + 800699e: f7ff fcdb bl 8006358 + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); + 80069a2: 6823 ldr r3, [r4, #0] + if (uECC_vli_testBit(Y1, 0)) { + 80069a4: 07db lsls r3, r3, #31 + 80069a6: d533 bpl.n 8006a10 + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + 80069a8: 463a mov r2, r7 + 80069aa: 4621 mov r1, r4 + 80069ac: 4620 mov r0, r4 + 80069ae: f7ff fa87 bl 8005ec0 + uECC_vli_rshift1(Y1, num_words_secp256k1); + 80069b2: 4641 mov r1, r8 + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + 80069b4: 4681 mov r9, r0 + uECC_vli_rshift1(Y1, num_words_secp256k1); + 80069b6: 4620 mov r0, r4 + 80069b8: f7ff f848 bl 8005a4c + Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); + 80069bc: 69e3 ldr r3, [r4, #28] + 80069be: ea43 73c9 orr.w r3, r3, r9, lsl #31 + 80069c2: 61e3 str r3, [r4, #28] + uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ + 80069c4: 4632 mov r2, r6 + 80069c6: 4621 mov r1, r4 + 80069c8: 4628 mov r0, r5 + 80069ca: f7ff f8fc bl 8005bc6 + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ + 80069ce: 463b mov r3, r7 + 80069d0: aa02 add r2, sp, #8 + 80069d2: 4629 mov r1, r5 + 80069d4: 4628 mov r0, r5 + 80069d6: f7ff fced bl 80063b4 + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ + 80069da: 463b mov r3, r7 + 80069dc: aa02 add r2, sp, #8 + 80069de: 4629 mov r1, r5 + 80069e0: 4628 mov r0, r5 + 80069e2: f7ff fce7 bl 80063b4 + uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ + 80069e6: a902 add r1, sp, #8 + 80069e8: 4608 mov r0, r1 + 80069ea: 463b mov r3, r7 + 80069ec: 462a mov r2, r5 + 80069ee: f7ff fce1 bl 80063b4 + uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ + 80069f2: 4633 mov r3, r6 + 80069f4: aa02 add r2, sp, #8 + 80069f6: 4621 mov r1, r4 + 80069f8: 4620 mov r0, r4 + 80069fa: f7ff f8d4 bl 8005ba6 + uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ + 80069fe: 463b mov r3, r7 + 8006a00: aa0a add r2, sp, #40 ; 0x28 + 8006a02: 4621 mov r1, r4 + 8006a04: 4620 mov r0, r4 + 8006a06: f7ff fcd5 bl 80063b4 +} + 8006a0a: b013 add sp, #76 ; 0x4c + 8006a0c: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + uECC_vli_rshift1(Y1, num_words_secp256k1); + 8006a10: 4641 mov r1, r8 + 8006a12: 4620 mov r0, r4 + 8006a14: f7ff f81a bl 8005a4c + 8006a18: e7d4 b.n 80069c4 + +08006a1a : +static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + 8006a1a: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8006a1e: b08a sub sp, #40 ; 0x28 + 8006a20: 4604 mov r4, r0 + 8006a22: 4615 mov r5, r2 + 8006a24: 460e mov r6, r1 + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 8006a26: 221c movs r2, #28 + 8006a28: 2100 movs r1, #0 + 8006a2a: a803 add r0, sp, #12 + 8006a2c: f006 ff7a bl 800d924 + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + 8006a30: 1d2f adds r7, r5, #4 + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 8006a32: 2303 movs r3, #3 + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 8006a34: 462a mov r2, r5 + 8006a36: 4631 mov r1, r6 + 8006a38: 4620 mov r0, r4 + wordcount_t num_words = curve->num_words; + 8006a3a: f995 8000 ldrsb.w r8, [r5] + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + 8006a3e: 9302 str r3, [sp, #8] + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + 8006a40: f7ff f8c1 bl 8005bc6 + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + 8006a44: 463b mov r3, r7 + 8006a46: aa02 add r2, sp, #8 + 8006a48: 4621 mov r1, r4 + 8006a4a: 4620 mov r0, r4 + 8006a4c: f7ff fcb2 bl 80063b4 + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + 8006a50: 462b mov r3, r5 + 8006a52: 4632 mov r2, r6 + 8006a54: 4621 mov r1, r4 + 8006a56: 4620 mov r0, r4 + 8006a58: f7ff f8a5 bl 8005ba6 + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ + 8006a5c: f8cd 8000 str.w r8, [sp] + 8006a60: 463b mov r3, r7 + 8006a62: f105 0284 add.w r2, r5, #132 ; 0x84 + 8006a66: 4621 mov r1, r4 + 8006a68: 4620 mov r0, r4 + 8006a6a: f7ff fc75 bl 8006358 +} + 8006a6e: b00a add sp, #40 ; 0x28 + 8006a70: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +08006a74 : + uECC_Curve curve) { + 8006a74: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + wordcount_t num_words = curve->num_words; + 8006a78: f993 8000 ldrsb.w r8, [r3] + uECC_Curve curve) { + 8006a7c: b092 sub sp, #72 ; 0x48 + 8006a7e: 4604 mov r4, r0 + 8006a80: 4689 mov r9, r1 + if (uECC_vli_isZero(Z1, num_words)) { + 8006a82: 4610 mov r0, r2 + 8006a84: 4641 mov r1, r8 + uECC_Curve curve) { + 8006a86: 4615 mov r5, r2 + 8006a88: 461e mov r6, r3 + if (uECC_vli_isZero(Z1, num_words)) { + 8006a8a: f7fe ff85 bl 8005998 + 8006a8e: 2800 cmp r0, #0 + 8006a90: f040 808e bne.w 8006bb0 + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + 8006a94: 4632 mov r2, r6 + 8006a96: 4649 mov r1, r9 + 8006a98: a802 add r0, sp, #8 + 8006a9a: f7ff f894 bl 8005bc6 + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + 8006a9e: 4633 mov r3, r6 + 8006aa0: aa02 add r2, sp, #8 + 8006aa2: 4621 mov r1, r4 + 8006aa4: a80a add r0, sp, #40 ; 0x28 + 8006aa6: f7ff f87e bl 8005ba6 + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + 8006aaa: a902 add r1, sp, #8 + 8006aac: 4608 mov r0, r1 + 8006aae: 4632 mov r2, r6 + 8006ab0: f7ff f889 bl 8005bc6 + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + 8006ab4: 4633 mov r3, r6 + 8006ab6: 462a mov r2, r5 + 8006ab8: 4649 mov r1, r9 + 8006aba: 4648 mov r0, r9 + 8006abc: f7ff f873 bl 8005ba6 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + 8006ac0: 1d37 adds r7, r6, #4 + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + 8006ac2: 4632 mov r2, r6 + 8006ac4: 4629 mov r1, r5 + 8006ac6: 4628 mov r0, r5 + 8006ac8: f7ff f87d bl 8005bc6 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + 8006acc: 463b mov r3, r7 + 8006ace: 462a mov r2, r5 + 8006ad0: 4621 mov r1, r4 + 8006ad2: 4620 mov r0, r4 + 8006ad4: f8cd 8000 str.w r8, [sp] + 8006ad8: f7ff fc3e bl 8006358 + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + 8006adc: 463b mov r3, r7 + 8006ade: 462a mov r2, r5 + 8006ae0: 4629 mov r1, r5 + 8006ae2: 4628 mov r0, r5 + 8006ae4: f8cd 8000 str.w r8, [sp] + 8006ae8: f7ff fc36 bl 8006358 + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + 8006aec: 463b mov r3, r7 + 8006aee: 462a mov r2, r5 + 8006af0: 4621 mov r1, r4 + 8006af2: 4628 mov r0, r5 + 8006af4: f7ff fc5e bl 80063b4 + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + 8006af8: 4633 mov r3, r6 + 8006afa: 462a mov r2, r5 + 8006afc: 4621 mov r1, r4 + 8006afe: 4620 mov r0, r4 + 8006b00: f7ff f851 bl 8005ba6 + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + 8006b04: 463b mov r3, r7 + 8006b06: 4622 mov r2, r4 + 8006b08: 4621 mov r1, r4 + 8006b0a: 4628 mov r0, r5 + 8006b0c: f8cd 8000 str.w r8, [sp] + 8006b10: f7ff fc22 bl 8006358 + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + 8006b14: 463b mov r3, r7 + 8006b16: f8cd 8000 str.w r8, [sp] + 8006b1a: 462a mov r2, r5 + 8006b1c: 4621 mov r1, r4 + 8006b1e: 4620 mov r0, r4 + 8006b20: f7ff fc1a bl 8006358 + 8006b24: 6823 ldr r3, [r4, #0] + if (uECC_vli_testBit(X1, 0)) { + 8006b26: 07db lsls r3, r3, #31 + 8006b28: d545 bpl.n 8006bb6 + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + 8006b2a: 463a mov r2, r7 + 8006b2c: 4621 mov r1, r4 + 8006b2e: 4620 mov r0, r4 + 8006b30: f7ff f9c6 bl 8005ec0 + uECC_vli_rshift1(X1, num_words); + 8006b34: 4641 mov r1, r8 + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + 8006b36: 4682 mov sl, r0 + uECC_vli_rshift1(X1, num_words); + 8006b38: 4620 mov r0, r4 + 8006b3a: f7fe ff87 bl 8005a4c + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + 8006b3e: f108 4380 add.w r3, r8, #1073741824 ; 0x40000000 + 8006b42: 3b01 subs r3, #1 + 8006b44: f854 2023 ldr.w r2, [r4, r3, lsl #2] + 8006b48: ea42 72ca orr.w r2, r2, sl, lsl #31 + 8006b4c: f844 2023 str.w r2, [r4, r3, lsl #2] + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + 8006b50: 4632 mov r2, r6 + 8006b52: 4621 mov r1, r4 + 8006b54: 4628 mov r0, r5 + 8006b56: f7ff f836 bl 8005bc6 + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + 8006b5a: 463b mov r3, r7 + 8006b5c: aa0a add r2, sp, #40 ; 0x28 + 8006b5e: 4629 mov r1, r5 + 8006b60: 4628 mov r0, r5 + 8006b62: f7ff fc27 bl 80063b4 + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + 8006b66: 463b mov r3, r7 + 8006b68: aa0a add r2, sp, #40 ; 0x28 + 8006b6a: 4629 mov r1, r5 + 8006b6c: 4628 mov r0, r5 + 8006b6e: f7ff fc21 bl 80063b4 + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + 8006b72: a90a add r1, sp, #40 ; 0x28 + 8006b74: 4608 mov r0, r1 + 8006b76: 463b mov r3, r7 + 8006b78: 462a mov r2, r5 + 8006b7a: f7ff fc1b bl 80063b4 + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + 8006b7e: 4633 mov r3, r6 + 8006b80: aa0a add r2, sp, #40 ; 0x28 + 8006b82: 4621 mov r1, r4 + 8006b84: 4620 mov r0, r4 + 8006b86: f7ff f80e bl 8005ba6 + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ + 8006b8a: aa02 add r2, sp, #8 + 8006b8c: 463b mov r3, r7 + 8006b8e: 4610 mov r0, r2 + 8006b90: 4621 mov r1, r4 + 8006b92: f7ff fc0f bl 80063b4 + uECC_vli_set(X1, Z1, num_words); + 8006b96: 4642 mov r2, r8 + 8006b98: 4629 mov r1, r5 + 8006b9a: 4620 mov r0, r4 + 8006b9c: f7fe ff35 bl 8005a0a + uECC_vli_set(Z1, Y1, num_words); + 8006ba0: 4649 mov r1, r9 + 8006ba2: 4628 mov r0, r5 + 8006ba4: f7fe ff31 bl 8005a0a + uECC_vli_set(Y1, t4, num_words); + 8006ba8: a902 add r1, sp, #8 + 8006baa: 4648 mov r0, r9 + 8006bac: f7fe ff2d bl 8005a0a +} + 8006bb0: b012 add sp, #72 ; 0x48 + 8006bb2: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + uECC_vli_rshift1(X1, num_words); + 8006bb6: 4641 mov r1, r8 + 8006bb8: 4620 mov r0, r4 + 8006bba: f7fe ff47 bl 8005a4c + 8006bbe: e7c7 b.n 8006b50 + +08006bc0 : + g_rng_function = rng_function; + 8006bc0: 4b01 ldr r3, [pc, #4] ; (8006bc8 ) + 8006bc2: 6018 str r0, [r3, #0] +} + 8006bc4: 4770 bx lr + 8006bc6: bf00 nop + 8006bc8: 2009e2a4 .word 0x2009e2a4 + +08006bcc : +uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } + 8006bcc: 4800 ldr r0, [pc, #0] ; (8006bd0 ) + 8006bce: 4770 bx lr + 8006bd0: 080109a4 .word 0x080109a4 + +08006bd4 : +uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } + 8006bd4: 4800 ldr r0, [pc, #0] ; (8006bd8 ) + 8006bd6: 4770 bx lr + 8006bd8: 080108f0 .word 0x080108f0 + +08006bdc : + uECC_Curve curve) { + 8006bdc: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8006be0: 4605 mov r5, r0 + 8006be2: b098 sub sp, #96 ; 0x60 + 8006be4: 460f mov r7, r1 + 8006be6: 4614 mov r4, r2 + 8006be8: 2640 movs r6, #64 ; 0x40 + if (!uECC_generate_random_int(private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + 8006bea: f102 0824 add.w r8, r2, #36 ; 0x24 + 8006bee: f9b4 3002 ldrsh.w r3, [r4, #2] + 8006bf2: f113 021f adds.w r2, r3, #31 + 8006bf6: bf48 it mi + 8006bf8: f103 023e addmi.w r2, r3, #62 ; 0x3e + 8006bfc: f342 1247 sbfx r2, r2, #5, #8 + 8006c00: 4641 mov r1, r8 + 8006c02: 4668 mov r0, sp + 8006c04: f7ff f920 bl 8005e48 + 8006c08: b330 cbz r0, 8006c58 + if (EccPoint_compute_public_key(public, private, curve)) { + 8006c0a: 4622 mov r2, r4 + 8006c0c: 4669 mov r1, sp + 8006c0e: a808 add r0, sp, #32 + 8006c10: f7ff fd8f bl 8006732 + 8006c14: b1f0 cbz r0, 8006c54 + uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), private); + 8006c16: f9b4 3002 ldrsh.w r3, [r4, #2] + 8006c1a: 1dd9 adds r1, r3, #7 + 8006c1c: bf48 it mi + 8006c1e: f103 010e addmi.w r1, r3, #14 + 8006c22: 466a mov r2, sp + 8006c24: 10c9 asrs r1, r1, #3 + 8006c26: 4638 mov r0, r7 + 8006c28: f7fe fff0 bl 8005c0c + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + 8006c2c: f994 1001 ldrsb.w r1, [r4, #1] + 8006c30: aa08 add r2, sp, #32 + 8006c32: 4628 mov r0, r5 + 8006c34: f7fe ffea bl 8005c0c + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + 8006c38: f994 1001 ldrsb.w r1, [r4, #1] + 8006c3c: f994 2000 ldrsb.w r2, [r4] + uECC_vli_nativeToBytes( + 8006c40: ab08 add r3, sp, #32 + 8006c42: 1868 adds r0, r5, r1 + 8006c44: eb03 0282 add.w r2, r3, r2, lsl #2 + 8006c48: f7fe ffe0 bl 8005c0c + return 1; + 8006c4c: 2001 movs r0, #1 +} + 8006c4e: b018 add sp, #96 ; 0x60 + 8006c50: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8006c54: 3e01 subs r6, #1 + 8006c56: d1ca bne.n 8006bee + return 0; + 8006c58: 2000 movs r0, #0 + 8006c5a: e7f8 b.n 8006c4e + +08006c5c : + uECC_Curve curve) { + 8006c5c: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8006c60: 461c mov r4, r3 + wordcount_t num_bytes = curve->num_bytes; + 8006c62: f993 6001 ldrsb.w r6, [r3, #1] + wordcount_t num_words = curve->num_words; + 8006c66: f993 9000 ldrsb.w r9, [r3] + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006c6a: f9b3 3002 ldrsh.w r3, [r3, #2] + uECC_Curve curve) { + 8006c6e: b0a6 sub sp, #152 ; 0x98 + 8006c70: 4617 mov r7, r2 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006c72: 1dda adds r2, r3, #7 + 8006c74: bf48 it mi + 8006c76: f103 020e addmi.w r2, r3, #14 + uECC_word_t *p2[2] = {private, tmp}; + 8006c7a: f10d 0818 add.w r8, sp, #24 + uECC_Curve curve) { + 8006c7e: 4605 mov r5, r0 + uECC_word_t *p2[2] = {private, tmp}; + 8006c80: f10d 0a38 add.w sl, sp, #56 ; 0x38 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006c84: 10d2 asrs r2, r2, #3 + 8006c86: 4640 mov r0, r8 + uECC_word_t *p2[2] = {private, tmp}; + 8006c88: f8cd 8010 str.w r8, [sp, #16] + 8006c8c: f8cd a014 str.w sl, [sp, #20] + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006c90: f7fe ffd0 bl 8005c34 + uECC_vli_bytesToNative(public, public_key, num_bytes); + 8006c94: 4629 mov r1, r5 + 8006c96: 4632 mov r2, r6 + 8006c98: a816 add r0, sp, #88 ; 0x58 + 8006c9a: f7fe ffcb bl 8005c34 + uECC_vli_bytesToNative(public + num_words, public_key + num_bytes, num_bytes); + 8006c9e: ab16 add r3, sp, #88 ; 0x58 + 8006ca0: 19a9 adds r1, r5, r6 + 8006ca2: eb03 0089 add.w r0, r3, r9, lsl #2 + 8006ca6: 4632 mov r2, r6 + 8006ca8: f7fe ffc4 bl 8005c34 + carry = regularize_k(private, private, tmp, curve); + 8006cac: 4623 mov r3, r4 + 8006cae: 4652 mov r2, sl + 8006cb0: 4641 mov r1, r8 + 8006cb2: 4640 mov r0, r8 + 8006cb4: f7ff f929 bl 8005f0a + if (g_rng_function) { + 8006cb8: 4b19 ldr r3, [pc, #100] ; (8006d20 ) + 8006cba: 681b ldr r3, [r3, #0] + carry = regularize_k(private, private, tmp, curve); + 8006cbc: 4605 mov r5, r0 + if (g_rng_function) { + 8006cbe: b163 cbz r3, 8006cda + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + 8006cc0: ab26 add r3, sp, #152 ; 0x98 + 8006cc2: eb03 0380 add.w r3, r3, r0, lsl #2 + 8006cc6: 464a mov r2, r9 + 8006cc8: f853 3c88 ldr.w r3, [r3, #-136] + 8006ccc: 9303 str r3, [sp, #12] + 8006cce: 4618 mov r0, r3 + 8006cd0: 1d21 adds r1, r4, #4 + 8006cd2: f7ff f8b9 bl 8005e48 + 8006cd6: 9b03 ldr r3, [sp, #12] + 8006cd8: b1f0 cbz r0, 8006d18 + EccPoint_mult(public, public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); + 8006cda: fab5 f185 clz r1, r5 + 8006cde: aa26 add r2, sp, #152 ; 0x98 + 8006ce0: 0949 lsrs r1, r1, #5 + 8006ce2: eb02 0181 add.w r1, r2, r1, lsl #2 + 8006ce6: 8862 ldrh r2, [r4, #2] + 8006ce8: 9401 str r4, [sp, #4] + 8006cea: 3201 adds r2, #1 + 8006cec: b212 sxth r2, r2 + 8006cee: 9200 str r2, [sp, #0] + 8006cf0: f851 2c88 ldr.w r2, [r1, #-136] + 8006cf4: a916 add r1, sp, #88 ; 0x58 + 8006cf6: 4608 mov r0, r1 + 8006cf8: f7ff fc50 bl 800659c + uECC_vli_nativeToBytes(secret, num_bytes, public); + 8006cfc: aa16 add r2, sp, #88 ; 0x58 + 8006cfe: 4631 mov r1, r6 + 8006d00: 4638 mov r0, r7 + 8006d02: f7fe ff83 bl 8005c0c + return !EccPoint_isZero(public, curve); + 8006d06: 7821 ldrb r1, [r4, #0] + 8006d08: 0049 lsls r1, r1, #1 + 8006d0a: b249 sxtb r1, r1 + 8006d0c: 4610 mov r0, r2 + 8006d0e: f7fe fe43 bl 8005998 + 8006d12: fab0 f080 clz r0, r0 + 8006d16: 0940 lsrs r0, r0, #5 +} + 8006d18: b026 add sp, #152 ; 0x98 + 8006d1a: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 8006d1e: bf00 nop + 8006d20: 2009e2a4 .word 0x2009e2a4 + +08006d24 : +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { + 8006d24: b530 push {r4, r5, lr} + for (i = 0; i < curve->num_bytes; ++i) { + 8006d26: 2400 movs r4, #0 + 8006d28: f992 5001 ldrsb.w r5, [r2, #1] + 8006d2c: b263 sxtb r3, r4 + 8006d2e: 429d cmp r5, r3 + 8006d30: dc08 bgt.n 8006d44 + compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); + 8006d32: eb00 0045 add.w r0, r0, r5, lsl #1 + 8006d36: f810 3c01 ldrb.w r3, [r0, #-1] + 8006d3a: f003 0301 and.w r3, r3, #1 + 8006d3e: 3302 adds r3, #2 + 8006d40: 700b strb r3, [r1, #0] +} + 8006d42: bd30 pop {r4, r5, pc} + compressed[i+1] = public_key[i]; + 8006d44: 5cc5 ldrb r5, [r0, r3] + 8006d46: 440b add r3, r1 + 8006d48: 3401 adds r4, #1 + 8006d4a: 705d strb r5, [r3, #1] + for (i = 0; i < curve->num_bytes; ++i) { + 8006d4c: e7ec b.n 8006d28 + +08006d4e : +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006d4e: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + uECC_word_t *y = point + curve->num_words; + 8006d52: f992 8000 ldrsb.w r8, [r2] +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006d56: b090 sub sp, #64 ; 0x40 + 8006d58: 4614 mov r4, r2 + 8006d5a: 4607 mov r7, r0 + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); + 8006d5c: f992 2001 ldrsb.w r2, [r2, #1] + uECC_word_t *y = point + curve->num_words; + 8006d60: eb0d 0588 add.w r5, sp, r8, lsl #2 +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + 8006d64: 460e mov r6, r1 + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); + 8006d66: 1c41 adds r1, r0, #1 + 8006d68: 4668 mov r0, sp + 8006d6a: f7fe ff63 bl 8005c34 + curve->x_side(y, point, curve); + 8006d6e: 4622 mov r2, r4 + 8006d70: f8d4 30ac ldr.w r3, [r4, #172] ; 0xac + 8006d74: 4669 mov r1, sp + 8006d76: 4628 mov r0, r5 + 8006d78: 4798 blx r3 + curve->mod_sqrt(y, curve); + 8006d7a: f8d4 30a8 ldr.w r3, [r4, #168] ; 0xa8 + 8006d7e: 4621 mov r1, r4 + 8006d80: 4628 mov r0, r5 + 8006d82: 4798 blx r3 + if ((y[0] & 0x01) != (compressed[0] & 0x01)) { + 8006d84: 783b ldrb r3, [r7, #0] + 8006d86: f85d 2028 ldr.w r2, [sp, r8, lsl #2] + 8006d8a: 4053 eors r3, r2 + 8006d8c: 07db lsls r3, r3, #31 + 8006d8e: d504 bpl.n 8006d9a + uECC_vli_sub(y, curve->p, y, curve->num_words); + 8006d90: 462a mov r2, r5 + 8006d92: 1d21 adds r1, r4, #4 + 8006d94: 4628 mov r0, r5 + 8006d96: f7fe ffd1 bl 8005d3c + uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); + 8006d9a: f994 1001 ldrsb.w r1, [r4, #1] + 8006d9e: 466a mov r2, sp + 8006da0: 4630 mov r0, r6 + 8006da2: f7fe ff33 bl 8005c0c + uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); + 8006da6: f994 1001 ldrsb.w r1, [r4, #1] + 8006daa: 462a mov r2, r5 + 8006dac: 1870 adds r0, r6, r1 + 8006dae: f7fe ff2d bl 8005c0c +} + 8006db2: b010 add sp, #64 ; 0x40 + 8006db4: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +08006db8 : +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006db8: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + if (EccPoint_isZero(point, curve)) { + 8006dbc: 780d ldrb r5, [r1, #0] + wordcount_t num_words = curve->num_words; + 8006dbe: f991 2000 ldrsb.w r2, [r1] +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006dc2: b092 sub sp, #72 ; 0x48 + 8006dc4: 460e mov r6, r1 + if (EccPoint_isZero(point, curve)) { + 8006dc6: 0069 lsls r1, r5, #1 + 8006dc8: b249 sxtb r1, r1 +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + 8006dca: 4607 mov r7, r0 + wordcount_t num_words = curve->num_words; + 8006dcc: 9201 str r2, [sp, #4] + if (EccPoint_isZero(point, curve)) { + 8006dce: f7fe fde3 bl 8005998 + 8006dd2: 4604 mov r4, r0 + 8006dd4: bb80 cbnz r0, 8006e38 + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + 8006dd6: f106 0804 add.w r8, r6, #4 + 8006dda: 9a01 ldr r2, [sp, #4] + 8006ddc: 4639 mov r1, r7 + 8006dde: 4640 mov r0, r8 + 8006de0: f7fe fe1f bl 8005a22 + 8006de4: 2801 cmp r0, #1 + 8006de6: d11a bne.n 8006e1e + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + 8006de8: 9a01 ldr r2, [sp, #4] + 8006dea: 4640 mov r0, r8 + 8006dec: eb07 0182 add.w r1, r7, r2, lsl #2 + 8006df0: f7fe fe17 bl 8005a22 + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + 8006df4: 2801 cmp r0, #1 + 8006df6: d112 bne.n 8006e1e + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + 8006df8: 4632 mov r2, r6 + 8006dfa: a802 add r0, sp, #8 + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + 8006dfc: f10d 0828 add.w r8, sp, #40 ; 0x28 + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + 8006e00: f7fe fee1 bl 8005bc6 + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + 8006e04: f8d6 30ac ldr.w r3, [r6, #172] ; 0xac + 8006e08: 4632 mov r2, r6 + 8006e0a: 4639 mov r1, r7 + 8006e0c: 4640 mov r0, r8 + 8006e0e: 4798 blx r3 + for (i = num_words - 1; i >= 0; --i) { + 8006e10: 1e6b subs r3, r5, #1 + 8006e12: b25b sxtb r3, r3 + 8006e14: 061a lsls r2, r3, #24 + 8006e16: d506 bpl.n 8006e26 + return (diff == 0); + 8006e18: fab4 f484 clz r4, r4 + 8006e1c: 0964 lsrs r4, r4, #5 +} + 8006e1e: 4620 mov r0, r4 + 8006e20: b012 add sp, #72 ; 0x48 + 8006e22: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + diff |= (left[i] ^ right[i]); + 8006e26: aa02 add r2, sp, #8 + 8006e28: f858 1023 ldr.w r1, [r8, r3, lsl #2] + 8006e2c: f852 2023 ldr.w r2, [r2, r3, lsl #2] + 8006e30: 404a eors r2, r1 + 8006e32: 4314 orrs r4, r2 + for (i = num_words - 1; i >= 0; --i) { + 8006e34: 3b01 subs r3, #1 + 8006e36: e7ed b.n 8006e14 + return 0; + 8006e38: 2400 movs r4, #0 + 8006e3a: e7f0 b.n 8006e1e + +08006e3c : +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { + 8006e3c: b530 push {r4, r5, lr} + 8006e3e: 460c mov r4, r1 + 8006e40: b091 sub sp, #68 ; 0x44 + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8006e42: f991 2001 ldrsb.w r2, [r1, #1] +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { + 8006e46: 4605 mov r5, r0 + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8006e48: 4601 mov r1, r0 + 8006e4a: 4668 mov r0, sp + 8006e4c: f7fe fef2 bl 8005c34 + public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); + 8006e50: f994 2001 ldrsb.w r2, [r4, #1] + 8006e54: f994 0000 ldrsb.w r0, [r4] + uECC_vli_bytesToNative( + 8006e58: 18a9 adds r1, r5, r2 + 8006e5a: eb0d 0080 add.w r0, sp, r0, lsl #2 + 8006e5e: f7fe fee9 bl 8005c34 + return uECC_valid_point(public, curve); + 8006e62: 4621 mov r1, r4 + 8006e64: 4668 mov r0, sp + 8006e66: f7ff ffa7 bl 8006db8 +} + 8006e6a: b011 add sp, #68 ; 0x44 + 8006e6c: bd30 pop {r4, r5, pc} + +08006e6e : +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006e6e: b570 push {r4, r5, r6, lr} + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006e70: f9b2 3002 ldrsh.w r3, [r2, #2] +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006e74: 4614 mov r4, r2 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006e76: 1dda adds r2, r3, #7 + 8006e78: bf48 it mi + 8006e7a: f103 020e addmi.w r2, r3, #14 +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006e7e: b098 sub sp, #96 ; 0x60 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006e80: 10d2 asrs r2, r2, #3 +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + 8006e82: 460e mov r6, r1 + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + 8006e84: 4601 mov r1, r0 + 8006e86: 4668 mov r0, sp + 8006e88: f7fe fed4 bl 8005c34 + if (uECC_vli_isZero(private, BITS_TO_WORDS(curve->num_n_bits))) { + 8006e8c: f9b4 3002 ldrsh.w r3, [r4, #2] + 8006e90: f113 021f adds.w r2, r3, #31 + 8006e94: bf48 it mi + 8006e96: f103 023e addmi.w r2, r3, #62 ; 0x3e + 8006e9a: f342 1147 sbfx r1, r2, #5, #8 + 8006e9e: 4668 mov r0, sp + 8006ea0: f7fe fd7a bl 8005998 + 8006ea4: b110 cbz r0, 8006eac + return 0; + 8006ea6: 2000 movs r0, #0 +} + 8006ea8: b018 add sp, #96 ; 0x60 + 8006eaa: bd70 pop {r4, r5, r6, pc} + if (uECC_vli_cmp(curve->n, private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + 8006eac: 460a mov r2, r1 + 8006eae: f104 0024 add.w r0, r4, #36 ; 0x24 + 8006eb2: 4669 mov r1, sp + 8006eb4: f7fe ffb0 bl 8005e18 + 8006eb8: 2801 cmp r0, #1 + 8006eba: 4605 mov r5, r0 + 8006ebc: d1f3 bne.n 8006ea6 + if (!EccPoint_compute_public_key(public, private, curve)) { + 8006ebe: 4622 mov r2, r4 + 8006ec0: 4669 mov r1, sp + 8006ec2: a808 add r0, sp, #32 + 8006ec4: f7ff fc35 bl 8006732 + 8006ec8: 2800 cmp r0, #0 + 8006eca: d0ec beq.n 8006ea6 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + 8006ecc: f994 1001 ldrsb.w r1, [r4, #1] + 8006ed0: aa08 add r2, sp, #32 + 8006ed2: 4630 mov r0, r6 + 8006ed4: f7fe fe9a bl 8005c0c + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + 8006ed8: f994 1001 ldrsb.w r1, [r4, #1] + 8006edc: f994 2000 ldrsb.w r2, [r4] + uECC_vli_nativeToBytes( + 8006ee0: ab08 add r3, sp, #32 + 8006ee2: 1870 adds r0, r6, r1 + 8006ee4: eb03 0282 add.w r2, r3, r2, lsl #2 + 8006ee8: f7fe fe90 bl 8005c0c + return 1; + 8006eec: 4628 mov r0, r5 + 8006eee: e7db b.n 8006ea8 + +08006ef0 : + uECC_Curve curve) { + 8006ef0: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8006ef4: b08a sub sp, #40 ; 0x28 + 8006ef6: 4605 mov r5, r0 + 8006ef8: f8dd 9048 ldr.w r9, [sp, #72] ; 0x48 + 8006efc: 460e mov r6, r1 + 8006efe: 4617 mov r7, r2 + 8006f00: 4698 mov r8, r3 + 8006f02: 2440 movs r4, #64 ; 0x40 + if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + 8006f04: f109 0a24 add.w sl, r9, #36 ; 0x24 + 8006f08: f9b9 3002 ldrsh.w r3, [r9, #2] + 8006f0c: f113 021f adds.w r2, r3, #31 + 8006f10: bf48 it mi + 8006f12: f103 023e addmi.w r2, r3, #62 ; 0x3e + 8006f16: f342 1247 sbfx r2, r2, #5, #8 + 8006f1a: 4651 mov r1, sl + 8006f1c: a802 add r0, sp, #8 + 8006f1e: f7fe ff93 bl 8005e48 + 8006f22: b150 cbz r0, 8006f3a + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, curve)) { + 8006f24: e9cd 8900 strd r8, r9, [sp] + 8006f28: ab02 add r3, sp, #8 + 8006f2a: 463a mov r2, r7 + 8006f2c: 4631 mov r1, r6 + 8006f2e: 4628 mov r0, r5 + 8006f30: f7ff fc2a bl 8006788 + 8006f34: b928 cbnz r0, 8006f42 + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 8006f36: 3c01 subs r4, #1 + 8006f38: d1e6 bne.n 8006f08 + return 0; + 8006f3a: 2000 movs r0, #0 +} + 8006f3c: b00a add sp, #40 ; 0x28 + 8006f3e: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + return 1; + 8006f42: 2001 movs r0, #1 + 8006f44: e7fa b.n 8006f3c + +08006f46 : +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve) { + 8006f46: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 8006f4a: b091 sub sp, #68 ; 0x44 + 8006f4c: 4693 mov fp, r2 + uint8_t *K = hash_context->tmp; + uint8_t *V = K + hash_context->result_size; + wordcount_t num_bytes = curve->num_bytes; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006f4e: 9a1b ldr r2, [sp, #108] ; 0x6c + 8006f50: f9b2 8002 ldrsh.w r8, [r2, #2] + uint8_t *V = K + hash_context->result_size; + 8006f54: e9d3 6504 ldrd r6, r5, [r3, #16] + uECC_Curve curve) { + 8006f58: 461c mov r4, r3 + wordcount_t num_bytes = curve->num_bytes; + 8006f5a: 9b1b ldr r3, [sp, #108] ; 0x6c + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006f5c: f118 071f adds.w r7, r8, #31 + 8006f60: bf48 it mi + 8006f62: f108 073e addmi.w r7, r8, #62 ; 0x3e + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t tries; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) { + 8006f66: 2200 movs r2, #0 + wordcount_t num_bytes = curve->num_bytes; + 8006f68: f993 3001 ldrsb.w r3, [r3, #1] + uECC_Curve curve) { + 8006f6c: 4681 mov r9, r0 + 8006f6e: 468a mov sl, r1 + uint8_t *V = K + hash_context->result_size; + 8006f70: 442e add r6, r5 + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 8006f72: f347 1747 sbfx r7, r7, #5, #8 + V[i] = 0x01; + 8006f76: 2001 movs r0, #1 + K[i] = 0; + 8006f78: 4694 mov ip, r2 + for (i = 0; i < hash_context->result_size; ++i) { + 8006f7a: 6921 ldr r1, [r4, #16] + 8006f7c: 4291 cmp r1, r2 + 8006f7e: f200 8086 bhi.w 800708e + } + + /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + 8006f82: 4629 mov r1, r5 + 8006f84: 4620 mov r0, r4 + 8006f86: 9303 str r3, [sp, #12] + 8006f88: f7fe fe74 bl 8005c74 + V[hash_context->result_size] = 0x00; + 8006f8c: 6922 ldr r2, [r4, #16] + 8006f8e: 2100 movs r1, #0 + 8006f90: 54b1 strb r1, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 8006f92: 6922 ldr r2, [r4, #16] + 8006f94: 4631 mov r1, r6 + 8006f96: 3201 adds r2, #1 + 8006f98: 4620 mov r0, r4 + 8006f9a: f7fe fe8c bl 8005cb6 + HMAC_update(hash_context, private_key, num_bytes); + 8006f9e: 9b03 ldr r3, [sp, #12] + 8006fa0: 4649 mov r1, r9 + 8006fa2: 461a mov r2, r3 + 8006fa4: 4620 mov r0, r4 + 8006fa6: f7fe fe86 bl 8005cb6 + HMAC_update(hash_context, message_hash, hash_size); + 8006faa: 465a mov r2, fp + 8006fac: 4651 mov r1, sl + 8006fae: 4620 mov r0, r4 + 8006fb0: f7fe fe81 bl 8005cb6 + HMAC_finish(hash_context, K, K); + 8006fb4: 462a mov r2, r5 + 8006fb6: 4629 mov r1, r5 + 8006fb8: 4620 mov r0, r4 + 8006fba: f7fe fe7e bl 8005cba + + update_V(hash_context, K, V); + 8006fbe: 4632 mov r2, r6 + 8006fc0: 4629 mov r1, r5 + 8006fc2: 4620 mov r0, r4 + 8006fc4: f7fe fea8 bl 8005d18 + + /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + 8006fc8: 4629 mov r1, r5 + 8006fca: 4620 mov r0, r4 + 8006fcc: f7fe fe52 bl 8005c74 + V[hash_context->result_size] = 0x01; + 8006fd0: 6922 ldr r2, [r4, #16] + 8006fd2: 2101 movs r1, #1 + 8006fd4: 54b1 strb r1, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 8006fd6: 6922 ldr r2, [r4, #16] + 8006fd8: 4620 mov r0, r4 + 8006fda: 440a add r2, r1 + 8006fdc: 4631 mov r1, r6 + 8006fde: f7fe fe6a bl 8005cb6 + HMAC_update(hash_context, private_key, num_bytes); + 8006fe2: 9b03 ldr r3, [sp, #12] + 8006fe4: 4649 mov r1, r9 + 8006fe6: 461a mov r2, r3 + 8006fe8: 4620 mov r0, r4 + 8006fea: f7fe fe64 bl 8005cb6 + HMAC_update(hash_context, message_hash, hash_size); + 8006fee: 465a mov r2, fp + 8006ff0: 4651 mov r1, sl + 8006ff2: 4620 mov r0, r4 + 8006ff4: f7fe fe5f bl 8005cb6 + HMAC_finish(hash_context, K, K); + 8006ff8: 462a mov r2, r5 + 8006ffa: 4629 mov r1, r5 + 8006ffc: 4620 mov r0, r4 + 8006ffe: f7fe fe5c bl 8005cba + + update_V(hash_context, K, V); + 8007002: 4632 mov r2, r6 + 8007004: 4629 mov r1, r5 + 8007006: 4620 mov r0, r4 + 8007008: f7fe fe86 bl 8005d18 + wordcount_t T_bytes = 0; + for (;;) { + update_V(hash_context, K, V); + for (i = 0; i < hash_context->result_size; ++i) { + T_ptr[T_bytes++] = V[i]; + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + 800700c: 00bb lsls r3, r7, #2 + 800700e: 9304 str r3, [sp, #16] + goto filled; + } + } + } + filled: + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + 8007010: 017b lsls r3, r7, #5 + 8007012: 9305 str r3, [sp, #20] + uECC_word_t mask = (uECC_word_t)-1; + T[num_n_words - 1] &= + mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); + 8007014: ebc8 1347 rsb r3, r8, r7, lsl #5 + 8007018: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800701c: b21b sxth r3, r3 + 800701e: fa22 f303 lsr.w r3, r2, r3 + 8007022: 9306 str r3, [sp, #24] + 8007024: 2340 movs r3, #64 ; 0x40 + 8007026: 9303 str r3, [sp, #12] + T[num_n_words - 1] &= + 8007028: 4417 add r7, r2 + 800702a: 446b add r3, sp + 800702c: eb03 0787 add.w r7, r3, r7, lsl #2 + wordcount_t T_bytes = 0; + 8007030: 2300 movs r3, #0 + update_V(hash_context, K, V); + 8007032: 4632 mov r2, r6 + 8007034: 4629 mov r1, r5 + 8007036: 4620 mov r0, r4 + 8007038: 9307 str r3, [sp, #28] + 800703a: f7fe fe6d bl 8005d18 + for (i = 0; i < hash_context->result_size; ++i) { + 800703e: 6920 ldr r0, [r4, #16] + 8007040: 9b07 ldr r3, [sp, #28] + 8007042: 4631 mov r1, r6 + 8007044: 4430 add r0, r6 + 8007046: 461a mov r2, r3 + T_ptr[T_bytes++] = V[i]; + 8007048: ab08 add r3, sp, #32 + 800704a: eb03 0c02 add.w ip, r3, r2 + for (i = 0; i < hash_context->result_size; ++i) { + 800704e: 4288 cmp r0, r1 + 8007050: 4613 mov r3, r2 + 8007052: f102 0201 add.w r2, r2, #1 + 8007056: b252 sxtb r2, r2 + 8007058: d0eb beq.n 8007032 + T_ptr[T_bytes++] = V[i]; + 800705a: f811 3b01 ldrb.w r3, [r1], #1 + 800705e: f88c 3000 strb.w r3, [ip] + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + 8007062: 9b04 ldr r3, [sp, #16] + 8007064: 4293 cmp r3, r2 + 8007066: dcef bgt.n 8007048 + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + 8007068: 9b05 ldr r3, [sp, #20] + 800706a: 4598 cmp r8, r3 + 800706c: db14 blt.n 8007098 + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, T, signature, curve)) { + 800706e: 9b1b ldr r3, [sp, #108] ; 0x6c + 8007070: 9301 str r3, [sp, #4] + 8007072: 9b1a ldr r3, [sp, #104] ; 0x68 + 8007074: 9300 str r3, [sp, #0] + 8007076: 465a mov r2, fp + 8007078: ab08 add r3, sp, #32 + 800707a: 4651 mov r1, sl + 800707c: 4648 mov r0, r9 + 800707e: f7ff fb83 bl 8006788 + 8007082: b180 cbz r0, 80070a6 + return 1; + 8007084: 2301 movs r3, #1 + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + } + return 0; +} + 8007086: 4618 mov r0, r3 + 8007088: b011 add sp, #68 ; 0x44 + 800708a: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + V[i] = 0x01; + 800708e: 54b0 strb r0, [r6, r2] + K[i] = 0; + 8007090: f805 c002 strb.w ip, [r5, r2] + for (i = 0; i < hash_context->result_size; ++i) { + 8007094: 3201 adds r2, #1 + 8007096: e770 b.n 8006f7a + T[num_n_words - 1] &= + 8007098: f857 3c20 ldr.w r3, [r7, #-32] + 800709c: 9a06 ldr r2, [sp, #24] + 800709e: 4013 ands r3, r2 + 80070a0: f847 3c20 str.w r3, [r7, #-32] + 80070a4: e7e3 b.n 800706e + 80070a6: 9007 str r0, [sp, #28] + HMAC_init(hash_context, K); + 80070a8: 4629 mov r1, r5 + 80070aa: 4620 mov r0, r4 + 80070ac: f7fe fde2 bl 8005c74 + V[hash_context->result_size] = 0x00; + 80070b0: 6922 ldr r2, [r4, #16] + 80070b2: 9b07 ldr r3, [sp, #28] + 80070b4: 54b3 strb r3, [r6, r2] + HMAC_update(hash_context, V, hash_context->result_size + 1); + 80070b6: 6922 ldr r2, [r4, #16] + 80070b8: 4631 mov r1, r6 + 80070ba: 3201 adds r2, #1 + 80070bc: 4620 mov r0, r4 + 80070be: f7fe fdfa bl 8005cb6 + HMAC_finish(hash_context, K, K); + 80070c2: 462a mov r2, r5 + 80070c4: 4629 mov r1, r5 + 80070c6: 4620 mov r0, r4 + 80070c8: f7fe fdf7 bl 8005cba + update_V(hash_context, K, V); + 80070cc: 4632 mov r2, r6 + 80070ce: 4629 mov r1, r5 + 80070d0: 4620 mov r0, r4 + 80070d2: f7fe fe21 bl 8005d18 + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + 80070d6: 9b03 ldr r3, [sp, #12] + 80070d8: 3b01 subs r3, #1 + 80070da: 9303 str r3, [sp, #12] + 80070dc: 9b07 ldr r3, [sp, #28] + 80070de: d1a7 bne.n 8007030 + 80070e0: e7d1 b.n 8007086 + +080070e2 : + +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve) { + 80070e2: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 80070e6: ed2d 8b02 vpush {d8} + 80070ea: b0fb sub sp, #492 ; 0x1ec + 80070ec: 461c mov r4, r3 + 80070ee: 9d86 ldr r5, [sp, #536] ; 0x218 + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 80070f0: f9b5 3002 ldrsh.w r3, [r5, #2] + wordcount_t num_words = curve->num_words; + 80070f4: f995 8000 ldrsb.w r8, [r5] + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + 80070f8: f113 061f adds.w r6, r3, #31 + 80070fc: bf48 it mi + 80070fe: f103 063e addmi.w r6, r3, #62 ; 0x3e + 8007102: f346 1647 sbfx r6, r6, #5, #8 + + rx[num_n_words - 1] = 0; + 8007106: f106 3aff add.w sl, r6, #4294967295 ; 0xffffffff + uECC_Curve curve) { + 800710a: 4691 mov r9, r2 + rx[num_n_words - 1] = 0; + 800710c: aa22 add r2, sp, #136 ; 0x88 + 800710e: 2300 movs r3, #0 + 8007110: f842 302a str.w r3, [r2, sl, lsl #2] + r[num_n_words - 1] = 0; + 8007114: aa7a add r2, sp, #488 ; 0x1e8 + 8007116: eb02 028a add.w r2, r2, sl, lsl #2 + uECC_Curve curve) { + 800711a: 4607 mov r7, r0 + r[num_n_words - 1] = 0; + 800711c: f842 3cc0 str.w r3, [r2, #-192] + s[num_n_words - 1] = 0; + 8007120: f842 3ca0 str.w r3, [r2, #-160] + uECC_Curve curve) { + 8007124: ee08 1a90 vmov s17, r1 + + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + 8007128: f995 2001 ldrsb.w r2, [r5, #1] + 800712c: 4601 mov r1, r0 + 800712e: a85a add r0, sp, #360 ; 0x168 + 8007130: f7fe fd80 bl 8005c34 + uECC_vli_bytesToNative( + public + num_words, public_key + curve->num_bytes, curve->num_bytes); + 8007134: ea4f 0388 mov.w r3, r8, lsl #2 + 8007138: f995 2001 ldrsb.w r2, [r5, #1] + 800713c: 9304 str r3, [sp, #16] + uECC_vli_bytesToNative( + 800713e: ab5a add r3, sp, #360 ; 0x168 + 8007140: eb03 0388 add.w r3, r3, r8, lsl #2 + 8007144: 4618 mov r0, r3 + 8007146: 18b9 adds r1, r7, r2 + 8007148: ee08 3a10 vmov s16, r3 + 800714c: f7fe fd72 bl 8005c34 + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + 8007150: 4621 mov r1, r4 + 8007152: f995 2001 ldrsb.w r2, [r5, #1] + 8007156: a84a add r0, sp, #296 ; 0x128 + 8007158: f7fe fd6c bl 8005c34 + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + 800715c: f995 2001 ldrsb.w r2, [r5, #1] + 8007160: a852 add r0, sp, #328 ; 0x148 + 8007162: 18a1 adds r1, r4, r2 + 8007164: f7fe fd66 bl 8005c34 + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + 8007168: 4641 mov r1, r8 + 800716a: a84a add r0, sp, #296 ; 0x128 + 800716c: f7fe fc14 bl 8005998 + 8007170: 2300 movs r3, #0 + 8007172: 4604 mov r4, r0 + 8007174: 2800 cmp r0, #0 + 8007176: f040 812b bne.w 80073d0 + 800717a: a852 add r0, sp, #328 ; 0x148 + 800717c: f7fe fc0c bl 8005998 + 8007180: 9002 str r0, [sp, #8] + 8007182: 2800 cmp r0, #0 + 8007184: f040 8126 bne.w 80073d4 + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 8007188: f105 0b24 add.w fp, r5, #36 ; 0x24 + 800718c: 4632 mov r2, r6 + 800718e: a94a add r1, sp, #296 ; 0x128 + 8007190: 4658 mov r0, fp + 8007192: f7fe fc46 bl 8005a22 + 8007196: 2801 cmp r0, #1 + 8007198: f040 811e bne.w 80073d8 + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + 800719c: 4632 mov r2, r6 + 800719e: a952 add r1, sp, #328 ; 0x148 + 80071a0: 4658 mov r0, fp + 80071a2: f7fe fc3e bl 8005a22 + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 80071a6: 2801 cmp r0, #1 + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + 80071a8: 9005 str r0, [sp, #20] + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + 80071aa: f040 8115 bne.w 80073d8 + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + 80071ae: ac1a add r4, sp, #104 ; 0x68 + u1[num_n_words - 1] = 0; + 80071b0: af0a add r7, sp, #40 ; 0x28 + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + 80071b2: 4633 mov r3, r6 + 80071b4: 465a mov r2, fp + 80071b6: 4620 mov r0, r4 + 80071b8: f7ff f84e bl 8006258 + u1[num_n_words - 1] = 0; + 80071bc: 9b02 ldr r3, [sp, #8] + 80071be: f847 302a str.w r3, [r7, sl, lsl #2] + bits2int(u1, message_hash, hash_size, curve); + 80071c2: 464a mov r2, r9 + 80071c4: 4638 mov r0, r7 + 80071c6: ee18 1a90 vmov r1, s17 + 80071ca: 462b mov r3, r5 + 80071cc: f7fe fddd bl 8005d8a + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + 80071d0: 4639 mov r1, r7 + 80071d2: 4638 mov r0, r7 + 80071d4: 465b mov r3, fp + 80071d6: 4622 mov r2, r4 + 80071d8: 9600 str r6, [sp, #0] + 80071da: f7fe fc44 bl 8005a66 + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, public, num_words); + 80071de: f50d 7ad4 add.w sl, sp, #424 ; 0x1a8 + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + 80071e2: 465b mov r3, fp + 80071e4: 4622 mov r2, r4 + 80071e6: a94a add r1, sp, #296 ; 0x128 + 80071e8: a812 add r0, sp, #72 ; 0x48 + 80071ea: 9600 str r6, [sp, #0] + 80071ec: f7fe fc3b bl 8005a66 + uECC_vli_set(sum, public, num_words); + 80071f0: 4642 mov r2, r8 + 80071f2: 4650 mov r0, sl + 80071f4: a95a add r1, sp, #360 ; 0x168 + 80071f6: f7fe fc08 bl 8005a0a + uECC_vli_set(sum + num_words, public + num_words, num_words); + 80071fa: 9b04 ldr r3, [sp, #16] + 80071fc: eb0a 0903 add.w r9, sl, r3 + 8007200: ee18 1a10 vmov r1, s16 + 8007204: 4648 mov r0, r9 + 8007206: f7fe fc00 bl 8005a0a + uECC_vli_set(tx, curve->G, num_words); + 800720a: f105 0344 add.w r3, r5, #68 ; 0x44 + 800720e: 4619 mov r1, r3 + 8007210: a832 add r0, sp, #200 ; 0xc8 + 8007212: 9303 str r3, [sp, #12] + 8007214: f7fe fbf9 bl 8005a0a + uECC_vli_set(ty, curve->G + num_words, num_words); + 8007218: e9dd 3103 ldrd r3, r1, [sp, #12] + 800721c: a83a add r0, sp, #232 ; 0xe8 + 800721e: 1859 adds r1, r3, r1 + 8007220: f7fe fbf3 bl 8005a0a + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + 8007224: 1d2b adds r3, r5, #4 + 8007226: ee08 3a10 vmov s16, r3 + 800722a: 4651 mov r1, sl + 800722c: aa32 add r2, sp, #200 ; 0xc8 + 800722e: 4620 mov r0, r4 + 8007230: f7ff f8c0 bl 80063b4 + XYcZ_add(tx, ty, sum, sum + num_words, curve); + 8007234: 464b mov r3, r9 + 8007236: 4652 mov r2, sl + 8007238: a93a add r1, sp, #232 ; 0xe8 + 800723a: a832 add r0, sp, #200 ; 0xc8 + 800723c: 9500 str r5, [sp, #0] + 800723e: f7ff f94d bl 80064dc + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + 8007242: ee18 2a10 vmov r2, s16 + 8007246: 4643 mov r3, r8 + 8007248: 4621 mov r1, r4 + 800724a: 4620 mov r0, r4 + 800724c: f7ff f804 bl 8006258 + apply_z(sum, sum + num_words, z, curve); + 8007250: 462b mov r3, r5 + 8007252: 4649 mov r1, r9 + 8007254: 4650 mov r0, sl + 8007256: 4622 mov r2, r4 + 8007258: f7fe fcb9 bl 8005bce + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + 800725c: 9a02 ldr r2, [sp, #8] + 800725e: 9206 str r2, [sp, #24] + points[1] = curve->G; + 8007260: 9a03 ldr r2, [sp, #12] + 8007262: 9207 str r2, [sp, #28] + points[2] = public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 8007264: 4631 mov r1, r6 + points[2] = public; + 8007266: aa5a add r2, sp, #360 ; 0x168 + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 8007268: 4638 mov r0, r7 + points[3] = sum; + 800726a: e9cd 2a08 strd r2, sl, [sp, #32] + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + 800726e: f7fe fbac bl 80059ca + 8007272: 4631 mov r1, r6 + 8007274: 4682 mov sl, r0 + 8007276: a812 add r0, sp, #72 ; 0x48 + 8007278: f7fe fba7 bl 80059ca + return (a > b ? a : b); + 800727c: 4550 cmp r0, sl + 800727e: bfb8 it lt + 8007280: 4650 movlt r0, sl + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8007282: fa1f f980 uxth.w r9, r0 + 8007286: f109 31ff add.w r1, r9, #4294967295 ; 0xffffffff + 800728a: b209 sxth r1, r1 + 800728c: 4638 mov r0, r7 + 800728e: 9103 str r1, [sp, #12] + 8007290: f7fe fb91 bl 80059b6 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 8007294: 9903 ldr r1, [sp, #12] + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 8007296: 1e07 subs r7, r0, #0 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 8007298: a812 add r0, sp, #72 ; 0x48 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 800729a: bf18 it ne + 800729c: 2701 movne r7, #1 + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + 800729e: f7fe fb8a bl 80059b6 + 80072a2: 2800 cmp r0, #0 + 80072a4: bf14 ite ne + 80072a6: 2002 movne r0, #2 + 80072a8: 2000 moveq r0, #0 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 80072aa: ab06 add r3, sp, #24 + 80072ac: 4307 orrs r7, r0 + uECC_vli_set(rx, point, num_words); + 80072ae: 4642 mov r2, r8 + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + 80072b0: f853 1027 ldr.w r1, [r3, r7, lsl #2] + uECC_vli_set(rx, point, num_words); + 80072b4: a822 add r0, sp, #136 ; 0x88 + 80072b6: f7fe fba8 bl 8005a0a + uECC_vli_set(ry, point + num_words, num_words); + 80072ba: 9b04 ldr r3, [sp, #16] + 80072bc: f10d 0aa8 add.w sl, sp, #168 ; 0xa8 + 80072c0: 4419 add r1, r3 + 80072c2: 4650 mov r0, sl + 80072c4: f7fe fba1 bl 8005a0a + uECC_vli_clear(z, num_words); + 80072c8: 4641 mov r1, r8 + 80072ca: 4620 mov r0, r4 + 80072cc: f7fe fb5e bl 800598c + z[0] = 1; + 80072d0: 9b05 ldr r3, [sp, #20] + 80072d2: 6023 str r3, [r4, #0] + + for (i = num_bits - 2; i >= 0; --i) { + 80072d4: f1a9 0902 sub.w r9, r9, #2 + 80072d8: ab22 add r3, sp, #136 ; 0x88 + 80072da: fa0f f989 sxth.w r9, r9 + 80072de: 9303 str r3, [sp, #12] + 80072e0: f1b9 0f00 cmp.w r9, #0 + 80072e4: da26 bge.n 8007334 + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + 80072e6: ee18 2a10 vmov r2, s16 + 80072ea: 4643 mov r3, r8 + 80072ec: 4621 mov r1, r4 + 80072ee: 4620 mov r0, r4 + 80072f0: f7fe ffb2 bl 8006258 + apply_z(rx, ry, z, curve); + 80072f4: 9803 ldr r0, [sp, #12] + 80072f6: 462b mov r3, r5 + 80072f8: 4622 mov r2, r4 + 80072fa: 4651 mov r1, sl + 80072fc: f7fe fc67 bl 8005bce + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + 8007300: 9903 ldr r1, [sp, #12] + 8007302: 4632 mov r2, r6 + 8007304: 4658 mov r0, fp + 8007306: f7fe fb8c bl 8005a22 + 800730a: 2801 cmp r0, #1 + 800730c: d003 beq.n 8007316 + uECC_vli_sub(rx, rx, curve->n, num_n_words); + 800730e: 465a mov r2, fp + 8007310: 4608 mov r0, r1 + 8007312: f7fe fd13 bl 8005d3c + for (i = num_words - 1; i >= 0; --i) { + 8007316: f108 33ff add.w r3, r8, #4294967295 ; 0xffffffff + 800731a: b25b sxtb r3, r3 + diff |= (left[i] ^ right[i]); + 800731c: a94a add r1, sp, #296 ; 0x128 + for (i = num_words - 1; i >= 0; --i) { + 800731e: 061a lsls r2, r3, #24 + 8007320: d54b bpl.n 80073ba + return (diff == 0); + 8007322: 9b02 ldr r3, [sp, #8] + 8007324: fab3 f083 clz r0, r3 + 8007328: 0940 lsrs r0, r0, #5 + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words)); +} + 800732a: b07b add sp, #492 ; 0x1ec + 800732c: ecbd 8b02 vpop {d8} + 8007330: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + curve->double_jacobian(rx, ry, z, curve); + 8007334: 462b mov r3, r5 + 8007336: 4622 mov r2, r4 + 8007338: f8d5 70a4 ldr.w r7, [r5, #164] ; 0xa4 + 800733c: 9803 ldr r0, [sp, #12] + 800733e: 4651 mov r1, sl + 8007340: 47b8 blx r7 + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + 8007342: 4649 mov r1, r9 + 8007344: a80a add r0, sp, #40 ; 0x28 + 8007346: f7fe fb36 bl 80059b6 + 800734a: 4649 mov r1, r9 + 800734c: 1e07 subs r7, r0, #0 + 800734e: a812 add r0, sp, #72 ; 0x48 + 8007350: bf18 it ne + 8007352: 2701 movne r7, #1 + 8007354: f7fe fb2f bl 80059b6 + 8007358: 2800 cmp r0, #0 + 800735a: bf14 ite ne + 800735c: 2002 movne r0, #2 + 800735e: 2000 moveq r0, #0 + 8007360: 4307 orrs r7, r0 + point = points[index]; + 8007362: ab06 add r3, sp, #24 + 8007364: f853 1027 ldr.w r1, [r3, r7, lsl #2] + if (point) { + 8007368: b311 cbz r1, 80073b0 + uECC_vli_set(tx, point, num_words); + 800736a: 4642 mov r2, r8 + 800736c: a832 add r0, sp, #200 ; 0xc8 + 800736e: f7fe fb4c bl 8005a0a + uECC_vli_set(ty, point + num_words, num_words); + 8007372: 9b04 ldr r3, [sp, #16] + 8007374: a83a add r0, sp, #232 ; 0xe8 + 8007376: 4419 add r1, r3 + 8007378: f7fe fb47 bl 8005a0a + apply_z(tx, ty, z, curve); + 800737c: 4601 mov r1, r0 + 800737e: 462b mov r3, r5 + 8007380: 4622 mov r2, r4 + 8007382: a832 add r0, sp, #200 ; 0xc8 + 8007384: f7fe fc23 bl 8005bce + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + 8007388: ee18 3a10 vmov r3, s16 + 800738c: 9903 ldr r1, [sp, #12] + 800738e: aa32 add r2, sp, #200 ; 0xc8 + 8007390: a842 add r0, sp, #264 ; 0x108 + 8007392: f7ff f80f bl 80063b4 + XYcZ_add(tx, ty, rx, ry, curve); + 8007396: 9a03 ldr r2, [sp, #12] + 8007398: 9500 str r5, [sp, #0] + 800739a: 4653 mov r3, sl + 800739c: a93a add r1, sp, #232 ; 0xe8 + 800739e: a832 add r0, sp, #200 ; 0xc8 + 80073a0: f7ff f89c bl 80064dc + uECC_vli_modMult_fast(z, z, tz, curve); + 80073a4: 462b mov r3, r5 + 80073a6: aa42 add r2, sp, #264 ; 0x108 + 80073a8: 4621 mov r1, r4 + 80073aa: 4620 mov r0, r4 + 80073ac: f7fe fbfb bl 8005ba6 + for (i = num_bits - 2; i >= 0; --i) { + 80073b0: f109 39ff add.w r9, r9, #4294967295 ; 0xffffffff + 80073b4: fa0f f989 sxth.w r9, r9 + 80073b8: e792 b.n 80072e0 + diff |= (left[i] ^ right[i]); + 80073ba: 9a03 ldr r2, [sp, #12] + 80073bc: f851 0023 ldr.w r0, [r1, r3, lsl #2] + 80073c0: f852 2023 ldr.w r2, [r2, r3, lsl #2] + 80073c4: 4042 eors r2, r0 + 80073c6: 9802 ldr r0, [sp, #8] + 80073c8: 4310 orrs r0, r2 + 80073ca: 9002 str r0, [sp, #8] + for (i = num_words - 1; i >= 0; --i) { + 80073cc: 3b01 subs r3, #1 + 80073ce: e7a6 b.n 800731e + return 0; + 80073d0: 4618 mov r0, r3 + 80073d2: e7aa b.n 800732a + 80073d4: 4620 mov r0, r4 + 80073d6: e7a8 b.n 800732a + 80073d8: 9802 ldr r0, [sp, #8] + 80073da: e7a6 b.n 800732a + +080073dc : +const uint32_t MSIRangeTable[12] = {100000, 200000, 400000, 800000, 1000000, 2000000, \ + 4000000, 8000000, 16000000, 24000000, 32000000, 48000000}; +uint32_t SystemCoreClock; + +// TODO: cleanup HAL stuff to not use this +uint32_t HAL_GetTick(void) { return 53; } + 80073dc: 2035 movs r0, #53 ; 0x35 + 80073de: 4770 bx lr + +080073e0 : +uint32_t uwTickPrio = 0; /* (1UL << __NVIC_PRIO_BITS); * Invalid priority */ + +// unwanted junk from stm32l4xx_hal_rcc.c +HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority) { return 0; } + 80073e0: 2000 movs r0, #0 + 80073e2: 4770 bx lr + +080073e4 : + * or PWR_REGULATOR_VOLTAGE_SCALE1_BOOST when applicable) + */ +uint32_t HAL_PWREx_GetVoltageRange(void) +{ +#if defined(PWR_CR5_R1MODE) + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 80073e4: 4b07 ldr r3, [pc, #28] ; (8007404 ) + 80073e6: 6818 ldr r0, [r3, #0] + 80073e8: f400 60c0 and.w r0, r0, #1536 ; 0x600 + 80073ec: f5b0 6f80 cmp.w r0, #1024 ; 0x400 + 80073f0: d006 beq.n 8007400 + { + return PWR_REGULATOR_VOLTAGE_SCALE2; + } + else if (READ_BIT(PWR->CR5, PWR_CR5_R1MODE) == PWR_CR5_R1MODE) + 80073f2: f8d3 0080 ldr.w r0, [r3, #128] ; 0x80 + { + /* PWR_CR5_R1MODE bit set means that Range 1 Boost is disabled */ + return PWR_REGULATOR_VOLTAGE_SCALE1; + 80073f6: f410 7080 ands.w r0, r0, #256 ; 0x100 + 80073fa: bf18 it ne + 80073fc: f44f 7000 movne.w r0, #512 ; 0x200 + return PWR_REGULATOR_VOLTAGE_SCALE1_BOOST; + } +#else + return (PWR->CR1 & PWR_CR1_VOS); +#endif +} + 8007400: 4770 bx lr + 8007402: bf00 nop + 8007404: 40007000 .word 0x40007000 + +08007408 : + uint32_t wait_loop_index; + + assert_param(IS_PWR_VOLTAGE_SCALING_RANGE(VoltageScaling)); + +#if defined(PWR_CR5_R1MODE) + if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1_BOOST) + 8007408: 4b29 ldr r3, [pc, #164] ; (80074b0 ) + { + /* If current range is range 2 */ + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 800740a: 681a ldr r2, [r3, #0] + if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1_BOOST) + 800740c: bb30 cbnz r0, 800745c + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 800740e: f402 62c0 and.w r2, r2, #1536 ; 0x600 + 8007412: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + { + /* Make sure Range 1 Boost is enabled */ + CLEAR_BIT(PWR->CR5, PWR_CR5_R1MODE); + 8007416: f8d3 2080 ldr.w r2, [r3, #128] ; 0x80 + 800741a: f422 7280 bic.w r2, r2, #256 ; 0x100 + 800741e: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007422: d11a bne.n 800745a + + /* Set Range 1 */ + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE1); + 8007424: 681a ldr r2, [r3, #0] + 8007426: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 800742a: f442 7200 orr.w r2, r2, #512 ; 0x200 + 800742e: 601a str r2, [r3, #0] + + /* Wait until VOSF is cleared */ + wait_loop_index = ((PWR_FLAG_SETTING_DELAY_US * SystemCoreClock) / 1000000U) + 1; + 8007430: 4a20 ldr r2, [pc, #128] ; (80074b4 ) + 8007432: 6812 ldr r2, [r2, #0] + 8007434: 2132 movs r1, #50 ; 0x32 + 8007436: 434a muls r2, r1 + 8007438: 491f ldr r1, [pc, #124] ; (80074b8 ) + 800743a: fbb2 f2f1 udiv r2, r2, r1 + 800743e: 3201 adds r2, #1 + while ((HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) && (wait_loop_index != 0U)) + 8007440: 6959 ldr r1, [r3, #20] + 8007442: 0549 lsls r1, r1, #21 + 8007444: d500 bpl.n 8007448 + 8007446: b922 cbnz r2, 8007452 + { + wait_loop_index--; + } + if (HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) + 8007448: 695b ldr r3, [r3, #20] + 800744a: 0558 lsls r0, r3, #21 + 800744c: d403 bmi.n 8007456 + /* No need to wait for VOSF to be cleared for this transition */ + } + } +#endif + + return HAL_OK; + 800744e: 2000 movs r0, #0 +} + 8007450: 4770 bx lr + wait_loop_index--; + 8007452: 3a01 subs r2, #1 + 8007454: e7f4 b.n 8007440 + return HAL_TIMEOUT; + 8007456: 2003 movs r0, #3 + 8007458: 4770 bx lr + CLEAR_BIT(PWR->CR5, PWR_CR5_R1MODE); + 800745a: 4770 bx lr + else if (VoltageScaling == PWR_REGULATOR_VOLTAGE_SCALE1) + 800745c: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8007460: d11f bne.n 80074a2 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007462: f402 62c0 and.w r2, r2, #1536 ; 0x600 + 8007466: f5b2 6f80 cmp.w r2, #1024 ; 0x400 + SET_BIT(PWR->CR5, PWR_CR5_R1MODE); + 800746a: f8d3 2080 ldr.w r2, [r3, #128] ; 0x80 + 800746e: f442 7280 orr.w r2, r2, #256 ; 0x100 + 8007472: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + if (READ_BIT(PWR->CR1, PWR_CR1_VOS) == PWR_REGULATOR_VOLTAGE_SCALE2) + 8007476: d1ea bne.n 800744e + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE1); + 8007478: 681a ldr r2, [r3, #0] + 800747a: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 800747e: f442 7200 orr.w r2, r2, #512 ; 0x200 + 8007482: 601a str r2, [r3, #0] + wait_loop_index = ((PWR_FLAG_SETTING_DELAY_US * SystemCoreClock) / 1000000U) + 1; + 8007484: 4a0b ldr r2, [pc, #44] ; (80074b4 ) + 8007486: 6812 ldr r2, [r2, #0] + 8007488: 2132 movs r1, #50 ; 0x32 + 800748a: 434a muls r2, r1 + 800748c: 490a ldr r1, [pc, #40] ; (80074b8 ) + 800748e: fbb2 f2f1 udiv r2, r2, r1 + 8007492: 3201 adds r2, #1 + while ((HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_VOSF)) && (wait_loop_index != 0U)) + 8007494: 6959 ldr r1, [r3, #20] + 8007496: 0549 lsls r1, r1, #21 + 8007498: d5d6 bpl.n 8007448 + 800749a: 2a00 cmp r2, #0 + 800749c: d0d4 beq.n 8007448 + wait_loop_index--; + 800749e: 3a01 subs r2, #1 + 80074a0: e7f8 b.n 8007494 + MODIFY_REG(PWR->CR1, PWR_CR1_VOS, PWR_REGULATOR_VOLTAGE_SCALE2); + 80074a2: f422 62c0 bic.w r2, r2, #1536 ; 0x600 + 80074a6: f442 6280 orr.w r2, r2, #1024 ; 0x400 + 80074aa: 601a str r2, [r3, #0] + 80074ac: e7cf b.n 800744e + 80074ae: bf00 nop + 80074b0: 40007000 .word 0x40007000 + 80074b4: 2009e2ac .word 0x2009e2ac + 80074b8: 000f4240 .word 0x000f4240 + +080074bc : + +__weak void HAL_SDEx_DriveTransceiver_1_8V_Callback(FlagStatus status) +{ + // unused? +} + 80074bc: 4770 bx lr + ... + +080074c0 <__NVIC_SystemReset>: + 80074c0: f3bf 8f4f dsb sy + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80074c4: 4905 ldr r1, [pc, #20] ; (80074dc <__NVIC_SystemReset+0x1c>) + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80074c6: 4b06 ldr r3, [pc, #24] ; (80074e0 <__NVIC_SystemReset+0x20>) + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + 80074c8: 68ca ldr r2, [r1, #12] + 80074ca: f402 62e0 and.w r2, r2, #1792 ; 0x700 + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + 80074ce: 4313 orrs r3, r2 + 80074d0: 60cb str r3, [r1, #12] + 80074d2: f3bf 8f4f dsb sy + __NOP(); + 80074d6: bf00 nop + for(;;) /* wait until reset */ + 80074d8: e7fd b.n 80074d6 <__NVIC_SystemReset+0x16> + 80074da: bf00 nop + 80074dc: e000ed00 .word 0xe000ed00 + 80074e0: 05fa0004 .word 0x05fa0004 + +080074e4 : +{ + 80074e4: b510 push {r4, lr} + 80074e6: 3801 subs r0, #1 + 80074e8: 440a add r2, r1 + *(acc) ^= *(more); + 80074ea: f811 4b01 ldrb.w r4, [r1], #1 + 80074ee: f810 3f01 ldrb.w r3, [r0, #1]! + for(; len; len--, more++, acc++) { + 80074f2: 4291 cmp r1, r2 + *(acc) ^= *(more); + 80074f4: ea83 0304 eor.w r3, r3, r4 + 80074f8: 7003 strb r3, [r0, #0] + for(; len; len--, more++, acc++) { + 80074fa: d1f6 bne.n 80074ea + } +} + 80074fc: bd10 pop {r4, pc} + ... + +08007500 : + +// se2_write1() +// + static bool +se2_write1(uint8_t cmd, uint8_t arg) +{ + 8007500: b51f push {r0, r1, r2, r3, r4, lr} + uint8_t data[3] = { cmd, 1, arg }; + 8007502: 2301 movs r3, #1 + 8007504: f88d 300d strb.w r3, [sp, #13] + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 8007508: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + uint8_t data[3] = { cmd, 1, arg }; + 800750c: f88d 000c strb.w r0, [sp, #12] + 8007510: f88d 100e strb.w r1, [sp, #14] + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 8007514: 9300 str r3, [sp, #0] + 8007516: aa03 add r2, sp, #12 + 8007518: 2303 movs r3, #3 + 800751a: 2136 movs r1, #54 ; 0x36 + 800751c: 4804 ldr r0, [pc, #16] ; (8007530 ) + 800751e: f004 fb79 bl 800bc14 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 8007522: 3800 subs r0, #0 + 8007524: bf18 it ne + 8007526: 2001 movne r0, #1 + 8007528: b005 add sp, #20 + 800752a: f85d fb04 ldr.w pc, [sp], #4 + 800752e: bf00 nop + 8007530: 2009e3f0 .word 0x2009e3f0 + +08007534 : + +// se2_write2() +// + static bool +se2_write2(uint8_t cmd, uint8_t arg1, uint8_t arg2) +{ + 8007534: b51f push {r0, r1, r2, r3, r4, lr} + uint8_t data[4] = { cmd, 2, arg1, arg2 }; + 8007536: 2302 movs r3, #2 + 8007538: f88d 300d strb.w r3, [sp, #13] + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 800753c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + uint8_t data[4] = { cmd, 2, arg1, arg2 }; + 8007540: f88d 000c strb.w r0, [sp, #12] + 8007544: f88d 100e strb.w r1, [sp, #14] + 8007548: f88d 200f strb.w r2, [sp, #15] + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 800754c: 9300 str r3, [sp, #0] + 800754e: aa03 add r2, sp, #12 + 8007550: 2304 movs r3, #4 + 8007552: 2136 movs r1, #54 ; 0x36 + 8007554: 4804 ldr r0, [pc, #16] ; (8007568 ) + 8007556: f004 fb5d bl 800bc14 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 800755a: 3800 subs r0, #0 + 800755c: bf18 it ne + 800755e: 2001 movne r0, #1 + 8007560: b005 add sp, #20 + 8007562: f85d fb04 ldr.w pc, [sp], #4 + 8007566: bf00 nop + 8007568: 2009e3f0 .word 0x2009e3f0 + +0800756c : + +// se2_write_n() +// + static bool +se2_write_n(uint8_t cmd, uint8_t *param1, const uint8_t *data_in, uint8_t len) +{ + 800756c: b5f0 push {r4, r5, r6, r7, lr} + 800756e: 460d mov r5, r1 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007570: 2d00 cmp r5, #0 + 8007572: bf14 ite ne + 8007574: 2403 movne r4, #3 + 8007576: 2402 moveq r4, #2 + 8007578: 441c add r4, r3 +{ + 800757a: 4611 mov r1, r2 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 800757c: f104 0207 add.w r2, r4, #7 +{ + 8007580: b083 sub sp, #12 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007582: f402 727e and.w r2, r2, #1016 ; 0x3f8 +{ + 8007586: af02 add r7, sp, #8 + uint8_t data[2 + (param1?1:0) + len], *p = data; + 8007588: ebad 0d02 sub.w sp, sp, r2 + 800758c: ae02 add r6, sp, #8 + + *(p++) = cmd; + *(p++) = sizeof(data) - 2; + 800758e: f1a4 0202 sub.w r2, r4, #2 + *(p++) = cmd; + 8007592: f88d 0008 strb.w r0, [sp, #8] + *(p++) = sizeof(data) - 2; + 8007596: 7072 strb r2, [r6, #1] + if(param1) { + *(p++) = *param1; + 8007598: bf1b ittet ne + 800759a: 782a ldrbne r2, [r5, #0] + 800759c: 70b2 strbne r2, [r6, #2] + *(p++) = sizeof(data) - 2; + 800759e: f10d 000a addeq.w r0, sp, #10 + *(p++) = *param1; + 80075a2: f10d 000b addne.w r0, sp, #11 + } + if(len) { + 80075a6: b113 cbz r3, 80075ae + memcpy(p, data_in, len); + 80075a8: 461a mov r2, r3 + 80075aa: f006 f9ad bl 800d908 + } + + HAL_StatusTypeDef rv = HAL_I2C_Master_Transmit(&i2c_port, I2C_ADDR, + 80075ae: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 80075b2: 9300 str r3, [sp, #0] + 80075b4: 4632 mov r2, r6 + 80075b6: 4623 mov r3, r4 + 80075b8: 2136 movs r1, #54 ; 0x36 + 80075ba: 4804 ldr r0, [pc, #16] ; (80075cc ) + 80075bc: f004 fb2a bl 800bc14 + data, sizeof(data), HAL_MAX_DELAY); + + return (rv != HAL_OK); +} + 80075c0: 3800 subs r0, #0 + 80075c2: bf18 it ne + 80075c4: 2001 movne r0, #1 + 80075c6: 3704 adds r7, #4 + 80075c8: 46bd mov sp, r7 + 80075ca: bdf0 pop {r4, r5, r6, r7, pc} + 80075cc: 2009e3f0 .word 0x2009e3f0 + +080075d0 : + +// rng_for_uECC() +// + static int +rng_for_uECC(uint8_t *dest, unsigned size) +{ + 80075d0: b508 push {r3, lr} + 'dest' was filled with random data, or 0 if the random data could not be generated. + The filled-in values should be either truly random, or from a cryptographically-secure PRNG. + + typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); + */ + rng_buffer(dest, size); + 80075d2: f7fb f983 bl 80028dc + + return 1; +} + 80075d6: 2001 movs r0, #1 + 80075d8: bd08 pop {r3, pc} + ... + +080075dc : +{ + 80075dc: b508 push {r3, lr} + 80075de: 4602 mov r2, r0 + CALL_CHECK(se2_write_n(0x87, NULL, data, len)); + 80075e0: b2cb uxtb r3, r1 + 80075e2: 2087 movs r0, #135 ; 0x87 + 80075e4: 2100 movs r1, #0 + 80075e6: f7ff ffc1 bl 800756c + 80075ea: b118 cbz r0, 80075f4 + 80075ec: 4802 ldr r0, [pc, #8] ; (80075f8 ) + 80075ee: 21c1 movs r1, #193 ; 0xc1 + 80075f0: f006 f9a6 bl 800d940 +} + 80075f4: bd08 pop {r3, pc} + 80075f6: bf00 nop + 80075f8: 2009e394 .word 0x2009e394 + +080075fc : +{ + 80075fc: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + HAL_StatusTypeDef rv = HAL_I2C_Master_Receive(&i2c_port, I2C_ADDR, rx, len, HAL_MAX_DELAY); + 8007600: f8df 9044 ldr.w r9, [pc, #68] ; 8007648 +{ + 8007604: 4604 mov r4, r0 + 8007606: 460d mov r5, r1 + 8007608: f44f 7696 mov.w r6, #300 ; 0x12c + HAL_StatusTypeDef rv = HAL_I2C_Master_Receive(&i2c_port, I2C_ADDR, rx, len, HAL_MAX_DELAY); + 800760c: b287 uxth r7, r0 + 800760e: f04f 38ff mov.w r8, #4294967295 ; 0xffffffff + 8007612: f8cd 8000 str.w r8, [sp] + 8007616: 463b mov r3, r7 + 8007618: 462a mov r2, r5 + 800761a: 2136 movs r1, #54 ; 0x36 + 800761c: 4648 mov r0, r9 + 800761e: f004 fbad bl 800bd7c + if(rv == HAL_OK) { + 8007622: b938 cbnz r0, 8007634 + if(rx[0] != len-1) { + 8007624: 782b ldrb r3, [r5, #0] + 8007626: 3c01 subs r4, #1 + 8007628: 42a3 cmp r3, r4 + 800762a: d10a bne.n 8007642 + return rx[1]; + 800762c: 7868 ldrb r0, [r5, #1] +} + 800762e: b003 add sp, #12 + 8007630: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + delay_ms(1); + 8007634: 2001 movs r0, #1 + 8007636: f7fc fa25 bl 8003a84 + for(int tries=0; tries<300; tries++) { + 800763a: 3e01 subs r6, #1 + 800763c: d1e9 bne.n 8007612 + return RC_NO_ACK; + 800763e: 200f movs r0, #15 + 8007640: e7f5 b.n 800762e + return RC_WRONG_SIZE; + 8007642: 201f movs r0, #31 + 8007644: e7f3 b.n 800762e + 8007646: bf00 nop + 8007648: 2009e3f0 .word 0x2009e3f0 + +0800764c : +{ + 800764c: b507 push {r0, r1, r2, lr} + return se2_read_n(2, rx); + 800764e: 2002 movs r0, #2 + 8007650: a901 add r1, sp, #4 + 8007652: f7ff ffd3 bl 80075fc +} + 8007656: b003 add sp, #12 + 8007658: f85d fb04 ldr.w pc, [sp], #4 + +0800765c : +{ + 800765c: b507 push {r0, r1, r2, lr} + CALL_CHECK(se2_write_n(0x96, &page_num, data, 32)); + 800765e: 2320 movs r3, #32 +{ + 8007660: 460a mov r2, r1 + 8007662: f88d 0007 strb.w r0, [sp, #7] + CALL_CHECK(se2_write_n(0x96, &page_num, data, 32)); + 8007666: f10d 0107 add.w r1, sp, #7 + 800766a: 2096 movs r0, #150 ; 0x96 + 800766c: f7ff ff7e bl 800756c + 8007670: b118 cbz r0, 800767a + 8007672: 21cb movs r1, #203 ; 0xcb + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007674: 4805 ldr r0, [pc, #20] ; (800768c ) + 8007676: f006 f963 bl 800d940 + 800767a: f7ff ffe7 bl 800764c + 800767e: 28aa cmp r0, #170 ; 0xaa + 8007680: d001 beq.n 8007686 + 8007682: 21cd movs r1, #205 ; 0xcd + 8007684: e7f6 b.n 8007674 +} + 8007686: b003 add sp, #12 + 8007688: f85d fb04 ldr.w pc, [sp], #4 + 800768c: 2009e394 .word 0x2009e394 + +08007690 : + ASSERT(pubkey_num < 2); + 8007690: 2801 cmp r0, #1 +{ + 8007692: b508 push {r3, lr} + ASSERT(pubkey_num < 2); + 8007694: d902 bls.n 800769c + 8007696: 480a ldr r0, [pc, #40] ; (80076c0 ) + 8007698: f7f9 f9ce bl 8000a38 + CALL_CHECK(se2_write1(0xcb, (wpe <<6) | pubkey_num)); + 800769c: ea40 1181 orr.w r1, r0, r1, lsl #6 + 80076a0: b2c9 uxtb r1, r1 + 80076a2: 20cb movs r0, #203 ; 0xcb + 80076a4: f7ff ff2c bl 8007500 + 80076a8: b118 cbz r0, 80076b2 + 80076aa: 21d9 movs r1, #217 ; 0xd9 + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 80076ac: 4805 ldr r0, [pc, #20] ; (80076c4 ) + 80076ae: f006 f947 bl 800d940 + 80076b2: f7ff ffcb bl 800764c + 80076b6: 28aa cmp r0, #170 ; 0xaa + 80076b8: d001 beq.n 80076be + 80076ba: 21db movs r1, #219 ; 0xdb + 80076bc: e7f6 b.n 80076ac +} + 80076be: bd08 pop {r3, pc} + 80076c0: 0801046c .word 0x0801046c + 80076c4: 2009e394 .word 0x2009e394 + +080076c8 : +{ + 80076c8: b570 push {r4, r5, r6, lr} + 80076ca: b0dc sub sp, #368 ; 0x170 + 80076cc: 460d mov r5, r1 + 80076ce: f88d 0007 strb.w r0, [sp, #7] + rng_buffer(chal, sizeof(chal)); + 80076d2: 2120 movs r1, #32 + 80076d4: a802 add r0, sp, #8 +{ + 80076d6: 4616 mov r6, r2 + 80076d8: 461c mov r4, r3 + rng_buffer(chal, sizeof(chal)); + 80076da: f7fb f8ff bl 80028dc + se2_write_buffer(chal, sizeof(chal)); + 80076de: 2120 movs r1, #32 + 80076e0: a802 add r0, sp, #8 + 80076e2: f7ff ff7b bl 80075dc + CALL_CHECK(se2_write1(0xa5, (keynum<<5) | page_num)); + 80076e6: f89d 3007 ldrb.w r3, [sp, #7] + 80076ea: ea43 1146 orr.w r1, r3, r6, lsl #5 + 80076ee: b2c9 uxtb r1, r1 + 80076f0: 20a5 movs r0, #165 ; 0xa5 + 80076f2: f7ff ff05 bl 8007500 + 80076f6: b118 cbz r0, 8007700 + 80076f8: 21eb movs r1, #235 ; 0xeb + CHECK_RIGHT(se2_read_n(sizeof(check), check) == RC_SUCCESS); + 80076fa: 481e ldr r0, [pc, #120] ; (8007774 ) + 80076fc: f006 f920 bl 800d940 + 8007700: a912 add r1, sp, #72 ; 0x48 + 8007702: 2022 movs r0, #34 ; 0x22 + 8007704: f7ff ff7a bl 80075fc + 8007708: 28aa cmp r0, #170 ; 0xaa + 800770a: d001 beq.n 8007710 + 800770c: 21ee movs r1, #238 ; 0xee + 800770e: e7f4 b.n 80076fa + hmac_sha256_init(&ctx); + 8007710: a81b add r0, sp, #108 ; 0x6c + 8007712: f7fe f8bb bl 800588c + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 8007716: 4b18 ldr r3, [pc, #96] ; (8007778 ) + 8007718: 4918 ldr r1, [pc, #96] ; (800777c ) + 800771a: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 800771e: 33b0 adds r3, #176 ; 0xb0 + 8007720: 2aff cmp r2, #255 ; 0xff + 8007722: bf18 it ne + 8007724: 4619 movne r1, r3 + 8007726: a81b add r0, sp, #108 ; 0x6c + 8007728: 2208 movs r2, #8 + 800772a: 3160 adds r1, #96 ; 0x60 + 800772c: f7fe f8b4 bl 8005898 + hmac_sha256_update(&ctx, data, 32); + 8007730: 4629 mov r1, r5 + 8007732: a81b add r0, sp, #108 ; 0x6c + 8007734: 2220 movs r2, #32 + 8007736: f7fe f8af bl 8005898 + hmac_sha256_update(&ctx, chal, 32); + 800773a: a902 add r1, sp, #8 + 800773c: a81b add r0, sp, #108 ; 0x6c + 800773e: 2220 movs r2, #32 + 8007740: f7fe f8aa bl 8005898 + hmac_sha256_update(&ctx, &page_num, 1); + 8007744: f10d 0107 add.w r1, sp, #7 + 8007748: a81b add r0, sp, #108 ; 0x6c + 800774a: 2201 movs r2, #1 + 800774c: f7fe f8a4 bl 8005898 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 8007750: a81b add r0, sp, #108 ; 0x6c + 8007752: 490b ldr r1, [pc, #44] ; (8007780 ) + 8007754: 2202 movs r2, #2 + 8007756: f7fe f89f bl 8005898 + hmac_sha256_final(&ctx, secret, expect); + 800775a: aa0a add r2, sp, #40 ; 0x28 + 800775c: 4621 mov r1, r4 + 800775e: a81b add r0, sp, #108 ; 0x6c + 8007760: f7fe f8b0 bl 80058c4 + return check_equal(expect, check+2, 32); + 8007764: 2220 movs r2, #32 + 8007766: f10d 014a add.w r1, sp, #74 ; 0x4a + 800776a: a80a add r0, sp, #40 ; 0x28 + 800776c: f7fb f867 bl 800283e +} + 8007770: b05c add sp, #368 ; 0x170 + 8007772: bd70 pop {r4, r5, r6, pc} + 8007774: 2009e394 .word 0x2009e394 + 8007778: 0801c000 .word 0x0801c000 + 800777c: 2009e2b4 .word 0x2009e2b4 + 8007780: 08010ac0 .word 0x08010ac0 + +08007784 : +{ + 8007784: b570 push {r4, r5, r6, lr} + 8007786: 4604 mov r4, r0 + 8007788: b08a sub sp, #40 ; 0x28 + 800778a: 460d mov r5, r1 + CALL_CHECK(se2_write1(0x69, page_num)); + 800778c: 4601 mov r1, r0 + 800778e: 2069 movs r0, #105 ; 0x69 +{ + 8007790: 4616 mov r6, r2 + CALL_CHECK(se2_write1(0x69, page_num)); + 8007792: f7ff feb5 bl 8007500 + 8007796: b120 cbz r0, 80077a2 + 8007798: f44f 7185 mov.w r1, #266 ; 0x10a + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 800779c: 481c ldr r0, [pc, #112] ; (8007810 ) + 800779e: f006 f8cf bl 800d940 + 80077a2: a901 add r1, sp, #4 + 80077a4: 2022 movs r0, #34 ; 0x22 + 80077a6: f7ff ff29 bl 80075fc + 80077aa: 28aa cmp r0, #170 ; 0xaa + 80077ac: d002 beq.n 80077b4 + 80077ae: f240 110d movw r1, #269 ; 0x10d + 80077b2: e7f3 b.n 800779c + CHECK_RIGHT(rx[0] == 33); + 80077b4: f89d 3004 ldrb.w r3, [sp, #4] + 80077b8: 2b21 cmp r3, #33 ; 0x21 + 80077ba: d002 beq.n 80077c2 + 80077bc: f240 110f movw r1, #271 ; 0x10f + 80077c0: e7ec b.n 800779c + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 80077c2: f89d 3005 ldrb.w r3, [sp, #5] + 80077c6: 2baa cmp r3, #170 ; 0xaa + 80077c8: d002 beq.n 80077d0 + 80077ca: f44f 7188 mov.w r1, #272 ; 0x110 + 80077ce: e7e5 b.n 800779c + memcpy(data, rx+2, 32); + 80077d0: f10d 0306 add.w r3, sp, #6 + 80077d4: 462a mov r2, r5 + 80077d6: f10d 0126 add.w r1, sp, #38 ; 0x26 + 80077da: f853 0b04 ldr.w r0, [r3], #4 + 80077de: f842 0b04 str.w r0, [r2], #4 + 80077e2: 428b cmp r3, r1 + 80077e4: d1f9 bne.n 80077da + if(!verify) return; + 80077e6: b186 cbz r6, 800780a + CHECK_RIGHT(se2_verify_page(page_num, data, 0, SE2_SECRETS->pairing)); + 80077e8: 4b0a ldr r3, [pc, #40] ; (8007814 ) + 80077ea: 4a0b ldr r2, [pc, #44] ; (8007818 ) + 80077ec: f893 10b0 ldrb.w r1, [r3, #176] ; 0xb0 + 80077f0: 4b0a ldr r3, [pc, #40] ; (800781c ) + 80077f2: 4620 mov r0, r4 + 80077f4: 29ff cmp r1, #255 ; 0xff + 80077f6: bf18 it ne + 80077f8: 4613 movne r3, r2 + 80077fa: 2200 movs r2, #0 + 80077fc: 4629 mov r1, r5 + 80077fe: f7ff ff63 bl 80076c8 + 8007802: b910 cbnz r0, 800780a + 8007804: f44f 718b mov.w r1, #278 ; 0x116 + 8007808: e7c8 b.n 800779c +} + 800780a: b00a add sp, #40 ; 0x28 + 800780c: bd70 pop {r4, r5, r6, pc} + 800780e: bf00 nop + 8007810: 2009e394 .word 0x2009e394 + 8007814: 0801c000 .word 0x0801c000 + 8007818: 0801c0b0 .word 0x0801c0b0 + 800781c: 2009e2b4 .word 0x2009e2b4 + +08007820 : +{ + 8007820: b570 push {r4, r5, r6, lr} + 8007822: b0d6 sub sp, #344 ; 0x158 + 8007824: 461e mov r6, r3 + ASSERT((keynum == 0) || (keynum == 2)); + 8007826: f032 0302 bics.w r3, r2, #2 +{ + 800782a: 460c mov r4, r1 + 800782c: 4615 mov r5, r2 + 800782e: f88d 0007 strb.w r0, [sp, #7] + ASSERT((keynum == 0) || (keynum == 2)); + 8007832: d002 beq.n 800783a + 8007834: 4831 ldr r0, [pc, #196] ; (80078fc ) + 8007836: f7f9 f8ff bl 8000a38 + CALL_CHECK(se2_write1(0x4b, (keynum << 6) | page_num)); + 800783a: f89d 1007 ldrb.w r1, [sp, #7] + 800783e: ea41 1182 orr.w r1, r1, r2, lsl #6 + 8007842: b2c9 uxtb r1, r1 + 8007844: 204b movs r0, #75 ; 0x4b + 8007846: f7ff fe5b bl 8007500 + 800784a: b120 cbz r0, 8007856 + 800784c: f44f 71b3 mov.w r1, #358 ; 0x166 + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 8007850: 482b ldr r0, [pc, #172] ; (8007900 ) + 8007852: f006 f875 bl 800d940 + 8007856: a90a add r1, sp, #40 ; 0x28 + 8007858: 202a movs r0, #42 ; 0x2a + 800785a: f7ff fecf bl 80075fc + 800785e: 28aa cmp r0, #170 ; 0xaa + 8007860: d002 beq.n 8007868 + 8007862: f240 1169 movw r1, #361 ; 0x169 + 8007866: e7f3 b.n 8007850 + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 8007868: f89d 3029 ldrb.w r3, [sp, #41] ; 0x29 + 800786c: 2baa cmp r3, #170 ; 0xaa + 800786e: d002 beq.n 8007876 + 8007870: f240 116b movw r1, #363 ; 0x16b + 8007874: e7ec b.n 8007850 + memcpy(data, rx+2+8, 32); + 8007876: f10d 0332 add.w r3, sp, #50 ; 0x32 + 800787a: 4622 mov r2, r4 + 800787c: f10d 0152 add.w r1, sp, #82 ; 0x52 + 8007880: f853 0b04 ldr.w r0, [r3], #4 + 8007884: f842 0b04 str.w r0, [r2], #4 + 8007888: 428b cmp r3, r1 + 800788a: d1f9 bne.n 8007880 + hmac_sha256_init(&ctx); + 800788c: a815 add r0, sp, #84 ; 0x54 + 800788e: f7fd fffd bl 800588c + hmac_sha256_update(&ctx, chal, 8); + 8007892: 2208 movs r2, #8 + 8007894: f10d 012a add.w r1, sp, #42 ; 0x2a + 8007898: a815 add r0, sp, #84 ; 0x54 + 800789a: f7fd fffd bl 8005898 + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 800789e: 4b19 ldr r3, [pc, #100] ; (8007904 ) + 80078a0: 4919 ldr r1, [pc, #100] ; (8007908 ) + 80078a2: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80078a6: 33b0 adds r3, #176 ; 0xb0 + 80078a8: 2aff cmp r2, #255 ; 0xff + 80078aa: bf18 it ne + 80078ac: 4619 movne r1, r3 + 80078ae: 3160 adds r1, #96 ; 0x60 + 80078b0: 2208 movs r2, #8 + 80078b2: a815 add r0, sp, #84 ; 0x54 + 80078b4: f7fd fff0 bl 8005898 + hmac_sha256_update(&ctx, &page_num, 1); + 80078b8: 2201 movs r2, #1 + 80078ba: f10d 0107 add.w r1, sp, #7 + 80078be: a815 add r0, sp, #84 ; 0x54 + 80078c0: f7fd ffea bl 8005898 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 80078c4: 4911 ldr r1, [pc, #68] ; (800790c ) + 80078c6: 2202 movs r2, #2 + 80078c8: a815 add r0, sp, #84 ; 0x54 + 80078ca: f7fd ffe5 bl 8005898 + hmac_sha256_final(&ctx, secret, otp); + 80078ce: aa02 add r2, sp, #8 + 80078d0: 4631 mov r1, r6 + 80078d2: a815 add r0, sp, #84 ; 0x54 + 80078d4: f7fd fff6 bl 80058c4 + xor_mixin(data, otp, 32); + 80078d8: 2220 movs r2, #32 + 80078da: a902 add r1, sp, #8 + 80078dc: 4620 mov r0, r4 + 80078de: f7ff fe01 bl 80074e4 + CHECK_RIGHT(se2_verify_page(page_num, data, keynum, secret)); + 80078e2: f89d 0007 ldrb.w r0, [sp, #7] + 80078e6: 4633 mov r3, r6 + 80078e8: 462a mov r2, r5 + 80078ea: 4621 mov r1, r4 + 80078ec: f7ff feec bl 80076c8 + 80078f0: b910 cbnz r0, 80078f8 + 80078f2: f44f 71c0 mov.w r1, #384 ; 0x180 + 80078f6: e7ab b.n 8007850 +} + 80078f8: b056 add sp, #344 ; 0x158 + 80078fa: bd70 pop {r4, r5, r6, pc} + 80078fc: 0801046c .word 0x0801046c + 8007900: 2009e394 .word 0x2009e394 + 8007904: 0801c000 .word 0x0801c000 + 8007908: 2009e2b4 .word 0x2009e2b4 + 800790c: 08010ac0 .word 0x08010ac0 + +08007910 : +{ + 8007910: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8007914: 460e mov r6, r1 + ASSERT((keynum == 0) || (keynum == 2)); + 8007916: f032 0102 bics.w r1, r2, #2 +{ + 800791a: b0e4 sub sp, #400 ; 0x190 + 800791c: 4604 mov r4, r0 + 800791e: 4617 mov r7, r2 + 8007920: 4698 mov r8, r3 + ASSERT((keynum == 0) || (keynum == 2)); + 8007922: d002 beq.n 800792a + 8007924: 4849 ldr r0, [pc, #292] ; (8007a4c ) + 8007926: f7f9 f887 bl 8000a38 + se2_read_encrypted(page_num, old_data, keynum, secret); + 800792a: a901 add r1, sp, #4 + 800792c: f7ff ff78 bl 8007820 + uint8_t PGDV = page_num | 0x80; + 8007930: f064 037f orn r3, r4, #127 ; 0x7f + rng_buffer(&chal_check[32], 8); + 8007934: 2108 movs r1, #8 + 8007936: a821 add r0, sp, #132 ; 0x84 + uint8_t PGDV = page_num | 0x80; + 8007938: f88d 3002 strb.w r3, [sp, #2] + rng_buffer(&chal_check[32], 8); + 800793c: f7fa ffce bl 80028dc + hmac_sha256_init(&ctx); + 8007940: a823 add r0, sp, #140 ; 0x8c + 8007942: f7fd ffa3 bl 800588c + hmac_sha256_update(&ctx, &chal_check[32], 8); + 8007946: 2208 movs r2, #8 + 8007948: a921 add r1, sp, #132 ; 0x84 + 800794a: a823 add r0, sp, #140 ; 0x8c + 800794c: f7fd ffa4 bl 8005898 + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 8007950: 4b3f ldr r3, [pc, #252] ; (8007a50 ) + 8007952: 4940 ldr r1, [pc, #256] ; (8007a54 ) + 8007954: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 8007958: 33b0 adds r3, #176 ; 0xb0 + 800795a: 2aff cmp r2, #255 ; 0xff + 800795c: bf18 it ne + 800795e: 4619 movne r1, r3 + 8007960: 3160 adds r1, #96 ; 0x60 + 8007962: 2208 movs r2, #8 + 8007964: a823 add r0, sp, #140 ; 0x8c + 8007966: f7fd ff97 bl 8005898 + hmac_sha256_update(&ctx, &PGDV, 1); + 800796a: 2201 movs r2, #1 + 800796c: f10d 0102 add.w r1, sp, #2 + 8007970: a823 add r0, sp, #140 ; 0x8c + 8007972: f7fd ff91 bl 8005898 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 8007976: 4938 ldr r1, [pc, #224] ; (8007a58 ) + 8007978: 2202 movs r2, #2 + 800797a: a823 add r0, sp, #140 ; 0x8c + 800797c: f7fd ff8c bl 8005898 + ASSERT(ctx.num_pending == 19); + 8007980: 9b63 ldr r3, [sp, #396] ; 0x18c + 8007982: 2b13 cmp r3, #19 + 8007984: d1ce bne.n 8007924 + hmac_sha256_final(&ctx, secret, otp); + 8007986: aa09 add r2, sp, #36 ; 0x24 + 8007988: 4641 mov r1, r8 + 800798a: a823 add r0, sp, #140 ; 0x8c + 800798c: f7fd ff9a bl 80058c4 + memcpy(tmp, data, 32); + 8007990: 4635 mov r5, r6 + 8007992: aa11 add r2, sp, #68 ; 0x44 + 8007994: f106 0c20 add.w ip, r6, #32 + 8007998: 6828 ldr r0, [r5, #0] + 800799a: 6869 ldr r1, [r5, #4] + 800799c: 4613 mov r3, r2 + 800799e: c303 stmia r3!, {r0, r1} + 80079a0: 3508 adds r5, #8 + 80079a2: 4565 cmp r5, ip + 80079a4: 461a mov r2, r3 + 80079a6: d1f7 bne.n 8007998 + xor_mixin(tmp, otp, 32); + 80079a8: 2220 movs r2, #32 + 80079aa: a909 add r1, sp, #36 ; 0x24 + 80079ac: a811 add r0, sp, #68 ; 0x44 + 80079ae: f7ff fd99 bl 80074e4 + hmac_sha256_init(&ctx); + 80079b2: a823 add r0, sp, #140 ; 0x8c + 80079b4: f7fd ff6a bl 800588c + hmac_sha256_update(&ctx, SE2_SECRETS->romid, 8); + 80079b8: 4b25 ldr r3, [pc, #148] ; (8007a50 ) + 80079ba: 4926 ldr r1, [pc, #152] ; (8007a54 ) + 80079bc: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80079c0: 33b0 adds r3, #176 ; 0xb0 + 80079c2: 2aff cmp r2, #255 ; 0xff + 80079c4: bf18 it ne + 80079c6: 4619 movne r1, r3 + 80079c8: 3160 adds r1, #96 ; 0x60 + 80079ca: 2208 movs r2, #8 + 80079cc: a823 add r0, sp, #140 ; 0x8c + 80079ce: f7fd ff63 bl 8005898 + hmac_sha256_update(&ctx, old_data, 32); + 80079d2: 2220 movs r2, #32 + 80079d4: a901 add r1, sp, #4 + 80079d6: a823 add r0, sp, #140 ; 0x8c + 80079d8: f7fd ff5e bl 8005898 + hmac_sha256_update(&ctx, data, 32); + 80079dc: 2220 movs r2, #32 + 80079de: 4631 mov r1, r6 + 80079e0: a823 add r0, sp, #140 ; 0x8c + 80079e2: f7fd ff59 bl 8005898 + hmac_sha256_update(&ctx, &PGDV, 1); + 80079e6: 2201 movs r2, #1 + 80079e8: f10d 0102 add.w r1, sp, #2 + 80079ec: a823 add r0, sp, #140 ; 0x8c + 80079ee: f7fd ff53 bl 8005898 + hmac_sha256_update(&ctx, DEV_MANID, 2); + 80079f2: 4919 ldr r1, [pc, #100] ; (8007a58 ) + 80079f4: 2202 movs r2, #2 + 80079f6: a823 add r0, sp, #140 ; 0x8c + 80079f8: f7fd ff4e bl 8005898 + ASSERT(ctx.num_pending == 75); + 80079fc: 9b63 ldr r3, [sp, #396] ; 0x18c + 80079fe: 2b4b cmp r3, #75 ; 0x4b + 8007a00: d190 bne.n 8007924 + hmac_sha256_final(&ctx, secret, chal_check); + 8007a02: aa19 add r2, sp, #100 ; 0x64 + 8007a04: 4641 mov r1, r8 + 8007a06: a823 add r0, sp, #140 ; 0x8c + 8007a08: f7fd ff5c bl 80058c4 + se2_write_buffer(chal_check, sizeof(chal_check)); + 8007a0c: 2128 movs r1, #40 ; 0x28 + 8007a0e: a819 add r0, sp, #100 ; 0x64 + 8007a10: f7ff fde4 bl 80075dc + uint8_t pn = (keynum << 6) | page_num; + 8007a14: ea44 1487 orr.w r4, r4, r7, lsl #6 + CALL_CHECK(se2_write_n(0x99, &pn, tmp, 32)); + 8007a18: 2320 movs r3, #32 + 8007a1a: aa11 add r2, sp, #68 ; 0x44 + 8007a1c: f10d 0103 add.w r1, sp, #3 + 8007a20: 2099 movs r0, #153 ; 0x99 + uint8_t pn = (keynum << 6) | page_num; + 8007a22: f88d 4003 strb.w r4, [sp, #3] + CALL_CHECK(se2_write_n(0x99, &pn, tmp, 32)); + 8007a26: f7ff fda1 bl 800756c + 8007a2a: b120 cbz r0, 8007a36 + 8007a2c: f44f 71aa mov.w r1, #340 ; 0x154 + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007a30: 480a ldr r0, [pc, #40] ; (8007a5c ) + 8007a32: f005 ff85 bl 800d940 + 8007a36: f7ff fe09 bl 800764c + 8007a3a: 28aa cmp r0, #170 ; 0xaa + 8007a3c: d002 beq.n 8007a44 + 8007a3e: f44f 71ab mov.w r1, #342 ; 0x156 + 8007a42: e7f5 b.n 8007a30 +} + 8007a44: b064 add sp, #400 ; 0x190 + 8007a46: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + 8007a4a: bf00 nop + 8007a4c: 0801046c .word 0x0801046c + 8007a50: 0801c000 .word 0x0801c000 + 8007a54: 2009e2b4 .word 0x2009e2b4 + 8007a58: 08010ac0 .word 0x08010ac0 + 8007a5c: 2009e394 .word 0x2009e394 + +08007a60 : +{ + 8007a60: b508 push {r3, lr} + 8007a62: 4601 mov r1, r0 + CALL_CHECK(se2_write1(0xaa, page_num)); + 8007a64: 20aa movs r0, #170 ; 0xaa + 8007a66: f7ff fd4b bl 8007500 + 8007a6a: b120 cbz r0, 8007a76 + 8007a6c: 4804 ldr r0, [pc, #16] ; (8007a80 ) + 8007a6e: f240 118b movw r1, #395 ; 0x18b + 8007a72: f005 ff65 bl 800d940 +} + 8007a76: e8bd 4008 ldmia.w sp!, {r3, lr} + return se2_read1(); + 8007a7a: f7ff bde7 b.w 800764c + 8007a7e: bf00 nop + 8007a80: 2009e394 .word 0x2009e394 + +08007a84 : +{ + 8007a84: b538 push {r3, r4, r5, lr} + 8007a86: 460c mov r4, r1 + 8007a88: 4605 mov r5, r0 + if(se2_get_protection(page_num) == flags) { + 8007a8a: f7ff ffe9 bl 8007a60 + 8007a8e: 42a0 cmp r0, r4 + 8007a90: d011 beq.n 8007ab6 + CALL_CHECK(se2_write2(0xc3, page_num, flags)); + 8007a92: 4622 mov r2, r4 + 8007a94: 4629 mov r1, r5 + 8007a96: 20c3 movs r0, #195 ; 0xc3 + 8007a98: f7ff fd4c bl 8007534 + 8007a9c: b120 cbz r0, 8007aa8 + 8007a9e: f240 119b movw r1, #411 ; 0x19b + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007aa2: 4805 ldr r0, [pc, #20] ; (8007ab8 ) + 8007aa4: f005 ff4c bl 800d940 + 8007aa8: f7ff fdd0 bl 800764c + 8007aac: 28aa cmp r0, #170 ; 0xaa + 8007aae: d002 beq.n 8007ab6 + 8007ab0: f240 119d movw r1, #413 ; 0x19d + 8007ab4: e7f5 b.n 8007aa2 +} + 8007ab6: bd38 pop {r3, r4, r5, pc} + 8007ab8: 2009e394 .word 0x2009e394 + +08007abc : +{ + 8007abc: b500 push {lr} + if(setjmp(error_env)) { + 8007abe: 4812 ldr r0, [pc, #72] ; (8007b08 ) +{ + 8007ac0: b089 sub sp, #36 ; 0x24 + if(setjmp(error_env)) { + 8007ac2: f005 ff37 bl 800d934 + 8007ac6: b120 cbz r0, 8007ad2 + oled_show(screen_se2_issue); + 8007ac8: 4810 ldr r0, [pc, #64] ; (8007b0c ) + 8007aca: f7f9 fad3 bl 8001074 + LOCKUP_FOREVER(); + 8007ace: f7fc f8d3 bl 8003c78 + rng_delay(); + 8007ad2: f7fa ff19 bl 8002908 + if(rom_secrets->se2.pairing[0] == 0xff) { + 8007ad6: 4b0e ldr r3, [pc, #56] ; (8007b10 ) + 8007ad8: f893 30b0 ldrb.w r3, [r3, #176] ; 0xb0 + 8007adc: 2bff cmp r3, #255 ; 0xff + 8007ade: d00f beq.n 8007b00 + se2_read_page(PGN_ROM_OPTIONS, tmp, true); + 8007ae0: 2201 movs r2, #1 + 8007ae2: 4669 mov r1, sp + 8007ae4: 201c movs r0, #28 + 8007ae6: f7ff fe4d bl 8007784 + CHECK_RIGHT(check_equal(&tmp[24], rom_secrets->se2.romid, 8)); + 8007aea: 490a ldr r1, [pc, #40] ; (8007b14 ) + 8007aec: 2208 movs r2, #8 + 8007aee: a806 add r0, sp, #24 + 8007af0: f7fa fea5 bl 800283e + 8007af4: b920 cbnz r0, 8007b00 + 8007af6: 4804 ldr r0, [pc, #16] ; (8007b08 ) + 8007af8: f240 11b5 movw r1, #437 ; 0x1b5 + 8007afc: f005 ff20 bl 800d940 +} + 8007b00: b009 add sp, #36 ; 0x24 + 8007b02: f85d fb04 ldr.w pc, [sp], #4 + 8007b06: bf00 nop + 8007b08: 2009e394 .word 0x2009e394 + 8007b0c: 0800f3d7 .word 0x0800f3d7 + 8007b10: 0801c000 .word 0x0801c000 + 8007b14: 0801c110 .word 0x0801c110 + +08007b18 : +{ + 8007b18: b510 push {r4, lr} + if(setjmp(error_env)) fatal_mitm(); + 8007b1a: 4817 ldr r0, [pc, #92] ; (8007b78 ) +{ + 8007b1c: b088 sub sp, #32 + if(setjmp(error_env)) fatal_mitm(); + 8007b1e: f005 ff09 bl 800d934 + 8007b22: 4604 mov r4, r0 + 8007b24: b108 cbz r0, 8007b2a + 8007b26: f7f8 ff91 bl 8000a4c + uint8_t z32[32] = {0}; + 8007b2a: 221c movs r2, #28 + 8007b2c: 4601 mov r1, r0 + 8007b2e: 9000 str r0, [sp, #0] + 8007b30: a801 add r0, sp, #4 + 8007b32: f005 fef7 bl 800d924 + se2_write_page(PGN_PUBKEY_S+0, z32); + 8007b36: 4669 mov r1, sp + 8007b38: 201e movs r0, #30 + 8007b3a: f7ff fd8f bl 800765c + se2_write_page(PGN_PUBKEY_S+1, z32); + 8007b3e: 4669 mov r1, sp + 8007b40: 201f movs r0, #31 + 8007b42: f7ff fd8b bl 800765c + se2_write_buffer(z32, 32); + 8007b46: 2120 movs r1, #32 + 8007b48: 4668 mov r0, sp + 8007b4a: f7ff fd47 bl 80075dc + CALL_CHECK(se2_write2(0x3c, (2<<6), 0)); + 8007b4e: 4622 mov r2, r4 + 8007b50: 2180 movs r1, #128 ; 0x80 + 8007b52: 203c movs r0, #60 ; 0x3c + 8007b54: f7ff fcee bl 8007534 + 8007b58: b120 cbz r0, 8007b64 + 8007b5a: f240 11cd movw r1, #461 ; 0x1cd + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007b5e: 4806 ldr r0, [pc, #24] ; (8007b78 ) + 8007b60: f005 feee bl 800d940 + 8007b64: f7ff fd72 bl 800764c + 8007b68: 28aa cmp r0, #170 ; 0xaa + 8007b6a: d002 beq.n 8007b72 + 8007b6c: f44f 71e7 mov.w r1, #462 ; 0x1ce + 8007b70: e7f5 b.n 8007b5e +} + 8007b72: b008 add sp, #32 + 8007b74: bd10 pop {r4, pc} + 8007b76: bf00 nop + 8007b78: 2009e394 .word 0x2009e394 + +08007b7c : +{ + 8007b7c: b570 push {r4, r5, r6, lr} + if((setjmp(error_env))) { + 8007b7e: 485b ldr r0, [pc, #364] ; (8007cec ) +{ + 8007b80: b090 sub sp, #64 ; 0x40 + if((setjmp(error_env))) { + 8007b82: f005 fed7 bl 800d934 + 8007b86: 4604 mov r4, r0 + 8007b88: b120 cbz r0, 8007b94 + oled_show(screen_se2_issue); + 8007b8a: 4859 ldr r0, [pc, #356] ; (8007cf0 ) + 8007b8c: f7f9 fa72 bl 8001074 + LOCKUP_FOREVER(); + 8007b90: f7fc f872 bl 8003c78 + if(rom_secrets->se2.pairing[0] != 0xff) { + 8007b94: 4b57 ldr r3, [pc, #348] ; (8007cf4 ) + 8007b96: f893 10b0 ldrb.w r1, [r3, #176] ; 0xb0 + 8007b9a: 29ff cmp r1, #255 ; 0xff + 8007b9c: f040 80a0 bne.w 8007ce0 + memset(&_tbd, 0xff, sizeof(_tbd)); + 8007ba0: 4d55 ldr r5, [pc, #340] ; (8007cf8 ) + 8007ba2: 22e0 movs r2, #224 ; 0xe0 + 8007ba4: 4628 mov r0, r5 + 8007ba6: f005 febd bl 800d924 + rng_buffer(_tbd.tpin_key, 32); + 8007baa: 2120 movs r1, #32 + 8007bac: f105 0080 add.w r0, r5, #128 ; 0x80 + 8007bb0: f7fa fe94 bl 80028dc + se2_read_page(PGN_ROM_OPTIONS, tmp, false); + 8007bb4: 4622 mov r2, r4 + 8007bb6: 4669 mov r1, sp + 8007bb8: 201c movs r0, #28 + 8007bba: f7ff fde3 bl 8007784 + ASSERT(tmp[1] == 0x00); // check ANON is not set + 8007bbe: f89d 3001 ldrb.w r3, [sp, #1] + 8007bc2: b113 cbz r3, 8007bca + 8007bc4: 484d ldr r0, [pc, #308] ; (8007cfc ) + 8007bc6: f7f8 ff37 bl 8000a38 + memcpy(_tbd.romid, tmp+24, 8); + 8007bca: ab06 add r3, sp, #24 + 8007bcc: cb03 ldmia r3!, {r0, r1} + 8007bce: 6628 str r0, [r5, #96] ; 0x60 + 8007bd0: 6669 str r1, [r5, #100] ; 0x64 + rng_buffer(tmp, 32); + 8007bd2: 4668 mov r0, sp + 8007bd4: 2120 movs r1, #32 + 8007bd6: f7fa fe81 bl 80028dc + se2_write_page(PGN_SECRET_B, tmp); + 8007bda: 4669 mov r1, sp + 8007bdc: 201a movs r0, #26 + 8007bde: f7ff fd3d bl 800765c + se2_pick_keypair(0, true); + 8007be2: 2101 movs r1, #1 + 8007be4: 4620 mov r0, r4 + 8007be6: f7ff fd53 bl 8007690 + se2_read_page(PGN_PUBKEY_A, &_tbd.pubkey_A[0], false); + 8007bea: 4622 mov r2, r4 + 8007bec: f105 0120 add.w r1, r5, #32 + 8007bf0: 2010 movs r0, #16 + 8007bf2: f7ff fdc7 bl 8007784 + memset(tmp, 0, 32); + 8007bf6: 2620 movs r6, #32 + se2_read_page(PGN_PUBKEY_A+1, &_tbd.pubkey_A[32], false); + 8007bf8: 4622 mov r2, r4 + 8007bfa: f105 0140 add.w r1, r5, #64 ; 0x40 + 8007bfe: 2011 movs r0, #17 + 8007c00: f7ff fdc0 bl 8007784 + memset(tmp, 0, 32); + 8007c04: 4632 mov r2, r6 + 8007c06: 4621 mov r1, r4 + 8007c08: 4668 mov r0, sp + 8007c0a: f005 fe8b bl 800d924 + se2_write_page(PGN_PRIVKEY_B, tmp); + 8007c0e: 4669 mov r1, sp + 8007c10: 2017 movs r0, #23 + 8007c12: f7ff fd23 bl 800765c + se2_write_page(PGN_PRIVKEY_B+1, tmp); + 8007c16: 4669 mov r1, sp + 8007c18: 2018 movs r0, #24 + 8007c1a: f7ff fd1f bl 800765c + se2_write_page(PGN_PUBKEY_B, tmp); + 8007c1e: 4669 mov r1, sp + 8007c20: 2012 movs r0, #18 + 8007c22: f7ff fd1b bl 800765c + se2_write_page(PGN_PUBKEY_B+1, tmp); + 8007c26: 4669 mov r1, sp + 8007c28: 2013 movs r0, #19 + 8007c2a: f7ff fd17 bl 800765c + rng_buffer(_tbd.pairing, 32); + 8007c2e: 4631 mov r1, r6 + 8007c30: 4628 mov r0, r5 + 8007c32: f7fa fe53 bl 80028dc + } while(_tbd.pairing[0] == 0xff); + 8007c36: 782b ldrb r3, [r5, #0] + 8007c38: 2bff cmp r3, #255 ; 0xff + 8007c3a: d0f8 beq.n 8007c2e + se2_write_page(PGN_SECRET_A, _tbd.pairing); + 8007c3c: 4629 mov r1, r5 + 8007c3e: 2019 movs r0, #25 + rng_buffer(tmp, 32); + 8007c40: 466d mov r5, sp + se2_write_page(PGN_SECRET_A, _tbd.pairing); + 8007c42: f7ff fd0b bl 800765c + rng_buffer(tmp, 32); + 8007c46: 2120 movs r1, #32 + 8007c48: 4628 mov r0, r5 + 8007c4a: f7fa fe47 bl 80028dc + se2_write_page(PGN_SE2_EASY_KEY, tmp); + 8007c4e: 4629 mov r1, r5 + 8007c50: 200e movs r0, #14 + 8007c52: f7ff fd03 bl 800765c + memset(tmp, 0, 32); + 8007c56: 2220 movs r2, #32 + 8007c58: 2100 movs r1, #0 + 8007c5a: 4628 mov r0, r5 + 8007c5c: f005 fe62 bl 800d924 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007c60: 4626 mov r6, r4 + se2_write_page(pn, tmp); + 8007c62: b2f0 uxtb r0, r6 + 8007c64: 4629 mov r1, r5 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007c66: 3601 adds r6, #1 + se2_write_page(pn, tmp); + 8007c68: f7ff fcf8 bl 800765c + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007c6c: 2e0e cmp r6, #14 + 8007c6e: d1f8 bne.n 8007c62 + flash_save_se2_data(&_tbd); + 8007c70: 4821 ldr r0, [pc, #132] ; (8007cf8 ) + 8007c72: f7fa fb43 bl 80022fc + se2_set_protection(PGN_SECRET_A, PROT_WP); + 8007c76: 2102 movs r1, #2 + 8007c78: 2019 movs r0, #25 + 8007c7a: f7ff ff03 bl 8007a84 + se2_set_protection(PGN_SECRET_B, PROT_WP); + 8007c7e: 2102 movs r1, #2 + 8007c80: 201a movs r0, #26 + 8007c82: f7ff feff bl 8007a84 + se2_set_protection(PGN_PUBKEY_A, PROT_WP); + 8007c86: 2102 movs r1, #2 + 8007c88: 2010 movs r0, #16 + 8007c8a: f7ff fefb bl 8007a84 + se2_set_protection(PGN_PUBKEY_B, PROT_WP); + 8007c8e: 2102 movs r1, #2 + 8007c90: 2012 movs r0, #18 + 8007c92: f7ff fef7 bl 8007a84 + se2_set_protection(PGN_SE2_EASY_KEY, PROT_EPH); + 8007c96: 2110 movs r1, #16 + 8007c98: 4630 mov r0, r6 + 8007c9a: f7ff fef3 bl 8007a84 + se2_set_protection(pn, PROT_EPH); + 8007c9e: 2510 movs r5, #16 + 8007ca0: b2e0 uxtb r0, r4 + 8007ca2: 4629 mov r1, r5 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007ca4: 3401 adds r4, #1 + se2_set_protection(pn, PROT_EPH); + 8007ca6: f7ff feed bl 8007a84 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8007caa: 2c0e cmp r4, #14 + 8007cac: d1f8 bne.n 8007ca0 + se2_set_protection(PGN_ROM_OPTIONS, PROT_APH); // not planning to change + 8007cae: 2108 movs r1, #8 + 8007cb0: 201c movs r0, #28 + 8007cb2: f7ff fee7 bl 8007a84 + se2_read_page(PGN_DEC_COUNTER, tmp, false); + 8007cb6: 2200 movs r2, #0 + 8007cb8: a908 add r1, sp, #32 + 8007cba: 201b movs r0, #27 + 8007cbc: f7ff fd62 bl 8007784 + if(tmp[2] == 0xff) { + 8007cc0: f89d 3022 ldrb.w r3, [sp, #34] ; 0x22 + 8007cc4: 2bff cmp r3, #255 ; 0xff + 8007cc6: d10d bne.n 8007ce4 + tmp[0] = val & 0x0ff; + 8007cc8: 2380 movs r3, #128 ; 0x80 + 8007cca: f88d 3020 strb.w r3, [sp, #32] + se2_write_page(PGN_DEC_COUNTER, tmp); + 8007cce: a908 add r1, sp, #32 + tmp[1] = (val >> 8) & 0x0ff; + 8007cd0: 2300 movs r3, #0 + se2_write_page(PGN_DEC_COUNTER, tmp); + 8007cd2: 201b movs r0, #27 + tmp[1] = (val >> 8) & 0x0ff; + 8007cd4: f88d 3021 strb.w r3, [sp, #33] ; 0x21 + tmp[2] = (val >> 16) & 0x01; + 8007cd8: f88d 3022 strb.w r3, [sp, #34] ; 0x22 + se2_write_page(PGN_DEC_COUNTER, tmp); + 8007cdc: f7ff fcbe bl 800765c +} + 8007ce0: b010 add sp, #64 ; 0x40 + 8007ce2: bd70 pop {r4, r5, r6, pc} + puts("ctr set?"); // not expected, but keep going + 8007ce4: 4806 ldr r0, [pc, #24] ; (8007d00 ) + 8007ce6: f7fd f9bf bl 8005068 + 8007cea: e7f9 b.n 8007ce0 + 8007cec: 2009e394 .word 0x2009e394 + 8007cf0: 0800f3d7 .word 0x0800f3d7 + 8007cf4: 0801c000 .word 0x0801c000 + 8007cf8: 2009e2b4 .word 0x2009e2b4 + 8007cfc: 0801046c .word 0x0801046c + 8007d00: 08010aa0 .word 0x08010aa0 + +08007d04 : +{ + 8007d04: b510 push {r4, lr} + 8007d06: b08a sub sp, #40 ; 0x28 + 8007d08: 9001 str r0, [sp, #4] + if(setjmp(error_env)) fatal_mitm(); + 8007d0a: 481e ldr r0, [pc, #120] ; (8007d84 ) + 8007d0c: f005 fe12 bl 800d934 + 8007d10: b108 cbz r0, 8007d16 + 8007d12: f7f8 fe9b bl 8000a4c + ASSERT(check_all_ones(rom_secrets->se2.auth_pubkey, 64)); + 8007d16: 481c ldr r0, [pc, #112] ; (8007d88 ) + 8007d18: 2140 movs r1, #64 ; 0x40 + 8007d1a: f7fa fd77 bl 800280c + 8007d1e: b910 cbnz r0, 8007d26 + 8007d20: 481a ldr r0, [pc, #104] ; (8007d8c ) + 8007d22: f7f8 fe89 bl 8000a38 + memcpy(&_tbd, &rom_secrets->se2, sizeof(_tbd)); + 8007d26: 4c1a ldr r4, [pc, #104] ; (8007d90 ) + 8007d28: 491a ldr r1, [pc, #104] ; (8007d94 ) + 8007d2a: 22e0 movs r2, #224 ; 0xe0 + 8007d2c: 4620 mov r0, r4 + 8007d2e: f005 fdeb bl 800d908 + rng_buffer(tmp, 32); + 8007d32: 2120 movs r1, #32 + 8007d34: a802 add r0, sp, #8 + 8007d36: f7fa fdd1 bl 80028dc + se2_write_page(PGN_SE2_HARD_KEY, tmp); + 8007d3a: a902 add r1, sp, #8 + 8007d3c: 200f movs r0, #15 + 8007d3e: f7ff fc8d bl 800765c + se2_write_page(PGN_PUBKEY_C, &pubkey[0]); + 8007d42: 9901 ldr r1, [sp, #4] + 8007d44: 2014 movs r0, #20 + 8007d46: f7ff fc89 bl 800765c + se2_write_page(PGN_PUBKEY_C+1, &pubkey[32]); + 8007d4a: 9b01 ldr r3, [sp, #4] + 8007d4c: 2015 movs r0, #21 + 8007d4e: f103 0120 add.w r1, r3, #32 + 8007d52: f7ff fc83 bl 800765c + memcpy(_tbd.auth_pubkey, pubkey, 64); + 8007d56: 9b01 ldr r3, [sp, #4] + 8007d58: 34a0 adds r4, #160 ; 0xa0 + 8007d5a: f103 0240 add.w r2, r3, #64 ; 0x40 + 8007d5e: f853 1b04 ldr.w r1, [r3], #4 + 8007d62: f844 1b04 str.w r1, [r4], #4 + 8007d66: 4293 cmp r3, r2 + 8007d68: d1f9 bne.n 8007d5e + flash_save_se2_data(&_tbd); + 8007d6a: 4809 ldr r0, [pc, #36] ; (8007d90 ) + 8007d6c: f7fa fac6 bl 80022fc + se2_set_protection(PGN_SE2_HARD_KEY, PROT_WP | PROT_ECH | PROT_ECW); + 8007d70: 21c2 movs r1, #194 ; 0xc2 + 8007d72: 200f movs r0, #15 + 8007d74: f7ff fe86 bl 8007a84 + se2_set_protection(PGN_PUBKEY_C, PROT_WP | PROT_RP | PROT_AUTH); + 8007d78: 2123 movs r1, #35 ; 0x23 + 8007d7a: 2014 movs r0, #20 + 8007d7c: f7ff fe82 bl 8007a84 +} + 8007d80: b00a add sp, #40 ; 0x28 + 8007d82: bd10 pop {r4, pc} + 8007d84: 2009e394 .word 0x2009e394 + 8007d88: 0801c150 .word 0x0801c150 + 8007d8c: 0801046c .word 0x0801046c + 8007d90: 2009e2b4 .word 0x2009e2b4 + 8007d94: 0801c0b0 .word 0x0801c0b0 + +08007d98 : +{ + 8007d98: b530 push {r4, r5, lr} + 8007d9a: 4614 mov r4, r2 + ASSERT(pin_len >= 0); // 12-12 typical, but empty = blank PIN + 8007d9c: 1e0a subs r2, r1, #0 +{ + 8007d9e: b0c5 sub sp, #276 ; 0x114 + 8007da0: 4605 mov r5, r0 + ASSERT(pin_len >= 0); // 12-12 typical, but empty = blank PIN + 8007da2: da02 bge.n 8007daa + 8007da4: 4812 ldr r0, [pc, #72] ; (8007df0 ) + 8007da6: f7f8 fe47 bl 8000a38 + hmac_sha256_init(&ctx); + 8007daa: a803 add r0, sp, #12 + 8007dac: 9201 str r2, [sp, #4] + 8007dae: f7fd fd6d bl 800588c + hmac_sha256_update(&ctx, (uint8_t *)pin, pin_len); + 8007db2: 9a01 ldr r2, [sp, #4] + 8007db4: 4629 mov r1, r5 + 8007db6: a803 add r0, sp, #12 + 8007db8: f7fd fd6e bl 8005898 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, tpin_hash); + 8007dbc: 4b0d ldr r3, [pc, #52] ; (8007df4 ) + 8007dbe: 490e ldr r1, [pc, #56] ; (8007df8 ) + 8007dc0: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 8007dc4: 33b0 adds r3, #176 ; 0xb0 + 8007dc6: 2aff cmp r2, #255 ; 0xff + 8007dc8: bf18 it ne + 8007dca: 4619 movne r1, r3 + 8007dcc: a803 add r0, sp, #12 + 8007dce: 4622 mov r2, r4 + 8007dd0: 3180 adds r1, #128 ; 0x80 + 8007dd2: f7fd fd77 bl 80058c4 + sha256_single(tpin_hash, 32, tpin_hash); + 8007dd6: 4622 mov r2, r4 + 8007dd8: 4620 mov r0, r4 + 8007dda: 2120 movs r1, #32 + 8007ddc: f7fd fd36 bl 800584c + sha256_single(tpin_hash, 32, tpin_hash); + 8007de0: 4622 mov r2, r4 + 8007de2: 2120 movs r1, #32 + 8007de4: 4620 mov r0, r4 + 8007de6: f7fd fd31 bl 800584c +} + 8007dea: b045 add sp, #276 ; 0x114 + 8007dec: bd30 pop {r4, r5, pc} + 8007dee: bf00 nop + 8007df0: 0801046c .word 0x0801046c + 8007df4: 0801c000 .word 0x0801c000 + 8007df8: 2009e2b4 .word 0x2009e2b4 + +08007dfc : + +// p256_gen_keypair() +// + void +p256_gen_keypair(uint8_t privkey[32], uint8_t pubkey[64]) +{ + 8007dfc: b538 push {r3, r4, r5, lr} + 8007dfe: 4605 mov r5, r0 + uECC_set_rng(rng_for_uECC); + 8007e00: 4808 ldr r0, [pc, #32] ; (8007e24 ) +{ + 8007e02: 460c mov r4, r1 + uECC_set_rng(rng_for_uECC); + 8007e04: f7fe fedc bl 8006bc0 + + int ok = uECC_make_key(pubkey, privkey, uECC_secp256r1()); + 8007e08: f7fe fee0 bl 8006bcc + 8007e0c: 4629 mov r1, r5 + 8007e0e: 4602 mov r2, r0 + 8007e10: 4620 mov r0, r4 + 8007e12: f7fe fee3 bl 8006bdc + ASSERT(ok == 1); + 8007e16: 2801 cmp r0, #1 + 8007e18: d002 beq.n 8007e20 + 8007e1a: 4803 ldr r0, [pc, #12] ; (8007e28 ) + 8007e1c: f7f8 fe0c bl 8000a38 +} + 8007e20: bd38 pop {r3, r4, r5, pc} + 8007e22: bf00 nop + 8007e24: 080075d1 .word 0x080075d1 + 8007e28: 0801046c .word 0x0801046c + +08007e2c : + +// ps256_ecdh() +// + void +ps256_ecdh(const uint8_t pubkey[64], const uint8_t privkey[32], uint8_t result[32]) +{ + 8007e2c: b513 push {r0, r1, r4, lr} + 8007e2e: 4604 mov r4, r0 + uECC_set_rng(rng_for_uECC); + 8007e30: 4809 ldr r0, [pc, #36] ; (8007e58 ) +{ + 8007e32: e9cd 2100 strd r2, r1, [sp] + uECC_set_rng(rng_for_uECC); + 8007e36: f7fe fec3 bl 8006bc0 + + int ok = uECC_shared_secret(pubkey, privkey, result, uECC_secp256r1()); + 8007e3a: f7fe fec7 bl 8006bcc + 8007e3e: e9dd 2100 ldrd r2, r1, [sp] + 8007e42: 4603 mov r3, r0 + 8007e44: 4620 mov r0, r4 + 8007e46: f7fe ff09 bl 8006c5c + ASSERT(ok == 1); + 8007e4a: 2801 cmp r0, #1 + 8007e4c: d002 beq.n 8007e54 + 8007e4e: 4803 ldr r0, [pc, #12] ; (8007e5c ) + 8007e50: f7f8 fdf2 bl 8000a38 +} + 8007e54: b002 add sp, #8 + 8007e56: bd10 pop {r4, pc} + 8007e58: 080075d1 .word 0x080075d1 + 8007e5c: 0801046c .word 0x0801046c + +08007e60 : + +// se2_read_hard_secret() +// + static bool +se2_read_hard_secret(uint8_t hard_key[32], const uint8_t pin_digest[32]) +{ + 8007e60: b510 push {r4, lr} + 8007e62: b0e8 sub sp, #416 ; 0x1a0 + 8007e64: e9cd 0102 strd r0, r1, [sp, #8] + if(setjmp(error_env)) { + 8007e68: 4836 ldr r0, [pc, #216] ; (8007f44 ) + 8007e6a: f005 fd63 bl 800d934 + 8007e6e: 2800 cmp r0, #0 + 8007e70: d165 bne.n 8007f3e + // + SHA256_CTX ctx; + + // pick a temp key pair, share public part w/ SE2 + uint8_t tmp_privkey[32], tmp_pubkey[64]; + p256_gen_keypair(tmp_privkey, tmp_pubkey); + 8007e72: a925 add r1, sp, #148 ; 0x94 + 8007e74: a805 add r0, sp, #20 + 8007e76: f7ff ffc1 bl 8007dfc + + // - this can be mitm-ed, but we sign it next so doesn't matter + se2_write_page(PGN_PUBKEY_S, &tmp_pubkey[0]); + 8007e7a: a925 add r1, sp, #148 ; 0x94 + 8007e7c: 201e movs r0, #30 + 8007e7e: f7ff fbed bl 800765c + se2_write_page(PGN_PUBKEY_S+1, &tmp_pubkey[32]); + 8007e82: a92d add r1, sp, #180 ; 0xb4 + 8007e84: 201f movs r0, #31 + 8007e86: f7ff fbe9 bl 800765c + + // pick nonce + uint8_t chal[32+32]; + rng_buffer(chal, sizeof(chal)); + 8007e8a: 2140 movs r1, #64 ; 0x40 + 8007e8c: a835 add r0, sp, #212 ; 0xd4 + 8007e8e: f7fa fd25 bl 80028dc + se2_write_buffer(chal, sizeof(chal)); + 8007e92: 2140 movs r1, #64 ; 0x40 + 8007e94: a835 add r0, sp, #212 ; 0xd4 + 8007e96: f7ff fba1 bl 80075dc + + // md = ngu.hash.sha256s(T_pubkey + chal[0:32]) + sha256_init(&ctx); + 8007e9a: a855 add r0, sp, #340 ; 0x154 + 8007e9c: f7fd fc6e bl 800577c + sha256_update(&ctx, tmp_pubkey, 64); + 8007ea0: 2240 movs r2, #64 ; 0x40 + 8007ea2: a925 add r1, sp, #148 ; 0x94 + 8007ea4: a855 add r0, sp, #340 ; 0x154 + 8007ea6: f7fd fc77 bl 8005798 + sha256_update(&ctx, chal, 32); // only first 32 bytes + 8007eaa: 2220 movs r2, #32 + 8007eac: a935 add r1, sp, #212 ; 0xd4 + 8007eae: a855 add r0, sp, #340 ; 0x154 + 8007eb0: f7fd fc72 bl 8005798 + + uint8_t md[32]; + sha256_final(&ctx, md); + 8007eb4: a90d add r1, sp, #52 ; 0x34 + 8007eb6: a855 add r0, sp, #340 ; 0x154 + 8007eb8: f7fd fcb4 bl 8005824 + // Get that digest signed by SE1 now, and doing that requires + // the main pin, because the required slot requires auth by that key. + // - this is the critical step attackers would not be able to emulate w/o SE1 contents + // - fails here if PIN wrong + uint8_t signature[64]; + int arc = ae_sign_authed(KEYNUM_joiner_key, md, signature, KEYNUM_main_pin, pin_digest); + 8007ebc: 9b03 ldr r3, [sp, #12] + 8007ebe: 9300 str r3, [sp, #0] + 8007ec0: aa45 add r2, sp, #276 ; 0x114 + 8007ec2: 2303 movs r3, #3 + 8007ec4: a90d add r1, sp, #52 ; 0x34 + 8007ec6: 2007 movs r0, #7 + 8007ec8: f7fb f83e bl 8002f48 + CHECK_RIGHT(arc == 0); + 8007ecc: 4604 mov r4, r0 + 8007ece: b120 cbz r0, 8007eda + 8007ed0: f240 4152 movw r1, #1106 ; 0x452 + + // "Authenticate ECDSA Public Key" = 0xA8 + // cs_offset=32 ecdh_keynum=0=pubA ECDH=1 WR=0 + uint8_t param = ((32-1) << 3) | (0 << 2) | 0x2; + se2_write_n(0xA8, ¶m, signature, 64); + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007ed4: 481b ldr r0, [pc, #108] ; (8007f44 ) + 8007ed6: f005 fd33 bl 800d940 + uint8_t param = ((32-1) << 3) | (0 << 2) | 0x2; + 8007eda: 23fa movs r3, #250 ; 0xfa + 8007edc: f88d 3013 strb.w r3, [sp, #19] + se2_write_n(0xA8, ¶m, signature, 64); + 8007ee0: aa45 add r2, sp, #276 ; 0x114 + 8007ee2: 2340 movs r3, #64 ; 0x40 + 8007ee4: f10d 0113 add.w r1, sp, #19 + 8007ee8: 20a8 movs r0, #168 ; 0xa8 + 8007eea: f7ff fb3f bl 800756c + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8007eee: f7ff fbad bl 800764c + 8007ef2: 28aa cmp r0, #170 ; 0xaa + 8007ef4: d002 beq.n 8007efc + 8007ef6: f44f 618b mov.w r1, #1112 ; 0x458 + 8007efa: e7eb b.n 8007ed4 + + uint8_t shared_x[32], shared_secret[32]; + ps256_ecdh(rom_secrets->se2.pubkey_A, tmp_privkey, shared_x); + 8007efc: aa15 add r2, sp, #84 ; 0x54 + 8007efe: a905 add r1, sp, #20 + 8007f00: 4811 ldr r0, [pc, #68] ; (8007f48 ) + 8007f02: f7ff ff93 bl 8007e2c + + // shared secret S will be SHA over X of shared ECDH point + chal[32:] + // s = ngu.hash.sha256s(x + chal[32:]) + sha256_init(&ctx); + 8007f06: a855 add r0, sp, #340 ; 0x154 + 8007f08: f7fd fc38 bl 800577c + sha256_update(&ctx, shared_x, 32); + 8007f0c: 2220 movs r2, #32 + 8007f0e: a915 add r1, sp, #84 ; 0x54 + 8007f10: a855 add r0, sp, #340 ; 0x154 + 8007f12: f7fd fc41 bl 8005798 + sha256_update(&ctx, &chal[32], 32); // second half + 8007f16: 2220 movs r2, #32 + 8007f18: a93d add r1, sp, #244 ; 0xf4 + 8007f1a: a855 add r0, sp, #340 ; 0x154 + 8007f1c: f7fd fc3c bl 8005798 + sha256_final(&ctx, shared_secret); + 8007f20: a91d add r1, sp, #116 ; 0x74 + 8007f22: a855 add r0, sp, #340 ; 0x154 + 8007f24: f7fd fc7e bl 8005824 + + se2_read_encrypted(PGN_SE2_HARD_KEY, hard_key, 2, shared_secret); + 8007f28: 200f movs r0, #15 + 8007f2a: 9902 ldr r1, [sp, #8] + 8007f2c: ab1d add r3, sp, #116 ; 0x74 + 8007f2e: 2202 movs r2, #2 + 8007f30: f7ff fc76 bl 8007820 + + // CONCERN: secret "S" is retained in SE2's sram. No API to clear it. + // - but you'd need to see our copy of that value to make use of it + // - and PIN checked already to get here, so you could re-do anyway + se2_clear_volatile(); + 8007f34: f7ff fdf0 bl 8007b18 + + return false; + 8007f38: 4620 mov r0, r4 +} + 8007f3a: b068 add sp, #416 ; 0x1a0 + 8007f3c: bd10 pop {r4, pc} + return true; + 8007f3e: 2001 movs r0, #1 + 8007f40: e7fb b.n 8007f3a + 8007f42: bf00 nop + 8007f44: 2009e394 .word 0x2009e394 + 8007f48: 0801c0d0 .word 0x0801c0d0 + +08007f4c : + +// se2_calc_seed_key() +// + static bool +se2_calc_seed_key(uint8_t aes_key[32], const mcu_key_t *mcu_key, const uint8_t pin_digest[32]) +{ + 8007f4c: b570 push {r4, r5, r6, lr} + 8007f4e: b0d2 sub sp, #328 ; 0x148 + 8007f50: 4614 mov r4, r2 + // Gather key parts from all over. Combine them w/ HMAC into a AES-256 key + uint8_t se1_easy_key[32], se1_hard_key[32]; + se2_read_encrypted(PGN_SE2_EASY_KEY, se1_easy_key, 0, rom_secrets->se2.pairing); + 8007f52: 4b15 ldr r3, [pc, #84] ; (8007fa8 ) + 8007f54: 2200 movs r2, #0 +{ + 8007f56: 4605 mov r5, r0 + 8007f58: 460e mov r6, r1 + se2_read_encrypted(PGN_SE2_EASY_KEY, se1_easy_key, 0, rom_secrets->se2.pairing); + 8007f5a: 200e movs r0, #14 + 8007f5c: a901 add r1, sp, #4 + 8007f5e: f7ff fc5f bl 8007820 + + if(se2_read_hard_secret(se1_hard_key, pin_digest)) return true; + 8007f62: 4621 mov r1, r4 + 8007f64: a809 add r0, sp, #36 ; 0x24 + 8007f66: f7ff ff7b bl 8007e60 + 8007f6a: 4604 mov r4, r0 + 8007f6c: b9c8 cbnz r0, 8007fa2 + + HMAC_CTX ctx; + hmac_sha256_init(&ctx); + 8007f6e: a811 add r0, sp, #68 ; 0x44 + 8007f70: f7fd fc8c bl 800588c + hmac_sha256_update(&ctx, mcu_key->value, 32); + 8007f74: 2220 movs r2, #32 + 8007f76: 4631 mov r1, r6 + 8007f78: a811 add r0, sp, #68 ; 0x44 + 8007f7a: f7fd fc8d bl 8005898 + hmac_sha256_update(&ctx, se1_hard_key, 32); + 8007f7e: 2220 movs r2, #32 + 8007f80: a909 add r1, sp, #36 ; 0x24 + 8007f82: a811 add r0, sp, #68 ; 0x44 + 8007f84: f7fd fc88 bl 8005898 + hmac_sha256_update(&ctx, se1_easy_key, 32); + 8007f88: 2220 movs r2, #32 + 8007f8a: a901 add r1, sp, #4 + 8007f8c: a811 add r0, sp, #68 ; 0x44 + 8007f8e: f7fd fc83 bl 8005898 + + // combine them all using anther MCU key via HMAC-SHA256 + hmac_sha256_final(&ctx, rom_secrets->mcu_hmac_key, aes_key); + 8007f92: a811 add r0, sp, #68 ; 0x44 + 8007f94: 4905 ldr r1, [pc, #20] ; (8007fac ) + 8007f96: 462a mov r2, r5 + 8007f98: f7fd fc94 bl 80058c4 + hmac_sha256_init(&ctx); // clear secrets + 8007f9c: a811 add r0, sp, #68 ; 0x44 + 8007f9e: f7fd fc75 bl 800588c + + return false; +} + 8007fa2: 4620 mov r0, r4 + 8007fa4: b052 add sp, #328 ; 0x148 + 8007fa6: bd70 pop {r4, r5, r6, pc} + 8007fa8: 0801c0b0 .word 0x0801c0b0 + 8007fac: 0801c090 .word 0x0801c090 + +08007fb0 : +{ + 8007fb0: b5f0 push {r4, r5, r6, r7, lr} + if(i2c_port.Instance == I2C2) { + 8007fb2: 4e1b ldr r6, [pc, #108] ; (8008020 ) + 8007fb4: 4f1b ldr r7, [pc, #108] ; (8008024 ) + 8007fb6: 6833 ldr r3, [r6, #0] + 8007fb8: 42bb cmp r3, r7 +{ + 8007fba: b089 sub sp, #36 ; 0x24 + if(i2c_port.Instance == I2C2) { + 8007fbc: d02e beq.n 800801c + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8007fbe: 4b1a ldr r3, [pc, #104] ; (8008028 ) + GPIO_InitTypeDef setup = { + 8007fc0: 4d1a ldr r5, [pc, #104] ; (800802c ) + __HAL_RCC_GPIOB_CLK_ENABLE(); + 8007fc2: 6cda ldr r2, [r3, #76] ; 0x4c + 8007fc4: f042 0202 orr.w r2, r2, #2 + 8007fc8: 64da str r2, [r3, #76] ; 0x4c + 8007fca: 6cda ldr r2, [r3, #76] ; 0x4c + 8007fcc: f002 0202 and.w r2, r2, #2 + 8007fd0: 9201 str r2, [sp, #4] + 8007fd2: 9a01 ldr r2, [sp, #4] + __HAL_RCC_I2C2_CLK_ENABLE(); + 8007fd4: 6d9a ldr r2, [r3, #88] ; 0x58 + 8007fd6: f442 0280 orr.w r2, r2, #4194304 ; 0x400000 + 8007fda: 659a str r2, [r3, #88] ; 0x58 + 8007fdc: 6d9b ldr r3, [r3, #88] ; 0x58 + 8007fde: f403 0380 and.w r3, r3, #4194304 ; 0x400000 + 8007fe2: 9302 str r3, [sp, #8] + 8007fe4: 9b02 ldr r3, [sp, #8] + GPIO_InitTypeDef setup = { + 8007fe6: cd0f ldmia r5!, {r0, r1, r2, r3} + 8007fe8: ac03 add r4, sp, #12 + 8007fea: c40f stmia r4!, {r0, r1, r2, r3} + 8007fec: 682b ldr r3, [r5, #0] + HAL_GPIO_Init(GPIOB, &setup); + 8007fee: 4810 ldr r0, [pc, #64] ; (8008030 ) + GPIO_InitTypeDef setup = { + 8007ff0: 6023 str r3, [r4, #0] + HAL_GPIO_Init(GPIOB, &setup); + 8007ff2: a903 add r1, sp, #12 + 8007ff4: f7f9 f8fe bl 80011f4 + memset(&i2c_port, 0, sizeof(i2c_port)); + 8007ff8: 2244 movs r2, #68 ; 0x44 + 8007ffa: 2100 movs r1, #0 + 8007ffc: f106 0008 add.w r0, r6, #8 + 8008000: f005 fc90 bl 800d924 + i2c_port.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + 8008004: 2301 movs r3, #1 + 8008006: 60f3 str r3, [r6, #12] + HAL_StatusTypeDef rv = HAL_I2C_Init(&i2c_port); + 8008008: 4630 mov r0, r6 + i2c_port.Init.Timing = 0x00b03fb8; // 400khz "fast mode" in CubeMX @ 120Mhz (measured ok) + 800800a: 4b0a ldr r3, [pc, #40] ; (8008034 ) + i2c_port.Instance = I2C2; + 800800c: 6037 str r7, [r6, #0] + i2c_port.Init.Timing = 0x00b03fb8; // 400khz "fast mode" in CubeMX @ 120Mhz (measured ok) + 800800e: 6073 str r3, [r6, #4] + HAL_StatusTypeDef rv = HAL_I2C_Init(&i2c_port); + 8008010: f003 fdae bl 800bb70 + ASSERT(rv == HAL_OK); + 8008014: b110 cbz r0, 800801c + 8008016: 4808 ldr r0, [pc, #32] ; (8008038 ) + 8008018: f7f8 fd0e bl 8000a38 +} + 800801c: b009 add sp, #36 ; 0x24 + 800801e: bdf0 pop {r4, r5, r6, r7, pc} + 8008020: 2009e3f0 .word 0x2009e3f0 + 8008024: 40005800 .word 0x40005800 + 8008028: 40021000 .word 0x40021000 + 800802c: 08010aac .word 0x08010aac + 8008030: 48000400 .word 0x48000400 + 8008034: 00b03fb8 .word 0x00b03fb8 + 8008038: 0801046c .word 0x0801046c + +0800803c : +{ + 800803c: b5f0 push {r4, r5, r6, r7, lr} + 800803e: b089 sub sp, #36 ; 0x24 + se2_setup(); + 8008040: f7ff ffb6 bl 8007fb0 + if(setjmp(error_env)) fatal_mitm(); + 8008044: 480f ldr r0, [pc, #60] ; (8008084 ) + 8008046: f005 fc75 bl 800d934 + 800804a: 4604 mov r4, r0 + 800804c: b108 cbz r0, 8008052 + 800804e: f7f8 fcfd bl 8000a4c + uint8_t tmp[32] = {0}; + 8008052: 9000 str r0, [sp, #0] + 8008054: 4601 mov r1, r0 + 8008056: 221c movs r2, #28 + 8008058: a801 add r0, sp, #4 + 800805a: f005 fc63 bl 800d924 + se2_write_encrypted(pn, tmp, 0, SE2_SECRETS->pairing); + 800805e: 4f0a ldr r7, [pc, #40] ; (8008088 ) + 8008060: 4e0a ldr r6, [pc, #40] ; (800808c ) + 8008062: 4d0b ldr r5, [pc, #44] ; (8008090 ) + 8008064: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 8008068: b2e0 uxtb r0, r4 + 800806a: 2bff cmp r3, #255 ; 0xff + 800806c: bf0c ite eq + 800806e: 4633 moveq r3, r6 + 8008070: 462b movne r3, r5 + 8008072: 2200 movs r2, #0 + 8008074: 4669 mov r1, sp + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 8008076: 3401 adds r4, #1 + se2_write_encrypted(pn, tmp, 0, SE2_SECRETS->pairing); + 8008078: f7ff fc4a bl 8007910 + for(int pn=0; pn <= PGN_LAST_TRICK; pn++) { + 800807c: 2c0e cmp r4, #14 + 800807e: d1f1 bne.n 8008064 +} + 8008080: b009 add sp, #36 ; 0x24 + 8008082: bdf0 pop {r4, r5, r6, r7, pc} + 8008084: 2009e394 .word 0x2009e394 + 8008088: 0801c000 .word 0x0801c000 + 800808c: 2009e2b4 .word 0x2009e2b4 + 8008090: 0801c0b0 .word 0x0801c0b0 + +08008094 : +{ + 8008094: b5f0 push {r4, r5, r6, r7, lr} + 8008096: b087 sub sp, #28 + 8008098: e9cd 0102 strd r0, r1, [sp, #8] + if(setjmp(error_env)) fatal_mitm(); + 800809c: 4816 ldr r0, [pc, #88] ; (80080f8 ) +{ + 800809e: 9201 str r2, [sp, #4] + if(setjmp(error_env)) fatal_mitm(); + 80080a0: f005 fc48 bl 800d934 + 80080a4: b108 cbz r0, 80080aa + 80080a6: f7f8 fcd1 bl 8000a4c + se2_read_encrypted(slot_num+1, &data[0], 0, SE2_SECRETS->pairing); + 80080aa: 4f14 ldr r7, [pc, #80] ; (80080fc ) + 80080ac: 9005 str r0, [sp, #20] + se2_setup(); + 80080ae: f7ff ff7f bl 8007fb0 + se2_read_encrypted(slot_num+1, &data[0], 0, SE2_SECRETS->pairing); + 80080b2: f89d 4008 ldrb.w r4, [sp, #8] + 80080b6: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 80080ba: 4e11 ldr r6, [pc, #68] ; (8008100 ) + 80080bc: 4d11 ldr r5, [pc, #68] ; (8008104 ) + 80080be: 9a05 ldr r2, [sp, #20] + 80080c0: 9901 ldr r1, [sp, #4] + 80080c2: 9204 str r2, [sp, #16] + 80080c4: 1c60 adds r0, r4, #1 + 80080c6: 2bff cmp r3, #255 ; 0xff + 80080c8: bf0c ite eq + 80080ca: 4633 moveq r3, r6 + 80080cc: 462b movne r3, r5 + 80080ce: b2c0 uxtb r0, r0 + 80080d0: f7ff fba6 bl 8007820 + if(tc_flags & TC_XPRV_WALLET) { + 80080d4: 9b03 ldr r3, [sp, #12] + 80080d6: 051b lsls r3, r3, #20 + 80080d8: d50c bpl.n 80080f4 + se2_read_encrypted(slot_num+2, &data[32], 0, SE2_SECRETS->pairing); + 80080da: f897 30b0 ldrb.w r3, [r7, #176] ; 0xb0 + 80080de: 9901 ldr r1, [sp, #4] + 80080e0: 9a04 ldr r2, [sp, #16] + 80080e2: 3402 adds r4, #2 + 80080e4: 2bff cmp r3, #255 ; 0xff + 80080e6: bf0c ite eq + 80080e8: 4633 moveq r3, r6 + 80080ea: 462b movne r3, r5 + 80080ec: 3120 adds r1, #32 + 80080ee: b2e0 uxtb r0, r4 + 80080f0: f7ff fb96 bl 8007820 +} + 80080f4: b007 add sp, #28 + 80080f6: bdf0 pop {r4, r5, r6, r7, pc} + 80080f8: 2009e394 .word 0x2009e394 + 80080fc: 0801c000 .word 0x0801c000 + 8008100: 2009e2b4 .word 0x2009e2b4 + 8008104: 0801c0b0 .word 0x0801c0b0 + +08008108 : +{ + 8008108: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 800810c: b0fe sub sp, #504 ; 0x1f8 + 800810e: e9cd 1002 strd r1, r0, [sp, #8] + 8008112: e9cd 2300 strd r2, r3, [sp] + se2_setup(); + 8008116: f7ff ff4b bl 8007fb0 + if(setjmp(error_env)) { + 800811a: 4864 ldr r0, [pc, #400] ; (80082ac ) + 800811c: f005 fc0a bl 800d934 + 8008120: 4604 mov r4, r0 + 8008122: b138 cbz r0, 8008134 + if(!safety_mode) fatal_mitm(); + 8008124: 9b01 ldr r3, [sp, #4] + 8008126: b11b cbz r3, 8008130 + return false; + 8008128: 2000 movs r0, #0 +} + 800812a: b07e add sp, #504 ; 0x1f8 + 800812c: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if(!safety_mode) fatal_mitm(); + 8008130: f7f8 fc8c bl 8000a4c + if(!pin_len) return false; + 8008134: 9b02 ldr r3, [sp, #8] + 8008136: 2b00 cmp r3, #0 + 8008138: d0f6 beq.n 8008128 + trick_pin_hash(pin, pin_len, tpin_hash); + 800813a: 9803 ldr r0, [sp, #12] + se2_read_encrypted(pn, slots[i], 0, SE2_SECRETS->pairing); + 800813c: f8df a174 ldr.w sl, [pc, #372] ; 80082b4 + 8008140: f8df 9174 ldr.w r9, [pc, #372] ; 80082b8 + 8008144: f8df 8174 ldr.w r8, [pc, #372] ; 80082bc + trick_pin_hash(pin, pin_len, tpin_hash); + 8008148: aa06 add r2, sp, #24 + 800814a: 4619 mov r1, r3 + 800814c: f7ff fe24 bl 8007d98 + 8008150: ad0e add r5, sp, #56 ; 0x38 + 8008152: 462f mov r7, r5 + int pn = PGN_TRICK(0); + 8008154: 4626 mov r6, r4 + se2_read_encrypted(pn, slots[i], 0, SE2_SECRETS->pairing); + 8008156: f89a 30b0 ldrb.w r3, [sl, #176] ; 0xb0 + 800815a: 4639 mov r1, r7 + 800815c: 2bff cmp r3, #255 ; 0xff + 800815e: bf0c ite eq + 8008160: 464b moveq r3, r9 + 8008162: 4643 movne r3, r8 + 8008164: b2f0 uxtb r0, r6 + 8008166: 2200 movs r2, #0 + for(int i=0; ipairing); + 800816a: f7ff fb59 bl 8007820 + for(int i=0; i + se2_clear_volatile(); + 8008176: f7ff fccf bl 8007b18 + uint32_t blank = 0; + 800817a: 2700 movs r7, #0 + int found = -1; + 800817c: f04f 36ff mov.w r6, #4294967295 ; 0xffffffff + if(check_equal(here, tpin_hash, 28)) { + 8008180: f04f 091c mov.w r9, #28 + blank |= (!!check_all_zeros(here, 32)) << i; + 8008184: f04f 0820 mov.w r8, #32 + if(check_equal(here, tpin_hash, 28)) { + 8008188: 464a mov r2, r9 + 800818a: a906 add r1, sp, #24 + 800818c: 4628 mov r0, r5 + 800818e: f7fa fb56 bl 800283e + blank |= (!!check_all_zeros(here, 32)) << i; + 8008192: 4641 mov r1, r8 + if(check_equal(here, tpin_hash, 28)) { + 8008194: 2800 cmp r0, #0 + 8008196: bf18 it ne + 8008198: 4626 movne r6, r4 + blank |= (!!check_all_zeros(here, 32)) << i; + 800819a: 4628 mov r0, r5 + 800819c: f7fa fb40 bl 8002820 + 80081a0: 40a0 lsls r0, r4 + for(int i=0; i + rng_delay(); + 80081b0: f7fa fbaa bl 8002908 + memset(found_slot, 0, sizeof(trick_slot_t)); + 80081b4: 9800 ldr r0, [sp, #0] + 80081b6: 2280 movs r2, #128 ; 0x80 + 80081b8: 2100 movs r1, #0 + 80081ba: f005 fbb3 bl 800d924 + if(safety_mode) { + 80081be: 9b01 ldr r3, [sp, #4] + 80081c0: b10b cbz r3, 80081c6 + found_slot->blank_slots = blank; + 80081c2: 9b00 ldr r3, [sp, #0] + 80081c4: 65df str r7, [r3, #92] ; 0x5c + if(found >= 0) { + 80081c6: 1c72 adds r2, r6, #1 + 80081c8: d069 beq.n 800829e + found_slot->slot_num = found; + 80081ca: 9b00 ldr r3, [sp, #0] + 80081cc: 0174 lsls r4, r6, #5 + 80081ce: 601e str r6, [r3, #0] + memcpy(meta, &slots[found][28], 4); + 80081d0: ab15 add r3, sp, #84 ; 0x54 + xor_mixin(meta, &tpin_hash[28], 4); + 80081d2: 2204 movs r2, #4 + memcpy(meta, &slots[found][28], 4); + 80081d4: 591b ldr r3, [r3, r4] + 80081d6: 9305 str r3, [sp, #20] + xor_mixin(meta, &tpin_hash[28], 4); + 80081d8: a90d add r1, sp, #52 ; 0x34 + 80081da: a805 add r0, sp, #20 + 80081dc: f7ff f982 bl 80074e4 + memcpy(&found_slot->tc_flags, &meta[0], 2); + 80081e0: 9b00 ldr r3, [sp, #0] + 80081e2: f8bd 5014 ldrh.w r5, [sp, #20] + memcpy(&found_slot->tc_arg, &meta[2], 2); + 80081e6: 9a00 ldr r2, [sp, #0] + memcpy(&found_slot->tc_flags, &meta[0], 2); + 80081e8: 809d strh r5, [r3, #4] + memcpy(&found_slot->tc_arg, &meta[2], 2); + 80081ea: f8bd 3016 ldrh.w r3, [sp, #22] + 80081ee: 80d3 strh r3, [r2, #6] + if(todo & TC_WORD_WALLET) { + 80081f0: 04eb lsls r3, r5, #19 + 80081f2: d513 bpl.n 800821c + if(found+1 < NUM_TRICKS) { + 80081f4: 2e0c cmp r6, #12 + 80081f6: dc0e bgt.n 8008216 + memcpy(found_slot->xdata, &slots[found+1][0], 32); + 80081f8: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 80081fc: eb0d 0403 add.w r4, sp, r3 + 8008200: f5a4 73d0 sub.w r3, r4, #416 ; 0x1a0 + 8008204: 3208 adds r2, #8 + 8008206: f5a4 74c0 sub.w r4, r4, #384 ; 0x180 + 800820a: f853 1b04 ldr.w r1, [r3], #4 + 800820e: f842 1b04 str.w r1, [r2], #4 + 8008212: 42a3 cmp r3, r4 + 8008214: d1f9 bne.n 800820a + if(!safety_mode && todo) { + 8008216: 9b01 ldr r3, [sp, #4] + 8008218: b33b cbz r3, 800826a + 800821a: e03e b.n 800829a + } else if(todo & TC_XPRV_WALLET) { + 800821c: 052f lsls r7, r5, #20 + 800821e: d521 bpl.n 8008264 + if(found+2 < NUM_TRICKS) { + 8008220: 2e0b cmp r6, #11 + 8008222: dcf8 bgt.n 8008216 + memcpy(&found_slot->xdata[0], &slots[found+1][0], 32); + 8008224: 9900 ldr r1, [sp, #0] + 8008226: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 800822a: 446b add r3, sp + 800822c: f5a3 72d0 sub.w r2, r3, #416 ; 0x1a0 + 8008230: 3108 adds r1, #8 + 8008232: f5a3 73c0 sub.w r3, r3, #384 ; 0x180 + 8008236: f852 0b04 ldr.w r0, [r2], #4 + 800823a: f841 0b04 str.w r0, [r1], #4 + 800823e: 429a cmp r2, r3 + 8008240: d1f9 bne.n 8008236 + memcpy(&found_slot->xdata[32], &slots[found+2][0], 32); + 8008242: f504 73fc add.w r3, r4, #504 ; 0x1f8 + 8008246: 9a00 ldr r2, [sp, #0] + 8008248: eb0d 0403 add.w r4, sp, r3 + 800824c: f5a4 73c0 sub.w r3, r4, #384 ; 0x180 + 8008250: 3228 adds r2, #40 ; 0x28 + 8008252: f5a4 74b0 sub.w r4, r4, #352 ; 0x160 + 8008256: f853 1b04 ldr.w r1, [r3], #4 + 800825a: f842 1b04 str.w r1, [r2], #4 + 800825e: 42a3 cmp r3, r4 + 8008260: d1f9 bne.n 8008256 + 8008262: e7d8 b.n 8008216 + if(!safety_mode && todo) { + 8008264: 9b01 ldr r3, [sp, #4] + 8008266: b9c3 cbnz r3, 800829a + 8008268: b1bd cbz r5, 800829a + if(todo & TC_WIPE) { + 800826a: 0428 lsls r0, r5, #16 + 800826c: d50a bpl.n 8008284 + mcu_key_clear(NULL); + 800826e: 2000 movs r0, #0 + 8008270: f7fa f9b8 bl 80025e4 + if(todo == TC_WIPE) { + 8008274: f5b5 4f00 cmp.w r5, #32768 ; 0x8000 + 8008278: d104 bne.n 8008284 + oled_show(screen_wiped); + 800827a: 480d ldr r0, [pc, #52] ; (80082b0 ) + 800827c: f7f8 fefa bl 8001074 + LOCKUP_FOREVER(); + 8008280: f7fb fcfa bl 8003c78 + if(todo & TC_BRICK) { + 8008284: 0469 lsls r1, r5, #17 + 8008286: d403 bmi.n 8008290 + if(todo & TC_REBOOT) { + 8008288: 05aa lsls r2, r5, #22 + 800828a: d504 bpl.n 8008296 + NVIC_SystemReset(); + 800828c: f7ff f918 bl 80074c0 <__NVIC_SystemReset> + fast_brick(); + 8008290: f7fa fa6c bl 800276c + 8008294: e7f8 b.n 8008288 + if(todo & TC_FAKE_OUT) { + 8008296: 04ab lsls r3, r5, #18 + 8008298: d401 bmi.n 800829e + return true; + 800829a: 2001 movs r0, #1 + 800829c: e745 b.n 800812a + found_slot->slot_num = -1; + 800829e: 9a00 ldr r2, [sp, #0] + 80082a0: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 80082a4: 6013 str r3, [r2, #0] + rng_delay(); + 80082a6: f7fa fb2f bl 8002908 + 80082aa: e73d b.n 8008128 + 80082ac: 2009e394 .word 0x2009e394 + 80082b0: 08010174 .word 0x08010174 + 80082b4: 0801c000 .word 0x0801c000 + 80082b8: 2009e2b4 .word 0x2009e2b4 + 80082bc: 0801c0b0 .word 0x0801c0b0 + +080082c0 : +{ + 80082c0: b510 push {r4, lr} + 80082c2: b0a2 sub sp, #136 ; 0x88 + 80082c4: 4604 mov r4, r0 + bool is_trick = se2_test_trick_pin("!p", 2, &slot, true); + 80082c6: 2301 movs r3, #1 + 80082c8: 481a ldr r0, [pc, #104] ; (8008334 ) + 80082ca: aa02 add r2, sp, #8 + 80082cc: 2102 movs r1, #2 + 80082ce: f7ff ff1b bl 8008108 + if(!is_trick) return; + 80082d2: b368 cbz r0, 8008330 + if(num_fails >= slot.tc_arg) { + 80082d4: f8bd 300e ldrh.w r3, [sp, #14] + 80082d8: 42a3 cmp r3, r4 + 80082da: dc29 bgt.n 8008330 + if(slot.tc_flags & TC_WIPE) { + 80082dc: f9bd 300c ldrsh.w r3, [sp, #12] + 80082e0: f8bd 000c ldrh.w r0, [sp, #12] + 80082e4: 2b00 cmp r3, #0 + 80082e6: da17 bge.n 8008318 + if(slot.tc_flags & TC_BRICK) { + 80082e8: f410 4080 ands.w r0, r0, #16384 ; 0x4000 + 80082ec: d00d beq.n 800830a + const mcu_key_t *cur = mcu_key_get(&valid); + 80082ee: f10d 0007 add.w r0, sp, #7 + 80082f2: f7fa f957 bl 80025a4 + if(valid) { + 80082f6: f89d 3007 ldrb.w r3, [sp, #7] + 80082fa: b16b cbz r3, 8008318 + mcu_key_clear(cur); + 80082fc: f7fa f972 bl 80025e4 + oled_show(screen_wiped); + 8008300: 480d ldr r0, [pc, #52] ; (8008338 ) + 8008302: f7f8 feb7 bl 8001074 + LOCKUP_FOREVER(); + 8008306: f7fb fcb7 bl 8003c78 + mcu_key_clear(NULL); // does valid key check + 800830a: f7fa f96b bl 80025e4 + if(slot.tc_flags == TC_WIPE) { + 800830e: f8bd 300c ldrh.w r3, [sp, #12] + 8008312: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + 8008316: d0f3 beq.n 8008300 + if(slot.tc_flags & TC_BRICK) { + 8008318: f8bd 300c ldrh.w r3, [sp, #12] + 800831c: 045a lsls r2, r3, #17 + 800831e: d501 bpl.n 8008324 + fast_brick(); + 8008320: f7fa fa24 bl 800276c + if(slot.tc_flags & TC_REBOOT) { + 8008324: f8bd 300c ldrh.w r3, [sp, #12] + 8008328: 059b lsls r3, r3, #22 + 800832a: d501 bpl.n 8008330 + NVIC_SystemReset(); + 800832c: f7ff f8c8 bl 80074c0 <__NVIC_SystemReset> +} + 8008330: b022 add sp, #136 ; 0x88 + 8008332: bd10 pop {r4, pc} + 8008334: 08010aa9 .word 0x08010aa9 + 8008338: 08010174 .word 0x08010174 + +0800833c : +{ + 800833c: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 8008340: b094 sub sp, #80 ; 0x50 + 8008342: 9001 str r0, [sp, #4] + se2_setup(); + 8008344: f7ff fe34 bl 8007fb0 + if(setjmp(error_env)) { + 8008348: 4848 ldr r0, [pc, #288] ; (800846c ) + 800834a: f005 faf3 bl 800d934 + 800834e: 4604 mov r4, r0 + 8008350: 2800 cmp r0, #0 + 8008352: f040 8088 bne.w 8008466 + if((config->slot_num < 0) || (config->slot_num >= NUM_TRICKS) ) { + 8008356: 9b01 ldr r3, [sp, #4] + 8008358: 681b ldr r3, [r3, #0] + 800835a: 2b0d cmp r3, #13 + 800835c: d804 bhi.n 8008368 + if((config->slot_num >= NUM_TRICKS-1) && (config->tc_flags & TC_WORD_WALLET) ) { + 800835e: d106 bne.n 800836e + 8008360: 9b01 ldr r3, [sp, #4] + 8008362: 889b ldrh r3, [r3, #4] + 8008364: 04d9 lsls r1, r3, #19 + 8008366: d504 bpl.n 8008372 + return EPIN_RANGE_ERR; + 8008368: f06f 0466 mvn.w r4, #102 ; 0x66 + 800836c: e01f b.n 80083ae + if((config->slot_num >= NUM_TRICKS-2) && (config->tc_flags & TC_XPRV_WALLET) ) { + 800836e: 2b0c cmp r3, #12 + 8008370: d103 bne.n 800837a + 8008372: 9b01 ldr r3, [sp, #4] + 8008374: 889b ldrh r3, [r3, #4] + 8008376: 051a lsls r2, r3, #20 + 8008378: d4f6 bmi.n 8008368 + if(config->pin_len > sizeof(config->pin)) { + 800837a: 9b01 ldr r3, [sp, #4] + 800837c: 6d99 ldr r1, [r3, #88] ; 0x58 + 800837e: 2910 cmp r1, #16 + 8008380: d8f2 bhi.n 8008368 + if(config->blank_slots) { + 8008382: 6ddd ldr r5, [r3, #92] ; 0x5c + 8008384: b31d cbz r5, 80083ce + uint8_t zeros[32] = { 0 }; + 8008386: 2100 movs r1, #0 + 8008388: 221c movs r2, #28 + 800838a: a805 add r0, sp, #20 + 800838c: 9104 str r1, [sp, #16] + 800838e: f005 fac9 bl 800d924 + se2_write_encrypted(PGN_TRICK(i), zeros, 0, SE2_SECRETS->pairing); + 8008392: f8df 80e4 ldr.w r8, [pc, #228] ; 8008478 + 8008396: 4f36 ldr r7, [pc, #216] ; (8008470 ) + 8008398: 4e36 ldr r6, [pc, #216] ; (8008474 ) + for(int i=0; iblank_slots) { + 800839c: 9a01 ldr r2, [sp, #4] + uint32_t mask = (1 << i); + 800839e: 2301 movs r3, #1 + if(mask & config->blank_slots) { + 80083a0: 6dd2 ldr r2, [r2, #92] ; 0x5c + uint32_t mask = (1 << i); + 80083a2: 40ab lsls r3, r5 + if(mask & config->blank_slots) { + 80083a4: 4213 tst r3, r2 + 80083a6: d106 bne.n 80083b6 + for(int i=0; i +} + 80083ae: 4620 mov r0, r4 + 80083b0: b014 add sp, #80 ; 0x50 + 80083b2: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + se2_write_encrypted(PGN_TRICK(i), zeros, 0, SE2_SECRETS->pairing); + 80083b6: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + 80083ba: 2200 movs r2, #0 + 80083bc: 2bff cmp r3, #255 ; 0xff + 80083be: bf0c ite eq + 80083c0: 463b moveq r3, r7 + 80083c2: 4633 movne r3, r6 + 80083c4: a904 add r1, sp, #16 + 80083c6: b2e8 uxtb r0, r5 + 80083c8: f7ff faa2 bl 8007910 + 80083cc: e7ec b.n 80083a8 + trick_pin_hash(config->pin, config->pin_len, tpin_digest); + 80083ce: 9b01 ldr r3, [sp, #4] + se2_write_encrypted(PGN_TRICK(config->slot_num), tpin_digest, 0, SE2_SECRETS->pairing); + 80083d0: f8df 80a4 ldr.w r8, [pc, #164] ; 8008478 + 80083d4: 4f26 ldr r7, [pc, #152] ; (8008470 ) + 80083d6: 4e27 ldr r6, [pc, #156] ; (8008474 ) + trick_pin_hash(config->pin, config->pin_len, tpin_digest); + 80083d8: f103 0048 add.w r0, r3, #72 ; 0x48 + 80083dc: aa0c add r2, sp, #48 ; 0x30 + 80083de: f7ff fcdb bl 8007d98 + memcpy(&meta[0], &config->tc_flags, 2); + 80083e2: 9b01 ldr r3, [sp, #4] + 80083e4: 889b ldrh r3, [r3, #4] + 80083e6: f8ad 300c strh.w r3, [sp, #12] + memcpy(&meta[2], &config->tc_arg, 2); + 80083ea: 9b01 ldr r3, [sp, #4] + xor_mixin(&tpin_digest[28], meta, 4); + 80083ec: 2204 movs r2, #4 + memcpy(&meta[2], &config->tc_arg, 2); + 80083ee: 88db ldrh r3, [r3, #6] + 80083f0: f8ad 300e strh.w r3, [sp, #14] + xor_mixin(&tpin_digest[28], meta, 4); + 80083f4: a903 add r1, sp, #12 + 80083f6: a813 add r0, sp, #76 ; 0x4c + 80083f8: f7ff f874 bl 80074e4 + se2_write_encrypted(PGN_TRICK(config->slot_num), tpin_digest, 0, SE2_SECRETS->pairing); + 80083fc: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + 8008400: 9801 ldr r0, [sp, #4] + 8008402: 2bff cmp r3, #255 ; 0xff + 8008404: bf0c ite eq + 8008406: 463b moveq r3, r7 + 8008408: 4633 movne r3, r6 + 800840a: 7800 ldrb r0, [r0, #0] + 800840c: 462a mov r2, r5 + 800840e: a90c add r1, sp, #48 ; 0x30 + 8008410: f7ff fa7e bl 8007910 + if(config->tc_flags & (TC_WORD_WALLET | TC_XPRV_WALLET)) { + 8008414: 9b01 ldr r3, [sp, #4] + 8008416: 889b ldrh r3, [r3, #4] + 8008418: f403 53c0 and.w r3, r3, #6144 ; 0x1800 + 800841c: b9a3 cbnz r3, 8008448 + if(config->tc_flags & TC_XPRV_WALLET) { + 800841e: 9b01 ldr r3, [sp, #4] + 8008420: 889b ldrh r3, [r3, #4] + 8008422: 051b lsls r3, r3, #20 + 8008424: d5c3 bpl.n 80083ae + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 8008426: 9901 ldr r1, [sp, #4] + 0, SE2_SECRETS->pairing); + 8008428: 4b13 ldr r3, [pc, #76] ; (8008478 ) + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 800842a: f851 0b28 ldr.w r0, [r1], #40 + 0, SE2_SECRETS->pairing); + 800842e: f893 50b0 ldrb.w r5, [r3, #176] ; 0xb0 + se2_write_encrypted(PGN_TRICK(config->slot_num+2), &config->xdata[32], + 8008432: 4a10 ldr r2, [pc, #64] ; (8008474 ) + 8008434: 4b0e ldr r3, [pc, #56] ; (8008470 ) + 8008436: 3002 adds r0, #2 + 8008438: 2dff cmp r5, #255 ; 0xff + 800843a: bf18 it ne + 800843c: 4613 movne r3, r2 + 800843e: b2c0 uxtb r0, r0 + 8008440: 2200 movs r2, #0 + 8008442: f7ff fa65 bl 8007910 + 8008446: e7b2 b.n 80083ae + se2_write_encrypted(PGN_TRICK(config->slot_num+1), &config->xdata[0], + 8008448: 9901 ldr r1, [sp, #4] + 0, SE2_SECRETS->pairing); + 800844a: f898 30b0 ldrb.w r3, [r8, #176] ; 0xb0 + se2_write_encrypted(PGN_TRICK(config->slot_num+1), &config->xdata[0], + 800844e: f851 0b08 ldr.w r0, [r1], #8 + 8008452: 3001 adds r0, #1 + 8008454: 2bff cmp r3, #255 ; 0xff + 8008456: bf0c ite eq + 8008458: 463b moveq r3, r7 + 800845a: 4633 movne r3, r6 + 800845c: 462a mov r2, r5 + 800845e: b2c0 uxtb r0, r0 + 8008460: f7ff fa56 bl 8007910 + 8008464: e7db b.n 800841e + return EPIN_SE2_FAIL; + 8008466: f06f 0472 mvn.w r4, #114 ; 0x72 + 800846a: e7a0 b.n 80083ae + 800846c: 2009e394 .word 0x2009e394 + 8008470: 2009e2b4 .word 0x2009e2b4 + 8008474: 0801c0b0 .word 0x0801c0b0 + 8008478: 0801c000 .word 0x0801c000 + +0800847c : +// + bool +se2_encrypt_secret(const uint8_t secret[], int secret_len, int offset, + uint8_t main_slot[], uint8_t *check_value, + const uint8_t pin_digest[32]) +{ + 800847c: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 8008480: f5ad 7d10 sub.w sp, sp, #576 ; 0x240 + 8008484: 4699 mov r9, r3 + 8008486: 4682 mov sl, r0 + 8008488: 460f mov r7, r1 + 800848a: 4614 mov r4, r2 + 800848c: f8dd 8260 ldr.w r8, [sp, #608] ; 0x260 + se2_setup(); + 8008490: f7ff fd8e bl 8007fb0 + + bool is_valid; + const mcu_key_t *cur = mcu_key_get(&is_valid); + 8008494: f10d 000b add.w r0, sp, #11 + 8008498: f7fa f884 bl 80025a4 + + if(!is_valid) { + 800849c: f89d 300b ldrb.w r3, [sp, #11] + 80084a0: b953 cbnz r3, 80084b8 + if(!check_value) { + 80084a2: f1b8 0f00 cmp.w r8, #0 + 80084a6: d105 bne.n 80084b4 + // problem: we are not writing the check value but it would be changed. + // ie: change long secret before real secret--unlikely + return true; + 80084a8: 2501 movs r5, #1 + ctx.num_pending = 32; + aes_done(&ctx, check_value, 32, aes_key, nonce); + } + + return false; +} + 80084aa: 4628 mov r0, r5 + 80084ac: f50d 7d10 add.w sp, sp, #576 ; 0x240 + 80084b0: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + cur = mcu_key_pick(); + 80084b4: f7fa f8de bl 8002674 + if(se2_calc_seed_key(aes_key, cur, pin_digest)) return true; + 80084b8: 4601 mov r1, r0 + 80084ba: 9a99 ldr r2, [sp, #612] ; 0x264 + 80084bc: a807 add r0, sp, #28 + 80084be: f7ff fd45 bl 8007f4c + 80084c2: 4605 mov r5, r0 + 80084c4: 2800 cmp r0, #0 + 80084c6: d1ef bne.n 80084a8 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80084c8: 4b16 ldr r3, [pc, #88] ; (8008524 ) + 80084ca: cb0f ldmia r3, {r0, r1, r2, r3} + 80084cc: ae03 add r6, sp, #12 + 80084ce: 46b4 mov ip, r6 + 80084d0: e8ac 0007 stmia.w ip!, {r0, r1, r2} + nonce[15] = offset / AES_BLOCK_SIZE; + 80084d4: 2c00 cmp r4, #0 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80084d6: f82c 3b02 strh.w r3, [ip], #2 + nonce[15] = offset / AES_BLOCK_SIZE; + 80084da: bfb8 it lt + 80084dc: 340f addlt r4, #15 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 80084de: 0c1b lsrs r3, r3, #16 + 80084e0: f88c 3000 strb.w r3, [ip] + aes_init(&ctx); + 80084e4: a80f add r0, sp, #60 ; 0x3c + nonce[15] = offset / AES_BLOCK_SIZE; + 80084e6: 1124 asrs r4, r4, #4 + 80084e8: 73f4 strb r4, [r6, #15] + aes_init(&ctx); + 80084ea: f000 f92b bl 8008744 + aes_add(&ctx, secret, secret_len); + 80084ee: 463a mov r2, r7 + 80084f0: 4651 mov r1, sl + 80084f2: a80f add r0, sp, #60 ; 0x3c + 80084f4: f000 f92c bl 8008750 + aes_done(&ctx, main_slot, secret_len, aes_key, nonce); + 80084f8: 9600 str r6, [sp, #0] + 80084fa: ab07 add r3, sp, #28 + 80084fc: 463a mov r2, r7 + 80084fe: 4649 mov r1, r9 + 8008500: a80f add r0, sp, #60 ; 0x3c + 8008502: f000 f93b bl 800877c + if(check_value) { + 8008506: f1b8 0f00 cmp.w r8, #0 + 800850a: d0ce beq.n 80084aa + aes_init(&ctx); + 800850c: a80f add r0, sp, #60 ; 0x3c + 800850e: f000 f919 bl 8008744 + ctx.num_pending = 32; + 8008512: 2220 movs r2, #32 + aes_done(&ctx, check_value, 32, aes_key, nonce); + 8008514: 9600 str r6, [sp, #0] + 8008516: ab07 add r3, sp, #28 + 8008518: 4641 mov r1, r8 + 800851a: a80f add r0, sp, #60 ; 0x3c + ctx.num_pending = 32; + 800851c: 928f str r2, [sp, #572] ; 0x23c + aes_done(&ctx, check_value, 32, aes_key, nonce); + 800851e: f000 f92d bl 800877c + 8008522: e7c2 b.n 80084aa + 8008524: 0801c090 .word 0x0801c090 + +08008528 : +// + void +se2_decrypt_secret(uint8_t secret[], int secret_len, int offset, + const uint8_t main_slot[], const uint8_t *check_value, + const uint8_t pin_digest[32], bool *is_valid) +{ + 8008528: b530 push {r4, r5, lr} + 800852a: f5ad 7d1f sub.w sp, sp, #636 ; 0x27c + 800852e: e9cd 2306 strd r2, r3, [sp, #24] + 8008532: 9005 str r0, [sp, #20] + 8008534: 9103 str r1, [sp, #12] + se2_setup(); + 8008536: f7ff fd3b bl 8007fb0 + + const mcu_key_t *cur = mcu_key_get(is_valid); + 800853a: 98a4 ldr r0, [sp, #656] ; 0x290 + 800853c: f7fa f832 bl 80025a4 + if(!*is_valid) { + 8008540: 9ba4 ldr r3, [sp, #656] ; 0x290 + const mcu_key_t *cur = mcu_key_get(is_valid); + 8008542: 9004 str r0, [sp, #16] + if(!*is_valid) { + 8008544: 781b ldrb r3, [r3, #0] + 8008546: b133 cbz r3, 8008556 + // no key set? won't be able to decrypt. + return; + } + + int line_num; + if((line_num = setjmp(error_env))) { + 8008548: 4825 ldr r0, [pc, #148] ; (80085e0 ) + 800854a: f005 f9f3 bl 800d934 + 800854e: b128 cbz r0, 800855c + // internal failures / broken i2c buses will come here + *is_valid = false; + 8008550: 9aa4 ldr r2, [sp, #656] ; 0x290 + 8008552: 2300 movs r3, #0 + 8008554: 7013 strb r3, [r2, #0] + + // decrypt the real data + aes_init(&ctx); + aes_add(&ctx, main_slot, secret_len); + aes_done(&ctx, secret, secret_len, aes_key, nonce); +} + 8008556: f50d 7d1f add.w sp, sp, #636 ; 0x27c + 800855a: bd30 pop {r4, r5, pc} + if(se2_calc_seed_key(aes_key, cur, pin_digest)) { + 800855c: 9aa3 ldr r2, [sp, #652] ; 0x28c + 800855e: 9904 ldr r1, [sp, #16] + 8008560: a80d add r0, sp, #52 ; 0x34 + 8008562: f7ff fcf3 bl 8007f4c + 8008566: 2800 cmp r0, #0 + 8008568: d1f2 bne.n 8008550 + memcpy(nonce, rom_secrets->mcu_hmac_key, sizeof(nonce)-1); + 800856a: 4b1e ldr r3, [pc, #120] ; (80085e4 ) + 800856c: cb0f ldmia r3, {r0, r1, r2, r3} + 800856e: ad09 add r5, sp, #36 ; 0x24 + 8008570: 462c mov r4, r5 + 8008572: c407 stmia r4!, {r0, r1, r2} + 8008574: f824 3b02 strh.w r3, [r4], #2 + 8008578: 0c1b lsrs r3, r3, #16 + 800857a: 7023 strb r3, [r4, #0] + nonce[15] = offset / AES_BLOCK_SIZE; + 800857c: 9b06 ldr r3, [sp, #24] + 800857e: 2b00 cmp r3, #0 + 8008580: bfb8 it lt + 8008582: 330f addlt r3, #15 + 8008584: 111b asrs r3, r3, #4 + 8008586: 73eb strb r3, [r5, #15] + if(check_value) { + 8008588: 9ba2 ldr r3, [sp, #648] ; 0x288 + 800858a: b1bb cbz r3, 80085bc + aes_init(&ctx); + 800858c: a81d add r0, sp, #116 ; 0x74 + 800858e: f000 f8d9 bl 8008744 + aes_add(&ctx, check_value, 32); + 8008592: 99a2 ldr r1, [sp, #648] ; 0x288 + 8008594: 2220 movs r2, #32 + 8008596: a81d add r0, sp, #116 ; 0x74 + 8008598: f000 f8da bl 8008750 + aes_done(&ctx, got, 32, aes_key, nonce); + 800859c: ab09 add r3, sp, #36 ; 0x24 + 800859e: 9300 str r3, [sp, #0] + 80085a0: a915 add r1, sp, #84 ; 0x54 + 80085a2: a81d add r0, sp, #116 ; 0x74 + 80085a4: ab0d add r3, sp, #52 ; 0x34 + 80085a6: 2220 movs r2, #32 + 80085a8: f000 f8e8 bl 800877c + if(!check_all_zeros(got, 32)) { + 80085ac: 2120 movs r1, #32 + 80085ae: a815 add r0, sp, #84 ; 0x54 + 80085b0: f7fa f936 bl 8002820 + 80085b4: b910 cbnz r0, 80085bc + *is_valid = false; + 80085b6: 9ba4 ldr r3, [sp, #656] ; 0x290 + 80085b8: 7018 strb r0, [r3, #0] + return; + 80085ba: e7cc b.n 8008556 + aes_init(&ctx); + 80085bc: a81d add r0, sp, #116 ; 0x74 + 80085be: f000 f8c1 bl 8008744 + aes_add(&ctx, main_slot, secret_len); + 80085c2: 9a03 ldr r2, [sp, #12] + 80085c4: 9907 ldr r1, [sp, #28] + 80085c6: a81d add r0, sp, #116 ; 0x74 + 80085c8: f000 f8c2 bl 8008750 + aes_done(&ctx, secret, secret_len, aes_key, nonce); + 80085cc: ab09 add r3, sp, #36 ; 0x24 + 80085ce: 9300 str r3, [sp, #0] + 80085d0: 9a03 ldr r2, [sp, #12] + 80085d2: 9905 ldr r1, [sp, #20] + 80085d4: ab0d add r3, sp, #52 ; 0x34 + 80085d6: a81d add r0, sp, #116 ; 0x74 + 80085d8: f000 f8d0 bl 800877c + 80085dc: e7bb b.n 8008556 + 80085de: bf00 nop + 80085e0: 2009e394 .word 0x2009e394 + 80085e4: 0801c090 .word 0x0801c090 + +080085e8 : +// +// Hash up a PIN code for login attempt: to tie it into SE2's contents. +// + void +se2_pin_hash(uint8_t digest_io[32], uint32_t purpose) +{ + 80085e8: b5f0 push {r4, r5, r6, r7, lr} + if(purpose != PIN_PURPOSE_NORMAL) { + 80085ea: 4b41 ldr r3, [pc, #260] ; (80086f0 ) +{ + 80085ec: b0d5 sub sp, #340 ; 0x154 + if(purpose != PIN_PURPOSE_NORMAL) { + 80085ee: 4299 cmp r1, r3 +{ + 80085f0: e9cd 0100 strd r0, r1, [sp] + if(purpose != PIN_PURPOSE_NORMAL) { + 80085f4: d17a bne.n 80086ec + // do nothing except for real PIN case (ie. not for prefix words) + return; + } + + se2_setup(); + 80085f6: f7ff fcdb bl 8007fb0 + if((setjmp(error_env))) { + 80085fa: 483e ldr r0, [pc, #248] ; (80086f4 ) + 80085fc: f005 f99a bl 800d934 + 8008600: 4604 mov r4, r0 + 8008602: b120 cbz r0, 800860e + oled_show(screen_se2_issue); + 8008604: 483c ldr r0, [pc, #240] ; (80086f8 ) + 8008606: f7f8 fd35 bl 8001074 + + LOCKUP_FOREVER(); + 800860a: f7fb fb35 bl 8003c78 + uint8_t rx[34]; // 2 bytes of len+status, then 32 bytes of data + uint8_t tmp[32]; + HMAC_CTX ctx; + + // HMAC(key=tpin_key, msg=given hash so far) + hmac_sha256_init(&ctx); + 800860e: a813 add r0, sp, #76 ; 0x4c + 8008610: f7fd f93c bl 800588c + hmac_sha256_update(&ctx, digest_io, 32); + 8008614: 9900 ldr r1, [sp, #0] + 8008616: 2220 movs r2, #32 + 8008618: a813 add r0, sp, #76 ; 0x4c + 800861a: f7fd f93d bl 8005898 + hmac_sha256_update(&ctx, (uint8_t *)&purpose, 4); + 800861e: 2204 movs r2, #4 + 8008620: eb0d 0102 add.w r1, sp, r2 + 8008624: a813 add r0, sp, #76 ; 0x4c + 8008626: f7fd f937 bl 8005898 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, tmp); + 800862a: 4b34 ldr r3, [pc, #208] ; (80086fc ) + 800862c: 4934 ldr r1, [pc, #208] ; (8008700 ) + 800862e: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 8008632: 33b0 adds r3, #176 ; 0xb0 + 8008634: 2aff cmp r2, #255 ; 0xff + 8008636: bf18 it ne + 8008638: 4619 movne r1, r3 + 800863a: 3180 adds r1, #128 ; 0x80 + 800863c: aa02 add r2, sp, #8 + 800863e: a813 add r0, sp, #76 ; 0x4c + 8008640: f7fd f940 bl 80058c4 + + // NOTE: exposed as cleartext here + se2_write_buffer(tmp, 32); + 8008644: 2120 movs r1, #32 + 8008646: a802 add r0, sp, #8 + 8008648: f7fe ffc8 bl 80075dc + 800864c: 25aa movs r5, #170 ; 0xaa + se2_write_buffer(rx+2, 32); + } + + // HMAC(key=secret-B, msg=consts+easy_key+buffer+consts) + // - result put in secret-S (ram) + CALL_CHECK(se2_write2(0x3c, (2<<6) | (1<<4) | PGN_SE2_EASY_KEY, 0)); + 800864e: 269e movs r6, #158 ; 0x9e + 8008650: 273c movs r7, #60 ; 0x3c + 8008652: 4622 mov r2, r4 + 8008654: 4631 mov r1, r6 + 8008656: 4638 mov r0, r7 + 8008658: f7fe ff6c bl 8007534 + 800865c: b150 cbz r0, 8008674 + 800865e: f240 511d movw r1, #1309 ; 0x51d + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8008662: 4824 ldr r0, [pc, #144] ; (80086f4 ) + 8008664: f005 f96c bl 800d940 + se2_write_buffer(rx+2, 32); + 8008668: 2120 movs r1, #32 + 800866a: f10d 002a add.w r0, sp, #42 ; 0x2a + 800866e: f7fe ffb5 bl 80075dc + 8008672: e7ee b.n 8008652 + CHECK_RIGHT(se2_read1() == RC_SUCCESS); + 8008674: f7fe ffea bl 800764c + 8008678: 28aa cmp r0, #170 ; 0xaa + 800867a: d002 beq.n 8008682 + 800867c: f240 511e movw r1, #1310 ; 0x51e + 8008680: e7ef b.n 8008662 + + // HMAC(key=S, msg=counter+junk), so we have something to read out + // - not 100% clear what contents of 'buffer' are here, but seems + // to be deterministic and unchanged from prev command + CALL_CHECK(se2_write1(0xa5, (2<<5) | PGN_DEC_COUNTER)); + 8008682: 215b movs r1, #91 ; 0x5b + 8008684: 20a5 movs r0, #165 ; 0xa5 + 8008686: f7fe ff3b bl 8007500 + 800868a: b110 cbz r0, 8008692 + 800868c: f240 5123 movw r1, #1315 ; 0x523 + 8008690: e7e7 b.n 8008662 + + CHECK_RIGHT(se2_read_n(sizeof(rx), rx) == RC_SUCCESS); + 8008692: a90a add r1, sp, #40 ; 0x28 + 8008694: 2022 movs r0, #34 ; 0x22 + 8008696: f7fe ffb1 bl 80075fc + 800869a: 28aa cmp r0, #170 ; 0xaa + 800869c: d002 beq.n 80086a4 + 800869e: f240 5125 movw r1, #1317 ; 0x525 + 80086a2: e7de b.n 8008662 + CHECK_RIGHT(rx[1] == RC_SUCCESS); + 80086a4: f89d 3029 ldrb.w r3, [sp, #41] ; 0x29 + 80086a8: 2baa cmp r3, #170 ; 0xaa + 80086aa: d002 beq.n 80086b2 + 80086ac: f240 5126 movw r1, #1318 ; 0x526 + 80086b0: e7d7 b.n 8008662 + for(int i=0; i + } + + // one final HMAC because we had to read cleartext from bus + hmac_sha256_init(&ctx); + 80086b6: a813 add r0, sp, #76 ; 0x4c + 80086b8: f7fd f8e8 bl 800588c + hmac_sha256_update(&ctx, rx+2, 32); + 80086bc: 2220 movs r2, #32 + 80086be: f10d 012a add.w r1, sp, #42 ; 0x2a + 80086c2: a813 add r0, sp, #76 ; 0x4c + 80086c4: f7fd f8e8 bl 8005898 + hmac_sha256_update(&ctx, digest_io, 32); + 80086c8: 9900 ldr r1, [sp, #0] + 80086ca: 2220 movs r2, #32 + 80086cc: a813 add r0, sp, #76 ; 0x4c + 80086ce: f7fd f8e3 bl 8005898 + hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, digest_io); + 80086d2: 4b0a ldr r3, [pc, #40] ; (80086fc ) + 80086d4: 490a ldr r1, [pc, #40] ; (8008700 ) + 80086d6: f893 20b0 ldrb.w r2, [r3, #176] ; 0xb0 + 80086da: 33b0 adds r3, #176 ; 0xb0 + 80086dc: 2aff cmp r2, #255 ; 0xff + 80086de: bf18 it ne + 80086e0: 4619 movne r1, r3 + 80086e2: 3180 adds r1, #128 ; 0x80 + 80086e4: 9a00 ldr r2, [sp, #0] + 80086e6: a813 add r0, sp, #76 ; 0x4c + 80086e8: f7fd f8ec bl 80058c4 +} + 80086ec: b055 add sp, #340 ; 0x154 + 80086ee: bdf0 pop {r4, r5, r6, r7, pc} + 80086f0: 334d1858 .word 0x334d1858 + 80086f4: 2009e394 .word 0x2009e394 + 80086f8: 0800f3d7 .word 0x0800f3d7 + 80086fc: 0801c000 .word 0x0801c000 + 8008700: 2009e2b4 .word 0x2009e2b4 + +08008704 : +// +// Read some random bytes, which we know cannot be MitM'ed. +// + void +se2_read_rng(uint8_t value[8]) +{ + 8008704: b500 push {lr} + 8008706: b08b sub sp, #44 ; 0x2c + 8008708: 9001 str r0, [sp, #4] + // funny business means MitM here + se2_setup(); + 800870a: f7ff fc51 bl 8007fb0 + if(setjmp(error_env)) fatal_mitm(); + 800870e: 4809 ldr r0, [pc, #36] ; (8008734 ) + 8008710: f005 f910 bl 800d934 + 8008714: b108 cbz r0, 800871a + 8008716: f7f8 f999 bl 8000a4c + + // read a field with "RPS" bytes, and verify those were read true + uint8_t tmp[32]; + se2_read_page(PGN_ROM_OPTIONS, tmp, true); + 800871a: a902 add r1, sp, #8 + 800871c: 2201 movs r2, #1 + 800871e: 201c movs r0, #28 + 8008720: f7ff f830 bl 8007784 + + memcpy(value, &tmp[4], 8); + 8008724: ab03 add r3, sp, #12 + 8008726: cb03 ldmia r3!, {r0, r1} + 8008728: 9b01 ldr r3, [sp, #4] + 800872a: 6018 str r0, [r3, #0] + 800872c: 6059 str r1, [r3, #4] +} + 800872e: b00b add sp, #44 ; 0x2c + 8008730: f85d fb04 ldr.w pc, [sp], #4 + 8008734: 2009e394 .word 0x2009e394 + +08008738 : + uint32_t rv; + + if(((uint32_t)src) & 0x3) { + memcpy(&rv, *src, 4); + } else { + rv = *(uint32_t *)(*src); + 8008738: 6803 ldr r3, [r0, #0] + 800873a: f853 2b04 ldr.w r2, [r3], #4 + } + (*src) += 4; + 800873e: 6003 str r3, [r0, #0] + + return __REV(rv); +} + 8008740: ba10 rev r0, r2 + 8008742: 4770 bx lr + +08008744 : + memset(ctx, 0, sizeof(AES_CTX)); + 8008744: f44f 7201 mov.w r2, #516 ; 0x204 + 8008748: 2100 movs r1, #0 + 800874a: f005 b8eb b.w 800d924 + ... + +08008750 : +{ + 8008750: b538 push {r3, r4, r5, lr} + 8008752: 4605 mov r5, r0 + memcpy(ctx->pending+ctx->num_pending, data_in, len); + 8008754: f8d0 0200 ldr.w r0, [r0, #512] ; 0x200 + 8008758: 4428 add r0, r5 +{ + 800875a: 4614 mov r4, r2 + memcpy(ctx->pending+ctx->num_pending, data_in, len); + 800875c: f005 f8d4 bl 800d908 + ctx->num_pending += len; + 8008760: f8d5 2200 ldr.w r2, [r5, #512] ; 0x200 + 8008764: 4422 add r2, r4 + ASSERT(ctx->num_pending < sizeof(ctx->pending)); + 8008766: f5b2 7f00 cmp.w r2, #512 ; 0x200 + ctx->num_pending += len; + 800876a: f8c5 2200 str.w r2, [r5, #512] ; 0x200 + ASSERT(ctx->num_pending < sizeof(ctx->pending)); + 800876e: d302 bcc.n 8008776 + 8008770: 4801 ldr r0, [pc, #4] ; (8008778 ) + 8008772: f7f8 f961 bl 8000a38 +} + 8008776: bd38 pop {r3, r4, r5, pc} + 8008778: 0801046c .word 0x0801046c + +0800877c : +// +// Do the decryption. +// + void +aes_done(AES_CTX *ctx, uint8_t data_out[], uint32_t len, const uint8_t key[32], const uint8_t nonce[AES_BLOCK_SIZE]) +{ + 800877c: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + 8008780: 4688 mov r8, r1 + 8008782: 4611 mov r1, r2 + ASSERT(len <= ctx->num_pending); + 8008784: f8d0 2200 ldr.w r2, [r0, #512] ; 0x200 +{ + 8008788: b085 sub sp, #20 + ASSERT(len <= ctx->num_pending); + 800878a: 428a cmp r2, r1 +{ + 800878c: f8dd 9030 ldr.w r9, [sp, #48] ; 0x30 + 8008790: 4606 mov r6, r0 + ASSERT(len <= ctx->num_pending); + 8008792: d202 bcs.n 800879a + 8008794: 4858 ldr r0, [pc, #352] ; (80088f8 ) + 8008796: f7f8 f94f bl 8000a38 + + // enable clock to block + __HAL_RCC_AES_CLK_ENABLE(); + 800879a: 4d58 ldr r5, [pc, #352] ; (80088fc ) + + // most changes have to be made w/ module disabled + AES->CR &= ~AES_CR_EN; + 800879c: 4c58 ldr r4, [pc, #352] ; (8008900 ) + __HAL_RCC_AES_CLK_ENABLE(); + 800879e: 6cea ldr r2, [r5, #76] ; 0x4c + 80087a0: f442 3280 orr.w r2, r2, #65536 ; 0x10000 + 80087a4: 64ea str r2, [r5, #76] ; 0x4c + 80087a6: 6cea ldr r2, [r5, #76] ; 0x4c + 80087a8: f402 3280 and.w r2, r2, #65536 ; 0x10000 + 80087ac: 9201 str r2, [sp, #4] + 80087ae: 9a01 ldr r2, [sp, #4] + AES->CR &= ~AES_CR_EN; + 80087b0: 6822 ldr r2, [r4, #0] + 80087b2: f022 0201 bic.w r2, r2, #1 + 80087b6: 6022 str r2, [r4, #0] + + // set the key size and operation mode + MODIFY_REG(AES->CR, AES_CR_KEYSIZE, CRYP_KEYSIZE_256B); + 80087b8: 6822 ldr r2, [r4, #0] + 80087ba: f442 2280 orr.w r2, r2, #262144 ; 0x40000 + 80087be: 6022 str r2, [r4, #0] + MODIFY_REG(AES->CR, AES_CR_DATATYPE|AES_CR_MODE|AES_CR_CHMOD, + 80087c0: 6827 ldr r7, [r4, #0] + 80087c2: f427 3780 bic.w r7, r7, #65536 ; 0x10000 + 80087c6: f027 077e bic.w r7, r7, #126 ; 0x7e + 80087ca: f047 0744 orr.w r7, r7, #68 ; 0x44 + 80087ce: 6027 str r7, [r4, #0] + CRYP_DATATYPE_8B | CRYP_ALGOMODE_ENCRYPT | CRYP_CHAINMODE_AES_CTR); + + // load key and IV values + const uint8_t *K = key; + AES->KEYR7 = word_pump_bytes(&K); + 80087d0: a802 add r0, sp, #8 + const uint8_t *K = key; + 80087d2: 9302 str r3, [sp, #8] + AES->KEYR7 = word_pump_bytes(&K); + 80087d4: f7ff ffb0 bl 8008738 + 80087d8: 63e0 str r0, [r4, #60] ; 0x3c + AES->KEYR6 = word_pump_bytes(&K); + 80087da: a802 add r0, sp, #8 + 80087dc: f7ff ffac bl 8008738 + 80087e0: 63a0 str r0, [r4, #56] ; 0x38 + AES->KEYR5 = word_pump_bytes(&K); + 80087e2: a802 add r0, sp, #8 + 80087e4: f7ff ffa8 bl 8008738 + 80087e8: 6360 str r0, [r4, #52] ; 0x34 + AES->KEYR4 = word_pump_bytes(&K); + 80087ea: a802 add r0, sp, #8 + 80087ec: f7ff ffa4 bl 8008738 + 80087f0: 6320 str r0, [r4, #48] ; 0x30 + AES->KEYR3 = word_pump_bytes(&K); + 80087f2: a802 add r0, sp, #8 + 80087f4: f7ff ffa0 bl 8008738 + 80087f8: 61e0 str r0, [r4, #28] + AES->KEYR2 = word_pump_bytes(&K); + 80087fa: a802 add r0, sp, #8 + 80087fc: f7ff ff9c bl 8008738 + 8008800: 61a0 str r0, [r4, #24] + AES->KEYR1 = word_pump_bytes(&K); + 8008802: a802 add r0, sp, #8 + 8008804: f7ff ff98 bl 8008738 + 8008808: 6160 str r0, [r4, #20] + AES->KEYR0 = word_pump_bytes(&K); + 800880a: a802 add r0, sp, #8 + 800880c: f7ff ff94 bl 8008738 + 8008810: 6120 str r0, [r4, #16] + + if(nonce) { + 8008812: f1b9 0f00 cmp.w r9, #0 + 8008816: d045 beq.n 80088a4 + const uint8_t *N = nonce; + AES->IVR3 = word_pump_bytes(&N); + 8008818: a803 add r0, sp, #12 + const uint8_t *N = nonce; + 800881a: f8cd 900c str.w r9, [sp, #12] + AES->IVR3 = word_pump_bytes(&N); + 800881e: f7ff ff8b bl 8008738 + 8008822: 62e0 str r0, [r4, #44] ; 0x2c + AES->IVR2 = word_pump_bytes(&N); + 8008824: a803 add r0, sp, #12 + 8008826: f7ff ff87 bl 8008738 + 800882a: 62a0 str r0, [r4, #40] ; 0x28 + AES->IVR1 = word_pump_bytes(&N); + 800882c: a803 add r0, sp, #12 + 800882e: f7ff ff83 bl 8008738 + 8008832: 6260 str r0, [r4, #36] ; 0x24 + AES->IVR0 = word_pump_bytes(&N); + 8008834: a803 add r0, sp, #12 + 8008836: f7ff ff7f bl 8008738 + 800883a: 6220 str r0, [r4, #32] + AES->IVR1 = 0; + AES->IVR0 = 0; // maybe should be byte-swapped one, but whatever + } + + // Enable the Peripheral + AES->CR |= AES_CR_EN; + 800883c: 4b30 ldr r3, [pc, #192] ; (8008900 ) + 800883e: 681a ldr r2, [r3, #0] + + ASSERT((((uint32_t)&ctx->pending) & 3) == 0); // safe because of special attr + 8008840: 07b0 lsls r0, r6, #30 + AES->CR |= AES_CR_EN; + 8008842: f042 0201 orr.w r2, r2, #1 + 8008846: 601a str r2, [r3, #0] + ASSERT((((uint32_t)&ctx->pending) & 3) == 0); // safe because of special attr + 8008848: d1a4 bne.n 8008794 + + uint32_t *p = (uint32_t *)ctx->pending; + for(int i=0; i < ctx->num_pending; i += 16) { + 800884a: f06f 070f mvn.w r7, #15 + 800884e: f8d6 0200 ldr.w r0, [r6, #512] ; 0x200 + 8008852: f106 0410 add.w r4, r6, #16 + 8008856: 1bbf subs r7, r7, r6 + 8008858: 193a adds r2, r7, r4 + 800885a: 4282 cmp r2, r0 + 800885c: db2b blt.n 80088b6 + *out = AES->DOUTR; out++; + *out = AES->DOUTR; out++; + *out = AES->DOUTR; + } + + memcpy(data_out, ctx->pending, len); + 800885e: 460a mov r2, r1 + 8008860: 4640 mov r0, r8 + 8008862: 4631 mov r1, r6 + 8008864: f005 f850 bl 800d908 + + memset(ctx, 0, sizeof(AES_CTX)); + 8008868: f44f 7201 mov.w r2, #516 ; 0x204 + 800886c: 2100 movs r1, #0 + 800886e: 4630 mov r0, r6 + 8008870: f005 f858 bl 800d924 + + // reset state of chip block, and leave clock off as well + __HAL_RCC_AES_CLK_ENABLE(); + 8008874: 6ceb ldr r3, [r5, #76] ; 0x4c + 8008876: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 800887a: 64eb str r3, [r5, #76] ; 0x4c + 800887c: 6ceb ldr r3, [r5, #76] ; 0x4c + 800887e: f403 3380 and.w r3, r3, #65536 ; 0x10000 + 8008882: 9303 str r3, [sp, #12] + 8008884: 9b03 ldr r3, [sp, #12] + __HAL_RCC_AES_FORCE_RESET(); + 8008886: 6aeb ldr r3, [r5, #44] ; 0x2c + 8008888: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 800888c: 62eb str r3, [r5, #44] ; 0x2c + __HAL_RCC_AES_RELEASE_RESET(); + 800888e: 6aeb ldr r3, [r5, #44] ; 0x2c + 8008890: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 8008894: 62eb str r3, [r5, #44] ; 0x2c + __HAL_RCC_AES_CLK_DISABLE(); + 8008896: 6ceb ldr r3, [r5, #76] ; 0x4c + 8008898: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 800889c: 64eb str r3, [r5, #76] ; 0x4c +} + 800889e: b005 add sp, #20 + 80088a0: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + AES->IVR3 = 0; + 80088a4: f8c4 902c str.w r9, [r4, #44] ; 0x2c + AES->IVR2 = 0; + 80088a8: f8c4 9028 str.w r9, [r4, #40] ; 0x28 + AES->IVR1 = 0; + 80088ac: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + AES->IVR0 = 0; // maybe should be byte-swapped one, but whatever + 80088b0: f8c4 9020 str.w r9, [r4, #32] + 80088b4: e7c2 b.n 800883c + AES->DINR = *p; p++; + 80088b6: f854 2c10 ldr.w r2, [r4, #-16] + 80088ba: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80088bc: f854 2c0c ldr.w r2, [r4, #-12] + 80088c0: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80088c2: f854 2c08 ldr.w r2, [r4, #-8] + 80088c6: 609a str r2, [r3, #8] + AES->DINR = *p; p++; + 80088c8: f854 2c04 ldr.w r2, [r4, #-4] + 80088cc: 609a str r2, [r3, #8] + while(HAL_IS_BIT_CLR(AES->SR, AES_SR_CCF)) { + 80088ce: 685a ldr r2, [r3, #4] + 80088d0: 07d2 lsls r2, r2, #31 + 80088d2: d5fc bpl.n 80088ce + SET_BIT(AES->CR, CRYP_CCF_CLEAR); + 80088d4: 681a ldr r2, [r3, #0] + 80088d6: f042 0280 orr.w r2, r2, #128 ; 0x80 + 80088da: 601a str r2, [r3, #0] + *out = AES->DOUTR; out++; + 80088dc: 68da ldr r2, [r3, #12] + 80088de: f844 2c10 str.w r2, [r4, #-16] + *out = AES->DOUTR; out++; + 80088e2: 68da ldr r2, [r3, #12] + 80088e4: f844 2c0c str.w r2, [r4, #-12] + *out = AES->DOUTR; out++; + 80088e8: 68da ldr r2, [r3, #12] + 80088ea: f844 2c08 str.w r2, [r4, #-8] + *out = AES->DOUTR; + 80088ee: 68da ldr r2, [r3, #12] + 80088f0: f844 2c04 str.w r2, [r4, #-4] + for(int i=0; i < ctx->num_pending; i += 16) { + 80088f4: 3410 adds r4, #16 + 80088f6: e7af b.n 8008858 + 80088f8: 0801046c .word 0x0801046c + 80088fc: 40021000 .word 0x40021000 + 8008900: 50060000 .word 0x50060000 + +08008904 : + voltage range. + * @param msirange MSI range value from RCC_MSIRANGE_0 to RCC_MSIRANGE_11 + * @retval HAL status + */ +static HAL_StatusTypeDef RCC_SetFlashLatencyFromMSIRange(uint32_t msirange) +{ + 8008904: b537 push {r0, r1, r2, r4, r5, lr} + uint32_t vos; + uint32_t latency = FLASH_LATENCY_0; /* default value 0WS */ + + if(__HAL_RCC_PWR_IS_CLK_ENABLED()) + 8008906: 4d1c ldr r5, [pc, #112] ; (8008978 ) + 8008908: 6dab ldr r3, [r5, #88] ; 0x58 + 800890a: 00da lsls r2, r3, #3 +{ + 800890c: 4604 mov r4, r0 + if(__HAL_RCC_PWR_IS_CLK_ENABLED()) + 800890e: d518 bpl.n 8008942 + { + vos = HAL_PWREx_GetVoltageRange(); + 8008910: f7fe fd68 bl 80073e4 + __HAL_RCC_PWR_CLK_ENABLE(); + vos = HAL_PWREx_GetVoltageRange(); + __HAL_RCC_PWR_CLK_DISABLE(); + } + + if(vos == PWR_REGULATOR_VOLTAGE_SCALE1) + 8008914: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8008918: d123 bne.n 8008962 + { + if(msirange > RCC_MSIRANGE_8) + 800891a: 2c80 cmp r4, #128 ; 0x80 + 800891c: d928 bls.n 8008970 + latency = FLASH_LATENCY_2; /* 2WS */ + } + else + { + /* MSI 24Mhz or 32Mhz */ + latency = FLASH_LATENCY_1; /* 1WS */ + 800891e: 2ca0 cmp r4, #160 ; 0xa0 + 8008920: bf8c ite hi + 8008922: 2002 movhi r0, #2 + 8008924: 2001 movls r0, #1 + /* else MSI < 8Mhz default FLASH_LATENCY_0 0WS */ + } +#endif + } + + __HAL_FLASH_SET_LATENCY(latency); + 8008926: 4a15 ldr r2, [pc, #84] ; (800897c ) + 8008928: 6813 ldr r3, [r2, #0] + 800892a: f023 030f bic.w r3, r3, #15 + 800892e: 4303 orrs r3, r0 + 8008930: 6013 str r3, [r2, #0] + + /* Check that the new number of wait states is taken into account to access the Flash + memory by reading the FLASH_ACR register */ + if(__HAL_FLASH_GET_LATENCY() != latency) + 8008932: 6813 ldr r3, [r2, #0] + 8008934: f003 030f and.w r3, r3, #15 + { + return HAL_ERROR; + } + + return HAL_OK; +} + 8008938: 1a18 subs r0, r3, r0 + 800893a: bf18 it ne + 800893c: 2001 movne r0, #1 + 800893e: b003 add sp, #12 + 8008940: bd30 pop {r4, r5, pc} + __HAL_RCC_PWR_CLK_ENABLE(); + 8008942: 6dab ldr r3, [r5, #88] ; 0x58 + 8008944: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 8008948: 65ab str r3, [r5, #88] ; 0x58 + 800894a: 6dab ldr r3, [r5, #88] ; 0x58 + 800894c: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 8008950: 9301 str r3, [sp, #4] + 8008952: 9b01 ldr r3, [sp, #4] + vos = HAL_PWREx_GetVoltageRange(); + 8008954: f7fe fd46 bl 80073e4 + __HAL_RCC_PWR_CLK_DISABLE(); + 8008958: 6dab ldr r3, [r5, #88] ; 0x58 + 800895a: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800895e: 65ab str r3, [r5, #88] ; 0x58 + 8008960: e7d8 b.n 8008914 + if(msirange >= RCC_MSIRANGE_8) + 8008962: 2c7f cmp r4, #127 ; 0x7f + 8008964: d806 bhi.n 8008974 + if(msirange == RCC_MSIRANGE_7) + 8008966: f1a4 0370 sub.w r3, r4, #112 ; 0x70 + 800896a: 4258 negs r0, r3 + 800896c: 4158 adcs r0, r3 + 800896e: e7da b.n 8008926 + uint32_t latency = FLASH_LATENCY_0; /* default value 0WS */ + 8008970: 2000 movs r0, #0 + 8008972: e7d8 b.n 8008926 + latency = FLASH_LATENCY_2; /* 2WS */ + 8008974: 2002 movs r0, #2 + 8008976: e7d6 b.n 8008926 + 8008978: 40021000 .word 0x40021000 + 800897c: 40022000 .word 0x40022000 + +08008980 : +{ + 8008980: b5f8 push {r3, r4, r5, r6, r7, lr} + SET_BIT(RCC->CR, RCC_CR_MSION); + 8008982: 4c32 ldr r4, [pc, #200] ; (8008a4c ) + 8008984: 6823 ldr r3, [r4, #0] + 8008986: f043 0301 orr.w r3, r3, #1 + 800898a: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800898c: f7fe fd26 bl 80073dc + 8008990: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 8008992: 6823 ldr r3, [r4, #0] + 8008994: 079b lsls r3, r3, #30 + 8008996: d543 bpl.n 8008a20 + MODIFY_REG(RCC->CR, RCC_CR_MSIRANGE, RCC_MSIRANGE_6); + 8008998: 6823 ldr r3, [r4, #0] + SystemCoreClock = MSI_VALUE; + 800899a: 4a2d ldr r2, [pc, #180] ; (8008a50 ) + MODIFY_REG(RCC->CR, RCC_CR_MSIRANGE, RCC_MSIRANGE_6); + 800899c: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 80089a0: f043 0360 orr.w r3, r3, #96 ; 0x60 + 80089a4: 6023 str r3, [r4, #0] + CLEAR_REG(RCC->CFGR); + 80089a6: 2300 movs r3, #0 + 80089a8: 60a3 str r3, [r4, #8] + SystemCoreClock = MSI_VALUE; + 80089aa: 4b2a ldr r3, [pc, #168] ; (8008a54 ) + 80089ac: 601a str r2, [r3, #0] + if(HAL_InitTick(uwTickPrio) != HAL_OK) + 80089ae: 4b2a ldr r3, [pc, #168] ; (8008a58 ) + 80089b0: 6818 ldr r0, [r3, #0] + 80089b2: f7fe fd15 bl 80073e0 + 80089b6: 4605 mov r5, r0 + 80089b8: 2800 cmp r0, #0 + 80089ba: d145 bne.n 8008a48 + tickstart = HAL_GetTick(); + 80089bc: f7fe fd0e bl 80073dc + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 80089c0: f241 3788 movw r7, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 80089c4: 4606 mov r6, r0 + while(READ_BIT(RCC->CFGR, RCC_CFGR_SWS) != RCC_CFGR_SWS_MSI) + 80089c6: 68a3 ldr r3, [r4, #8] + 80089c8: f013 0f0c tst.w r3, #12 + 80089cc: d130 bne.n 8008a30 + CLEAR_BIT(RCC->CR, RCC_CR_HSEON | RCC_CR_HSION | RCC_CR_HSIKERON| RCC_CR_HSIASFS | RCC_CR_PLLON | RCC_CR_PLLSAI1ON | RCC_CR_PLLSAI2ON); + 80089ce: 6822 ldr r2, [r4, #0] + 80089d0: 4b22 ldr r3, [pc, #136] ; (8008a5c ) + 80089d2: 4013 ands r3, r2 + 80089d4: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80089d6: f7fe fd01 bl 80073dc + 80089da: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY | RCC_CR_PLLSAI1RDY | RCC_CR_PLLSAI2RDY) != 0U) + 80089dc: 6823 ldr r3, [r4, #0] + 80089de: f013 5328 ands.w r3, r3, #704643072 ; 0x2a000000 + 80089e2: d12b bne.n 8008a3c + CLEAR_REG(RCC->PLLCFGR); + 80089e4: 60e3 str r3, [r4, #12] + SET_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN_4 ); + 80089e6: 68e2 ldr r2, [r4, #12] + 80089e8: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 80089ec: 60e2 str r2, [r4, #12] + CLEAR_REG(RCC->PLLSAI1CFGR); + 80089ee: 6123 str r3, [r4, #16] + SET_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N_4 ); + 80089f0: 6922 ldr r2, [r4, #16] + 80089f2: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 80089f6: 6122 str r2, [r4, #16] + CLEAR_REG(RCC->PLLSAI2CFGR); + 80089f8: 6163 str r3, [r4, #20] + SET_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N_4 ); + 80089fa: 6962 ldr r2, [r4, #20] + 80089fc: f442 5280 orr.w r2, r2, #4096 ; 0x1000 + 8008a00: 6162 str r2, [r4, #20] + CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); + 8008a02: 6822 ldr r2, [r4, #0] + 8008a04: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 8008a08: 6022 str r2, [r4, #0] + CLEAR_REG(RCC->CIER); + 8008a0a: 61a3 str r3, [r4, #24] + WRITE_REG(RCC->CICR, 0xFFFFFFFFU); + 8008a0c: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 8008a10: 6223 str r3, [r4, #32] + SET_BIT(RCC->CSR, RCC_CSR_RMVF); + 8008a12: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008a16: f443 0300 orr.w r3, r3, #8388608 ; 0x800000 + 8008a1a: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + return HAL_OK; + 8008a1e: e005 b.n 8008a2c + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 8008a20: f7fe fcdc bl 80073dc + 8008a24: 1b40 subs r0, r0, r5 + 8008a26: 2802 cmp r0, #2 + 8008a28: d9b3 bls.n 8008992 + return HAL_TIMEOUT; + 8008a2a: 2503 movs r5, #3 +} + 8008a2c: 4628 mov r0, r5 + 8008a2e: bdf8 pop {r3, r4, r5, r6, r7, pc} + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 8008a30: f7fe fcd4 bl 80073dc + 8008a34: 1b80 subs r0, r0, r6 + 8008a36: 42b8 cmp r0, r7 + 8008a38: d9c5 bls.n 80089c6 + 8008a3a: e7f6 b.n 8008a2a + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8008a3c: f7fe fcce bl 80073dc + 8008a40: 1b80 subs r0, r0, r6 + 8008a42: 2802 cmp r0, #2 + 8008a44: d9ca bls.n 80089dc + 8008a46: e7f0 b.n 8008a2a + return HAL_ERROR; + 8008a48: 2501 movs r5, #1 + 8008a4a: e7ef b.n 8008a2c + 8008a4c: 40021000 .word 0x40021000 + 8008a50: 003d0900 .word 0x003d0900 + 8008a54: 2009e2ac .word 0x2009e2ac + 8008a58: 2009e2b0 .word 0x2009e2b0 + 8008a5c: eafef4ff .word 0xeafef4ff + +08008a60 : +{ + 8008a60: b570 push {r4, r5, r6, lr} + __MCO1_CLK_ENABLE(); + 8008a62: 4c12 ldr r4, [pc, #72] ; (8008aac ) + 8008a64: 6ce3 ldr r3, [r4, #76] ; 0x4c + 8008a66: f043 0301 orr.w r3, r3, #1 + 8008a6a: 64e3 str r3, [r4, #76] ; 0x4c + 8008a6c: 6ce3 ldr r3, [r4, #76] ; 0x4c +{ + 8008a6e: b086 sub sp, #24 + __MCO1_CLK_ENABLE(); + 8008a70: f003 0301 and.w r3, r3, #1 + 8008a74: 9300 str r3, [sp, #0] + 8008a76: 9b00 ldr r3, [sp, #0] +{ + 8008a78: 4616 mov r6, r2 + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + 8008a7a: 2302 movs r3, #2 + 8008a7c: f44f 7280 mov.w r2, #256 ; 0x100 + 8008a80: e9cd 2301 strd r2, r3, [sp, #4] +{ + 8008a84: 460d mov r5, r1 + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + 8008a86: 9304 str r3, [sp, #16] + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 8008a88: a901 add r1, sp, #4 + GPIO_InitStruct.Pull = GPIO_NOPULL; + 8008a8a: 2300 movs r3, #0 + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 8008a8c: f04f 4090 mov.w r0, #1207959552 ; 0x48000000 + GPIO_InitStruct.Pull = GPIO_NOPULL; + 8008a90: 9303 str r3, [sp, #12] + GPIO_InitStruct.Alternate = GPIO_AF0_MCO; + 8008a92: 9305 str r3, [sp, #20] + HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct); + 8008a94: f7f8 fbae bl 80011f4 + MODIFY_REG(RCC->CFGR, (RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE), (RCC_MCOSource | RCC_MCODiv )); + 8008a98: 68a3 ldr r3, [r4, #8] + 8008a9a: f023 43fe bic.w r3, r3, #2130706432 ; 0x7f000000 + 8008a9e: ea43 0206 orr.w r2, r3, r6 + 8008aa2: 432a orrs r2, r5 + 8008aa4: 60a2 str r2, [r4, #8] +} + 8008aa6: b006 add sp, #24 + 8008aa8: bd70 pop {r4, r5, r6, pc} + 8008aaa: bf00 nop + 8008aac: 40021000 .word 0x40021000 + +08008ab0 : + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008ab0: 4b22 ldr r3, [pc, #136] ; (8008b3c ) + 8008ab2: 689a ldr r2, [r3, #8] + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8008ab4: 68d9 ldr r1, [r3, #12] + if((sysclk_source == RCC_CFGR_SWS_MSI) || + 8008ab6: f012 020c ands.w r2, r2, #12 + 8008aba: d005 beq.n 8008ac8 + 8008abc: 2a0c cmp r2, #12 + 8008abe: d115 bne.n 8008aec + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8008ac0: f001 0103 and.w r1, r1, #3 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_oscsource == RCC_PLLSOURCE_MSI))) + 8008ac4: 2901 cmp r1, #1 + 8008ac6: d118 bne.n 8008afa + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 8008ac8: 6819 ldr r1, [r3, #0] + msirange = MSIRangeTable[msirange]; + 8008aca: 481d ldr r0, [pc, #116] ; (8008b40 ) + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 8008acc: 0709 lsls r1, r1, #28 + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 8008ace: bf55 itete pl + 8008ad0: f8d3 1094 ldrpl.w r1, [r3, #148] ; 0x94 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 8008ad4: 6819 ldrmi r1, [r3, #0] + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 8008ad6: f3c1 2103 ubfxpl r1, r1, #8, #4 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 8008ada: f3c1 1103 ubfxmi r1, r1, #4, #4 + msirange = MSIRangeTable[msirange]; + 8008ade: f850 0021 ldr.w r0, [r0, r1, lsl #2] + if(sysclk_source == RCC_CFGR_SWS_MSI) + 8008ae2: b34a cbz r2, 8008b38 + if(sysclk_source == RCC_CFGR_SWS_PLL) + 8008ae4: 2a0c cmp r2, #12 + 8008ae6: d009 beq.n 8008afc + 8008ae8: 2000 movs r0, #0 + return sysclockfreq; + 8008aea: 4770 bx lr + else if(sysclk_source == RCC_CFGR_SWS_HSI) + 8008aec: 2a04 cmp r2, #4 + 8008aee: d022 beq.n 8008b36 + else if(sysclk_source == RCC_CFGR_SWS_HSE) + 8008af0: 2a08 cmp r2, #8 + 8008af2: 4814 ldr r0, [pc, #80] ; (8008b44 ) + 8008af4: bf18 it ne + 8008af6: 2000 movne r0, #0 + 8008af8: 4770 bx lr + uint32_t msirange = 0U, sysclockfreq = 0U; + 8008afa: 2000 movs r0, #0 + pllsource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8008afc: 68da ldr r2, [r3, #12] + 8008afe: f002 0203 and.w r2, r2, #3 + switch (pllsource) + 8008b02: 2a02 cmp r2, #2 + 8008b04: d015 beq.n 8008b32 + 8008b06: 490f ldr r1, [pc, #60] ; (8008b44 ) + 8008b08: 2a03 cmp r2, #3 + 8008b0a: bf08 it eq + 8008b0c: 4608 moveq r0, r1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008b0e: 68d9 ldr r1, [r3, #12] + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 8008b10: 68da ldr r2, [r3, #12] + 8008b12: f3c2 2206 ubfx r2, r2, #8, #7 + 8008b16: 4342 muls r2, r0 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008b18: 68d8 ldr r0, [r3, #12] + 8008b1a: f3c0 6041 ubfx r0, r0, #25, #2 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008b1e: f3c1 1103 ubfx r1, r1, #4, #4 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008b22: 3001 adds r0, #1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 8008b24: 3101 adds r1, #1 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8008b26: 0040 lsls r0, r0, #1 + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 8008b28: fbb2 f2f1 udiv r2, r2, r1 + sysclockfreq = pllvco / pllr; + 8008b2c: fbb2 f0f0 udiv r0, r2, r0 + 8008b30: 4770 bx lr + pllvco = HSI_VALUE; + 8008b32: 4805 ldr r0, [pc, #20] ; (8008b48 ) + 8008b34: e7eb b.n 8008b0e + sysclockfreq = HSI_VALUE; + 8008b36: 4804 ldr r0, [pc, #16] ; (8008b48 ) +} + 8008b38: 4770 bx lr + 8008b3a: bf00 nop + 8008b3c: 40021000 .word 0x40021000 + 8008b40: 08010a70 .word 0x08010a70 + 8008b44: 007a1200 .word 0x007a1200 + 8008b48: 00f42400 .word 0x00f42400 + +08008b4c : +{ + 8008b4c: e92d 43f7 stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, lr} + if(RCC_OscInitStruct == NULL) + 8008b50: 4605 mov r5, r0 + 8008b52: b908 cbnz r0, 8008b58 + return HAL_ERROR; + 8008b54: 2001 movs r0, #1 + 8008b56: e047 b.n 8008be8 + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008b58: 4c94 ldr r4, [pc, #592] ; (8008dac ) + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 8008b5a: 6803 ldr r3, [r0, #0] + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008b5c: 68a6 ldr r6, [r4, #8] + pll_config = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8008b5e: 68e7 ldr r7, [r4, #12] + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 8008b60: 06db lsls r3, r3, #27 + sysclk_source = __HAL_RCC_GET_SYSCLK_SOURCE(); + 8008b62: f006 060c and.w r6, r6, #12 + pll_config = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8008b66: f007 0703 and.w r7, r7, #3 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_MSI) == RCC_OSCILLATORTYPE_MSI) + 8008b6a: d575 bpl.n 8008c58 + if((sysclk_source == RCC_CFGR_SWS_MSI) || + 8008b6c: b11e cbz r6, 8008b76 + 8008b6e: 2e0c cmp r6, #12 + 8008b70: d154 bne.n 8008c1c + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_MSI))) + 8008b72: 2f01 cmp r7, #1 + 8008b74: d152 bne.n 8008c1c + if((READ_BIT(RCC->CR, RCC_CR_MSIRDY) != 0U) && (RCC_OscInitStruct->MSIState == RCC_MSI_OFF)) + 8008b76: 6823 ldr r3, [r4, #0] + 8008b78: 0798 lsls r0, r3, #30 + 8008b7a: d502 bpl.n 8008b82 + 8008b7c: 69ab ldr r3, [r5, #24] + 8008b7e: 2b00 cmp r3, #0 + 8008b80: d0e8 beq.n 8008b54 + if(RCC_OscInitStruct->MSIClockRange > __HAL_RCC_GET_MSI_RANGE()) + 8008b82: 6823 ldr r3, [r4, #0] + 8008b84: 6a28 ldr r0, [r5, #32] + 8008b86: 0719 lsls r1, r3, #28 + 8008b88: bf56 itet pl + 8008b8a: f8d4 3094 ldrpl.w r3, [r4, #148] ; 0x94 + 8008b8e: 6823 ldrmi r3, [r4, #0] + 8008b90: 091b lsrpl r3, r3, #4 + 8008b92: f003 03f0 and.w r3, r3, #240 ; 0xf0 + 8008b96: 4298 cmp r0, r3 + 8008b98: d929 bls.n 8008bee + if(RCC_SetFlashLatencyFromMSIRange(RCC_OscInitStruct->MSIClockRange) != HAL_OK) + 8008b9a: f7ff feb3 bl 8008904 + 8008b9e: 2800 cmp r0, #0 + 8008ba0: d1d8 bne.n 8008b54 + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 8008ba2: 6823 ldr r3, [r4, #0] + 8008ba4: f043 0308 orr.w r3, r3, #8 + 8008ba8: 6023 str r3, [r4, #0] + 8008baa: 6823 ldr r3, [r4, #0] + 8008bac: 6a2a ldr r2, [r5, #32] + 8008bae: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008bb2: 4313 orrs r3, r2 + 8008bb4: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 8008bb6: 6863 ldr r3, [r4, #4] + 8008bb8: 69ea ldr r2, [r5, #28] + 8008bba: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 8008bbe: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008bc2: 6063 str r3, [r4, #4] + SystemCoreClock = HAL_RCC_GetSysClockFreq() >> (AHBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos] & 0x1FU); + 8008bc4: f7ff ff74 bl 8008ab0 + 8008bc8: 68a3 ldr r3, [r4, #8] + 8008bca: 4a79 ldr r2, [pc, #484] ; (8008db0 ) + 8008bcc: f3c3 1303 ubfx r3, r3, #4, #4 + 8008bd0: 5cd3 ldrb r3, [r2, r3] + 8008bd2: f003 031f and.w r3, r3, #31 + 8008bd6: 40d8 lsrs r0, r3 + 8008bd8: 4b76 ldr r3, [pc, #472] ; (8008db4 ) + 8008bda: 6018 str r0, [r3, #0] + status = HAL_InitTick(uwTickPrio); + 8008bdc: 4b76 ldr r3, [pc, #472] ; (8008db8 ) + 8008bde: 6818 ldr r0, [r3, #0] + 8008be0: f7fe fbfe bl 80073e0 + if(status != HAL_OK) + 8008be4: 2800 cmp r0, #0 + 8008be6: d037 beq.n 8008c58 +} + 8008be8: b003 add sp, #12 + 8008bea: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 8008bee: 6823 ldr r3, [r4, #0] + 8008bf0: f043 0308 orr.w r3, r3, #8 + 8008bf4: 6023 str r3, [r4, #0] + 8008bf6: 6823 ldr r3, [r4, #0] + 8008bf8: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008bfc: 4303 orrs r3, r0 + 8008bfe: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 8008c00: 6863 ldr r3, [r4, #4] + 8008c02: 69ea ldr r2, [r5, #28] + 8008c04: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 8008c08: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008c0c: 6063 str r3, [r4, #4] + if(sysclk_source == RCC_CFGR_SWS_MSI) + 8008c0e: 2e00 cmp r6, #0 + 8008c10: d1d8 bne.n 8008bc4 + if(RCC_SetFlashLatencyFromMSIRange(RCC_OscInitStruct->MSIClockRange) != HAL_OK) + 8008c12: f7ff fe77 bl 8008904 + 8008c16: 2800 cmp r0, #0 + 8008c18: d0d4 beq.n 8008bc4 + 8008c1a: e79b b.n 8008b54 + if(RCC_OscInitStruct->MSIState != RCC_MSI_OFF) + 8008c1c: 69ab ldr r3, [r5, #24] + 8008c1e: 2b00 cmp r3, #0 + 8008c20: d03a beq.n 8008c98 + __HAL_RCC_MSI_ENABLE(); + 8008c22: 6823 ldr r3, [r4, #0] + 8008c24: f043 0301 orr.w r3, r3, #1 + 8008c28: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008c2a: f7fe fbd7 bl 80073dc + 8008c2e: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 8008c30: 6823 ldr r3, [r4, #0] + 8008c32: 079a lsls r2, r3, #30 + 8008c34: d528 bpl.n 8008c88 + __HAL_RCC_MSI_RANGE_CONFIG(RCC_OscInitStruct->MSIClockRange); + 8008c36: 6823 ldr r3, [r4, #0] + 8008c38: f043 0308 orr.w r3, r3, #8 + 8008c3c: 6023 str r3, [r4, #0] + 8008c3e: 6823 ldr r3, [r4, #0] + 8008c40: 6a2a ldr r2, [r5, #32] + 8008c42: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8008c46: 4313 orrs r3, r2 + 8008c48: 6023 str r3, [r4, #0] + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->MSICalibrationValue); + 8008c4a: 6863 ldr r3, [r4, #4] + 8008c4c: 69ea ldr r2, [r5, #28] + 8008c4e: f423 437f bic.w r3, r3, #65280 ; 0xff00 + 8008c52: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008c56: 6063 str r3, [r4, #4] + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE) + 8008c58: 682b ldr r3, [r5, #0] + 8008c5a: 07d8 lsls r0, r3, #31 + 8008c5c: d42d bmi.n 8008cba + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI) == RCC_OSCILLATORTYPE_HSI) + 8008c5e: 682b ldr r3, [r5, #0] + 8008c60: 0799 lsls r1, r3, #30 + 8008c62: d46b bmi.n 8008d3c + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSI) == RCC_OSCILLATORTYPE_LSI) + 8008c64: 682b ldr r3, [r5, #0] + 8008c66: 0718 lsls r0, r3, #28 + 8008c68: f100 80a8 bmi.w 8008dbc + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE) + 8008c6c: 682b ldr r3, [r5, #0] + 8008c6e: 0759 lsls r1, r3, #29 + 8008c70: f100 80ce bmi.w 8008e10 + if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI48) == RCC_OSCILLATORTYPE_HSI48) + 8008c74: 682b ldr r3, [r5, #0] + 8008c76: 069f lsls r7, r3, #26 + 8008c78: f100 8137 bmi.w 8008eea + if(RCC_OscInitStruct->PLL.PLLState != RCC_PLL_NONE) + 8008c7c: 6aab ldr r3, [r5, #40] ; 0x28 + 8008c7e: 2b00 cmp r3, #0 + 8008c80: f040 815d bne.w 8008f3e + return HAL_OK; + 8008c84: 2000 movs r0, #0 + 8008c86: e7af b.n 8008be8 + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 8008c88: f7fe fba8 bl 80073dc + 8008c8c: eba0 0008 sub.w r0, r0, r8 + 8008c90: 2802 cmp r0, #2 + 8008c92: d9cd bls.n 8008c30 + return HAL_TIMEOUT; + 8008c94: 2003 movs r0, #3 + 8008c96: e7a7 b.n 8008be8 + __HAL_RCC_MSI_DISABLE(); + 8008c98: 6823 ldr r3, [r4, #0] + 8008c9a: f023 0301 bic.w r3, r3, #1 + 8008c9e: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008ca0: f7fe fb9c bl 80073dc + 8008ca4: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_MSIRDY) != 0U) + 8008ca6: 6823 ldr r3, [r4, #0] + 8008ca8: 079b lsls r3, r3, #30 + 8008caa: d5d5 bpl.n 8008c58 + if((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE) + 8008cac: f7fe fb96 bl 80073dc + 8008cb0: eba0 0008 sub.w r0, r0, r8 + 8008cb4: 2802 cmp r0, #2 + 8008cb6: d9f6 bls.n 8008ca6 + 8008cb8: e7ec b.n 8008c94 + if((sysclk_source == RCC_CFGR_SWS_HSE) || + 8008cba: 2e08 cmp r6, #8 + 8008cbc: d003 beq.n 8008cc6 + 8008cbe: 2e0c cmp r6, #12 + 8008cc0: d108 bne.n 8008cd4 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_HSE))) + 8008cc2: 2f03 cmp r7, #3 + 8008cc4: d106 bne.n 8008cd4 + if((READ_BIT(RCC->CR, RCC_CR_HSERDY) != 0U) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF)) + 8008cc6: 6823 ldr r3, [r4, #0] + 8008cc8: 039a lsls r2, r3, #14 + 8008cca: d5c8 bpl.n 8008c5e + 8008ccc: 686b ldr r3, [r5, #4] + 8008cce: 2b00 cmp r3, #0 + 8008cd0: d1c5 bne.n 8008c5e + 8008cd2: e73f b.n 8008b54 + __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState); + 8008cd4: 686b ldr r3, [r5, #4] + 8008cd6: f5b3 3f80 cmp.w r3, #65536 ; 0x10000 + 8008cda: d110 bne.n 8008cfe + 8008cdc: 6823 ldr r3, [r4, #0] + 8008cde: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8008ce2: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008ce4: f7fe fb7a bl 80073dc + 8008ce8: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U) + 8008cea: 6823 ldr r3, [r4, #0] + 8008cec: 039b lsls r3, r3, #14 + 8008cee: d4b6 bmi.n 8008c5e + if((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE) + 8008cf0: f7fe fb74 bl 80073dc + 8008cf4: eba0 0008 sub.w r0, r0, r8 + 8008cf8: 2864 cmp r0, #100 ; 0x64 + 8008cfa: d9f6 bls.n 8008cea + 8008cfc: e7ca b.n 8008c94 + __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState); + 8008cfe: f5b3 2fa0 cmp.w r3, #327680 ; 0x50000 + 8008d02: d104 bne.n 8008d0e + 8008d04: 6823 ldr r3, [r4, #0] + 8008d06: f443 2380 orr.w r3, r3, #262144 ; 0x40000 + 8008d0a: 6023 str r3, [r4, #0] + 8008d0c: e7e6 b.n 8008cdc + 8008d0e: 6822 ldr r2, [r4, #0] + 8008d10: f422 3280 bic.w r2, r2, #65536 ; 0x10000 + 8008d14: 6022 str r2, [r4, #0] + 8008d16: 6822 ldr r2, [r4, #0] + 8008d18: f422 2280 bic.w r2, r2, #262144 ; 0x40000 + 8008d1c: 6022 str r2, [r4, #0] + if(RCC_OscInitStruct->HSEState != RCC_HSE_OFF) + 8008d1e: 2b00 cmp r3, #0 + 8008d20: d1e0 bne.n 8008ce4 + tickstart = HAL_GetTick(); + 8008d22: f7fe fb5b bl 80073dc + 8008d26: 4680 mov r8, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSERDY) != 0U) + 8008d28: 6823 ldr r3, [r4, #0] + 8008d2a: 0398 lsls r0, r3, #14 + 8008d2c: d597 bpl.n 8008c5e + if((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE) + 8008d2e: f7fe fb55 bl 80073dc + 8008d32: eba0 0008 sub.w r0, r0, r8 + 8008d36: 2864 cmp r0, #100 ; 0x64 + 8008d38: d9f6 bls.n 8008d28 + 8008d3a: e7ab b.n 8008c94 + if((sysclk_source == RCC_CFGR_SWS_HSI) || + 8008d3c: 2e04 cmp r6, #4 + 8008d3e: d003 beq.n 8008d48 + 8008d40: 2e0c cmp r6, #12 + 8008d42: d110 bne.n 8008d66 + ((sysclk_source == RCC_CFGR_SWS_PLL) && (pll_config == RCC_PLLSOURCE_HSI))) + 8008d44: 2f02 cmp r7, #2 + 8008d46: d10e bne.n 8008d66 + if((READ_BIT(RCC->CR, RCC_CR_HSIRDY) != 0U) && (RCC_OscInitStruct->HSIState == RCC_HSI_OFF)) + 8008d48: 6823 ldr r3, [r4, #0] + 8008d4a: 0559 lsls r1, r3, #21 + 8008d4c: d503 bpl.n 8008d56 + 8008d4e: 68eb ldr r3, [r5, #12] + 8008d50: 2b00 cmp r3, #0 + 8008d52: f43f aeff beq.w 8008b54 + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue); + 8008d56: 6863 ldr r3, [r4, #4] + 8008d58: 692a ldr r2, [r5, #16] + 8008d5a: f023 43fe bic.w r3, r3, #2130706432 ; 0x7f000000 + 8008d5e: ea43 6302 orr.w r3, r3, r2, lsl #24 + 8008d62: 6063 str r3, [r4, #4] + 8008d64: e77e b.n 8008c64 + if(RCC_OscInitStruct->HSIState != RCC_HSI_OFF) + 8008d66: 68eb ldr r3, [r5, #12] + 8008d68: b17b cbz r3, 8008d8a + __HAL_RCC_HSI_ENABLE(); + 8008d6a: 6823 ldr r3, [r4, #0] + 8008d6c: f443 7380 orr.w r3, r3, #256 ; 0x100 + 8008d70: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008d72: f7fe fb33 bl 80073dc + 8008d76: 4607 mov r7, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 8008d78: 6823 ldr r3, [r4, #0] + 8008d7a: 055a lsls r2, r3, #21 + 8008d7c: d4eb bmi.n 8008d56 + if((HAL_GetTick() - tickstart) > HSI_TIMEOUT_VALUE) + 8008d7e: f7fe fb2d bl 80073dc + 8008d82: 1bc0 subs r0, r0, r7 + 8008d84: 2802 cmp r0, #2 + 8008d86: d9f7 bls.n 8008d78 + 8008d88: e784 b.n 8008c94 + __HAL_RCC_HSI_DISABLE(); + 8008d8a: 6823 ldr r3, [r4, #0] + 8008d8c: f423 7380 bic.w r3, r3, #256 ; 0x100 + 8008d90: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008d92: f7fe fb23 bl 80073dc + 8008d96: 4607 mov r7, r0 + while(READ_BIT(RCC->CR, RCC_CR_HSIRDY) != 0U) + 8008d98: 6823 ldr r3, [r4, #0] + 8008d9a: 055b lsls r3, r3, #21 + 8008d9c: f57f af62 bpl.w 8008c64 + if((HAL_GetTick() - tickstart) > HSI_TIMEOUT_VALUE) + 8008da0: f7fe fb1c bl 80073dc + 8008da4: 1bc0 subs r0, r0, r7 + 8008da6: 2802 cmp r0, #2 + 8008da8: d9f6 bls.n 8008d98 + 8008daa: e773 b.n 8008c94 + 8008dac: 40021000 .word 0x40021000 + 8008db0: 08010a58 .word 0x08010a58 + 8008db4: 2009e2ac .word 0x2009e2ac + 8008db8: 2009e2b0 .word 0x2009e2b0 + if(RCC_OscInitStruct->LSIState != RCC_LSI_OFF) + 8008dbc: 696b ldr r3, [r5, #20] + 8008dbe: b19b cbz r3, 8008de8 + __HAL_RCC_LSI_ENABLE(); + 8008dc0: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008dc4: f043 0301 orr.w r3, r3, #1 + 8008dc8: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + tickstart = HAL_GetTick(); + 8008dcc: f7fe fb06 bl 80073dc + 8008dd0: 4607 mov r7, r0 + while(READ_BIT(RCC->CSR, RCC_CSR_LSIRDY) == 0U) + 8008dd2: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008dd6: 079a lsls r2, r3, #30 + 8008dd8: f53f af48 bmi.w 8008c6c + if((HAL_GetTick() - tickstart) > LSI_TIMEOUT_VALUE) + 8008ddc: f7fe fafe bl 80073dc + 8008de0: 1bc0 subs r0, r0, r7 + 8008de2: 2802 cmp r0, #2 + 8008de4: d9f5 bls.n 8008dd2 + 8008de6: e755 b.n 8008c94 + __HAL_RCC_LSI_DISABLE(); + 8008de8: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008dec: f023 0301 bic.w r3, r3, #1 + 8008df0: f8c4 3094 str.w r3, [r4, #148] ; 0x94 + tickstart = HAL_GetTick(); + 8008df4: f7fe faf2 bl 80073dc + 8008df8: 4607 mov r7, r0 + while(READ_BIT(RCC->CSR, RCC_CSR_LSIRDY) != 0U) + 8008dfa: f8d4 3094 ldr.w r3, [r4, #148] ; 0x94 + 8008dfe: 079b lsls r3, r3, #30 + 8008e00: f57f af34 bpl.w 8008c6c + if((HAL_GetTick() - tickstart) > LSI_TIMEOUT_VALUE) + 8008e04: f7fe faea bl 80073dc + 8008e08: 1bc0 subs r0, r0, r7 + 8008e0a: 2802 cmp r0, #2 + 8008e0c: d9f5 bls.n 8008dfa + 8008e0e: e741 b.n 8008c94 + if(HAL_IS_BIT_CLR(RCC->APB1ENR1, RCC_APB1ENR1_PWREN)) + 8008e10: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008e12: 00df lsls r7, r3, #3 + 8008e14: d429 bmi.n 8008e6a + __HAL_RCC_PWR_CLK_ENABLE(); + 8008e16: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008e18: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 8008e1c: 65a3 str r3, [r4, #88] ; 0x58 + 8008e1e: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008e20: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 8008e24: 9301 str r3, [sp, #4] + 8008e26: 9b01 ldr r3, [sp, #4] + pwrclkchanged = SET; + 8008e28: f04f 0801 mov.w r8, #1 + if(HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP)) + 8008e2c: 4f9c ldr r7, [pc, #624] ; (80090a0 ) + 8008e2e: 683b ldr r3, [r7, #0] + 8008e30: 05d8 lsls r0, r3, #23 + 8008e32: d51d bpl.n 8008e70 + __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState); + 8008e34: 68ab ldr r3, [r5, #8] + 8008e36: 2b01 cmp r3, #1 + 8008e38: d12b bne.n 8008e92 + 8008e3a: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008e3e: f043 0301 orr.w r3, r3, #1 + 8008e42: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + tickstart = HAL_GetTick(); + 8008e46: f7fe fac9 bl 80073dc + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008e4a: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8008e4e: 4607 mov r7, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0U) + 8008e50: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008e54: 079a lsls r2, r3, #30 + 8008e56: d542 bpl.n 8008ede + if(pwrclkchanged == SET) + 8008e58: f1b8 0f00 cmp.w r8, #0 + 8008e5c: f43f af0a beq.w 8008c74 + __HAL_RCC_PWR_CLK_DISABLE(); + 8008e60: 6da3 ldr r3, [r4, #88] ; 0x58 + 8008e62: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8008e66: 65a3 str r3, [r4, #88] ; 0x58 + 8008e68: e704 b.n 8008c74 + FlagStatus pwrclkchanged = RESET; + 8008e6a: f04f 0800 mov.w r8, #0 + 8008e6e: e7dd b.n 8008e2c + SET_BIT(PWR->CR1, PWR_CR1_DBP); + 8008e70: 683b ldr r3, [r7, #0] + 8008e72: f443 7380 orr.w r3, r3, #256 ; 0x100 + 8008e76: 603b str r3, [r7, #0] + tickstart = HAL_GetTick(); + 8008e78: f7fe fab0 bl 80073dc + 8008e7c: 4681 mov r9, r0 + while(HAL_IS_BIT_CLR(PWR->CR1, PWR_CR1_DBP)) + 8008e7e: 683b ldr r3, [r7, #0] + 8008e80: 05d9 lsls r1, r3, #23 + 8008e82: d4d7 bmi.n 8008e34 + if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) + 8008e84: f7fe faaa bl 80073dc + 8008e88: eba0 0009 sub.w r0, r0, r9 + 8008e8c: 2802 cmp r0, #2 + 8008e8e: d9f6 bls.n 8008e7e + 8008e90: e700 b.n 8008c94 + __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState); + 8008e92: 2b05 cmp r3, #5 + 8008e94: d106 bne.n 8008ea4 + 8008e96: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008e9a: f043 0304 orr.w r3, r3, #4 + 8008e9e: f8c4 3090 str.w r3, [r4, #144] ; 0x90 + 8008ea2: e7ca b.n 8008e3a + 8008ea4: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8008ea8: f022 0201 bic.w r2, r2, #1 + 8008eac: f8c4 2090 str.w r2, [r4, #144] ; 0x90 + 8008eb0: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8008eb4: f022 0204 bic.w r2, r2, #4 + 8008eb8: f8c4 2090 str.w r2, [r4, #144] ; 0x90 + if(RCC_OscInitStruct->LSEState != RCC_LSE_OFF) + 8008ebc: 2b00 cmp r3, #0 + 8008ebe: d1c2 bne.n 8008e46 + tickstart = HAL_GetTick(); + 8008ec0: f7fe fa8c bl 80073dc + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008ec4: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8008ec8: 4607 mov r7, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) != 0U) + 8008eca: f8d4 3090 ldr.w r3, [r4, #144] ; 0x90 + 8008ece: 079b lsls r3, r3, #30 + 8008ed0: d5c2 bpl.n 8008e58 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008ed2: f7fe fa83 bl 80073dc + 8008ed6: 1bc0 subs r0, r0, r7 + 8008ed8: 4548 cmp r0, r9 + 8008eda: d9f6 bls.n 8008eca + 8008edc: e6da b.n 8008c94 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8008ede: f7fe fa7d bl 80073dc + 8008ee2: 1bc0 subs r0, r0, r7 + 8008ee4: 4548 cmp r0, r9 + 8008ee6: d9b3 bls.n 8008e50 + 8008ee8: e6d4 b.n 8008c94 + if(RCC_OscInitStruct->HSI48State != RCC_HSI48_OFF) + 8008eea: 6a6b ldr r3, [r5, #36] ; 0x24 + 8008eec: b19b cbz r3, 8008f16 + __HAL_RCC_HSI48_ENABLE(); + 8008eee: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008ef2: f043 0301 orr.w r3, r3, #1 + 8008ef6: f8c4 3098 str.w r3, [r4, #152] ; 0x98 + tickstart = HAL_GetTick(); + 8008efa: f7fe fa6f bl 80073dc + 8008efe: 4607 mov r7, r0 + while(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48RDY) == 0U) + 8008f00: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008f04: 0798 lsls r0, r3, #30 + 8008f06: f53f aeb9 bmi.w 8008c7c + if((HAL_GetTick() - tickstart) > HSI48_TIMEOUT_VALUE) + 8008f0a: f7fe fa67 bl 80073dc + 8008f0e: 1bc0 subs r0, r0, r7 + 8008f10: 2802 cmp r0, #2 + 8008f12: d9f5 bls.n 8008f00 + 8008f14: e6be b.n 8008c94 + __HAL_RCC_HSI48_DISABLE(); + 8008f16: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008f1a: f023 0301 bic.w r3, r3, #1 + 8008f1e: f8c4 3098 str.w r3, [r4, #152] ; 0x98 + tickstart = HAL_GetTick(); + 8008f22: f7fe fa5b bl 80073dc + 8008f26: 4607 mov r7, r0 + while(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48RDY) != 0U) + 8008f28: f8d4 3098 ldr.w r3, [r4, #152] ; 0x98 + 8008f2c: 0799 lsls r1, r3, #30 + 8008f2e: f57f aea5 bpl.w 8008c7c + if((HAL_GetTick() - tickstart) > HSI48_TIMEOUT_VALUE) + 8008f32: f7fe fa53 bl 80073dc + 8008f36: 1bc0 subs r0, r0, r7 + 8008f38: 2802 cmp r0, #2 + 8008f3a: d9f5 bls.n 8008f28 + 8008f3c: e6aa b.n 8008c94 + if(RCC_OscInitStruct->PLL.PLLState == RCC_PLL_ON) + 8008f3e: 2b02 cmp r3, #2 + 8008f40: f040 808c bne.w 800905c + pll_config = RCC->PLLCFGR; + 8008f44: 68e3 ldr r3, [r4, #12] + if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) || + 8008f46: 6aea ldr r2, [r5, #44] ; 0x2c + 8008f48: f003 0103 and.w r1, r3, #3 + 8008f4c: 4291 cmp r1, r2 + 8008f4e: d122 bne.n 8008f96 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != ((RCC_OscInitStruct->PLL.PLLM - 1U) << RCC_PLLCFGR_PLLM_Pos)) || + 8008f50: 6b29 ldr r1, [r5, #48] ; 0x30 + 8008f52: f003 02f0 and.w r2, r3, #240 ; 0xf0 + 8008f56: 3901 subs r1, #1 + if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) || + 8008f58: ebb2 1f01 cmp.w r2, r1, lsl #4 + 8008f5c: d11b bne.n 8008f96 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)) || + 8008f5e: 6b69 ldr r1, [r5, #52] ; 0x34 + 8008f60: f403 42fe and.w r2, r3, #32512 ; 0x7f00 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != ((RCC_OscInitStruct->PLL.PLLM - 1U) << RCC_PLLCFGR_PLLM_Pos)) || + 8008f64: ebb2 2f01 cmp.w r2, r1, lsl #8 + 8008f68: d115 bne.n 8008f96 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLPDIV) != (RCC_OscInitStruct->PLL.PLLP << RCC_PLLCFGR_PLLPDIV_Pos)) || + 8008f6a: 6ba9 ldr r1, [r5, #56] ; 0x38 + 8008f6c: f003 4278 and.w r2, r3, #4160749568 ; 0xf8000000 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)) || + 8008f70: ebb2 6fc1 cmp.w r2, r1, lsl #27 + 8008f74: d10f bne.n 8008f96 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != ((((RCC_OscInitStruct->PLL.PLLQ) >> 1U) - 1U) << RCC_PLLCFGR_PLLQ_Pos)) || + 8008f76: 6bea ldr r2, [r5, #60] ; 0x3c + 8008f78: 0852 lsrs r2, r2, #1 + 8008f7a: f403 01c0 and.w r1, r3, #6291456 ; 0x600000 + 8008f7e: 3a01 subs r2, #1 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLPDIV) != (RCC_OscInitStruct->PLL.PLLP << RCC_PLLCFGR_PLLPDIV_Pos)) || + 8008f80: ebb1 5f42 cmp.w r1, r2, lsl #21 + 8008f84: d107 bne.n 8008f96 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLR) != ((((RCC_OscInitStruct->PLL.PLLR) >> 1U) - 1U) << RCC_PLLCFGR_PLLR_Pos))) + 8008f86: 6c2a ldr r2, [r5, #64] ; 0x40 + 8008f88: 0852 lsrs r2, r2, #1 + 8008f8a: f003 63c0 and.w r3, r3, #100663296 ; 0x6000000 + 8008f8e: 3a01 subs r2, #1 + (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != ((((RCC_OscInitStruct->PLL.PLLQ) >> 1U) - 1U) << RCC_PLLCFGR_PLLQ_Pos)) || + 8008f90: ebb3 6f42 cmp.w r3, r2, lsl #25 + 8008f94: d049 beq.n 800902a + if(sysclk_source != RCC_CFGR_SWS_PLL) + 8008f96: 2e0c cmp r6, #12 + 8008f98: f43f addc beq.w 8008b54 + if((READ_BIT(RCC->CR, RCC_CR_PLLSAI1ON) != 0U) + 8008f9c: 6823 ldr r3, [r4, #0] + 8008f9e: 015a lsls r2, r3, #5 + 8008fa0: f53f add8 bmi.w 8008b54 + || (READ_BIT(RCC->CR, RCC_CR_PLLSAI2ON) != 0U) + 8008fa4: 6823 ldr r3, [r4, #0] + 8008fa6: 00db lsls r3, r3, #3 + 8008fa8: f53f add4 bmi.w 8008b54 + __HAL_RCC_PLL_DISABLE(); + 8008fac: 6823 ldr r3, [r4, #0] + 8008fae: f023 7380 bic.w r3, r3, #16777216 ; 0x1000000 + 8008fb2: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 8008fb4: f7fe fa12 bl 80073dc + 8008fb8: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != 0U) + 8008fba: 6823 ldr r3, [r4, #0] + 8008fbc: 019f lsls r7, r3, #6 + 8008fbe: d42e bmi.n 800901e + __HAL_RCC_PLL_CONFIG(RCC_OscInitStruct->PLL.PLLSource, + 8008fc0: 68e2 ldr r2, [r4, #12] + 8008fc2: 4b38 ldr r3, [pc, #224] ; (80090a4 ) + 8008fc4: 4013 ands r3, r2 + 8008fc6: 6aea ldr r2, [r5, #44] ; 0x2c + 8008fc8: 4313 orrs r3, r2 + 8008fca: 6b6a ldr r2, [r5, #52] ; 0x34 + 8008fcc: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8008fd0: 6baa ldr r2, [r5, #56] ; 0x38 + 8008fd2: ea43 63c2 orr.w r3, r3, r2, lsl #27 + 8008fd6: 6b2a ldr r2, [r5, #48] ; 0x30 + 8008fd8: 3a01 subs r2, #1 + 8008fda: ea43 1302 orr.w r3, r3, r2, lsl #4 + 8008fde: 6bea ldr r2, [r5, #60] ; 0x3c + 8008fe0: 0852 lsrs r2, r2, #1 + 8008fe2: 3a01 subs r2, #1 + 8008fe4: ea43 5342 orr.w r3, r3, r2, lsl #21 + 8008fe8: 6c2a ldr r2, [r5, #64] ; 0x40 + 8008fea: 0852 lsrs r2, r2, #1 + 8008fec: 3a01 subs r2, #1 + 8008fee: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8008ff2: 60e3 str r3, [r4, #12] + __HAL_RCC_PLL_ENABLE(); + 8008ff4: 6823 ldr r3, [r4, #0] + 8008ff6: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8008ffa: 6023 str r3, [r4, #0] + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SYSCLK); + 8008ffc: 68e3 ldr r3, [r4, #12] + 8008ffe: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8009002: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8009004: f7fe f9ea bl 80073dc + 8009008: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 800900a: 6823 ldr r3, [r4, #0] + 800900c: 0198 lsls r0, r3, #6 + 800900e: f53f ae39 bmi.w 8008c84 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8009012: f7fe f9e3 bl 80073dc + 8009016: 1b40 subs r0, r0, r5 + 8009018: 2802 cmp r0, #2 + 800901a: d9f6 bls.n 800900a + 800901c: e63a b.n 8008c94 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 800901e: f7fe f9dd bl 80073dc + 8009022: 1b80 subs r0, r0, r6 + 8009024: 2802 cmp r0, #2 + 8009026: d9c8 bls.n 8008fba + 8009028: e634 b.n 8008c94 + if(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 800902a: 6823 ldr r3, [r4, #0] + 800902c: 0199 lsls r1, r3, #6 + 800902e: f53f ae29 bmi.w 8008c84 + __HAL_RCC_PLL_ENABLE(); + 8009032: 6823 ldr r3, [r4, #0] + 8009034: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8009038: 6023 str r3, [r4, #0] + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SYSCLK); + 800903a: 68e3 ldr r3, [r4, #12] + 800903c: f043 7380 orr.w r3, r3, #16777216 ; 0x1000000 + 8009040: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8009042: f7fe f9cb bl 80073dc + 8009046: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 8009048: 6823 ldr r3, [r4, #0] + 800904a: 019a lsls r2, r3, #6 + 800904c: f53f ae1a bmi.w 8008c84 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8009050: f7fe f9c4 bl 80073dc + 8009054: 1b40 subs r0, r0, r5 + 8009056: 2802 cmp r0, #2 + 8009058: d9f6 bls.n 8009048 + 800905a: e61b b.n 8008c94 + if(sysclk_source != RCC_CFGR_SWS_PLL) + 800905c: 2e0c cmp r6, #12 + 800905e: f43f ad79 beq.w 8008b54 + __HAL_RCC_PLL_DISABLE(); + 8009062: 6823 ldr r3, [r4, #0] + 8009064: f023 7380 bic.w r3, r3, #16777216 ; 0x1000000 + 8009068: 6023 str r3, [r4, #0] + if(READ_BIT(RCC->CR, (RCC_CR_PLLSAI1RDY | RCC_CR_PLLSAI2RDY)) == 0U) + 800906a: 6823 ldr r3, [r4, #0] + 800906c: f013 5f20 tst.w r3, #671088640 ; 0x28000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 8009070: bf02 ittt eq + 8009072: 68e3 ldreq r3, [r4, #12] + 8009074: f023 0303 biceq.w r3, r3, #3 + 8009078: 60e3 streq r3, [r4, #12] + __HAL_RCC_PLLCLKOUT_DISABLE(RCC_PLL_SYSCLK | RCC_PLL_48M1CLK | RCC_PLL_SAI3CLK); + 800907a: 68e3 ldr r3, [r4, #12] + 800907c: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 8009080: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 8009084: 60e3 str r3, [r4, #12] + tickstart = HAL_GetTick(); + 8009086: f7fe f9a9 bl 80073dc + 800908a: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != 0U) + 800908c: 6823 ldr r3, [r4, #0] + 800908e: 019b lsls r3, r3, #6 + 8009090: f57f adf8 bpl.w 8008c84 + if((HAL_GetTick() - tickstart) > PLL_TIMEOUT_VALUE) + 8009094: f7fe f9a2 bl 80073dc + 8009098: 1b40 subs r0, r0, r5 + 800909a: 2802 cmp r0, #2 + 800909c: d9f6 bls.n 800908c + 800909e: e5f9 b.n 8008c94 + 80090a0: 40007000 .word 0x40007000 + 80090a4: 019d800c .word 0x019d800c + +080090a8 : +{ + 80090a8: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 80090ac: 460e mov r6, r1 + if(RCC_ClkInitStruct == NULL) + 80090ae: 4605 mov r5, r0 + 80090b0: b910 cbnz r0, 80090b8 + return HAL_ERROR; + 80090b2: 2001 movs r0, #1 +} + 80090b4: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + if(FLatency > __HAL_FLASH_GET_LATENCY()) + 80090b8: 4a6f ldr r2, [pc, #444] ; (8009278 ) + 80090ba: 6813 ldr r3, [r2, #0] + 80090bc: f003 030f and.w r3, r3, #15 + 80090c0: 428b cmp r3, r1 + 80090c2: d335 bcc.n 8009130 + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK) + 80090c4: 6829 ldr r1, [r5, #0] + 80090c6: f011 0701 ands.w r7, r1, #1 + 80090ca: d13c bne.n 8009146 + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK) + 80090cc: 682a ldr r2, [r5, #0] + 80090ce: 0791 lsls r1, r2, #30 + 80090d0: f140 80b7 bpl.w 8009242 + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider); + 80090d4: 4969 ldr r1, [pc, #420] ; (800927c ) + 80090d6: 68a8 ldr r0, [r5, #8] + 80090d8: 688b ldr r3, [r1, #8] + 80090da: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 80090de: 4303 orrs r3, r0 + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1); + 80090e0: 608b str r3, [r1, #8] + if(FLatency < __HAL_FLASH_GET_LATENCY()) + 80090e2: 4965 ldr r1, [pc, #404] ; (8009278 ) + 80090e4: 680b ldr r3, [r1, #0] + 80090e6: f003 030f and.w r3, r3, #15 + 80090ea: 42b3 cmp r3, r6 + 80090ec: f200 80b1 bhi.w 8009252 + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1) + 80090f0: f012 0f04 tst.w r2, #4 + 80090f4: 4c61 ldr r4, [pc, #388] ; (800927c ) + 80090f6: f040 80b8 bne.w 800926a + if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2) + 80090fa: 0713 lsls r3, r2, #28 + 80090fc: d506 bpl.n 800910c + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U)); + 80090fe: 68a3 ldr r3, [r4, #8] + 8009100: 692a ldr r2, [r5, #16] + 8009102: f423 5360 bic.w r3, r3, #14336 ; 0x3800 + 8009106: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 800910a: 60a3 str r3, [r4, #8] + SystemCoreClock = HAL_RCC_GetSysClockFreq() >> (AHBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos] & 0x1FU); + 800910c: f7ff fcd0 bl 8008ab0 + 8009110: 68a3 ldr r3, [r4, #8] + 8009112: 4a5b ldr r2, [pc, #364] ; (8009280 ) + 8009114: f3c3 1303 ubfx r3, r3, #4, #4 + 8009118: 5cd3 ldrb r3, [r2, r3] + 800911a: f003 031f and.w r3, r3, #31 + 800911e: 40d8 lsrs r0, r3 + 8009120: 4b58 ldr r3, [pc, #352] ; (8009284 ) + 8009122: 6018 str r0, [r3, #0] + status = HAL_InitTick(uwTickPrio); + 8009124: 4b58 ldr r3, [pc, #352] ; (8009288 ) + 8009126: 6818 ldr r0, [r3, #0] +} + 8009128: e8bd 43f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + status = HAL_InitTick(uwTickPrio); + 800912c: f7fe b958 b.w 80073e0 + __HAL_FLASH_SET_LATENCY(FLatency); + 8009130: 6813 ldr r3, [r2, #0] + 8009132: f023 030f bic.w r3, r3, #15 + 8009136: 430b orrs r3, r1 + 8009138: 6013 str r3, [r2, #0] + if(__HAL_FLASH_GET_LATENCY() != FLatency) + 800913a: 6813 ldr r3, [r2, #0] + 800913c: f003 030f and.w r3, r3, #15 + 8009140: 428b cmp r3, r1 + 8009142: d1b6 bne.n 80090b2 + 8009144: e7be b.n 80090c4 + if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) + 8009146: 686b ldr r3, [r5, #4] + 8009148: 4c4c ldr r4, [pc, #304] ; (800927c ) + 800914a: 2b03 cmp r3, #3 + 800914c: d163 bne.n 8009216 + if(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == 0U) + 800914e: 6823 ldr r3, [r4, #0] + 8009150: 019b lsls r3, r3, #6 + 8009152: d5ae bpl.n 80090b2 +static uint32_t RCC_GetSysClockFreqFromPLLSource(void) +{ + uint32_t msirange = 0U; + uint32_t pllvco, pllsource, pllr, pllm, sysclockfreq; /* no init needed */ + + if(__HAL_RCC_GET_PLL_OSCSOURCE() == RCC_PLLSOURCE_MSI) + 8009154: 68e3 ldr r3, [r4, #12] + 8009156: f003 0303 and.w r3, r3, #3 + 800915a: 2b01 cmp r3, #1 + 800915c: d145 bne.n 80091ea + { + /* Get MSI range source */ + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 800915e: 6823 ldr r3, [r4, #0] + else + { /* MSIRANGE from RCC_CR applies */ + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + } + /*MSI frequency range in HZ*/ + msirange = MSIRangeTable[msirange]; + 8009160: 4a4a ldr r2, [pc, #296] ; (800928c ) + if(READ_BIT(RCC->CR, RCC_CR_MSIRGSEL) == 0U) + 8009162: 071f lsls r7, r3, #28 + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 8009164: bf55 itete pl + 8009166: f8d4 3094 ldrpl.w r3, [r4, #148] ; 0x94 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 800916a: 6823 ldrmi r3, [r4, #0] + msirange = READ_BIT(RCC->CSR, RCC_CSR_MSISRANGE) >> RCC_CSR_MSISRANGE_Pos; + 800916c: f3c3 2303 ubfxpl r3, r3, #8, #4 + msirange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE) >> RCC_CR_MSIRANGE_Pos; + 8009170: f3c3 1303 ubfxmi r3, r3, #4, #4 + msirange = MSIRangeTable[msirange]; + 8009174: f852 2023 ldr.w r2, [r2, r3, lsl #2] + } + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE) * PLLN / PLLM + SYSCLK = PLL_VCO / PLLR + */ + pllsource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8009178: 68e3 ldr r3, [r4, #12] + 800917a: f003 0303 and.w r3, r3, #3 + + switch (pllsource) + 800917e: 2b02 cmp r3, #2 + 8009180: d035 beq.n 80091ee + 8009182: 4843 ldr r0, [pc, #268] ; (8009290 ) + 8009184: 2b03 cmp r3, #3 + 8009186: bf08 it eq + 8009188: 4602 moveq r2, r0 + case RCC_PLLSOURCE_MSI: /* MSI used as PLL clock source */ + default: + pllvco = msirange; + break; + } + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 800918a: 68e0 ldr r0, [r4, #12] + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 800918c: 68e3 ldr r3, [r4, #12] + 800918e: f3c3 2306 ubfx r3, r3, #8, #7 + 8009192: 4353 muls r3, r2 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 8009194: 68e2 ldr r2, [r4, #12] + 8009196: f3c2 6241 ubfx r2, r2, #25, #2 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 800919a: f3c0 1003 ubfx r0, r0, #4, #4 + pllr = ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U ) * 2U; + 800919e: 3201 adds r2, #1 + 80091a0: 0052 lsls r2, r2, #1 + pllm = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U ; + 80091a2: 3001 adds r0, #1 + pllvco = (pllvco * (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)) / pllm; + 80091a4: fbb3 f3f0 udiv r3, r3, r0 + sysclockfreq = pllvco / pllr; + 80091a8: fbb3 f3f2 udiv r3, r3, r2 + if(RCC_GetSysClockFreqFromPLLSource() > 80000000U) + 80091ac: 4a39 ldr r2, [pc, #228] ; (8009294 ) + 80091ae: 4293 cmp r3, r2 + 80091b0: d81f bhi.n 80091f2 + uint32_t hpre = RCC_SYSCLK_DIV1; + 80091b2: 2700 movs r7, #0 + MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_ClkInitStruct->SYSCLKSource); + 80091b4: 68a3 ldr r3, [r4, #8] + 80091b6: 686a ldr r2, [r5, #4] + 80091b8: f023 0303 bic.w r3, r3, #3 + 80091bc: 4313 orrs r3, r2 + 80091be: 60a3 str r3, [r4, #8] + tickstart = HAL_GetTick(); + 80091c0: f7fe f90c bl 80073dc + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 80091c4: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 80091c8: 4680 mov r8, r0 + while(__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos)) + 80091ca: 68a3 ldr r3, [r4, #8] + 80091cc: 686a ldr r2, [r5, #4] + 80091ce: f003 030c and.w r3, r3, #12 + 80091d2: ebb3 0f82 cmp.w r3, r2, lsl #2 + 80091d6: f43f af79 beq.w 80090cc + if((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE) + 80091da: f7fe f8ff bl 80073dc + 80091de: eba0 0008 sub.w r0, r0, r8 + 80091e2: 4548 cmp r0, r9 + 80091e4: d9f1 bls.n 80091ca + return HAL_TIMEOUT; + 80091e6: 2003 movs r0, #3 + 80091e8: e764 b.n 80090b4 + uint32_t msirange = 0U; + 80091ea: 2200 movs r2, #0 + 80091ec: e7c4 b.n 8009178 + pllvco = HSI_VALUE; + 80091ee: 4a2a ldr r2, [pc, #168] ; (8009298 ) + 80091f0: e7cb b.n 800918a + if(READ_BIT(RCC->CFGR, RCC_CFGR_HPRE) == RCC_SYSCLK_DIV1) + 80091f2: 68a3 ldr r3, [r4, #8] + 80091f4: f013 0ff0 tst.w r3, #240 ; 0xf0 + 80091f8: d107 bne.n 800920a + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV2); + 80091fa: 68a3 ldr r3, [r4, #8] + 80091fc: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8009200: f043 0380 orr.w r3, r3, #128 ; 0x80 + 8009204: 60a3 str r3, [r4, #8] + hpre = RCC_SYSCLK_DIV2; + 8009206: 2780 movs r7, #128 ; 0x80 + 8009208: e7d4 b.n 80091b4 + else if((((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK) && (RCC_ClkInitStruct->AHBCLKDivider == RCC_SYSCLK_DIV1)) + 800920a: 0788 lsls r0, r1, #30 + 800920c: d5d1 bpl.n 80091b2 + 800920e: 68ab ldr r3, [r5, #8] + 8009210: 2b00 cmp r3, #0 + 8009212: d1ce bne.n 80091b2 + 8009214: e7f1 b.n 80091fa + if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE) + 8009216: 2b02 cmp r3, #2 + 8009218: d10a bne.n 8009230 + if(READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U) + 800921a: 6823 ldr r3, [r4, #0] + 800921c: f413 3f00 tst.w r3, #131072 ; 0x20000 + if(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 8009220: f43f af47 beq.w 80090b2 + if(HAL_RCC_GetSysClockFreq() > 80000000U) + 8009224: f7ff fc44 bl 8008ab0 + 8009228: 4b1a ldr r3, [pc, #104] ; (8009294 ) + 800922a: 4298 cmp r0, r3 + 800922c: d9c1 bls.n 80091b2 + 800922e: e7e4 b.n 80091fa + else if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_MSI) + 8009230: b91b cbnz r3, 800923a + if(READ_BIT(RCC->CR, RCC_CR_MSIRDY) == 0U) + 8009232: 6823 ldr r3, [r4, #0] + 8009234: f013 0f02 tst.w r3, #2 + 8009238: e7f2 b.n 8009220 + if(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == 0U) + 800923a: 6823 ldr r3, [r4, #0] + 800923c: f413 6f80 tst.w r3, #1024 ; 0x400 + 8009240: e7ee b.n 8009220 + if(hpre == RCC_SYSCLK_DIV2) + 8009242: 2f80 cmp r7, #128 ; 0x80 + 8009244: f47f af4d bne.w 80090e2 + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1); + 8009248: 490c ldr r1, [pc, #48] ; (800927c ) + 800924a: 688b ldr r3, [r1, #8] + 800924c: f023 03f0 bic.w r3, r3, #240 ; 0xf0 + 8009250: e746 b.n 80090e0 + __HAL_FLASH_SET_LATENCY(FLatency); + 8009252: 680b ldr r3, [r1, #0] + 8009254: f023 030f bic.w r3, r3, #15 + 8009258: 4333 orrs r3, r6 + 800925a: 600b str r3, [r1, #0] + if(__HAL_FLASH_GET_LATENCY() != FLatency) + 800925c: 680b ldr r3, [r1, #0] + 800925e: f003 030f and.w r3, r3, #15 + 8009262: 42b3 cmp r3, r6 + 8009264: f47f af25 bne.w 80090b2 + 8009268: e742 b.n 80090f0 + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider); + 800926a: 68a3 ldr r3, [r4, #8] + 800926c: 68e9 ldr r1, [r5, #12] + 800926e: f423 63e0 bic.w r3, r3, #1792 ; 0x700 + 8009272: 430b orrs r3, r1 + 8009274: 60a3 str r3, [r4, #8] + 8009276: e740 b.n 80090fa + 8009278: 40022000 .word 0x40022000 + 800927c: 40021000 .word 0x40021000 + 8009280: 08010a58 .word 0x08010a58 + 8009284: 2009e2ac .word 0x2009e2ac + 8009288: 2009e2b0 .word 0x2009e2b0 + 800928c: 08010a70 .word 0x08010a70 + 8009290: 007a1200 .word 0x007a1200 + 8009294: 04c4b400 .word 0x04c4b400 + 8009298: 00f42400 .word 0x00f42400 + +0800929c : +} + 800929c: 4b01 ldr r3, [pc, #4] ; (80092a4 ) + 800929e: 6818 ldr r0, [r3, #0] + 80092a0: 4770 bx lr + 80092a2: bf00 nop + 80092a4: 2009e2ac .word 0x2009e2ac + +080092a8 : + return (HAL_RCC_GetHCLKFreq() >> (APBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos] & 0x1FU)); + 80092a8: 4b05 ldr r3, [pc, #20] ; (80092c0 ) + 80092aa: 4a06 ldr r2, [pc, #24] ; (80092c4 ) + 80092ac: 689b ldr r3, [r3, #8] + 80092ae: f3c3 2302 ubfx r3, r3, #8, #3 + 80092b2: 5cd3 ldrb r3, [r2, r3] + 80092b4: 4a04 ldr r2, [pc, #16] ; (80092c8 ) + 80092b6: 6810 ldr r0, [r2, #0] + 80092b8: f003 031f and.w r3, r3, #31 +} + 80092bc: 40d8 lsrs r0, r3 + 80092be: 4770 bx lr + 80092c0: 40021000 .word 0x40021000 + 80092c4: 08010a68 .word 0x08010a68 + 80092c8: 2009e2ac .word 0x2009e2ac + +080092cc : + return (HAL_RCC_GetHCLKFreq()>> (APBPrescTable[READ_BIT(RCC->CFGR, RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos] & 0x1FU)); + 80092cc: 4b05 ldr r3, [pc, #20] ; (80092e4 ) + 80092ce: 4a06 ldr r2, [pc, #24] ; (80092e8 ) + 80092d0: 689b ldr r3, [r3, #8] + 80092d2: f3c3 23c2 ubfx r3, r3, #11, #3 + 80092d6: 5cd3 ldrb r3, [r2, r3] + 80092d8: 4a04 ldr r2, [pc, #16] ; (80092ec ) + 80092da: 6810 ldr r0, [r2, #0] + 80092dc: f003 031f and.w r3, r3, #31 +} + 80092e0: 40d8 lsrs r0, r3 + 80092e2: 4770 bx lr + 80092e4: 40021000 .word 0x40021000 + 80092e8: 08010a68 .word 0x08010a68 + 80092ec: 2009e2ac .word 0x2009e2ac + +080092f0 : + RCC_OscInitStruct->OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_MSI | \ + 80092f0: 233f movs r3, #63 ; 0x3f + 80092f2: 6003 str r3, [r0, #0] + if(READ_BIT(RCC->CR, RCC_CR_HSEBYP) == RCC_CR_HSEBYP) + 80092f4: 4b2e ldr r3, [pc, #184] ; (80093b0 ) + 80092f6: 681a ldr r2, [r3, #0] + 80092f8: 0351 lsls r1, r2, #13 + 80092fa: d54a bpl.n 8009392 + RCC_OscInitStruct->HSEState = RCC_HSE_BYPASS; + 80092fc: f44f 22a0 mov.w r2, #327680 ; 0x50000 + RCC_OscInitStruct->HSEState = RCC_HSE_OFF; + 8009300: 6042 str r2, [r0, #4] + if(READ_BIT(RCC->CR, RCC_CR_MSION) == RCC_CR_MSION) + 8009302: 681a ldr r2, [r3, #0] + 8009304: f002 0201 and.w r2, r2, #1 + 8009308: 6182 str r2, [r0, #24] + RCC_OscInitStruct->MSICalibrationValue = READ_BIT(RCC->ICSCR, RCC_ICSCR_MSITRIM) >> RCC_ICSCR_MSITRIM_Pos; + 800930a: 685a ldr r2, [r3, #4] + 800930c: f3c2 2207 ubfx r2, r2, #8, #8 + 8009310: 61c2 str r2, [r0, #28] + RCC_OscInitStruct->MSIClockRange = READ_BIT(RCC->CR, RCC_CR_MSIRANGE); + 8009312: 681a ldr r2, [r3, #0] + 8009314: f002 02f0 and.w r2, r2, #240 ; 0xf0 + 8009318: 6202 str r2, [r0, #32] + if(READ_BIT(RCC->CR, RCC_CR_HSION) == RCC_CR_HSION) + 800931a: 681a ldr r2, [r3, #0] + RCC_OscInitStruct->HSIState = RCC_HSI_ON; + 800931c: f402 7280 and.w r2, r2, #256 ; 0x100 + 8009320: 60c2 str r2, [r0, #12] + RCC_OscInitStruct->HSICalibrationValue = READ_BIT(RCC->ICSCR, RCC_ICSCR_HSITRIM) >> RCC_ICSCR_HSITRIM_Pos; + 8009322: 685a ldr r2, [r3, #4] + 8009324: f3c2 6206 ubfx r2, r2, #24, #7 + 8009328: 6102 str r2, [r0, #16] + if(READ_BIT(RCC->BDCR, RCC_BDCR_LSEBYP) == RCC_BDCR_LSEBYP) + 800932a: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800932e: 0752 lsls r2, r2, #29 + 8009330: d536 bpl.n 80093a0 + RCC_OscInitStruct->LSEState = RCC_LSE_BYPASS; + 8009332: 2205 movs r2, #5 + RCC_OscInitStruct->LSEState = RCC_LSE_OFF; + 8009334: 6082 str r2, [r0, #8] + if(READ_BIT(RCC->CSR, RCC_CSR_LSION) == RCC_CSR_LSION) + 8009336: f8d3 2094 ldr.w r2, [r3, #148] ; 0x94 + 800933a: f002 0201 and.w r2, r2, #1 + 800933e: 6142 str r2, [r0, #20] + if(READ_BIT(RCC->CRRCR, RCC_CRRCR_HSI48ON) == RCC_CRRCR_HSI48ON) + 8009340: f8d3 2098 ldr.w r2, [r3, #152] ; 0x98 + 8009344: f002 0201 and.w r2, r2, #1 + 8009348: 6242 str r2, [r0, #36] ; 0x24 + if(READ_BIT(RCC->CR, RCC_CR_PLLON) == RCC_CR_PLLON) + 800934a: 681a ldr r2, [r3, #0] + RCC_OscInitStruct->PLL.PLLState = RCC_PLL_OFF; + 800934c: f012 7f80 tst.w r2, #16777216 ; 0x1000000 + 8009350: bf14 ite ne + 8009352: 2202 movne r2, #2 + 8009354: 2201 moveq r2, #1 + 8009356: 6282 str r2, [r0, #40] ; 0x28 + RCC_OscInitStruct->PLL.PLLSource = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC); + 8009358: 68da ldr r2, [r3, #12] + 800935a: f002 0203 and.w r2, r2, #3 + 800935e: 62c2 str r2, [r0, #44] ; 0x2c + RCC_OscInitStruct->PLL.PLLM = (READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U; + 8009360: 68da ldr r2, [r3, #12] + 8009362: f3c2 1203 ubfx r2, r2, #4, #4 + 8009366: 3201 adds r2, #1 + 8009368: 6302 str r2, [r0, #48] ; 0x30 + RCC_OscInitStruct->PLL.PLLN = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 800936a: 68da ldr r2, [r3, #12] + 800936c: f3c2 2206 ubfx r2, r2, #8, #7 + 8009370: 6342 str r2, [r0, #52] ; 0x34 + RCC_OscInitStruct->PLL.PLLQ = (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U); + 8009372: 68da ldr r2, [r3, #12] + 8009374: f3c2 5241 ubfx r2, r2, #21, #2 + 8009378: 3201 adds r2, #1 + 800937a: 0052 lsls r2, r2, #1 + 800937c: 63c2 str r2, [r0, #60] ; 0x3c + RCC_OscInitStruct->PLL.PLLR = (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1U) << 1U); + 800937e: 68da ldr r2, [r3, #12] + 8009380: f3c2 6241 ubfx r2, r2, #25, #2 + 8009384: 3201 adds r2, #1 + 8009386: 0052 lsls r2, r2, #1 + 8009388: 6402 str r2, [r0, #64] ; 0x40 + RCC_OscInitStruct->PLL.PLLP = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 800938a: 68db ldr r3, [r3, #12] + 800938c: 0edb lsrs r3, r3, #27 + 800938e: 6383 str r3, [r0, #56] ; 0x38 +} + 8009390: 4770 bx lr + else if(READ_BIT(RCC->CR, RCC_CR_HSEON) == RCC_CR_HSEON) + 8009392: 681a ldr r2, [r3, #0] + 8009394: f412 3280 ands.w r2, r2, #65536 ; 0x10000 + RCC_OscInitStruct->HSEState = RCC_HSE_ON; + 8009398: bf18 it ne + 800939a: f44f 3280 movne.w r2, #65536 ; 0x10000 + 800939e: e7af b.n 8009300 + else if(READ_BIT(RCC->BDCR, RCC_BDCR_LSEON) == RCC_BDCR_LSEON) + 80093a0: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 80093a4: f012 0201 ands.w r2, r2, #1 + RCC_OscInitStruct->LSEState = RCC_LSE_ON; + 80093a8: bf18 it ne + 80093aa: 2201 movne r2, #1 + 80093ac: e7c2 b.n 8009334 + 80093ae: bf00 nop + 80093b0: 40021000 .word 0x40021000 + +080093b4 : + RCC_ClkInitStruct->ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; + 80093b4: 230f movs r3, #15 + 80093b6: 6003 str r3, [r0, #0] + RCC_ClkInitStruct->SYSCLKSource = READ_BIT(RCC->CFGR, RCC_CFGR_SW); + 80093b8: 4b0b ldr r3, [pc, #44] ; (80093e8 ) + 80093ba: 689a ldr r2, [r3, #8] + 80093bc: f002 0203 and.w r2, r2, #3 + 80093c0: 6042 str r2, [r0, #4] + RCC_ClkInitStruct->AHBCLKDivider = READ_BIT(RCC->CFGR, RCC_CFGR_HPRE); + 80093c2: 689a ldr r2, [r3, #8] + 80093c4: f002 02f0 and.w r2, r2, #240 ; 0xf0 + 80093c8: 6082 str r2, [r0, #8] + RCC_ClkInitStruct->APB1CLKDivider = READ_BIT(RCC->CFGR, RCC_CFGR_PPRE1); + 80093ca: 689a ldr r2, [r3, #8] + 80093cc: f402 62e0 and.w r2, r2, #1792 ; 0x700 + 80093d0: 60c2 str r2, [r0, #12] + RCC_ClkInitStruct->APB2CLKDivider = (READ_BIT(RCC->CFGR, RCC_CFGR_PPRE2) >> 3U); + 80093d2: 689b ldr r3, [r3, #8] + 80093d4: 08db lsrs r3, r3, #3 + 80093d6: f403 63e0 and.w r3, r3, #1792 ; 0x700 + 80093da: 6103 str r3, [r0, #16] + *pFLatency = __HAL_FLASH_GET_LATENCY(); + 80093dc: 4b03 ldr r3, [pc, #12] ; (80093ec ) + 80093de: 681b ldr r3, [r3, #0] + 80093e0: f003 030f and.w r3, r3, #15 + 80093e4: 600b str r3, [r1, #0] +} + 80093e6: 4770 bx lr + 80093e8: 40021000 .word 0x40021000 + 80093ec: 40022000 .word 0x40022000 + +080093f0 : + SET_BIT(RCC->CR, RCC_CR_CSSON) ; + 80093f0: 4a02 ldr r2, [pc, #8] ; (80093fc ) + 80093f2: 6813 ldr r3, [r2, #0] + 80093f4: f443 2300 orr.w r3, r3, #524288 ; 0x80000 + 80093f8: 6013 str r3, [r2, #0] +} + 80093fa: 4770 bx lr + 80093fc: 40021000 .word 0x40021000 + +08009400 : +} + 8009400: 4770 bx lr + ... + +08009404 : +{ + 8009404: b510 push {r4, lr} + if(__HAL_RCC_GET_IT(RCC_IT_CSS)) + 8009406: 4c05 ldr r4, [pc, #20] ; (800941c ) + 8009408: 69e3 ldr r3, [r4, #28] + 800940a: 05db lsls r3, r3, #23 + 800940c: d504 bpl.n 8009418 + HAL_RCC_CSSCallback(); + 800940e: f7ff fff7 bl 8009400 + __HAL_RCC_CLEAR_IT(RCC_IT_CSS); + 8009412: f44f 7380 mov.w r3, #256 ; 0x100 + 8009416: 6223 str r3, [r4, #32] +} + 8009418: bd10 pop {r4, pc} + 800941a: bf00 nop + 800941c: 40021000 .word 0x40021000 + +08009420 : +#if defined(RCC_PLLP_SUPPORT) + uint32_t pllp = 0U; +#endif /* RCC_PLLP_SUPPORT */ + + /* Handle SAIs */ + if(PeriphClk == RCC_PERIPHCLK_SAI1) + 8009420: f5b0 6f00 cmp.w r0, #2048 ; 0x800 + 8009424: 4a3d ldr r2, [pc, #244] ; (800951c ) + 8009426: d108 bne.n 800943a + { + srcclk = __HAL_RCC_GET_SAI1_SOURCE(); + 8009428: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 800942c: f003 03e0 and.w r3, r3, #224 ; 0xe0 + if(srcclk == RCC_SAI1CLKSOURCE_PIN) + 8009430: 2b60 cmp r3, #96 ; 0x60 + 8009432: d12d bne.n 8009490 + { + frequency = EXTERNAL_SAI1_CLOCK_VALUE; + 8009434: f64b 3080 movw r0, #48000 ; 0xbb80 + 8009438: 4770 bx lr + /* Else, PLL clock output to check below */ + } +#if defined(SAI2) + else + { + if(PeriphClk == RCC_PERIPHCLK_SAI2) + 800943a: f5b0 5f80 cmp.w r0, #4096 ; 0x1000 + 800943e: d12a bne.n 8009496 + { + srcclk = __HAL_RCC_GET_SAI2_SOURCE(); + 8009440: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009444: f403 63e0 and.w r3, r3, #1792 ; 0x700 + if(srcclk == RCC_SAI2CLKSOURCE_PIN) + 8009448: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 800944c: d0f2 beq.n 8009434 + if(frequency == 0U) + { + pllvco = InputFrequency; + +#if defined(SAI2) + if((srcclk == RCC_SAI1CLKSOURCE_PLL) || (srcclk == RCC_SAI2CLKSOURCE_PLL)) + 800944e: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 8009452: d15c bne.n 800950e + { + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY) && (__HAL_RCC_GET_PLLCLKOUT_CONFIG(RCC_PLL_SAI3CLK) != 0U)) + 8009454: 6810 ldr r0, [r2, #0] + 8009456: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 800945a: d05d beq.n 8009518 + 800945c: 68d0 ldr r0, [r2, #12] + 800945e: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 8009462: d059 beq.n 8009518 + { + /* f(PLL Source) / PLLM */ + pllvco = (pllvco / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009464: 68d0 ldr r0, [r2, #12] + 8009466: f3c0 1003 ubfx r0, r0, #4, #4 + 800946a: 3001 adds r0, #1 + 800946c: fbb1 f0f0 udiv r0, r1, r0 + /* f(PLLSAI3CLK) = f(VCO input) * PLLN / PLLP */ + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009470: 68d1 ldr r1, [r2, #12] +#if defined(RCC_PLLP_DIV_2_31_SUPPORT) + pllp = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 8009472: 68d3 ldr r3, [r2, #12] +#endif + if(pllp == 0U) + 8009474: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009476: f3c1 2106 ubfx r1, r1, #8, #7 + if(pllp == 0U) + 800947a: d105 bne.n 8009488 + { + if(READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLP) != 0U) + 800947c: 68d3 ldr r3, [r2, #12] + { + pllp = 17U; + } + else + { + pllp = 7U; + 800947e: f413 3f00 tst.w r3, #131072 ; 0x20000 + 8009482: bf14 ite ne + 8009484: 2311 movne r3, #17 + 8009486: 2307 moveq r3, #7 + } + } + frequency = (pllvco * plln) / pllp; + 8009488: 4348 muls r0, r1 + 800948a: fbb0 f0f3 udiv r0, r0, r3 + 800948e: 4770 bx lr + if((srcclk == RCC_SAI1CLKSOURCE_PLL) || (srcclk == RCC_SAI2CLKSOURCE_PLL)) + 8009490: 2b40 cmp r3, #64 ; 0x40 + 8009492: d0df beq.n 8009454 + else if(srcclk == 0U) /* RCC_SAI1CLKSOURCE_PLLSAI1 || RCC_SAI2CLKSOURCE_PLLSAI1 */ + 8009494: b9ab cbnz r3, 80094c2 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY) && (__HAL_RCC_GET_PLLSAI1CLKOUT_CONFIG(RCC_PLLSAI1_SAI1CLK) != 0U)) + 8009496: 6810 ldr r0, [r2, #0] + 8009498: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 800949c: d03c beq.n 8009518 + 800949e: 6910 ldr r0, [r2, #16] + 80094a0: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 80094a4: d038 beq.n 8009518 + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 80094a6: 6913 ldr r3, [r2, #16] + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 80094a8: 6910 ldr r0, [r2, #16] + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 80094aa: f3c3 1303 ubfx r3, r3, #4, #4 + 80094ae: 3301 adds r3, #1 + 80094b0: fbb1 f1f3 udiv r1, r1, r3 + pllp = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1PDIV) >> RCC_PLLSAI1CFGR_PLLSAI1PDIV_Pos; + 80094b4: 6913 ldr r3, [r2, #16] + if(pllp == 0U) + 80094b6: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 80094b8: f3c0 2006 ubfx r0, r0, #8, #7 + if(pllp == 0U) + 80094bc: d1e4 bne.n 8009488 + if(READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1P) != 0U) + 80094be: 6913 ldr r3, [r2, #16] + 80094c0: e7dd b.n 800947e + else if((srcclk == RCC_SAI1CLKSOURCE_HSI) || (srcclk == RCC_SAI2CLKSOURCE_HSI)) + 80094c2: 2b80 cmp r3, #128 ; 0x80 + 80094c4: d106 bne.n 80094d4 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 80094c6: 6810 ldr r0, [r2, #0] + frequency = HSI_VALUE; + 80094c8: 4b15 ldr r3, [pc, #84] ; (8009520 ) + 80094ca: f410 6080 ands.w r0, r0, #1024 ; 0x400 + 80094ce: bf18 it ne + 80094d0: 4618 movne r0, r3 + 80094d2: 4770 bx lr + else if((srcclk == RCC_SAI1CLKSOURCE_PLLSAI2) || (srcclk == RCC_SAI2CLKSOURCE_PLLSAI2)) + 80094d4: 2b20 cmp r3, #32 + 80094d6: d002 beq.n 80094de + 80094d8: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 80094dc: d115 bne.n 800950a + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI2RDY) && (__HAL_RCC_GET_PLLSAI2CLKOUT_CONFIG(RCC_PLLSAI2_SAI2CLK) != 0U)) + 80094de: 6810 ldr r0, [r2, #0] + 80094e0: f010 5000 ands.w r0, r0, #536870912 ; 0x20000000 + 80094e4: d018 beq.n 8009518 + 80094e6: 6950 ldr r0, [r2, #20] + 80094e8: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 80094ec: d014 beq.n 8009518 + pllvco = (pllvco / ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2M) >> RCC_PLLSAI2CFGR_PLLSAI2M_Pos) + 1U)); + 80094ee: 6953 ldr r3, [r2, #20] + 80094f0: f3c3 1303 ubfx r3, r3, #4, #4 + 80094f4: 3301 adds r3, #1 + 80094f6: fbb1 f0f3 udiv r0, r1, r3 + plln = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 80094fa: 6951 ldr r1, [r2, #20] + pllp = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2PDIV) >> RCC_PLLSAI2CFGR_PLLSAI2PDIV_Pos; + 80094fc: 6953 ldr r3, [r2, #20] + if(pllp == 0U) + 80094fe: 0edb lsrs r3, r3, #27 + plln = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 8009500: f3c1 2106 ubfx r1, r1, #8, #7 + if(pllp == 0U) + 8009504: d1c0 bne.n 8009488 + if(READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2P) != 0U) + 8009506: 6953 ldr r3, [r2, #20] + 8009508: e7b9 b.n 800947e + 800950a: 2000 movs r0, #0 + /* No clock source, frequency default init at 0 */ + } + } + + + return frequency; + 800950c: 4770 bx lr + else if(srcclk == 0U) /* RCC_SAI1CLKSOURCE_PLLSAI1 || RCC_SAI2CLKSOURCE_PLLSAI1 */ + 800950e: 2b00 cmp r3, #0 + 8009510: d0c1 beq.n 8009496 + else if((srcclk == RCC_SAI1CLKSOURCE_HSI) || (srcclk == RCC_SAI2CLKSOURCE_HSI)) + 8009512: f5b3 6f80 cmp.w r3, #1024 ; 0x400 + 8009516: e7d5 b.n 80094c4 +} + 8009518: 4770 bx lr + 800951a: bf00 nop + 800951c: 40021000 .word 0x40021000 + 8009520: 00f42400 .word 0x00f42400 + +08009524 : +{ + 8009524: b5f8 push {r3, r4, r5, r6, r7, lr} + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009526: 4c3c ldr r4, [pc, #240] ; (8009618 ) + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai1->PLLSAI1Source) + 8009528: 6803 ldr r3, [r0, #0] + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800952a: 68e2 ldr r2, [r4, #12] +{ + 800952c: 4605 mov r5, r0 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800952e: 0790 lsls r0, r2, #30 +{ + 8009530: 460f mov r7, r1 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009532: d023 beq.n 800957c + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai1->PLLSAI1Source) + 8009534: 68e2 ldr r2, [r4, #12] + 8009536: f002 0203 and.w r2, r2, #3 + 800953a: 429a cmp r2, r3 + 800953c: d16a bne.n 8009614 + || + 800953e: 2a00 cmp r2, #0 + 8009540: d068 beq.n 8009614 + __HAL_RCC_PLLSAI1_DISABLE(); + 8009542: 6823 ldr r3, [r4, #0] + 8009544: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 8009548: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800954a: f7fd ff47 bl 80073dc + 800954e: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 8009550: 6823 ldr r3, [r4, #0] + 8009552: 011a lsls r2, r3, #4 + 8009554: d42d bmi.n 80095b2 + MODIFY_REG(RCC->PLLSAI1CFGR, + 8009556: 68ab ldr r3, [r5, #8] + 8009558: 021e lsls r6, r3, #8 + 800955a: 686b ldr r3, [r5, #4] + 800955c: 3b01 subs r3, #1 + 800955e: 0118 lsls r0, r3, #4 + if(Divider == DIVIDER_P_UPDATE) + 8009560: b377 cbz r7, 80095c0 + else if(Divider == DIVIDER_Q_UPDATE) + 8009562: 2f01 cmp r7, #1 + 8009564: d145 bne.n 80095f2 + MODIFY_REG(RCC->PLLSAI1CFGR, + 8009566: 692b ldr r3, [r5, #16] + 8009568: 6927 ldr r7, [r4, #16] + 800956a: 085b lsrs r3, r3, #1 + 800956c: 1e59 subs r1, r3, #1 + 800956e: 4b2b ldr r3, [pc, #172] ; (800961c ) + 8009570: 403b ands r3, r7 + 8009572: 4333 orrs r3, r6 + 8009574: 4303 orrs r3, r0 + 8009576: ea43 5341 orr.w r3, r3, r1, lsl #21 + 800957a: e029 b.n 80095d0 + switch(PllSai1->PLLSAI1Source) + 800957c: 2b02 cmp r3, #2 + 800957e: d00d beq.n 800959c + 8009580: 2b03 cmp r3, #3 + 8009582: d00f beq.n 80095a4 + 8009584: 2b01 cmp r3, #1 + 8009586: d145 bne.n 8009614 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_MSIRDY)) + 8009588: 6822 ldr r2, [r4, #0] + 800958a: f012 0f02 tst.w r2, #2 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 800958e: d041 beq.n 8009614 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, PllSai1->PLLSAI1Source); + 8009590: 68e0 ldr r0, [r4, #12] + 8009592: f020 0003 bic.w r0, r0, #3 + 8009596: 4318 orrs r0, r3 + 8009598: 60e0 str r0, [r4, #12] + if(status == HAL_OK) + 800959a: e7d2 b.n 8009542 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSIRDY)) + 800959c: 6822 ldr r2, [r4, #0] + 800959e: f412 6f80 tst.w r2, #1024 ; 0x400 + 80095a2: e7f4 b.n 800958e + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSERDY)) + 80095a4: 6822 ldr r2, [r4, #0] + 80095a6: 0391 lsls r1, r2, #14 + 80095a8: d4f2 bmi.n 8009590 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80095aa: 6822 ldr r2, [r4, #0] + 80095ac: f412 2f80 tst.w r2, #262144 ; 0x40000 + 80095b0: e7ed b.n 800958e + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 80095b2: f7fd ff13 bl 80073dc + 80095b6: 1b80 subs r0, r0, r6 + 80095b8: 2802 cmp r0, #2 + 80095ba: d9c9 bls.n 8009550 + status = HAL_TIMEOUT; + 80095bc: 2003 movs r0, #3 +} + 80095be: bdf8 pop {r3, r4, r5, r6, r7, pc} + MODIFY_REG(RCC->PLLSAI1CFGR, + 80095c0: 68e9 ldr r1, [r5, #12] + 80095c2: 6922 ldr r2, [r4, #16] + 80095c4: ea46 63c1 orr.w r3, r6, r1, lsl #27 + 80095c8: 4915 ldr r1, [pc, #84] ; (8009620 ) + 80095ca: 4011 ands r1, r2 + 80095cc: 430b orrs r3, r1 + 80095ce: 4303 orrs r3, r0 + MODIFY_REG(RCC->PLLSAI1CFGR, + 80095d0: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1_ENABLE(); + 80095d2: 6823 ldr r3, [r4, #0] + 80095d4: f043 6380 orr.w r3, r3, #67108864 ; 0x4000000 + 80095d8: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 80095da: f7fd feff bl 80073dc + 80095de: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 80095e0: 6823 ldr r3, [r4, #0] + 80095e2: 011b lsls r3, r3, #4 + 80095e4: d510 bpl.n 8009608 + __HAL_RCC_PLLSAI1CLKOUT_ENABLE(PllSai1->PLLSAI1ClockOut); + 80095e6: 6923 ldr r3, [r4, #16] + 80095e8: 69aa ldr r2, [r5, #24] + 80095ea: 4313 orrs r3, r2 + 80095ec: 6123 str r3, [r4, #16] + 80095ee: 2000 movs r0, #0 + return status; + 80095f0: e7e5 b.n 80095be + MODIFY_REG(RCC->PLLSAI1CFGR, + 80095f2: 696b ldr r3, [r5, #20] + 80095f4: 6921 ldr r1, [r4, #16] + 80095f6: 085b lsrs r3, r3, #1 + 80095f8: 1e5a subs r2, r3, #1 + 80095fa: 4b0a ldr r3, [pc, #40] ; (8009624 ) + 80095fc: 400b ands r3, r1 + 80095fe: 4333 orrs r3, r6 + 8009600: 4303 orrs r3, r0 + 8009602: ea43 6342 orr.w r3, r3, r2, lsl #25 + 8009606: e7e3 b.n 80095d0 + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 8009608: f7fd fee8 bl 80073dc + 800960c: 1b80 subs r0, r0, r6 + 800960e: 2802 cmp r0, #2 + 8009610: d9e6 bls.n 80095e0 + 8009612: e7d3 b.n 80095bc + status = HAL_ERROR; + 8009614: 2001 movs r0, #1 + 8009616: e7d2 b.n 80095be + 8009618: 40021000 .word 0x40021000 + 800961c: ff9f800f .word 0xff9f800f + 8009620: 07ff800f .word 0x07ff800f + 8009624: f9ff800f .word 0xf9ff800f + +08009628 : +static HAL_StatusTypeDef RCCEx_PLLSAI2_Config(RCC_PLLSAI2InitTypeDef *PllSai2, uint32_t Divider) + 8009628: b570 push {r4, r5, r6, lr} + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800962a: 4c2f ldr r4, [pc, #188] ; (80096e8 ) + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai2->PLLSAI2Source) + 800962c: 6803 ldr r3, [r0, #0] + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 800962e: 68e2 ldr r2, [r4, #12] +static HAL_StatusTypeDef RCCEx_PLLSAI2_Config(RCC_PLLSAI2InitTypeDef *PllSai2, uint32_t Divider) + 8009630: 4605 mov r5, r0 + if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_NONE) + 8009632: 0790 lsls r0, r2, #30 + 8009634: d026 beq.n 8009684 + if((__HAL_RCC_GET_PLL_OSCSOURCE() != PllSai2->PLLSAI2Source) + 8009636: 68e2 ldr r2, [r4, #12] + 8009638: f002 0203 and.w r2, r2, #3 + 800963c: 429a cmp r2, r3 + 800963e: d151 bne.n 80096e4 + || + 8009640: 2a00 cmp r2, #0 + 8009642: d04f beq.n 80096e4 + __HAL_RCC_PLLSAI2_DISABLE(); + 8009644: 6823 ldr r3, [r4, #0] + 8009646: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800964a: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800964c: f7fd fec6 bl 80073dc + 8009650: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 8009652: 6823 ldr r3, [r4, #0] + 8009654: 009a lsls r2, r3, #2 + 8009656: d430 bmi.n 80096ba + MODIFY_REG(RCC->PLLSAI2CFGR, + 8009658: e9d5 2302 ldrd r2, r3, [r5, #8] + 800965c: 06db lsls r3, r3, #27 + 800965e: 6961 ldr r1, [r4, #20] + 8009660: ea43 2302 orr.w r3, r3, r2, lsl #8 + 8009664: 4a21 ldr r2, [pc, #132] ; (80096ec ) + 8009666: 400a ands r2, r1 + 8009668: 4313 orrs r3, r2 + 800966a: 686a ldr r2, [r5, #4] + 800966c: 3a01 subs r2, #1 + 800966e: ea43 1302 orr.w r3, r3, r2, lsl #4 + 8009672: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2_ENABLE(); + 8009674: 6823 ldr r3, [r4, #0] + 8009676: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800967a: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800967c: f7fd feae bl 80073dc + 8009680: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 8009682: e026 b.n 80096d2 + switch(PllSai2->PLLSAI2Source) + 8009684: 2b02 cmp r3, #2 + 8009686: d00d beq.n 80096a4 + 8009688: 2b03 cmp r3, #3 + 800968a: d00f beq.n 80096ac + 800968c: 2b01 cmp r3, #1 + 800968e: d129 bne.n 80096e4 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_MSIRDY)) + 8009690: 6822 ldr r2, [r4, #0] + 8009692: f012 0f02 tst.w r2, #2 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 8009696: d025 beq.n 80096e4 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, PllSai2->PLLSAI2Source); + 8009698: 68e0 ldr r0, [r4, #12] + 800969a: f020 0003 bic.w r0, r0, #3 + 800969e: 4318 orrs r0, r3 + 80096a0: 60e0 str r0, [r4, #12] + if(status == HAL_OK) + 80096a2: e7cf b.n 8009644 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSIRDY)) + 80096a4: 6822 ldr r2, [r4, #0] + 80096a6: f412 6f80 tst.w r2, #1024 ; 0x400 + 80096aa: e7f4 b.n 8009696 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSERDY)) + 80096ac: 6822 ldr r2, [r4, #0] + 80096ae: 0391 lsls r1, r2, #14 + 80096b0: d4f2 bmi.n 8009698 + if(HAL_IS_BIT_CLR(RCC->CR, RCC_CR_HSEBYP)) + 80096b2: 6822 ldr r2, [r4, #0] + 80096b4: f412 2f80 tst.w r2, #262144 ; 0x40000 + 80096b8: e7ed b.n 8009696 + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 80096ba: f7fd fe8f bl 80073dc + 80096be: 1b80 subs r0, r0, r6 + 80096c0: 2802 cmp r0, #2 + 80096c2: d9c6 bls.n 8009652 + status = HAL_TIMEOUT; + 80096c4: 2003 movs r0, #3 +} + 80096c6: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 80096c8: f7fd fe88 bl 80073dc + 80096cc: 1b80 subs r0, r0, r6 + 80096ce: 2802 cmp r0, #2 + 80096d0: d8f8 bhi.n 80096c4 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 80096d2: 6823 ldr r3, [r4, #0] + 80096d4: 009b lsls r3, r3, #2 + 80096d6: d5f7 bpl.n 80096c8 + __HAL_RCC_PLLSAI2CLKOUT_ENABLE(PllSai2->PLLSAI2ClockOut); + 80096d8: 6963 ldr r3, [r4, #20] + 80096da: 69aa ldr r2, [r5, #24] + 80096dc: 4313 orrs r3, r2 + 80096de: 6163 str r3, [r4, #20] + 80096e0: 2000 movs r0, #0 + return status; + 80096e2: e7f0 b.n 80096c6 + status = HAL_ERROR; + 80096e4: 2001 movs r0, #1 + 80096e6: e7ee b.n 80096c6 + 80096e8: 40021000 .word 0x40021000 + 80096ec: 07ff800f .word 0x07ff800f + +080096f0 : +{ + 80096f0: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI1) == RCC_PERIPHCLK_SAI1)) + 80096f4: 6806 ldr r6, [r0, #0] + 80096f6: f416 6600 ands.w r6, r6, #2048 ; 0x800 +{ + 80096fa: 4604 mov r4, r0 + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI1) == RCC_PERIPHCLK_SAI1)) + 80096fc: d007 beq.n 800970e + switch(PeriphClkInit->Sai1ClockSelection) + 80096fe: 6ec1 ldr r1, [r0, #108] ; 0x6c + 8009700: 2940 cmp r1, #64 ; 0x40 + 8009702: d022 beq.n 800974a + 8009704: d812 bhi.n 800972c + 8009706: b331 cbz r1, 8009756 + 8009708: 2920 cmp r1, #32 + 800970a: d02b beq.n 8009764 + 800970c: 2601 movs r6, #1 + if((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SAI2) == RCC_PERIPHCLK_SAI2)) + 800970e: 6823 ldr r3, [r4, #0] + 8009710: 04db lsls r3, r3, #19 + 8009712: d509 bpl.n 8009728 + switch(PeriphClkInit->Sai2ClockSelection) + 8009714: 6f21 ldr r1, [r4, #112] ; 0x70 + 8009716: f5b1 7f00 cmp.w r1, #512 ; 0x200 + 800971a: d02f beq.n 800977c + 800971c: d826 bhi.n 800976c + 800971e: b399 cbz r1, 8009788 + 8009720: f5b1 7f80 cmp.w r1, #256 ; 0x100 + 8009724: d073 beq.n 800980e + 8009726: 2601 movs r6, #1 + 8009728: 4635 mov r5, r6 + 800972a: e03c b.n 80097a6 + switch(PeriphClkInit->Sai1ClockSelection) + 800972c: 2960 cmp r1, #96 ; 0x60 + 800972e: d001 beq.n 8009734 + 8009730: 2980 cmp r1, #128 ; 0x80 + 8009732: d1eb bne.n 800970c + __HAL_RCC_SAI1_CONFIG(PeriphClkInit->Sai1ClockSelection); + 8009734: 4a3b ldr r2, [pc, #236] ; (8009824 ) + 8009736: 6ee1 ldr r1, [r4, #108] ; 0x6c + 8009738: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 800973c: f023 03e0 bic.w r3, r3, #224 ; 0xe0 + 8009740: 430b orrs r3, r1 + 8009742: f8c2 309c str.w r3, [r2, #156] ; 0x9c + 8009746: 2600 movs r6, #0 + 8009748: e7e1 b.n 800970e + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 800974a: 4a36 ldr r2, [pc, #216] ; (8009824 ) + 800974c: 68d3 ldr r3, [r2, #12] + 800974e: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8009752: 60d3 str r3, [r2, #12] + if(ret == HAL_OK) + 8009754: e7ee b.n 8009734 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_P_UPDATE); + 8009756: 3004 adds r0, #4 + 8009758: f7ff fee4 bl 8009524 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 800975c: 4606 mov r6, r0 + if(ret == HAL_OK) + 800975e: 2800 cmp r0, #0 + 8009760: d1d5 bne.n 800970e + 8009762: e7e7 b.n 8009734 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 8009764: 3020 adds r0, #32 + 8009766: f7ff ff5f bl 8009628 + 800976a: e7f7 b.n 800975c + switch(PeriphClkInit->Sai2ClockSelection) + 800976c: f5b1 7f40 cmp.w r1, #768 ; 0x300 + 8009770: d002 beq.n 8009778 + 8009772: f5b1 6f80 cmp.w r1, #1024 ; 0x400 + 8009776: d1d6 bne.n 8009726 + 8009778: 4635 mov r5, r6 + 800977a: e009 b.n 8009790 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 800977c: 4a29 ldr r2, [pc, #164] ; (8009824 ) + 800977e: 68d3 ldr r3, [r2, #12] + 8009780: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8009784: 60d3 str r3, [r2, #12] + break; + 8009786: e7f7 b.n 8009778 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_P_UPDATE); + 8009788: 1d20 adds r0, r4, #4 + 800978a: f7ff fecb bl 8009524 + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 800978e: 4605 mov r5, r0 + if(ret == HAL_OK) + 8009790: 2d00 cmp r5, #0 + 8009792: d141 bne.n 8009818 + __HAL_RCC_SAI2_CONFIG(PeriphClkInit->Sai2ClockSelection); + 8009794: 4a23 ldr r2, [pc, #140] ; (8009824 ) + 8009796: 6f21 ldr r1, [r4, #112] ; 0x70 + 8009798: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 800979c: f423 63e0 bic.w r3, r3, #1792 ; 0x700 + 80097a0: 430b orrs r3, r1 + 80097a2: f8c2 309c str.w r3, [r2, #156] ; 0x9c + if((PeriphClkInit->PeriphClockSelection & RCC_PERIPHCLK_RTC) == RCC_PERIPHCLK_RTC) + 80097a6: 6823 ldr r3, [r4, #0] + 80097a8: 039f lsls r7, r3, #14 + 80097aa: f140 817d bpl.w 8009aa8 + if(__HAL_RCC_PWR_IS_CLK_DISABLED() != 0U) + 80097ae: 4f1d ldr r7, [pc, #116] ; (8009824 ) + 80097b0: 6dbb ldr r3, [r7, #88] ; 0x58 + 80097b2: 00d8 lsls r0, r3, #3 + 80097b4: d432 bmi.n 800981c + __HAL_RCC_PWR_CLK_ENABLE(); + 80097b6: 6dbb ldr r3, [r7, #88] ; 0x58 + 80097b8: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 80097bc: 65bb str r3, [r7, #88] ; 0x58 + 80097be: 6dbb ldr r3, [r7, #88] ; 0x58 + 80097c0: f003 5380 and.w r3, r3, #268435456 ; 0x10000000 + 80097c4: 9301 str r3, [sp, #4] + 80097c6: 9b01 ldr r3, [sp, #4] + pwrclkchanged = SET; + 80097c8: f04f 0801 mov.w r8, #1 + SET_BIT(PWR->CR1, PWR_CR1_DBP); + 80097cc: f8df 9058 ldr.w r9, [pc, #88] ; 8009828 + 80097d0: f8d9 3000 ldr.w r3, [r9] + 80097d4: f443 7380 orr.w r3, r3, #256 ; 0x100 + 80097d8: f8c9 3000 str.w r3, [r9] + tickstart = HAL_GetTick(); + 80097dc: f7fd fdfe bl 80073dc + 80097e0: 4682 mov sl, r0 + while(READ_BIT(PWR->CR1, PWR_CR1_DBP) == 0U) + 80097e2: f8d9 3000 ldr.w r3, [r9] + 80097e6: 05d9 lsls r1, r3, #23 + 80097e8: d520 bpl.n 800982c + if(ret == HAL_OK) + 80097ea: bb35 cbnz r5, 800983a + tmpregister = READ_BIT(RCC->BDCR, RCC_BDCR_RTCSEL); + 80097ec: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + if((tmpregister != RCC_RTCCLKSOURCE_NONE) && (tmpregister != PeriphClkInit->RTCClockSelection)) + 80097f0: f413 7340 ands.w r3, r3, #768 ; 0x300 + 80097f4: f040 812e bne.w 8009a54 + __HAL_RCC_RTC_CONFIG(PeriphClkInit->RTCClockSelection); + 80097f8: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 80097fc: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8009800: f423 7340 bic.w r3, r3, #768 ; 0x300 + 8009804: 4313 orrs r3, r2 + 8009806: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + 800980a: 4635 mov r5, r6 + 800980c: e015 b.n 800983a + ret = RCCEx_PLLSAI2_Config(&(PeriphClkInit->PLLSAI2), DIVIDER_P_UPDATE); + 800980e: f104 0020 add.w r0, r4, #32 + 8009812: f7ff ff09 bl 8009628 + 8009816: e7ba b.n 800978e + 8009818: 462e mov r6, r5 + 800981a: e7c4 b.n 80097a6 + FlagStatus pwrclkchanged = RESET; + 800981c: f04f 0800 mov.w r8, #0 + 8009820: e7d4 b.n 80097cc + 8009822: bf00 nop + 8009824: 40021000 .word 0x40021000 + 8009828: 40007000 .word 0x40007000 + if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) + 800982c: f7fd fdd6 bl 80073dc + 8009830: eba0 000a sub.w r0, r0, sl + 8009834: 2802 cmp r0, #2 + 8009836: d9d4 bls.n 80097e2 + ret = HAL_TIMEOUT; + 8009838: 2503 movs r5, #3 + if(pwrclkchanged == SET) + 800983a: f1b8 0f00 cmp.w r8, #0 + 800983e: d003 beq.n 8009848 + __HAL_RCC_PWR_CLK_DISABLE(); + 8009840: 6dbb ldr r3, [r7, #88] ; 0x58 + 8009842: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 8009846: 65bb str r3, [r7, #88] ; 0x58 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART1) == RCC_PERIPHCLK_USART1) + 8009848: 6823 ldr r3, [r4, #0] + 800984a: 07d8 lsls r0, r3, #31 + 800984c: d508 bpl.n 8009860 + __HAL_RCC_USART1_CONFIG(PeriphClkInit->Usart1ClockSelection); + 800984e: 49b2 ldr r1, [pc, #712] ; (8009b18 ) + 8009850: 6be0 ldr r0, [r4, #60] ; 0x3c + 8009852: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009856: f022 0203 bic.w r2, r2, #3 + 800985a: 4302 orrs r2, r0 + 800985c: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART2) == RCC_PERIPHCLK_USART2) + 8009860: 0799 lsls r1, r3, #30 + 8009862: d508 bpl.n 8009876 + __HAL_RCC_USART2_CONFIG(PeriphClkInit->Usart2ClockSelection); + 8009864: 49ac ldr r1, [pc, #688] ; (8009b18 ) + 8009866: 6c20 ldr r0, [r4, #64] ; 0x40 + 8009868: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 800986c: f022 020c bic.w r2, r2, #12 + 8009870: 4302 orrs r2, r0 + 8009872: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USART3) == RCC_PERIPHCLK_USART3) + 8009876: 075a lsls r2, r3, #29 + 8009878: d508 bpl.n 800988c + __HAL_RCC_USART3_CONFIG(PeriphClkInit->Usart3ClockSelection); + 800987a: 49a7 ldr r1, [pc, #668] ; (8009b18 ) + 800987c: 6c60 ldr r0, [r4, #68] ; 0x44 + 800987e: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009882: f022 0230 bic.w r2, r2, #48 ; 0x30 + 8009886: 4302 orrs r2, r0 + 8009888: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_UART4) == RCC_PERIPHCLK_UART4) + 800988c: 071f lsls r7, r3, #28 + 800988e: d508 bpl.n 80098a2 + __HAL_RCC_UART4_CONFIG(PeriphClkInit->Uart4ClockSelection); + 8009890: 49a1 ldr r1, [pc, #644] ; (8009b18 ) + 8009892: 6ca0 ldr r0, [r4, #72] ; 0x48 + 8009894: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009898: f022 02c0 bic.w r2, r2, #192 ; 0xc0 + 800989c: 4302 orrs r2, r0 + 800989e: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_UART5) == RCC_PERIPHCLK_UART5) + 80098a2: 06de lsls r6, r3, #27 + 80098a4: d508 bpl.n 80098b8 + __HAL_RCC_UART5_CONFIG(PeriphClkInit->Uart5ClockSelection); + 80098a6: 499c ldr r1, [pc, #624] ; (8009b18 ) + 80098a8: 6ce0 ldr r0, [r4, #76] ; 0x4c + 80098aa: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80098ae: f422 7240 bic.w r2, r2, #768 ; 0x300 + 80098b2: 4302 orrs r2, r0 + 80098b4: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPUART1) == RCC_PERIPHCLK_LPUART1) + 80098b8: 0698 lsls r0, r3, #26 + 80098ba: d508 bpl.n 80098ce + __HAL_RCC_LPUART1_CONFIG(PeriphClkInit->Lpuart1ClockSelection); + 80098bc: 4996 ldr r1, [pc, #600] ; (8009b18 ) + 80098be: 6d20 ldr r0, [r4, #80] ; 0x50 + 80098c0: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80098c4: f422 6240 bic.w r2, r2, #3072 ; 0xc00 + 80098c8: 4302 orrs r2, r0 + 80098ca: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPTIM1) == (RCC_PERIPHCLK_LPTIM1)) + 80098ce: 0599 lsls r1, r3, #22 + 80098d0: d508 bpl.n 80098e4 + __HAL_RCC_LPTIM1_CONFIG(PeriphClkInit->Lptim1ClockSelection); + 80098d2: 4991 ldr r1, [pc, #580] ; (8009b18 ) + 80098d4: 6e60 ldr r0, [r4, #100] ; 0x64 + 80098d6: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80098da: f422 2240 bic.w r2, r2, #786432 ; 0xc0000 + 80098de: 4302 orrs r2, r0 + 80098e0: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LPTIM2) == (RCC_PERIPHCLK_LPTIM2)) + 80098e4: 055a lsls r2, r3, #21 + 80098e6: d508 bpl.n 80098fa + __HAL_RCC_LPTIM2_CONFIG(PeriphClkInit->Lptim2ClockSelection); + 80098e8: 498b ldr r1, [pc, #556] ; (8009b18 ) + 80098ea: 6ea0 ldr r0, [r4, #104] ; 0x68 + 80098ec: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 80098f0: f422 1240 bic.w r2, r2, #3145728 ; 0x300000 + 80098f4: 4302 orrs r2, r0 + 80098f6: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C1) == RCC_PERIPHCLK_I2C1) + 80098fa: 065f lsls r7, r3, #25 + 80098fc: d508 bpl.n 8009910 + __HAL_RCC_I2C1_CONFIG(PeriphClkInit->I2c1ClockSelection); + 80098fe: 4986 ldr r1, [pc, #536] ; (8009b18 ) + 8009900: 6d60 ldr r0, [r4, #84] ; 0x54 + 8009902: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009906: f422 5240 bic.w r2, r2, #12288 ; 0x3000 + 800990a: 4302 orrs r2, r0 + 800990c: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C2) == RCC_PERIPHCLK_I2C2) + 8009910: 061e lsls r6, r3, #24 + 8009912: d508 bpl.n 8009926 + __HAL_RCC_I2C2_CONFIG(PeriphClkInit->I2c2ClockSelection); + 8009914: 4980 ldr r1, [pc, #512] ; (8009b18 ) + 8009916: 6da0 ldr r0, [r4, #88] ; 0x58 + 8009918: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 800991c: f422 4240 bic.w r2, r2, #49152 ; 0xc000 + 8009920: 4302 orrs r2, r0 + 8009922: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C3) == RCC_PERIPHCLK_I2C3) + 8009926: 05d8 lsls r0, r3, #23 + 8009928: d508 bpl.n 800993c + __HAL_RCC_I2C3_CONFIG(PeriphClkInit->I2c3ClockSelection); + 800992a: 497b ldr r1, [pc, #492] ; (8009b18 ) + 800992c: 6de0 ldr r0, [r4, #92] ; 0x5c + 800992e: f8d1 2088 ldr.w r2, [r1, #136] ; 0x88 + 8009932: f422 3240 bic.w r2, r2, #196608 ; 0x30000 + 8009936: 4302 orrs r2, r0 + 8009938: f8c1 2088 str.w r2, [r1, #136] ; 0x88 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_I2C4) == RCC_PERIPHCLK_I2C4) + 800993c: 02d9 lsls r1, r3, #11 + 800993e: d508 bpl.n 8009952 + __HAL_RCC_I2C4_CONFIG(PeriphClkInit->I2c4ClockSelection); + 8009940: 4975 ldr r1, [pc, #468] ; (8009b18 ) + 8009942: 6e20 ldr r0, [r4, #96] ; 0x60 + 8009944: f8d1 209c ldr.w r2, [r1, #156] ; 0x9c + 8009948: f022 0203 bic.w r2, r2, #3 + 800994c: 4302 orrs r2, r0 + 800994e: f8c1 209c str.w r2, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_USB) == (RCC_PERIPHCLK_USB)) + 8009952: 049a lsls r2, r3, #18 + 8009954: d510 bpl.n 8009978 + __HAL_RCC_USB_CONFIG(PeriphClkInit->UsbClockSelection); + 8009956: 4a70 ldr r2, [pc, #448] ; (8009b18 ) + 8009958: 6f61 ldr r1, [r4, #116] ; 0x74 + 800995a: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 800995e: f023 6340 bic.w r3, r3, #201326592 ; 0xc000000 + 8009962: 430b orrs r3, r1 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLL) + 8009964: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_USB_CONFIG(PeriphClkInit->UsbClockSelection); + 8009968: f8c2 3088 str.w r3, [r2, #136] ; 0x88 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLL) + 800996c: f040 809e bne.w 8009aac + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 8009970: 68d3 ldr r3, [r2, #12] + 8009972: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 8009976: 60d3 str r3, [r2, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_SDMMC1) == (RCC_PERIPHCLK_SDMMC1)) + 8009978: 6823 ldr r3, [r4, #0] + 800997a: 031b lsls r3, r3, #12 + 800997c: d50f bpl.n 800999e + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 800997e: 6fa1 ldr r1, [r4, #120] ; 0x78 + 8009980: 4b65 ldr r3, [pc, #404] ; (8009b18 ) + 8009982: f5b1 4f80 cmp.w r1, #16384 ; 0x4000 + 8009986: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 800998a: f040 809b bne.w 8009ac4 + 800998e: f442 4280 orr.w r2, r2, #16384 ; 0x4000 + 8009992: f8c3 209c str.w r2, [r3, #156] ; 0x9c + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_SAI3CLK); + 8009996: 68da ldr r2, [r3, #12] + 8009998: f442 3280 orr.w r2, r2, #65536 ; 0x10000 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 800999c: 60da str r2, [r3, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_RNG) == (RCC_PERIPHCLK_RNG)) + 800999e: 6823 ldr r3, [r4, #0] + 80099a0: 035f lsls r7, r3, #13 + 80099a2: d510 bpl.n 80099c6 + __HAL_RCC_RNG_CONFIG(PeriphClkInit->RngClockSelection); + 80099a4: 4a5c ldr r2, [pc, #368] ; (8009b18 ) + 80099a6: 6fe1 ldr r1, [r4, #124] ; 0x7c + 80099a8: f8d2 3088 ldr.w r3, [r2, #136] ; 0x88 + 80099ac: f023 6340 bic.w r3, r3, #201326592 ; 0xc000000 + 80099b0: 430b orrs r3, r1 + if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLL) + 80099b2: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_RNG_CONFIG(PeriphClkInit->RngClockSelection); + 80099b6: f8c2 3088 str.w r3, [r2, #136] ; 0x88 + if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLL) + 80099ba: f040 80a1 bne.w 8009b00 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 80099be: 68d3 ldr r3, [r2, #12] + 80099c0: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 80099c4: 60d3 str r3, [r2, #12] + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_ADC) == RCC_PERIPHCLK_ADC) + 80099c6: 6823 ldr r3, [r4, #0] + 80099c8: 045e lsls r6, r3, #17 + 80099ca: d513 bpl.n 80099f4 + __HAL_RCC_ADC_CONFIG(PeriphClkInit->AdcClockSelection); + 80099cc: 4952 ldr r1, [pc, #328] ; (8009b18 ) + 80099ce: f8d4 2080 ldr.w r2, [r4, #128] ; 0x80 + 80099d2: f8d1 3088 ldr.w r3, [r1, #136] ; 0x88 + 80099d6: f023 5340 bic.w r3, r3, #805306368 ; 0x30000000 + 80099da: 4313 orrs r3, r2 + if(PeriphClkInit->AdcClockSelection == RCC_ADCCLKSOURCE_PLLSAI1) + 80099dc: f1b2 5f80 cmp.w r2, #268435456 ; 0x10000000 + __HAL_RCC_ADC_CONFIG(PeriphClkInit->AdcClockSelection); + 80099e0: f8c1 3088 str.w r3, [r1, #136] ; 0x88 + if(PeriphClkInit->AdcClockSelection == RCC_ADCCLKSOURCE_PLLSAI1) + 80099e4: d106 bne.n 80099f4 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_R_UPDATE); + 80099e6: 2102 movs r1, #2 + 80099e8: 1d20 adds r0, r4, #4 + 80099ea: f7ff fd9b bl 8009524 + if(ret != HAL_OK) + 80099ee: 2800 cmp r0, #0 + 80099f0: bf18 it ne + 80099f2: 4605 movne r5, r0 + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_DFSDM1) == RCC_PERIPHCLK_DFSDM1) + 80099f4: 6822 ldr r2, [r4, #0] + 80099f6: 03d0 lsls r0, r2, #15 + 80099f8: d509 bpl.n 8009a0e + __HAL_RCC_DFSDM1_CONFIG(PeriphClkInit->Dfsdm1ClockSelection); + 80099fa: 4947 ldr r1, [pc, #284] ; (8009b18 ) + 80099fc: f8d4 0084 ldr.w r0, [r4, #132] ; 0x84 + 8009a00: f8d1 309c ldr.w r3, [r1, #156] ; 0x9c + 8009a04: f023 0304 bic.w r3, r3, #4 + 8009a08: 4303 orrs r3, r0 + 8009a0a: f8c1 309c str.w r3, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_DFSDM1AUDIO) == RCC_PERIPHCLK_DFSDM1AUDIO) + 8009a0e: 0291 lsls r1, r2, #10 + 8009a10: d509 bpl.n 8009a26 + __HAL_RCC_DFSDM1AUDIO_CONFIG(PeriphClkInit->Dfsdm1AudioClockSelection); + 8009a12: 4941 ldr r1, [pc, #260] ; (8009b18 ) + 8009a14: f8d4 0088 ldr.w r0, [r4, #136] ; 0x88 + 8009a18: f8d1 309c ldr.w r3, [r1, #156] ; 0x9c + 8009a1c: f023 0318 bic.w r3, r3, #24 + 8009a20: 4303 orrs r3, r0 + 8009a22: f8c1 309c str.w r3, [r1, #156] ; 0x9c + if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_OSPI) == RCC_PERIPHCLK_OSPI) + 8009a26: 01d3 lsls r3, r2, #7 + 8009a28: d510 bpl.n 8009a4c + __HAL_RCC_OSPI_CONFIG(PeriphClkInit->OspiClockSelection); + 8009a2a: 4a3b ldr r2, [pc, #236] ; (8009b18 ) + 8009a2c: f8d4 108c ldr.w r1, [r4, #140] ; 0x8c + 8009a30: f8d2 309c ldr.w r3, [r2, #156] ; 0x9c + 8009a34: f423 1340 bic.w r3, r3, #3145728 ; 0x300000 + 8009a38: 430b orrs r3, r1 + 8009a3a: f8c2 309c str.w r3, [r2, #156] ; 0x9c + if(PeriphClkInit->OspiClockSelection == RCC_OSPICLKSOURCE_PLL) + 8009a3e: f5b1 1f00 cmp.w r1, #2097152 ; 0x200000 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 8009a42: bf02 ittt eq + 8009a44: 68d3 ldreq r3, [r2, #12] + 8009a46: f443 1380 orreq.w r3, r3, #1048576 ; 0x100000 + 8009a4a: 60d3 streq r3, [r2, #12] +} + 8009a4c: 4628 mov r0, r5 + 8009a4e: b002 add sp, #8 + 8009a50: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if((tmpregister != RCC_RTCCLKSOURCE_NONE) && (tmpregister != PeriphClkInit->RTCClockSelection)) + 8009a54: f8d4 2090 ldr.w r2, [r4, #144] ; 0x90 + 8009a58: 429a cmp r2, r3 + 8009a5a: f43f aecd beq.w 80097f8 + tmpregister = READ_BIT(RCC->BDCR, ~(RCC_BDCR_RTCSEL)); + 8009a5e: f8d7 2090 ldr.w r2, [r7, #144] ; 0x90 + __HAL_RCC_BACKUPRESET_FORCE(); + 8009a62: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 8009a66: f443 3380 orr.w r3, r3, #65536 ; 0x10000 + 8009a6a: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + __HAL_RCC_BACKUPRESET_RELEASE(); + 8009a6e: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + tmpregister = READ_BIT(RCC->BDCR, ~(RCC_BDCR_RTCSEL)); + 8009a72: f422 7140 bic.w r1, r2, #768 ; 0x300 + __HAL_RCC_BACKUPRESET_RELEASE(); + 8009a76: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + if (HAL_IS_BIT_SET(tmpregister, RCC_BDCR_LSEON)) + 8009a7a: 07d2 lsls r2, r2, #31 + __HAL_RCC_BACKUPRESET_RELEASE(); + 8009a7c: f8c7 3090 str.w r3, [r7, #144] ; 0x90 + RCC->BDCR = tmpregister; + 8009a80: f8c7 1090 str.w r1, [r7, #144] ; 0x90 + if (HAL_IS_BIT_SET(tmpregister, RCC_BDCR_LSEON)) + 8009a84: f57f aeb8 bpl.w 80097f8 + tickstart = HAL_GetTick(); + 8009a88: f7fd fca8 bl 80073dc + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8009a8c: f241 3988 movw r9, #5000 ; 0x1388 + tickstart = HAL_GetTick(); + 8009a90: 4605 mov r5, r0 + while(READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0U) + 8009a92: f8d7 3090 ldr.w r3, [r7, #144] ; 0x90 + 8009a96: 079b lsls r3, r3, #30 + 8009a98: f53f aeae bmi.w 80097f8 + if((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) + 8009a9c: f7fd fc9e bl 80073dc + 8009aa0: 1b40 subs r0, r0, r5 + 8009aa2: 4548 cmp r0, r9 + 8009aa4: d9f5 bls.n 8009a92 + 8009aa6: e6c7 b.n 8009838 + 8009aa8: 4635 mov r5, r6 + 8009aaa: e6cd b.n 8009848 + if(PeriphClkInit->UsbClockSelection == RCC_USBCLKSOURCE_PLLSAI1) + 8009aac: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 8009ab0: f47f af62 bne.w 8009978 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 8009ab4: 2101 movs r1, #1 + 8009ab6: 1d20 adds r0, r4, #4 + 8009ab8: f7ff fd34 bl 8009524 + if(ret != HAL_OK) + 8009abc: 2800 cmp r0, #0 + 8009abe: bf18 it ne + 8009ac0: 4605 movne r5, r0 + 8009ac2: e759 b.n 8009978 + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 8009ac4: f422 4280 bic.w r2, r2, #16384 ; 0x4000 + 8009ac8: f8c3 209c str.w r2, [r3, #156] ; 0x9c + 8009acc: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009ad0: f022 6240 bic.w r2, r2, #201326592 ; 0xc000000 + 8009ad4: 430a orrs r2, r1 + if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLL) /* PLL "Q" ? */ + 8009ad6: f1b1 6f00 cmp.w r1, #134217728 ; 0x8000000 + __HAL_RCC_SDMMC1_CONFIG(PeriphClkInit->Sdmmc1ClockSelection); + 8009ada: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLL) /* PLL "Q" ? */ + 8009ade: d103 bne.n 8009ae8 + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL_48M1CLK); + 8009ae0: 68da ldr r2, [r3, #12] + 8009ae2: f442 1280 orr.w r2, r2, #1048576 ; 0x100000 + 8009ae6: e759 b.n 800999c + else if(PeriphClkInit->Sdmmc1ClockSelection == RCC_SDMMC1CLKSOURCE_PLLSAI1) + 8009ae8: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 8009aec: f47f af57 bne.w 800999e + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 8009af0: 2101 movs r1, #1 + 8009af2: 1d20 adds r0, r4, #4 + 8009af4: f7ff fd16 bl 8009524 + if(ret != HAL_OK) + 8009af8: 2800 cmp r0, #0 + 8009afa: bf18 it ne + 8009afc: 4605 movne r5, r0 + 8009afe: e74e b.n 800999e + else if(PeriphClkInit->RngClockSelection == RCC_RNGCLKSOURCE_PLLSAI1) + 8009b00: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 8009b04: f47f af5f bne.w 80099c6 + ret = RCCEx_PLLSAI1_Config(&(PeriphClkInit->PLLSAI1), DIVIDER_Q_UPDATE); + 8009b08: 2101 movs r1, #1 + 8009b0a: 1d20 adds r0, r4, #4 + 8009b0c: f7ff fd0a bl 8009524 + if(ret != HAL_OK) + 8009b10: 2800 cmp r0, #0 + 8009b12: bf18 it ne + 8009b14: 4605 movne r5, r0 + 8009b16: e756 b.n 80099c6 + 8009b18: 40021000 .word 0x40021000 + +08009b1c : + PeriphClkInit->PeriphClockSelection = RCC_PERIPHCLK_USART1 | RCC_PERIPHCLK_USART2 | RCC_PERIPHCLK_USART3 | RCC_PERIPHCLK_UART4 | RCC_PERIPHCLK_UART5 | \ + 8009b1c: 4b5b ldr r3, [pc, #364] ; (8009c8c ) + 8009b1e: 6003 str r3, [r0, #0] + PeriphClkInit->PLLSAI1.PLLSAI1Source = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC) >> RCC_PLLCFGR_PLLSRC_Pos; + 8009b20: 4b5b ldr r3, [pc, #364] ; (8009c90 ) + 8009b22: 68d9 ldr r1, [r3, #12] + 8009b24: f001 0103 and.w r1, r1, #3 + 8009b28: 6041 str r1, [r0, #4] + PeriphClkInit->PLLSAI1.PLLSAI1M = (READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U; + 8009b2a: 691a ldr r2, [r3, #16] + 8009b2c: f3c2 1203 ubfx r2, r2, #4, #4 + 8009b30: 3201 adds r2, #1 + 8009b32: 6082 str r2, [r0, #8] + PeriphClkInit->PLLSAI1.PLLSAI1N = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009b34: 691a ldr r2, [r3, #16] + 8009b36: f3c2 2206 ubfx r2, r2, #8, #7 + 8009b3a: 60c2 str r2, [r0, #12] + PeriphClkInit->PLLSAI1.PLLSAI1P = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1P) >> RCC_PLLSAI1CFGR_PLLSAI1P_Pos) << 4U) + 7U; + 8009b3c: 691a ldr r2, [r3, #16] + 8009b3e: 0b52 lsrs r2, r2, #13 + 8009b40: f002 0210 and.w r2, r2, #16 + 8009b44: 3207 adds r2, #7 + 8009b46: 6102 str r2, [r0, #16] + PeriphClkInit->PLLSAI1.PLLSAI1Q = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) * 2U; + 8009b48: 691a ldr r2, [r3, #16] + 8009b4a: f3c2 5241 ubfx r2, r2, #21, #2 + 8009b4e: 3201 adds r2, #1 + 8009b50: 0052 lsls r2, r2, #1 + 8009b52: 6142 str r2, [r0, #20] + PeriphClkInit->PLLSAI1.PLLSAI1R = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) * 2U; + 8009b54: 691a ldr r2, [r3, #16] + PeriphClkInit->PLLSAI2.PLLSAI2Source = PeriphClkInit->PLLSAI1.PLLSAI1Source; + 8009b56: 6201 str r1, [r0, #32] + PeriphClkInit->PLLSAI1.PLLSAI1R = ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) * 2U; + 8009b58: f3c2 6241 ubfx r2, r2, #25, #2 + 8009b5c: 3201 adds r2, #1 + 8009b5e: 0052 lsls r2, r2, #1 + 8009b60: 6182 str r2, [r0, #24] + PeriphClkInit->PLLSAI2.PLLSAI2M = (READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2M) >> RCC_PLLSAI2CFGR_PLLSAI2M_Pos) + 1U; + 8009b62: 695a ldr r2, [r3, #20] + 8009b64: f3c2 1203 ubfx r2, r2, #4, #4 + 8009b68: 3201 adds r2, #1 + 8009b6a: 6242 str r2, [r0, #36] ; 0x24 + PeriphClkInit->PLLSAI2.PLLSAI2N = READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2N) >> RCC_PLLSAI2CFGR_PLLSAI2N_Pos; + 8009b6c: 695a ldr r2, [r3, #20] + 8009b6e: f3c2 2206 ubfx r2, r2, #8, #7 + 8009b72: 6282 str r2, [r0, #40] ; 0x28 + PeriphClkInit->PLLSAI2.PLLSAI2P = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2P) >> RCC_PLLSAI2CFGR_PLLSAI2P_Pos) << 4U) + 7U; + 8009b74: 695a ldr r2, [r3, #20] + 8009b76: 0b52 lsrs r2, r2, #13 + 8009b78: f002 0210 and.w r2, r2, #16 + 8009b7c: 3207 adds r2, #7 + 8009b7e: 62c2 str r2, [r0, #44] ; 0x2c + PeriphClkInit->PLLSAI2.PLLSAI2Q = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2Q) >> RCC_PLLSAI2CFGR_PLLSAI2Q_Pos) + 1U) * 2U; + 8009b80: 695a ldr r2, [r3, #20] + 8009b82: f3c2 5241 ubfx r2, r2, #21, #2 + 8009b86: 3201 adds r2, #1 + 8009b88: 0052 lsls r2, r2, #1 + 8009b8a: 6302 str r2, [r0, #48] ; 0x30 + PeriphClkInit->PLLSAI2.PLLSAI2R = ((READ_BIT(RCC->PLLSAI2CFGR, RCC_PLLSAI2CFGR_PLLSAI2R)>> RCC_PLLSAI2CFGR_PLLSAI2R_Pos) + 1U) * 2U; + 8009b8c: 695a ldr r2, [r3, #20] + 8009b8e: f3c2 6241 ubfx r2, r2, #25, #2 + 8009b92: 3201 adds r2, #1 + 8009b94: 0052 lsls r2, r2, #1 + 8009b96: 6342 str r2, [r0, #52] ; 0x34 + PeriphClkInit->Usart1ClockSelection = __HAL_RCC_GET_USART1_SOURCE(); + 8009b98: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009b9c: f002 0203 and.w r2, r2, #3 + 8009ba0: 63c2 str r2, [r0, #60] ; 0x3c + PeriphClkInit->Usart2ClockSelection = __HAL_RCC_GET_USART2_SOURCE(); + 8009ba2: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009ba6: f002 020c and.w r2, r2, #12 + 8009baa: 6402 str r2, [r0, #64] ; 0x40 + PeriphClkInit->Usart3ClockSelection = __HAL_RCC_GET_USART3_SOURCE(); + 8009bac: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bb0: f002 0230 and.w r2, r2, #48 ; 0x30 + 8009bb4: 6442 str r2, [r0, #68] ; 0x44 + PeriphClkInit->Uart4ClockSelection = __HAL_RCC_GET_UART4_SOURCE(); + 8009bb6: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bba: f002 02c0 and.w r2, r2, #192 ; 0xc0 + 8009bbe: 6482 str r2, [r0, #72] ; 0x48 + PeriphClkInit->Uart5ClockSelection = __HAL_RCC_GET_UART5_SOURCE(); + 8009bc0: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bc4: f402 7240 and.w r2, r2, #768 ; 0x300 + 8009bc8: 64c2 str r2, [r0, #76] ; 0x4c + PeriphClkInit->Lpuart1ClockSelection = __HAL_RCC_GET_LPUART1_SOURCE(); + 8009bca: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bce: f402 6240 and.w r2, r2, #3072 ; 0xc00 + 8009bd2: 6502 str r2, [r0, #80] ; 0x50 + PeriphClkInit->I2c1ClockSelection = __HAL_RCC_GET_I2C1_SOURCE(); + 8009bd4: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bd8: f402 5240 and.w r2, r2, #12288 ; 0x3000 + 8009bdc: 6542 str r2, [r0, #84] ; 0x54 + PeriphClkInit->I2c2ClockSelection = __HAL_RCC_GET_I2C2_SOURCE(); + 8009bde: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009be2: f402 4240 and.w r2, r2, #49152 ; 0xc000 + 8009be6: 6582 str r2, [r0, #88] ; 0x58 + PeriphClkInit->I2c3ClockSelection = __HAL_RCC_GET_I2C3_SOURCE(); + 8009be8: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009bec: f402 3240 and.w r2, r2, #196608 ; 0x30000 + 8009bf0: 65c2 str r2, [r0, #92] ; 0x5c + PeriphClkInit->I2c4ClockSelection = __HAL_RCC_GET_I2C4_SOURCE(); + 8009bf2: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009bf6: f002 0203 and.w r2, r2, #3 + 8009bfa: 6602 str r2, [r0, #96] ; 0x60 + PeriphClkInit->Lptim1ClockSelection = __HAL_RCC_GET_LPTIM1_SOURCE(); + 8009bfc: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009c00: f402 2240 and.w r2, r2, #786432 ; 0xc0000 + 8009c04: 6642 str r2, [r0, #100] ; 0x64 + PeriphClkInit->Lptim2ClockSelection = __HAL_RCC_GET_LPTIM2_SOURCE(); + 8009c06: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009c0a: f402 1240 and.w r2, r2, #3145728 ; 0x300000 + 8009c0e: 6682 str r2, [r0, #104] ; 0x68 + PeriphClkInit->Sai1ClockSelection = __HAL_RCC_GET_SAI1_SOURCE(); + 8009c10: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009c14: f002 02e0 and.w r2, r2, #224 ; 0xe0 + 8009c18: 66c2 str r2, [r0, #108] ; 0x6c + PeriphClkInit->Sai2ClockSelection = __HAL_RCC_GET_SAI2_SOURCE(); + 8009c1a: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009c1e: f402 62e0 and.w r2, r2, #1792 ; 0x700 + 8009c22: 6702 str r2, [r0, #112] ; 0x70 + PeriphClkInit->RTCClockSelection = __HAL_RCC_GET_RTC_SOURCE(); + 8009c24: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 8009c28: f402 7240 and.w r2, r2, #768 ; 0x300 + 8009c2c: f8c0 2090 str.w r2, [r0, #144] ; 0x90 + PeriphClkInit->UsbClockSelection = __HAL_RCC_GET_USB_SOURCE(); + 8009c30: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009c34: f002 6240 and.w r2, r2, #201326592 ; 0xc000000 + 8009c38: 6742 str r2, [r0, #116] ; 0x74 + PeriphClkInit->Sdmmc1ClockSelection = __HAL_RCC_GET_SDMMC1_SOURCE(); + 8009c3a: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009c3e: 0452 lsls r2, r2, #17 + 8009c40: bf56 itet pl + 8009c42: f8d3 2088 ldrpl.w r2, [r3, #136] ; 0x88 + 8009c46: f44f 4280 movmi.w r2, #16384 ; 0x4000 + 8009c4a: f002 6240 andpl.w r2, r2, #201326592 ; 0xc000000 + 8009c4e: 6782 str r2, [r0, #120] ; 0x78 + PeriphClkInit->RngClockSelection = __HAL_RCC_GET_RNG_SOURCE(); + 8009c50: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009c54: f002 6240 and.w r2, r2, #201326592 ; 0xc000000 + 8009c58: 67c2 str r2, [r0, #124] ; 0x7c + PeriphClkInit->AdcClockSelection = __HAL_RCC_GET_ADC_SOURCE(); + 8009c5a: f8d3 2088 ldr.w r2, [r3, #136] ; 0x88 + 8009c5e: f002 5240 and.w r2, r2, #805306368 ; 0x30000000 + 8009c62: f8c0 2080 str.w r2, [r0, #128] ; 0x80 + PeriphClkInit->Dfsdm1ClockSelection = __HAL_RCC_GET_DFSDM1_SOURCE(); + 8009c66: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009c6a: f002 0204 and.w r2, r2, #4 + 8009c6e: f8c0 2084 str.w r2, [r0, #132] ; 0x84 + PeriphClkInit->Dfsdm1AudioClockSelection = __HAL_RCC_GET_DFSDM1AUDIO_SOURCE(); + 8009c72: f8d3 209c ldr.w r2, [r3, #156] ; 0x9c + 8009c76: f002 0218 and.w r2, r2, #24 + 8009c7a: f8c0 2088 str.w r2, [r0, #136] ; 0x88 + PeriphClkInit->OspiClockSelection = __HAL_RCC_GET_OSPI_SOURCE(); + 8009c7e: f8d3 309c ldr.w r3, [r3, #156] ; 0x9c + 8009c82: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + 8009c86: f8c0 308c str.w r3, [r0, #140] ; 0x8c +} + 8009c8a: 4770 bx lr + 8009c8c: 013f7fff .word 0x013f7fff + 8009c90: 40021000 .word 0x40021000 + +08009c94 : + if(PeriphClk == RCC_PERIPHCLK_RTC) + 8009c94: f5b0 3f00 cmp.w r0, #131072 ; 0x20000 +{ + 8009c98: b4f0 push {r4, r5, r6, r7} + 8009c9a: 4d9a ldr r5, [pc, #616] ; (8009f04 ) + if(PeriphClk == RCC_PERIPHCLK_RTC) + 8009c9c: d11c bne.n 8009cd8 + srcclk = __HAL_RCC_GET_RTC_SOURCE(); + 8009c9e: f8d5 3090 ldr.w r3, [r5, #144] ; 0x90 + 8009ca2: f403 7340 and.w r3, r3, #768 ; 0x300 + switch(srcclk) + 8009ca6: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 8009caa: f000 8085 beq.w 8009db8 + 8009cae: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 8009cb2: d00a beq.n 8009cca + 8009cb4: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 8009cb8: d154 bne.n 8009d64 + if(HAL_IS_BIT_SET(RCC->BDCR, RCC_BDCR_LSERDY)) + 8009cba: f8d5 0090 ldr.w r0, [r5, #144] ; 0x90 + frequency = LSE_VALUE; + 8009cbe: f010 0002 ands.w r0, r0, #2 + 8009cc2: bf18 it ne + 8009cc4: f44f 4000 movne.w r0, #32768 ; 0x8000 + 8009cc8: e11a b.n 8009f00 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY)) + 8009cca: 6828 ldr r0, [r5, #0] + frequency = HSE_VALUE / 32U; + 8009ccc: 4b8e ldr r3, [pc, #568] ; (8009f08 ) + 8009cce: f410 3000 ands.w r0, r0, #131072 ; 0x20000 + frequency = HSI_VALUE; + 8009cd2: bf18 it ne + 8009cd4: 4618 movne r0, r3 + 8009cd6: e113 b.n 8009f00 + pll_oscsource = __HAL_RCC_GET_PLL_OSCSOURCE(); + 8009cd8: 68eb ldr r3, [r5, #12] + 8009cda: f003 0303 and.w r3, r3, #3 + switch(pll_oscsource) + 8009cde: 2b02 cmp r3, #2 + 8009ce0: d02f beq.n 8009d42 + 8009ce2: 2b03 cmp r3, #3 + 8009ce4: d034 beq.n 8009d50 + 8009ce6: 2b01 cmp r3, #1 + 8009ce8: d137 bne.n 8009d5a + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_MSIRDY)) + 8009cea: 6829 ldr r1, [r5, #0] + 8009cec: f011 0102 ands.w r1, r1, #2 + 8009cf0: d00c beq.n 8009d0c + pllvco = MSIRangeTable[(__HAL_RCC_GET_MSI_RANGE() >> 4U)]; + 8009cf2: 682b ldr r3, [r5, #0] + 8009cf4: 4a85 ldr r2, [pc, #532] ; (8009f0c ) + 8009cf6: 0719 lsls r1, r3, #28 + 8009cf8: bf4b itete mi + 8009cfa: 682b ldrmi r3, [r5, #0] + 8009cfc: f8d5 3094 ldrpl.w r3, [r5, #148] ; 0x94 + 8009d00: f3c3 1303 ubfxmi r3, r3, #4, #4 + 8009d04: f3c3 2303 ubfxpl r3, r3, #8, #4 + 8009d08: f852 1023 ldr.w r1, [r2, r3, lsl #2] + switch(PeriphClk) + 8009d0c: f5b0 6f80 cmp.w r0, #1024 ; 0x400 + 8009d10: f000 8226 beq.w 800a160 + 8009d14: d858 bhi.n 8009dc8 + 8009d16: 2820 cmp r0, #32 + 8009d18: f000 81be beq.w 800a098 + 8009d1c: d824 bhi.n 8009d68 + 8009d1e: 2808 cmp r0, #8 + 8009d20: d81d bhi.n 8009d5e + 8009d22: 2800 cmp r0, #0 + 8009d24: f000 80ec beq.w 8009f00 + 8009d28: 3801 subs r0, #1 + 8009d2a: 2807 cmp r0, #7 + 8009d2c: d81a bhi.n 8009d64 + 8009d2e: e8df f010 tbh [pc, r0, lsl #1] + 8009d32: 0164 .short 0x0164 + 8009d34: 00190177 .word 0x00190177 + 8009d38: 00190189 .word 0x00190189 + 8009d3c: 00190019 .word 0x00190019 + 8009d40: 0196 .short 0x0196 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 8009d42: 6829 ldr r1, [r5, #0] + pllvco = HSI_VALUE; + 8009d44: 4b72 ldr r3, [pc, #456] ; (8009f10 ) + 8009d46: f411 6180 ands.w r1, r1, #1024 ; 0x400 + pllvco = HSE_VALUE; + 8009d4a: bf18 it ne + 8009d4c: 4619 movne r1, r3 + 8009d4e: e7dd b.n 8009d0c + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY)) + 8009d50: 6829 ldr r1, [r5, #0] + pllvco = HSE_VALUE; + 8009d52: 4b70 ldr r3, [pc, #448] ; (8009f14 ) + 8009d54: f411 3100 ands.w r1, r1, #131072 ; 0x20000 + 8009d58: e7f7 b.n 8009d4a + switch(pll_oscsource) + 8009d5a: 2100 movs r1, #0 + 8009d5c: e7d6 b.n 8009d0c + switch(PeriphClk) + 8009d5e: 2810 cmp r0, #16 + 8009d60: f000 818a beq.w 800a078 + 8009d64: 2000 movs r0, #0 + 8009d66: e0cb b.n 8009f00 + 8009d68: f5b0 7f80 cmp.w r0, #256 ; 0x100 + 8009d6c: f000 81ea beq.w 800a144 + 8009d70: d80f bhi.n 8009d92 + 8009d72: 2840 cmp r0, #64 ; 0x40 + 8009d74: f000 81d5 beq.w 800a122 + 8009d78: 2880 cmp r0, #128 ; 0x80 + 8009d7a: d1f3 bne.n 8009d64 + srcclk = __HAL_RCC_GET_I2C2_SOURCE(); + 8009d7c: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d80: f403 4340 and.w r3, r3, #49152 ; 0xc000 + switch(srcclk) + 8009d84: f5b3 4f80 cmp.w r3, #16384 ; 0x4000 + 8009d88: f000 8157 beq.w 800a03a + 8009d8c: f5b3 4f00 cmp.w r3, #32768 ; 0x8000 + 8009d90: e1d0 b.n 800a134 + switch(PeriphClk) + 8009d92: f5b0 7f00 cmp.w r0, #512 ; 0x200 + 8009d96: d1e5 bne.n 8009d64 + srcclk = __HAL_RCC_GET_LPTIM1_SOURCE(); + 8009d98: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009d9c: f403 2340 and.w r3, r3, #786432 ; 0xc0000 + switch(srcclk) + 8009da0: f5b3 2f00 cmp.w r3, #524288 ; 0x80000 + 8009da4: f000 8137 beq.w 800a016 + 8009da8: f200 81d7 bhi.w 800a15a + 8009dac: 2b00 cmp r3, #0 + 8009dae: f000 81c6 beq.w 800a13e + 8009db2: f5b3 2f80 cmp.w r3, #262144 ; 0x40000 + 8009db6: d1d5 bne.n 8009d64 + if(HAL_IS_BIT_SET(RCC->CSR, RCC_CSR_LSIRDY)) + 8009db8: f8d5 0094 ldr.w r0, [r5, #148] ; 0x94 + frequency = LSI_VALUE; + 8009dbc: f010 0002 ands.w r0, r0, #2 + 8009dc0: bf18 it ne + 8009dc2: f44f 40fa movne.w r0, #32000 ; 0x7d00 + 8009dc6: e09b b.n 8009f00 + switch(PeriphClk) + 8009dc8: f5b0 2f80 cmp.w r0, #262144 ; 0x40000 + 8009dcc: d040 beq.n 8009e50 + 8009dce: d819 bhi.n 8009e04 + 8009dd0: f5b0 5f00 cmp.w r0, #8192 ; 0x2000 + 8009dd4: d03c beq.n 8009e50 + 8009dd6: d808 bhi.n 8009dea + 8009dd8: f5b0 6f00 cmp.w r0, #2048 ; 0x800 + 8009ddc: d002 beq.n 8009de4 + 8009dde: f5b0 5f80 cmp.w r0, #4096 ; 0x1000 + 8009de2: d1bf bne.n 8009d64 +} + 8009de4: bcf0 pop {r4, r5, r6, r7} + frequency = RCCEx_GetSAIxPeriphCLKFreq(RCC_PERIPHCLK_SAI1, pllvco); + 8009de6: f7ff bb1b b.w 8009420 + switch(PeriphClk) + 8009dea: f5b0 4f80 cmp.w r0, #16384 ; 0x4000 + 8009dee: f000 8163 beq.w 800a0b8 + 8009df2: f5b0 3f80 cmp.w r0, #65536 ; 0x10000 + 8009df6: d1b5 bne.n 8009d64 + srcclk = __HAL_RCC_GET_DFSDM1_SOURCE(); + 8009df8: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + if(srcclk == RCC_DFSDM1CLKSOURCE_PCLK2) + 8009dfc: 075a lsls r2, r3, #29 + 8009dfe: f100 811c bmi.w 800a03a + 8009e02: e105 b.n 800a010 + switch(PeriphClk) + 8009e04: f5b0 1f00 cmp.w r0, #2097152 ; 0x200000 + 8009e08: f000 817c beq.w 800a104 + 8009e0c: d80f bhi.n 8009e2e + 8009e0e: f5b0 2f00 cmp.w r0, #524288 ; 0x80000 + 8009e12: f000 8081 beq.w 8009f18 + 8009e16: f5b0 1f80 cmp.w r0, #1048576 ; 0x100000 + 8009e1a: d1a3 bne.n 8009d64 + srcclk = __HAL_RCC_GET_I2C4_SOURCE(); + 8009e1c: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 8009e20: f003 0303 and.w r3, r3, #3 + switch(srcclk) + 8009e24: 2b01 cmp r3, #1 + 8009e26: f000 8108 beq.w 800a03a + 8009e2a: 2b02 cmp r3, #2 + 8009e2c: e182 b.n 800a134 + switch(PeriphClk) + 8009e2e: f1b0 7f80 cmp.w r0, #16777216 ; 0x1000000 + 8009e32: d197 bne.n 8009d64 + srcclk = __HAL_RCC_GET_OSPI_SOURCE(); + 8009e34: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 8009e38: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + switch(srcclk) + 8009e3c: f5b3 1f80 cmp.w r3, #1048576 ; 0x100000 + 8009e40: d033 beq.n 8009eaa + 8009e42: f5b3 1f00 cmp.w r3, #2097152 ; 0x200000 + 8009e46: f000 819c beq.w 800a182 + 8009e4a: 2b00 cmp r3, #0 + 8009e4c: d18a bne.n 8009d64 + 8009e4e: e0f4 b.n 800a03a + srcclk = READ_BIT(RCC->CCIPR, RCC_CCIPR_CLK48SEL); + 8009e50: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009e54: f003 6340 and.w r3, r3, #201326592 ; 0xc000000 + switch(srcclk) + 8009e58: f1b3 6f00 cmp.w r3, #134217728 ; 0x8000000 + 8009e5c: d037 beq.n 8009ece + 8009e5e: d820 bhi.n 8009ea2 + 8009e60: 2b00 cmp r3, #0 + 8009e62: f000 80c4 beq.w 8009fee + 8009e66: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + 8009e6a: f47f af7b bne.w 8009d64 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY)) + 8009e6e: 6828 ldr r0, [r5, #0] + 8009e70: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 8009e74: d044 beq.n 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1QEN)) + 8009e76: 6928 ldr r0, [r5, #16] + 8009e78: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009e7c: d040 beq.n 8009f00 + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009e7e: 692f ldr r7, [r5, #16] + 8009e80: f3c7 2706 ubfx r7, r7, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e84: 4379 muls r1, r7 + 8009e86: 692f ldr r7, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009e88: 6928 ldr r0, [r5, #16] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009e8a: f3c7 1703 ubfx r7, r7, #4, #4 + 8009e8e: 3701 adds r7, #1 + 8009e90: fbb1 f1f7 udiv r1, r1, r7 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009e94: f3c0 5041 ubfx r0, r0, #21, #2 + 8009e98: 3001 adds r0, #1 + 8009e9a: 0040 lsls r0, r0, #1 + 8009e9c: fbb1 f0f0 udiv r0, r1, r0 + 8009ea0: e02e b.n 8009f00 + 8009ea2: f1b3 6f40 cmp.w r3, #201326592 ; 0xc000000 + 8009ea6: f47f af5d bne.w 8009d64 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_MSIRDY)) + 8009eaa: 6828 ldr r0, [r5, #0] + 8009eac: f010 0002 ands.w r0, r0, #2 + 8009eb0: d026 beq.n 8009f00 + frequency = MSIRangeTable[(__HAL_RCC_GET_MSI_RANGE() >> 4U)]; + 8009eb2: 682b ldr r3, [r5, #0] + 8009eb4: 4a15 ldr r2, [pc, #84] ; (8009f0c ) + 8009eb6: 071b lsls r3, r3, #28 + 8009eb8: bf4b itete mi + 8009eba: 682b ldrmi r3, [r5, #0] + 8009ebc: f8d5 3094 ldrpl.w r3, [r5, #148] ; 0x94 + 8009ec0: f3c3 1303 ubfxmi r3, r3, #4, #4 + 8009ec4: f3c3 2303 ubfxpl r3, r3, #8, #4 + 8009ec8: f852 0023 ldr.w r0, [r2, r3, lsl #2] + 8009ecc: e018 b.n 8009f00 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009ece: 6828 ldr r0, [r5, #0] + 8009ed0: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009ed4: d014 beq.n 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 8009ed6: 68e8 ldr r0, [r5, #12] + 8009ed8: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009edc: d010 beq.n 8009f00 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009ede: 68e8 ldr r0, [r5, #12] + 8009ee0: f3c0 2006 ubfx r0, r0, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009ee4: 4348 muls r0, r1 + 8009ee6: 68e9 ldr r1, [r5, #12] + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009ee8: 68ed ldr r5, [r5, #12] + 8009eea: f3c5 5541 ubfx r5, r5, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009eee: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009ef2: 3501 adds r5, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009ef4: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009ef6: 006d lsls r5, r5, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009ef8: fbb0 f0f1 udiv r0, r0, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009efc: fbb0 f0f5 udiv r0, r0, r5 +} + 8009f00: bcf0 pop {r4, r5, r6, r7} + 8009f02: 4770 bx lr + 8009f04: 40021000 .word 0x40021000 + 8009f08: 0003d090 .word 0x0003d090 + 8009f0c: 08010a70 .word 0x08010a70 + 8009f10: 00f42400 .word 0x00f42400 + 8009f14: 007a1200 .word 0x007a1200 + if(HAL_IS_BIT_SET(RCC->CCIPR2, RCC_CCIPR2_SDMMCSEL)) /* PLL "P" ? */ + 8009f18: f8d5 009c ldr.w r0, [r5, #156] ; 0x9c + 8009f1c: f410 4080 ands.w r0, r0, #16384 ; 0x4000 + 8009f20: d01f beq.n 8009f62 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009f22: 6828 ldr r0, [r5, #0] + 8009f24: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009f28: d0ea beq.n 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLPEN)) + 8009f2a: 68e8 ldr r0, [r5, #12] + 8009f2c: f410 3080 ands.w r0, r0, #65536 ; 0x10000 + 8009f30: d0e6 beq.n 8009f00 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009f32: 68ee ldr r6, [r5, #12] + 8009f34: f3c6 2606 ubfx r6, r6, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009f38: fb01 f006 mul.w r0, r1, r6 + 8009f3c: 68ee ldr r6, [r5, #12] + pllp = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLPDIV) >> RCC_PLLCFGR_PLLPDIV_Pos; + 8009f3e: 68eb ldr r3, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009f40: f3c6 1603 ubfx r6, r6, #4, #4 + if(pllp == 0U) + 8009f44: 0edb lsrs r3, r3, #27 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009f46: f106 0601 add.w r6, r6, #1 + 8009f4a: fbb0 f0f6 udiv r0, r0, r6 + if(pllp == 0U) + 8009f4e: d105 bne.n 8009f5c + if(READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLP) != 0U) + 8009f50: 68eb ldr r3, [r5, #12] + pllp = 7U; + 8009f52: f413 3f00 tst.w r3, #131072 ; 0x20000 + 8009f56: bf14 ite ne + 8009f58: 2311 movne r3, #17 + 8009f5a: 2307 moveq r3, #7 + frequency = (pllvco / pllp); + 8009f5c: fbb0 f0f3 udiv r0, r0, r3 + 8009f60: e7ce b.n 8009f00 + srcclk = READ_BIT(RCC->CCIPR, RCC_CCIPR_CLK48SEL); + 8009f62: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009f66: f003 6340 and.w r3, r3, #201326592 ; 0xc000000 + switch(srcclk) + 8009f6a: f1b3 6f00 cmp.w r3, #134217728 ; 0x8000000 + 8009f6e: d024 beq.n 8009fba + 8009f70: d81e bhi.n 8009fb0 + 8009f72: 2b00 cmp r3, #0 + 8009f74: d03b beq.n 8009fee + 8009f76: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + 8009f7a: d1c1 bne.n 8009f00 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY)) + 8009f7c: 6828 ldr r0, [r5, #0] + 8009f7e: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 8009f82: d0bd beq.n 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1QEN)) + 8009f84: 6928 ldr r0, [r5, #16] + 8009f86: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009f8a: d0b9 beq.n 8009f00 + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 8009f8c: 692a ldr r2, [r5, #16] + 8009f8e: f3c2 2206 ubfx r2, r2, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009f92: 434a muls r2, r1 + 8009f94: 6929 ldr r1, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009f96: 6928 ldr r0, [r5, #16] + 8009f98: f3c0 5041 ubfx r0, r0, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009f9c: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009fa0: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009fa2: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009fa4: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 8009fa6: fbb2 f2f1 udiv r2, r2, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1Q) >> RCC_PLLSAI1CFGR_PLLSAI1Q_Pos) + 1U) << 1U)); + 8009faa: fbb2 f0f0 udiv r0, r2, r0 + 8009fae: e7a7 b.n 8009f00 + 8009fb0: f1b3 6f40 cmp.w r3, #201326592 ; 0xc000000 + 8009fb4: f43f af79 beq.w 8009eaa + 8009fb8: e7a2 b.n 8009f00 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 8009fba: 6828 ldr r0, [r5, #0] + 8009fbc: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 8009fc0: d09e beq.n 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 8009fc2: 68e8 ldr r0, [r5, #12] + 8009fc4: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 8009fc8: d09a beq.n 8009f00 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 8009fca: 68ec ldr r4, [r5, #12] + 8009fcc: f3c4 2406 ubfx r4, r4, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009fd0: 434c muls r4, r1 + 8009fd2: 68e9 ldr r1, [r5, #12] + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009fd4: 68e8 ldr r0, [r5, #12] + 8009fd6: f3c0 5041 ubfx r0, r0, #21, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009fda: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009fde: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009fe0: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009fe2: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 8009fe4: fbb4 f4f1 udiv r4, r4, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 8009fe8: fbb4 f0f0 udiv r0, r4, r0 + 8009fec: e788 b.n 8009f00 + if(HAL_IS_BIT_SET(RCC->CRRCR, RCC_CRRCR_HSI48RDY)) /* HSI48 ? */ + 8009fee: f8d5 0098 ldr.w r0, [r5, #152] ; 0x98 + frequency = HSI48_VALUE; + 8009ff2: 4b6f ldr r3, [pc, #444] ; (800a1b0 ) + 8009ff4: f010 0002 ands.w r0, r0, #2 + 8009ff8: e66b b.n 8009cd2 + srcclk = __HAL_RCC_GET_USART1_SOURCE(); + 8009ffa: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 8009ffe: f003 0303 and.w r3, r3, #3 + switch(srcclk) + 800a002: 2b02 cmp r3, #2 + 800a004: d007 beq.n 800a016 + 800a006: 2b03 cmp r3, #3 + 800a008: f43f ae57 beq.w 8009cba + 800a00c: 2b01 cmp r3, #1 + 800a00e: d014 beq.n 800a03a +} + 800a010: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetPCLK2Freq(); + 800a012: f7ff b95b b.w 80092cc + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSIRDY)) + 800a016: 6828 ldr r0, [r5, #0] + frequency = HSI_VALUE; + 800a018: 4b66 ldr r3, [pc, #408] ; (800a1b4 ) + 800a01a: f410 6080 ands.w r0, r0, #1024 ; 0x400 + 800a01e: e658 b.n 8009cd2 + srcclk = __HAL_RCC_GET_USART2_SOURCE(); + 800a020: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a024: f003 030c and.w r3, r3, #12 + switch(srcclk) + 800a028: 2b08 cmp r3, #8 + 800a02a: d0f4 beq.n 800a016 + 800a02c: d808 bhi.n 800a040 + 800a02e: 2b00 cmp r3, #0 + 800a030: f000 8085 beq.w 800a13e + 800a034: 2b04 cmp r3, #4 + 800a036: f47f ae95 bne.w 8009d64 +} + 800a03a: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetSysClockFreq(); + 800a03c: f7fe bd38 b.w 8008ab0 + 800a040: 2b0c cmp r3, #12 + 800a042: e639 b.n 8009cb8 + srcclk = __HAL_RCC_GET_USART3_SOURCE(); + 800a044: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a048: f003 0330 and.w r3, r3, #48 ; 0x30 + switch(srcclk) + 800a04c: 2b20 cmp r3, #32 + 800a04e: d0e2 beq.n 800a016 + 800a050: d803 bhi.n 800a05a + 800a052: 2b00 cmp r3, #0 + 800a054: d073 beq.n 800a13e + 800a056: 2b10 cmp r3, #16 + 800a058: e7ed b.n 800a036 + 800a05a: 2b30 cmp r3, #48 ; 0x30 + 800a05c: e62c b.n 8009cb8 + srcclk = __HAL_RCC_GET_UART4_SOURCE(); + 800a05e: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a062: f003 03c0 and.w r3, r3, #192 ; 0xc0 + switch(srcclk) + 800a066: 2b80 cmp r3, #128 ; 0x80 + 800a068: d0d5 beq.n 800a016 + 800a06a: d803 bhi.n 800a074 + 800a06c: 2b00 cmp r3, #0 + 800a06e: d066 beq.n 800a13e + 800a070: 2b40 cmp r3, #64 ; 0x40 + 800a072: e7e0 b.n 800a036 + 800a074: 2bc0 cmp r3, #192 ; 0xc0 + 800a076: e61f b.n 8009cb8 + srcclk = __HAL_RCC_GET_UART5_SOURCE(); + 800a078: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a07c: f403 7340 and.w r3, r3, #768 ; 0x300 + switch(srcclk) + 800a080: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800a084: d0c7 beq.n 800a016 + 800a086: d804 bhi.n 800a092 + 800a088: 2b00 cmp r3, #0 + 800a08a: d058 beq.n 800a13e + 800a08c: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800a090: e7d1 b.n 800a036 + 800a092: f5b3 7f40 cmp.w r3, #768 ; 0x300 + 800a096: e60f b.n 8009cb8 + srcclk = __HAL_RCC_GET_LPUART1_SOURCE(); + 800a098: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a09c: f403 6340 and.w r3, r3, #3072 ; 0xc00 + switch(srcclk) + 800a0a0: f5b3 6f00 cmp.w r3, #2048 ; 0x800 + 800a0a4: d0b7 beq.n 800a016 + 800a0a6: d804 bhi.n 800a0b2 + 800a0a8: 2b00 cmp r3, #0 + 800a0aa: d048 beq.n 800a13e + 800a0ac: f5b3 6f80 cmp.w r3, #1024 ; 0x400 + 800a0b0: e7c1 b.n 800a036 + 800a0b2: f5b3 6f40 cmp.w r3, #3072 ; 0xc00 + 800a0b6: e5ff b.n 8009cb8 + srcclk = __HAL_RCC_GET_ADC_SOURCE(); + 800a0b8: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a0bc: f003 5340 and.w r3, r3, #805306368 ; 0x30000000 + switch(srcclk) + 800a0c0: f1b3 5f80 cmp.w r3, #268435456 ; 0x10000000 + 800a0c4: d002 beq.n 800a0cc + 800a0c6: f1b3 5f40 cmp.w r3, #805306368 ; 0x30000000 + 800a0ca: e7b4 b.n 800a036 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLSAI1RDY) && (__HAL_RCC_GET_PLLSAI1CLKOUT_CONFIG(RCC_PLLSAI1_ADC1CLK) != 0U)) + 800a0cc: 6828 ldr r0, [r5, #0] + 800a0ce: f010 6000 ands.w r0, r0, #134217728 ; 0x8000000 + 800a0d2: f43f af15 beq.w 8009f00 + 800a0d6: 6928 ldr r0, [r5, #16] + 800a0d8: f010 7080 ands.w r0, r0, #16777216 ; 0x1000000 + 800a0dc: f43f af10 beq.w 8009f00 + plln = READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1N) >> RCC_PLLSAI1CFGR_PLLSAI1N_Pos; + 800a0e0: 692b ldr r3, [r5, #16] + 800a0e2: f3c3 2306 ubfx r3, r3, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 800a0e6: 434b muls r3, r1 + 800a0e8: 6929 ldr r1, [r5, #16] + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 800a0ea: 6928 ldr r0, [r5, #16] + 800a0ec: f3c0 6041 ubfx r0, r0, #25, #2 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 800a0f0: f3c1 1103 ubfx r1, r1, #4, #4 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 800a0f4: 3001 adds r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 800a0f6: 3101 adds r1, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 800a0f8: 0040 lsls r0, r0, #1 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1M) >> RCC_PLLSAI1CFGR_PLLSAI1M_Pos) + 1U)); + 800a0fa: fbb3 f3f1 udiv r3, r3, r1 + frequency = (pllvco / (((READ_BIT(RCC->PLLSAI1CFGR, RCC_PLLSAI1CFGR_PLLSAI1R) >> RCC_PLLSAI1CFGR_PLLSAI1R_Pos) + 1U) << 1U)); + 800a0fe: fbb3 f0f0 udiv r0, r3, r0 + 800a102: e6fd b.n 8009f00 + srcclk = __HAL_RCC_GET_DFSDM1AUDIO_SOURCE(); + 800a104: f8d5 309c ldr.w r3, [r5, #156] ; 0x9c + 800a108: f003 0318 and.w r3, r3, #24 + switch(srcclk) + 800a10c: 2b08 cmp r3, #8 + 800a10e: d082 beq.n 800a016 + 800a110: 2b10 cmp r3, #16 + 800a112: f43f aeca beq.w 8009eaa + 800a116: 2b00 cmp r3, #0 + 800a118: f47f ae24 bne.w 8009d64 + frequency = RCCEx_GetSAIxPeriphCLKFreq(RCC_PERIPHCLK_SAI1, pllvco); + 800a11c: f44f 6000 mov.w r0, #2048 ; 0x800 + 800a120: e660 b.n 8009de4 + srcclk = __HAL_RCC_GET_I2C1_SOURCE(); + 800a122: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a126: f403 5340 and.w r3, r3, #12288 ; 0x3000 + switch(srcclk) + 800a12a: f5b3 5f80 cmp.w r3, #4096 ; 0x1000 + 800a12e: d084 beq.n 800a03a + 800a130: f5b3 5f00 cmp.w r3, #8192 ; 0x2000 + 800a134: f43f af6f beq.w 800a016 + 800a138: 2b00 cmp r3, #0 + 800a13a: f47f ae13 bne.w 8009d64 +} + 800a13e: bcf0 pop {r4, r5, r6, r7} + frequency = HAL_RCC_GetPCLK1Freq(); + 800a140: f7ff b8b2 b.w 80092a8 + srcclk = __HAL_RCC_GET_I2C3_SOURCE(); + 800a144: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a148: f403 3340 and.w r3, r3, #196608 ; 0x30000 + switch(srcclk) + 800a14c: f5b3 3f80 cmp.w r3, #65536 ; 0x10000 + 800a150: f43f af73 beq.w 800a03a + 800a154: f5b3 3f00 cmp.w r3, #131072 ; 0x20000 + 800a158: e7ec b.n 800a134 + 800a15a: f5b3 2f40 cmp.w r3, #786432 ; 0xc0000 + 800a15e: e5ab b.n 8009cb8 + srcclk = __HAL_RCC_GET_LPTIM2_SOURCE(); + 800a160: f8d5 3088 ldr.w r3, [r5, #136] ; 0x88 + 800a164: f403 1340 and.w r3, r3, #3145728 ; 0x300000 + switch(srcclk) + 800a168: f5b3 1f00 cmp.w r3, #2097152 ; 0x200000 + 800a16c: f43f af53 beq.w 800a016 + 800a170: d804 bhi.n 800a17c + 800a172: 2b00 cmp r3, #0 + 800a174: d0e3 beq.n 800a13e + 800a176: f5b3 1f80 cmp.w r3, #1048576 ; 0x100000 + 800a17a: e61c b.n 8009db6 + 800a17c: f5b3 1f40 cmp.w r3, #3145728 ; 0x300000 + 800a180: e59a b.n 8009cb8 + if(HAL_IS_BIT_SET(RCC->CR, RCC_CR_PLLRDY)) + 800a182: 6828 ldr r0, [r5, #0] + 800a184: f010 7000 ands.w r0, r0, #33554432 ; 0x2000000 + 800a188: f43f aeba beq.w 8009f00 + if(HAL_IS_BIT_SET(RCC->PLLCFGR, RCC_PLLCFGR_PLLQEN)) + 800a18c: 68e8 ldr r0, [r5, #12] + 800a18e: f410 1080 ands.w r0, r0, #1048576 ; 0x100000 + 800a192: f43f aeb5 beq.w 8009f00 + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 800a196: 68e8 ldr r0, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 800a198: 68eb ldr r3, [r5, #12] + plln = READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos; + 800a19a: f3c0 2006 ubfx r0, r0, #8, #7 + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 800a19e: f3c3 1303 ubfx r3, r3, #4, #4 + 800a1a2: 4341 muls r1, r0 + 800a1a4: 3301 adds r3, #1 + frequency = (pllvco / (((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLQ) >> RCC_PLLCFGR_PLLQ_Pos) + 1U) << 1U)); + 800a1a6: 68e8 ldr r0, [r5, #12] + pllvco = ((pllvco * plln) / ((READ_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U)); + 800a1a8: fbb1 f1f3 udiv r1, r1, r3 + 800a1ac: e672 b.n 8009e94 + 800a1ae: bf00 nop + 800a1b0: 02dc6c00 .word 0x02dc6c00 + 800a1b4: 00f42400 .word 0x00f42400 + +0800a1b8 : +{ + 800a1b8: b570 push {r4, r5, r6, lr} + __HAL_RCC_PLLSAI1_DISABLE(); + 800a1ba: 4c20 ldr r4, [pc, #128] ; (800a23c ) + 800a1bc: 6823 ldr r3, [r4, #0] + 800a1be: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 800a1c2: 6023 str r3, [r4, #0] +{ + 800a1c4: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 800a1c6: f7fd f909 bl 80073dc + 800a1ca: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 800a1cc: 6823 ldr r3, [r4, #0] + 800a1ce: 011a lsls r2, r3, #4 + 800a1d0: d423 bmi.n 800a21a + __HAL_RCC_PLLSAI1_CONFIG(PLLSAI1Init->PLLSAI1M, PLLSAI1Init->PLLSAI1N, PLLSAI1Init->PLLSAI1P, PLLSAI1Init->PLLSAI1Q, PLLSAI1Init->PLLSAI1R); + 800a1d2: e9d5 2302 ldrd r2, r3, [r5, #8] + 800a1d6: 06db lsls r3, r3, #27 + 800a1d8: 6921 ldr r1, [r4, #16] + 800a1da: ea43 2302 orr.w r3, r3, r2, lsl #8 + 800a1de: 4a18 ldr r2, [pc, #96] ; (800a240 ) + 800a1e0: 400a ands r2, r1 + 800a1e2: 4313 orrs r3, r2 + 800a1e4: 686a ldr r2, [r5, #4] + 800a1e6: 3a01 subs r2, #1 + 800a1e8: ea43 1302 orr.w r3, r3, r2, lsl #4 + 800a1ec: 692a ldr r2, [r5, #16] + 800a1ee: 0852 lsrs r2, r2, #1 + 800a1f0: 3a01 subs r2, #1 + 800a1f2: ea43 5342 orr.w r3, r3, r2, lsl #21 + 800a1f6: 696a ldr r2, [r5, #20] + 800a1f8: 0852 lsrs r2, r2, #1 + 800a1fa: 3a01 subs r2, #1 + 800a1fc: ea43 6342 orr.w r3, r3, r2, lsl #25 + 800a200: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1CLKOUT_ENABLE(PLLSAI1Init->PLLSAI1ClockOut); + 800a202: 6923 ldr r3, [r4, #16] + 800a204: 69aa ldr r2, [r5, #24] + 800a206: 4313 orrs r3, r2 + 800a208: 6123 str r3, [r4, #16] + __HAL_RCC_PLLSAI1_ENABLE(); + 800a20a: 6823 ldr r3, [r4, #0] + 800a20c: f043 6380 orr.w r3, r3, #67108864 ; 0x4000000 + 800a210: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a212: f7fd f8e3 bl 80073dc + 800a216: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 800a218: e00b b.n 800a232 + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 800a21a: f7fd f8df bl 80073dc + 800a21e: 1b80 subs r0, r0, r6 + 800a220: 2802 cmp r0, #2 + 800a222: d9d3 bls.n 800a1cc + status = HAL_TIMEOUT; + 800a224: 2003 movs r0, #3 +} + 800a226: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 800a228: f7fd f8d8 bl 80073dc + 800a22c: 1b40 subs r0, r0, r5 + 800a22e: 2802 cmp r0, #2 + 800a230: d8f8 bhi.n 800a224 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) == 0U) + 800a232: 6823 ldr r3, [r4, #0] + 800a234: 011b lsls r3, r3, #4 + 800a236: d5f7 bpl.n 800a228 + 800a238: 2000 movs r0, #0 + return status; + 800a23a: e7f4 b.n 800a226 + 800a23c: 40021000 .word 0x40021000 + 800a240: 019d800f .word 0x019d800f + +0800a244 : +{ + 800a244: b538 push {r3, r4, r5, lr} + __HAL_RCC_PLLSAI1_DISABLE(); + 800a246: 4c11 ldr r4, [pc, #68] ; (800a28c ) + 800a248: 6823 ldr r3, [r4, #0] + 800a24a: f023 6380 bic.w r3, r3, #67108864 ; 0x4000000 + 800a24e: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a250: f7fd f8c4 bl 80073dc + 800a254: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI1RDY) != 0U) + 800a256: 6823 ldr r3, [r4, #0] + 800a258: f013 6300 ands.w r3, r3, #134217728 ; 0x8000000 + 800a25c: d10f bne.n 800a27e + HAL_StatusTypeDef status = HAL_OK; + 800a25e: 4618 mov r0, r3 + __HAL_RCC_PLLSAI1CLKOUT_DISABLE(RCC_PLLSAI1CFGR_PLLSAI1PEN|RCC_PLLSAI1CFGR_PLLSAI1QEN|RCC_PLLSAI1CFGR_PLLSAI1REN); + 800a260: 6923 ldr r3, [r4, #16] + 800a262: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 800a266: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 800a26a: 6123 str r3, [r4, #16] + if(READ_BIT(RCC->CR, (RCC_CR_PLLRDY | RCC_CR_PLLSAI2RDY)) == 0U) + 800a26c: 6823 ldr r3, [r4, #0] + 800a26e: f013 5f08 tst.w r3, #570425344 ; 0x22000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 800a272: bf02 ittt eq + 800a274: 68e3 ldreq r3, [r4, #12] + 800a276: f023 0303 biceq.w r3, r3, #3 + 800a27a: 60e3 streq r3, [r4, #12] +} + 800a27c: bd38 pop {r3, r4, r5, pc} + if((HAL_GetTick() - tickstart) > PLLSAI1_TIMEOUT_VALUE) + 800a27e: f7fd f8ad bl 80073dc + 800a282: 1b40 subs r0, r0, r5 + 800a284: 2802 cmp r0, #2 + 800a286: d9e6 bls.n 800a256 + status = HAL_TIMEOUT; + 800a288: 2003 movs r0, #3 + 800a28a: e7e9 b.n 800a260 + 800a28c: 40021000 .word 0x40021000 + +0800a290 : +{ + 800a290: b570 push {r4, r5, r6, lr} + __HAL_RCC_PLLSAI2_DISABLE(); + 800a292: 4c20 ldr r4, [pc, #128] ; (800a314 ) + 800a294: 6823 ldr r3, [r4, #0] + 800a296: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800a29a: 6023 str r3, [r4, #0] +{ + 800a29c: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 800a29e: f7fd f89d bl 80073dc + 800a2a2: 4606 mov r6, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 800a2a4: 6823 ldr r3, [r4, #0] + 800a2a6: 009a lsls r2, r3, #2 + 800a2a8: d423 bmi.n 800a2f2 + __HAL_RCC_PLLSAI2_CONFIG(PLLSAI2Init->PLLSAI2M, PLLSAI2Init->PLLSAI2N, PLLSAI2Init->PLLSAI2P, PLLSAI2Init->PLLSAI2Q, PLLSAI2Init->PLLSAI2R); + 800a2aa: e9d5 2302 ldrd r2, r3, [r5, #8] + 800a2ae: 06db lsls r3, r3, #27 + 800a2b0: 6961 ldr r1, [r4, #20] + 800a2b2: ea43 2302 orr.w r3, r3, r2, lsl #8 + 800a2b6: 4a18 ldr r2, [pc, #96] ; (800a318 ) + 800a2b8: 400a ands r2, r1 + 800a2ba: 4313 orrs r3, r2 + 800a2bc: 686a ldr r2, [r5, #4] + 800a2be: 3a01 subs r2, #1 + 800a2c0: ea43 1302 orr.w r3, r3, r2, lsl #4 + 800a2c4: 692a ldr r2, [r5, #16] + 800a2c6: 0852 lsrs r2, r2, #1 + 800a2c8: 3a01 subs r2, #1 + 800a2ca: ea43 5342 orr.w r3, r3, r2, lsl #21 + 800a2ce: 696a ldr r2, [r5, #20] + 800a2d0: 0852 lsrs r2, r2, #1 + 800a2d2: 3a01 subs r2, #1 + 800a2d4: ea43 6342 orr.w r3, r3, r2, lsl #25 + 800a2d8: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2CLKOUT_ENABLE(PLLSAI2Init->PLLSAI2ClockOut); + 800a2da: 6963 ldr r3, [r4, #20] + 800a2dc: 69aa ldr r2, [r5, #24] + 800a2de: 4313 orrs r3, r2 + 800a2e0: 6163 str r3, [r4, #20] + __HAL_RCC_PLLSAI2_ENABLE(); + 800a2e2: 6823 ldr r3, [r4, #0] + 800a2e4: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800a2e8: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a2ea: f7fd f877 bl 80073dc + 800a2ee: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 800a2f0: e00b b.n 800a30a + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a2f2: f7fd f873 bl 80073dc + 800a2f6: 1b80 subs r0, r0, r6 + 800a2f8: 2802 cmp r0, #2 + 800a2fa: d9d3 bls.n 800a2a4 + status = HAL_TIMEOUT; + 800a2fc: 2003 movs r0, #3 +} + 800a2fe: bd70 pop {r4, r5, r6, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a300: f7fd f86c bl 80073dc + 800a304: 1b40 subs r0, r0, r5 + 800a306: 2802 cmp r0, #2 + 800a308: d8f8 bhi.n 800a2fc + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) == 0U) + 800a30a: 6823 ldr r3, [r4, #0] + 800a30c: 009b lsls r3, r3, #2 + 800a30e: d5f7 bpl.n 800a300 + 800a310: 2000 movs r0, #0 + return status; + 800a312: e7f4 b.n 800a2fe + 800a314: 40021000 .word 0x40021000 + 800a318: 019d800f .word 0x019d800f + +0800a31c : +{ + 800a31c: b538 push {r3, r4, r5, lr} + __HAL_RCC_PLLSAI2_DISABLE(); + 800a31e: 4c11 ldr r4, [pc, #68] ; (800a364 ) + 800a320: 6823 ldr r3, [r4, #0] + 800a322: f023 5380 bic.w r3, r3, #268435456 ; 0x10000000 + 800a326: 6023 str r3, [r4, #0] + tickstart = HAL_GetTick(); + 800a328: f7fd f858 bl 80073dc + 800a32c: 4605 mov r5, r0 + while(READ_BIT(RCC->CR, RCC_CR_PLLSAI2RDY) != 0U) + 800a32e: 6823 ldr r3, [r4, #0] + 800a330: f013 5300 ands.w r3, r3, #536870912 ; 0x20000000 + 800a334: d10f bne.n 800a356 + HAL_StatusTypeDef status = HAL_OK; + 800a336: 4618 mov r0, r3 + __HAL_RCC_PLLSAI2CLKOUT_DISABLE(RCC_PLLSAI2CFGR_PLLSAI2PEN|RCC_PLLSAI2CFGR_PLLSAI2QEN|RCC_PLLSAI2CFGR_PLLSAI2REN); + 800a338: 6963 ldr r3, [r4, #20] + 800a33a: f023 7388 bic.w r3, r3, #17825792 ; 0x1100000 + 800a33e: f423 3380 bic.w r3, r3, #65536 ; 0x10000 + 800a342: 6163 str r3, [r4, #20] + if(READ_BIT(RCC->CR, (RCC_CR_PLLRDY | RCC_CR_PLLSAI1RDY)) == 0U) + 800a344: 6823 ldr r3, [r4, #0] + 800a346: f013 6f20 tst.w r3, #167772160 ; 0xa000000 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLSOURCE_NONE); + 800a34a: bf02 ittt eq + 800a34c: 68e3 ldreq r3, [r4, #12] + 800a34e: f023 0303 biceq.w r3, r3, #3 + 800a352: 60e3 streq r3, [r4, #12] +} + 800a354: bd38 pop {r3, r4, r5, pc} + if((HAL_GetTick() - tickstart) > PLLSAI2_TIMEOUT_VALUE) + 800a356: f7fd f841 bl 80073dc + 800a35a: 1b40 subs r0, r0, r5 + 800a35c: 2802 cmp r0, #2 + 800a35e: d9e6 bls.n 800a32e + status = HAL_TIMEOUT; + 800a360: 2003 movs r0, #3 + 800a362: e7e9 b.n 800a338 + 800a364: 40021000 .word 0x40021000 + +0800a368 : + __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(WakeUpClk); + 800a368: 4a03 ldr r2, [pc, #12] ; (800a378 ) + 800a36a: 6893 ldr r3, [r2, #8] + 800a36c: f423 4300 bic.w r3, r3, #32768 ; 0x8000 + 800a370: 4318 orrs r0, r3 + 800a372: 6090 str r0, [r2, #8] +} + 800a374: 4770 bx lr + 800a376: bf00 nop + 800a378: 40021000 .word 0x40021000 + +0800a37c : + __HAL_RCC_MSI_STANDBY_RANGE_CONFIG(MSIRange); + 800a37c: 4a04 ldr r2, [pc, #16] ; (800a390 ) + 800a37e: f8d2 3094 ldr.w r3, [r2, #148] ; 0x94 + 800a382: f423 6370 bic.w r3, r3, #3840 ; 0xf00 + 800a386: ea43 1000 orr.w r0, r3, r0, lsl #4 + 800a38a: f8c2 0094 str.w r0, [r2, #148] ; 0x94 +} + 800a38e: 4770 bx lr + 800a390: 40021000 .word 0x40021000 + +0800a394 : + SET_BIT(RCC->BDCR, RCC_BDCR_LSECSSON); + 800a394: 4a03 ldr r2, [pc, #12] ; (800a3a4 ) + 800a396: f8d2 3090 ldr.w r3, [r2, #144] ; 0x90 + 800a39a: f043 0320 orr.w r3, r3, #32 + 800a39e: f8c2 3090 str.w r3, [r2, #144] ; 0x90 +} + 800a3a2: 4770 bx lr + 800a3a4: 40021000 .word 0x40021000 + +0800a3a8 : + CLEAR_BIT(RCC->BDCR, RCC_BDCR_LSECSSON) ; + 800a3a8: 4b05 ldr r3, [pc, #20] ; (800a3c0 ) + 800a3aa: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800a3ae: f022 0220 bic.w r2, r2, #32 + 800a3b2: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + __HAL_RCC_DISABLE_IT(RCC_IT_LSECSS); + 800a3b6: 699a ldr r2, [r3, #24] + 800a3b8: f422 7200 bic.w r2, r2, #512 ; 0x200 + 800a3bc: 619a str r2, [r3, #24] +} + 800a3be: 4770 bx lr + 800a3c0: 40021000 .word 0x40021000 + +0800a3c4 : + SET_BIT(RCC->BDCR, RCC_BDCR_LSECSSON) ; + 800a3c4: 4b0a ldr r3, [pc, #40] ; (800a3f0 ) + 800a3c6: f8d3 2090 ldr.w r2, [r3, #144] ; 0x90 + 800a3ca: f042 0220 orr.w r2, r2, #32 + 800a3ce: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + __HAL_RCC_ENABLE_IT(RCC_IT_LSECSS); + 800a3d2: 699a ldr r2, [r3, #24] + 800a3d4: f442 7200 orr.w r2, r2, #512 ; 0x200 + 800a3d8: 619a str r2, [r3, #24] + __HAL_RCC_LSECSS_EXTI_ENABLE_IT(); + 800a3da: f5a3 3386 sub.w r3, r3, #68608 ; 0x10c00 + 800a3de: 681a ldr r2, [r3, #0] + 800a3e0: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 800a3e4: 601a str r2, [r3, #0] + __HAL_RCC_LSECSS_EXTI_ENABLE_RISING_EDGE(); + 800a3e6: 689a ldr r2, [r3, #8] + 800a3e8: f442 2200 orr.w r2, r2, #524288 ; 0x80000 + 800a3ec: 609a str r2, [r3, #8] +} + 800a3ee: 4770 bx lr + 800a3f0: 40021000 .word 0x40021000 + +0800a3f4 : +} + 800a3f4: 4770 bx lr + ... + +0800a3f8 : +{ + 800a3f8: b510 push {r4, lr} + if(__HAL_RCC_GET_IT(RCC_IT_LSECSS)) + 800a3fa: 4c05 ldr r4, [pc, #20] ; (800a410 ) + 800a3fc: 69e3 ldr r3, [r4, #28] + 800a3fe: 059b lsls r3, r3, #22 + 800a400: d504 bpl.n 800a40c + HAL_RCCEx_LSECSS_Callback(); + 800a402: f7ff fff7 bl 800a3f4 + __HAL_RCC_CLEAR_IT(RCC_IT_LSECSS); + 800a406: f44f 7300 mov.w r3, #512 ; 0x200 + 800a40a: 6223 str r3, [r4, #32] +} + 800a40c: bd10 pop {r4, pc} + 800a40e: bf00 nop + 800a410: 40021000 .word 0x40021000 + +0800a414 : + SET_BIT(RCC->CR, RCC_CR_MSIPLLEN) ; + 800a414: 4a02 ldr r2, [pc, #8] ; (800a420 ) + 800a416: 6813 ldr r3, [r2, #0] + 800a418: f043 0304 orr.w r3, r3, #4 + 800a41c: 6013 str r3, [r2, #0] +} + 800a41e: 4770 bx lr + 800a420: 40021000 .word 0x40021000 + +0800a424 : + CLEAR_BIT(RCC->CR, RCC_CR_MSIPLLEN) ; + 800a424: 4a02 ldr r2, [pc, #8] ; (800a430 ) + 800a426: 6813 ldr r3, [r2, #0] + 800a428: f023 0304 bic.w r3, r3, #4 + 800a42c: 6013 str r3, [r2, #0] +} + 800a42e: 4770 bx lr + 800a430: 40021000 .word 0x40021000 + +0800a434 : + MODIFY_REG(RCC->DLYCFGR, RCC_DLYCFGR_OCTOSPI1_DLY|RCC_DLYCFGR_OCTOSPI2_DLY, (Delay1 | (Delay2 << RCC_DLYCFGR_OCTOSPI2_DLY_Pos))) ; + 800a434: 4a05 ldr r2, [pc, #20] ; (800a44c ) + 800a436: f8d2 30a4 ldr.w r3, [r2, #164] ; 0xa4 + 800a43a: f023 03ff bic.w r3, r3, #255 ; 0xff + 800a43e: 4318 orrs r0, r3 + 800a440: ea40 1101 orr.w r1, r0, r1, lsl #4 + 800a444: f8c2 10a4 str.w r1, [r2, #164] ; 0xa4 +} + 800a448: 4770 bx lr + 800a44a: bf00 nop + 800a44c: 40021000 .word 0x40021000 + +0800a450 : + __HAL_RCC_CRS_FORCE_RESET(); + 800a450: 4b10 ldr r3, [pc, #64] ; (800a494 ) + 800a452: 6b9a ldr r2, [r3, #56] ; 0x38 + 800a454: f042 7280 orr.w r2, r2, #16777216 ; 0x1000000 + 800a458: 639a str r2, [r3, #56] ; 0x38 + __HAL_RCC_CRS_RELEASE_RESET(); + 800a45a: 6b9a ldr r2, [r3, #56] ; 0x38 + 800a45c: f022 7280 bic.w r2, r2, #16777216 ; 0x1000000 + 800a460: 639a str r2, [r3, #56] ; 0x38 + value = (pInit->Prescaler | pInit->Source | pInit->Polarity); + 800a462: e9d0 3200 ldrd r3, r2, [r0] + 800a466: 4313 orrs r3, r2 + 800a468: 6882 ldr r2, [r0, #8] + 800a46a: 4313 orrs r3, r2 + value |= pInit->ReloadValue; + 800a46c: 68c2 ldr r2, [r0, #12] + 800a46e: 4313 orrs r3, r2 + value |= (pInit->ErrorLimitValue << CRS_CFGR_FELIM_Pos); + 800a470: 6902 ldr r2, [r0, #16] + 800a472: ea43 4302 orr.w r3, r3, r2, lsl #16 + WRITE_REG(CRS->CFGR, value); + 800a476: 4a08 ldr r2, [pc, #32] ; (800a498 ) + 800a478: 6053 str r3, [r2, #4] + MODIFY_REG(CRS->CR, CRS_CR_TRIM, (pInit->HSI48CalibrationValue << CRS_CR_TRIM_Pos)); + 800a47a: 6813 ldr r3, [r2, #0] + 800a47c: 6941 ldr r1, [r0, #20] + 800a47e: f423 537c bic.w r3, r3, #16128 ; 0x3f00 + 800a482: ea43 2301 orr.w r3, r3, r1, lsl #8 + 800a486: 6013 str r3, [r2, #0] + SET_BIT(CRS->CR, CRS_CR_AUTOTRIMEN | CRS_CR_CEN); + 800a488: 6813 ldr r3, [r2, #0] + 800a48a: f043 0360 orr.w r3, r3, #96 ; 0x60 + 800a48e: 6013 str r3, [r2, #0] +} + 800a490: 4770 bx lr + 800a492: bf00 nop + 800a494: 40021000 .word 0x40021000 + 800a498: 40006000 .word 0x40006000 + +0800a49c : + SET_BIT(CRS->CR, CRS_CR_SWSYNC); + 800a49c: 4a02 ldr r2, [pc, #8] ; (800a4a8 ) + 800a49e: 6813 ldr r3, [r2, #0] + 800a4a0: f043 0380 orr.w r3, r3, #128 ; 0x80 + 800a4a4: 6013 str r3, [r2, #0] +} + 800a4a6: 4770 bx lr + 800a4a8: 40006000 .word 0x40006000 + +0800a4ac : + pSynchroInfo->ReloadValue = (READ_BIT(CRS->CFGR, CRS_CFGR_RELOAD)); + 800a4ac: 4b07 ldr r3, [pc, #28] ; (800a4cc ) + 800a4ae: 685a ldr r2, [r3, #4] + 800a4b0: b292 uxth r2, r2 + 800a4b2: 6002 str r2, [r0, #0] + pSynchroInfo->HSI48CalibrationValue = (READ_BIT(CRS->CR, CRS_CR_TRIM) >> CRS_CR_TRIM_Pos); + 800a4b4: 681a ldr r2, [r3, #0] + 800a4b6: f3c2 2205 ubfx r2, r2, #8, #6 + 800a4ba: 6042 str r2, [r0, #4] + pSynchroInfo->FreqErrorCapture = (READ_BIT(CRS->ISR, CRS_ISR_FECAP) >> CRS_ISR_FECAP_Pos); + 800a4bc: 689a ldr r2, [r3, #8] + 800a4be: 0c12 lsrs r2, r2, #16 + 800a4c0: 6082 str r2, [r0, #8] + pSynchroInfo->FreqErrorDirection = (READ_BIT(CRS->ISR, CRS_ISR_FEDIR)); + 800a4c2: 689b ldr r3, [r3, #8] + 800a4c4: f403 4300 and.w r3, r3, #32768 ; 0x8000 + 800a4c8: 60c3 str r3, [r0, #12] +} + 800a4ca: 4770 bx lr + 800a4cc: 40006000 .word 0x40006000 + +0800a4d0 : +{ + 800a4d0: b5f8 push {r3, r4, r5, r6, r7, lr} + 800a4d2: 4605 mov r5, r0 + tickstart = HAL_GetTick(); + 800a4d4: f7fc ff82 bl 80073dc + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCOK)) + 800a4d8: 4c1e ldr r4, [pc, #120] ; (800a554 ) + tickstart = HAL_GetTick(); + 800a4da: 4606 mov r6, r0 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCOK); + 800a4dc: 2701 movs r7, #1 + if(Timeout != HAL_MAX_DELAY) + 800a4de: 1c68 adds r0, r5, #1 + 800a4e0: d12f bne.n 800a542 + crsstatus = RCC_CRS_TIMEOUT; + 800a4e2: 2000 movs r0, #0 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCOK)) + 800a4e4: 68a2 ldr r2, [r4, #8] + 800a4e6: 07d1 lsls r1, r2, #31 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCOK); + 800a4e8: bf48 it mi + 800a4ea: 60e7 strmi r7, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCWARN)) + 800a4ec: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCOK; + 800a4ee: bf48 it mi + 800a4f0: f040 0002 orrmi.w r0, r0, #2 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCWARN)) + 800a4f4: 0792 lsls r2, r2, #30 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCWARN); + 800a4f6: bf44 itt mi + 800a4f8: 2202 movmi r2, #2 + 800a4fa: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_TRIMOVF)) + 800a4fc: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCWARN; + 800a4fe: bf48 it mi + 800a500: f040 0004 orrmi.w r0, r0, #4 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_TRIMOVF)) + 800a504: 0553 lsls r3, r2, #21 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_TRIMOVF); + 800a506: bf44 itt mi + 800a508: 2204 movmi r2, #4 + 800a50a: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCERR)) + 800a50c: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_TRIMOVF; + 800a50e: bf48 it mi + 800a510: f040 0020 orrmi.w r0, r0, #32 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCERR)) + 800a514: 05d1 lsls r1, r2, #23 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCERR); + 800a516: bf44 itt mi + 800a518: 2204 movmi r2, #4 + 800a51a: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCMISS)) + 800a51c: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCERR; + 800a51e: bf48 it mi + 800a520: f040 0008 orrmi.w r0, r0, #8 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_SYNCMISS)) + 800a524: 0592 lsls r2, r2, #22 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_SYNCMISS); + 800a526: bf44 itt mi + 800a528: 2204 movmi r2, #4 + 800a52a: 60e2 strmi r2, [r4, #12] + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_ESYNC)) + 800a52c: 68a2 ldr r2, [r4, #8] + crsstatus |= RCC_CRS_SYNCMISS; + 800a52e: bf48 it mi + 800a530: f040 0010 orrmi.w r0, r0, #16 + if(__HAL_RCC_CRS_GET_FLAG(RCC_CRS_FLAG_ESYNC)) + 800a534: 0713 lsls r3, r2, #28 + __HAL_RCC_CRS_CLEAR_FLAG(RCC_CRS_FLAG_ESYNC); + 800a536: bf44 itt mi + 800a538: 2208 movmi r2, #8 + 800a53a: 60e2 strmi r2, [r4, #12] + } while(RCC_CRS_NONE == crsstatus); + 800a53c: 2800 cmp r0, #0 + 800a53e: d0ce beq.n 800a4de +} + 800a540: bdf8 pop {r3, r4, r5, r6, r7, pc} + if(((HAL_GetTick() - tickstart) > Timeout) || (Timeout == 0U)) + 800a542: f7fc ff4b bl 80073dc + 800a546: 1b80 subs r0, r0, r6 + 800a548: 42a8 cmp r0, r5 + 800a54a: d801 bhi.n 800a550 + 800a54c: 2d00 cmp r5, #0 + 800a54e: d1c8 bne.n 800a4e2 + crsstatus = RCC_CRS_TIMEOUT; + 800a550: 2001 movs r0, #1 + 800a552: e7c7 b.n 800a4e4 + 800a554: 40006000 .word 0x40006000 + +0800a558 : + 800a558: 4770 bx lr + +0800a55a : + 800a55a: 4770 bx lr + +0800a55c : + 800a55c: 4770 bx lr + +0800a55e : +} + 800a55e: 4770 bx lr + +0800a560 : + uint32_t itflags = READ_REG(CRS->ISR); + 800a560: 491b ldr r1, [pc, #108] ; (800a5d0 ) +{ + 800a562: b508 push {r3, lr} + uint32_t itflags = READ_REG(CRS->ISR); + 800a564: 688b ldr r3, [r1, #8] + uint32_t itsources = READ_REG(CRS->CR); + 800a566: 680a ldr r2, [r1, #0] + if(((itflags & RCC_CRS_FLAG_SYNCOK) != 0U) && ((itsources & RCC_CRS_IT_SYNCOK) != 0U)) + 800a568: 07d8 lsls r0, r3, #31 + 800a56a: d506 bpl.n 800a57a + 800a56c: 07d0 lsls r0, r2, #31 + 800a56e: d504 bpl.n 800a57a + WRITE_REG(CRS->ICR, CRS_ICR_SYNCOKC); + 800a570: 2301 movs r3, #1 + 800a572: 60cb str r3, [r1, #12] + HAL_RCCEx_CRS_SyncOkCallback(); + 800a574: f7ff fff0 bl 800a558 +} + 800a578: bd08 pop {r3, pc} + else if(((itflags & RCC_CRS_FLAG_SYNCWARN) != 0U) && ((itsources & RCC_CRS_IT_SYNCWARN) != 0U)) + 800a57a: 0798 lsls r0, r3, #30 + 800a57c: d507 bpl.n 800a58e + 800a57e: 0791 lsls r1, r2, #30 + 800a580: d505 bpl.n 800a58e + WRITE_REG(CRS->ICR, CRS_ICR_SYNCWARNC); + 800a582: 4b13 ldr r3, [pc, #76] ; (800a5d0 ) + 800a584: 2202 movs r2, #2 + 800a586: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_SyncWarnCallback(); + 800a588: f7ff ffe7 bl 800a55a + 800a58c: e7f4 b.n 800a578 + else if(((itflags & RCC_CRS_FLAG_ESYNC) != 0U) && ((itsources & RCC_CRS_IT_ESYNC) != 0U)) + 800a58e: 0718 lsls r0, r3, #28 + 800a590: d507 bpl.n 800a5a2 + 800a592: 0711 lsls r1, r2, #28 + 800a594: d505 bpl.n 800a5a2 + WRITE_REG(CRS->ICR, CRS_ICR_ESYNCC); + 800a596: 4b0e ldr r3, [pc, #56] ; (800a5d0 ) + 800a598: 2208 movs r2, #8 + 800a59a: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_ExpectedSyncCallback(); + 800a59c: f7ff ffde bl 800a55c + 800a5a0: e7ea b.n 800a578 + if(((itflags & RCC_CRS_FLAG_ERR) != 0U) && ((itsources & RCC_CRS_IT_ERR) != 0U)) + 800a5a2: 0758 lsls r0, r3, #29 + 800a5a4: d5e8 bpl.n 800a578 + 800a5a6: 0751 lsls r1, r2, #29 + 800a5a8: d5e6 bpl.n 800a578 + crserror |= RCC_CRS_SYNCERR; + 800a5aa: f413 7080 ands.w r0, r3, #256 ; 0x100 + 800a5ae: bf18 it ne + 800a5b0: 2008 movne r0, #8 + if((itflags & RCC_CRS_FLAG_SYNCMISS) != 0U) + 800a5b2: 059a lsls r2, r3, #22 + crserror |= RCC_CRS_SYNCMISS; + 800a5b4: bf48 it mi + 800a5b6: f040 0010 orrmi.w r0, r0, #16 + if((itflags & RCC_CRS_FLAG_TRIMOVF) != 0U) + 800a5ba: 055b lsls r3, r3, #21 + WRITE_REG(CRS->ICR, CRS_ICR_ERRC); + 800a5bc: 4b04 ldr r3, [pc, #16] ; (800a5d0 ) + 800a5be: f04f 0204 mov.w r2, #4 + crserror |= RCC_CRS_TRIMOVF; + 800a5c2: bf48 it mi + 800a5c4: f040 0020 orrmi.w r0, r0, #32 + WRITE_REG(CRS->ICR, CRS_ICR_ERRC); + 800a5c8: 60da str r2, [r3, #12] + HAL_RCCEx_CRS_ErrorCallback(crserror); + 800a5ca: f7ff ffc8 bl 800a55e +} + 800a5ce: e7d3 b.n 800a578 + 800a5d0: 40006000 .word 0x40006000 + +0800a5d4 : + * processing is suspended when possible and the Peripheral feeding point reached at + * suspension time is stored in the handle for resumption later on. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_WriteData(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size) +{ + 800a5d4: b573 push {r0, r1, r4, r5, r6, lr} + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + + for(buffercounter = 0U; buffercounter < Size; buffercounter+=4U) + { + /* Write input data 4 bytes at a time */ + HASH->DIN = *(uint32_t*)inputaddr; + 800a5d6: 4d1e ldr r5, [pc, #120] ; (800a650 ) + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800a5d8: 9101 str r1, [sp, #4] +{ + 800a5da: 4604 mov r4, r0 + for(buffercounter = 0U; buffercounter < Size; buffercounter+=4U) + 800a5dc: 2100 movs r1, #0 + 800a5de: 4291 cmp r1, r2 + 800a5e0: d221 bcs.n 800a626 + HASH->DIN = *(uint32_t*)inputaddr; + 800a5e2: 9b01 ldr r3, [sp, #4] + 800a5e4: 681b ldr r3, [r3, #0] + 800a5e6: 606b str r3, [r5, #4] + inputaddr+=4U; + 800a5e8: 9b01 ldr r3, [sp, #4] + + /* If the suspension flag has been raised and if the processing is not about + to end, suspend processing */ + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a5ea: f894 0036 ldrb.w r0, [r4, #54] ; 0x36 + inputaddr+=4U; + 800a5ee: 3304 adds r3, #4 + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a5f0: 2801 cmp r0, #1 + inputaddr+=4U; + 800a5f2: 9301 str r3, [sp, #4] + if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4U) < Size)) + 800a5f4: f101 0304 add.w r3, r1, #4 + 800a5f8: d127 bne.n 800a64a + 800a5fa: 4293 cmp r3, r2 + 800a5fc: d225 bcs.n 800a64a + { + /* Wait for DINIS = 1, which occurs when 16 32-bit locations are free + in the input buffer */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800a5fe: 6a6e ldr r6, [r5, #36] ; 0x24 + 800a600: 07f6 lsls r6, r6, #31 + 800a602: d522 bpl.n 800a64a + /* Reset SuspendRequest */ + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + + /* Depending whether the key or the input data were fed to the Peripheral, the feeding point + reached at suspension time is not saved in the same handle fields */ + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a604: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a608: 2500 movs r5, #0 + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a60a: 2b02 cmp r3, #2 + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a60c: f884 5036 strb.w r5, [r4, #54] ; 0x36 + if ((hhash->Phase == HAL_HASH_PHASE_PROCESS) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2)) + 800a610: d001 beq.n 800a616 + 800a612: 2b04 cmp r3, #4 + 800a614: d109 bne.n 800a62a + { + /* Save current reading and writing locations of Input and Output buffers */ + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + /* Save the number of bytes that remain to be processed at this point */ + hhash->HashInCount = Size - (buffercounter + 4U); + 800a616: 3a04 subs r2, #4 + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + 800a618: 9b01 ldr r3, [sp, #4] + 800a61a: 60e3 str r3, [r4, #12] + hhash->HashInCount = Size - (buffercounter + 4U); + 800a61c: 1a52 subs r2, r2, r1 + 800a61e: 6222 str r2, [r4, #32] + __HAL_UNLOCK(hhash); + return HAL_ERROR; + } + + /* Set the HASH state to Suspended and exit to stop entering data */ + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a620: 2308 movs r3, #8 + 800a622: f884 3035 strb.w r3, [r4, #53] ; 0x35 + } /* if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) */ + } /* if ((hhash->SuspendRequest == HAL_HASH_SUSPEND) && ((buffercounter+4) < Size)) */ + } /* for(buffercounter = 0; buffercounter < Size; buffercounter+=4) */ + + /* At this point, all the data have been entered to the Peripheral: exit */ + return HAL_OK; + 800a626: 2000 movs r0, #0 + 800a628: e00d b.n 800a646 + else if ((hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) || (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_3)) + 800a62a: 2b03 cmp r3, #3 + 800a62c: d001 beq.n 800a632 + 800a62e: 2b05 cmp r3, #5 + 800a630: d105 bne.n 800a63e + hhash->HashKeyCount = Size - (buffercounter + 4U); + 800a632: 3a04 subs r2, #4 + hhash->pHashKeyBuffPtr = (uint8_t *)inputaddr; + 800a634: 9b01 ldr r3, [sp, #4] + 800a636: 6163 str r3, [r4, #20] + hhash->HashKeyCount = Size - (buffercounter + 4U); + 800a638: 1a52 subs r2, r2, r1 + 800a63a: 62a2 str r2, [r4, #40] ; 0x28 + 800a63c: e7f0 b.n 800a620 + hhash->State = HAL_HASH_STATE_READY; + 800a63e: f884 0035 strb.w r0, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800a642: f884 5034 strb.w r5, [r4, #52] ; 0x34 +} + 800a646: b002 add sp, #8 + 800a648: bd70 pop {r4, r5, r6, pc} + 800a64a: 4619 mov r1, r3 + 800a64c: e7c7 b.n 800a5de + 800a64e: bf00 nop + 800a650: 50060400 .word 0x50060400 + +0800a654 : + */ +static void HASH_GetDigest(uint8_t *pMsgDigest, uint8_t Size) +{ + uint32_t msgdigest = (uint32_t)pMsgDigest; + + switch(Size) + 800a654: 291c cmp r1, #28 + 800a656: d027 beq.n 800a6a8 + 800a658: d804 bhi.n 800a664 + 800a65a: 2910 cmp r1, #16 + 800a65c: d005 beq.n 800a66a + 800a65e: 2914 cmp r1, #20 + 800a660: d011 beq.n 800a686 + 800a662: 4770 bx lr + 800a664: 2920 cmp r1, #32 + 800a666: d037 beq.n 800a6d8 + 800a668: 4770 bx lr + { + /* Read the message digest */ + case 16: /* MD5 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a66a: 4b29 ldr r3, [pc, #164] ; (800a710 ) + 800a66c: 68da ldr r2, [r3, #12] + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); + 800a66e: ba12 rev r2, r2 + 800a670: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a672: 691a ldr r2, [r3, #16] + 800a674: ba12 rev r2, r2 + 800a676: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a678: 695a ldr r2, [r3, #20] + 800a67a: ba12 rev r2, r2 + 800a67c: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a67e: 699b ldr r3, [r3, #24] + 800a680: ba1b rev r3, r3 + 800a682: 60c3 str r3, [r0, #12] + break; + 800a684: 4770 bx lr + case 20: /* SHA1 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a686: 4b22 ldr r3, [pc, #136] ; (800a710 ) + 800a688: 68da ldr r2, [r3, #12] + 800a68a: ba12 rev r2, r2 + 800a68c: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a68e: 691a ldr r2, [r3, #16] + 800a690: ba12 rev r2, r2 + 800a692: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a694: 695a ldr r2, [r3, #20] + 800a696: ba12 rev r2, r2 + 800a698: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a69a: 699a ldr r2, [r3, #24] + 800a69c: ba12 rev r2, r2 + 800a69e: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a6a0: 69db ldr r3, [r3, #28] + 800a6a2: ba1b rev r3, r3 + 800a6a4: 6103 str r3, [r0, #16] + break; + 800a6a6: 4770 bx lr + case 28: /* SHA224 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a6a8: 4b19 ldr r3, [pc, #100] ; (800a710 ) + 800a6aa: 68da ldr r2, [r3, #12] + 800a6ac: ba12 rev r2, r2 + 800a6ae: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a6b0: 691a ldr r2, [r3, #16] + 800a6b2: ba12 rev r2, r2 + 800a6b4: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a6b6: 695a ldr r2, [r3, #20] + 800a6b8: ba12 rev r2, r2 + 800a6ba: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a6bc: 699a ldr r2, [r3, #24] + 800a6be: ba12 rev r2, r2 + 800a6c0: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a6c2: 69db ldr r3, [r3, #28] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a6c4: 4a13 ldr r2, [pc, #76] ; (800a714 ) + 800a6c6: ba1b rev r3, r3 + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a6c8: 6103 str r3, [r0, #16] + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a6ca: 6a53 ldr r3, [r2, #36] ; 0x24 + 800a6cc: ba1b rev r3, r3 + 800a6ce: 6143 str r3, [r0, #20] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[6]); + 800a6d0: 6a93 ldr r3, [r2, #40] ; 0x28 + 800a6d2: ba1b rev r3, r3 + 800a6d4: 6183 str r3, [r0, #24] + break; + 800a6d6: 4770 bx lr + case 32: /* SHA256 */ + *(uint32_t*)(msgdigest) = __REV(HASH->HR[0]); + 800a6d8: 4b0d ldr r3, [pc, #52] ; (800a710 ) + 800a6da: 68da ldr r2, [r3, #12] + 800a6dc: ba12 rev r2, r2 + 800a6de: 6002 str r2, [r0, #0] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[1]); + 800a6e0: 691a ldr r2, [r3, #16] + 800a6e2: ba12 rev r2, r2 + 800a6e4: 6042 str r2, [r0, #4] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[2]); + 800a6e6: 695a ldr r2, [r3, #20] + 800a6e8: ba12 rev r2, r2 + 800a6ea: 6082 str r2, [r0, #8] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[3]); + 800a6ec: 699a ldr r2, [r3, #24] + 800a6ee: ba12 rev r2, r2 + 800a6f0: 60c2 str r2, [r0, #12] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH->HR[4]); + 800a6f2: 69db ldr r3, [r3, #28] + 800a6f4: ba1b rev r3, r3 + 800a6f6: 6103 str r3, [r0, #16] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[5]); + 800a6f8: 4b06 ldr r3, [pc, #24] ; (800a714 ) + 800a6fa: 6a5a ldr r2, [r3, #36] ; 0x24 + 800a6fc: ba12 rev r2, r2 + 800a6fe: 6142 str r2, [r0, #20] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[6]); + 800a700: 6a9a ldr r2, [r3, #40] ; 0x28 + 800a702: ba12 rev r2, r2 + 800a704: 6182 str r2, [r0, #24] + msgdigest+=4U; + *(uint32_t*)(msgdigest) = __REV(HASH_DIGEST->HR[7]); + 800a706: 6adb ldr r3, [r3, #44] ; 0x2c + 800a708: ba1b rev r3, r3 + 800a70a: 61c3 str r3, [r0, #28] + break; + default: + break; + } +} + 800a70c: 4770 bx lr + 800a70e: bf00 nop + 800a710: 50060400 .word 0x50060400 + 800a714: 50060700 .word 0x50060700 + +0800a718 : + * @param Status the Flag status (SET or RESET). + * @param Timeout Timeout duration. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_WaitOnFlagUntilTimeout(HASH_HandleTypeDef *hhash, uint32_t Flag, FlagStatus Status, uint32_t Timeout) +{ + 800a718: e92d 43f8 stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, lr} + 800a71c: 4604 mov r4, r0 + 800a71e: 460e mov r6, r1 + 800a720: 4691 mov r9, r2 + 800a722: 461d mov r5, r3 + uint32_t tickstart = HAL_GetTick(); + 800a724: f7fc fe5a bl 80073dc + 800a728: f8df 805c ldr.w r8, [pc, #92] ; 800a788 + 800a72c: 4607 mov r7, r0 + + /* Wait until flag is set */ + if(Status == RESET) + 800a72e: f1b9 0f00 cmp.w r9, #0 + 800a732: d021 beq.n 800a778 + } + } + } + else + { + while(__HAL_HASH_GET_FLAG(Flag) != RESET) + 800a734: f8d8 3024 ldr.w r3, [r8, #36] ; 0x24 + 800a738: ea36 0303 bics.w r3, r6, r3 + 800a73c: d121 bne.n 800a782 + { + /* Check for the Timeout */ + if(Timeout != HAL_MAX_DELAY) + 800a73e: 1c6b adds r3, r5, #1 + 800a740: d0f8 beq.n 800a734 + { + if(((HAL_GetTick()-tickstart) > Timeout) || (Timeout == 0U)) + 800a742: f7fc fe4b bl 80073dc + 800a746: 1bc0 subs r0, r0, r7 + 800a748: 42a8 cmp r0, r5 + 800a74a: d80a bhi.n 800a762 + 800a74c: 2d00 cmp r5, #0 + 800a74e: d1f1 bne.n 800a734 + 800a750: e007 b.n 800a762 + if(Timeout != HAL_MAX_DELAY) + 800a752: 1c6a adds r2, r5, #1 + 800a754: d010 beq.n 800a778 + if(((HAL_GetTick()-tickstart) > Timeout) || (Timeout == 0U)) + 800a756: f7fc fe41 bl 80073dc + 800a75a: 1bc0 subs r0, r0, r7 + 800a75c: 42a8 cmp r0, r5 + 800a75e: d800 bhi.n 800a762 + 800a760: b955 cbnz r5, 800a778 + { + /* Set State to Ready to be able to restart later on */ + hhash->State = HAL_HASH_STATE_READY; + 800a762: 2301 movs r3, #1 + 800a764: f884 3035 strb.w r3, [r4, #53] ; 0x35 + /* Store time out issue in handle status */ + hhash->Status = HAL_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hhash); + 800a768: 2200 movs r2, #0 + hhash->Status = HAL_TIMEOUT; + 800a76a: 2303 movs r3, #3 + 800a76c: f884 302c strb.w r3, [r4, #44] ; 0x2c + __HAL_UNLOCK(hhash); + 800a770: f884 2034 strb.w r2, [r4, #52] ; 0x34 + + return HAL_TIMEOUT; + 800a774: 4618 mov r0, r3 + 800a776: e005 b.n 800a784 + while(__HAL_HASH_GET_FLAG(Flag) == RESET) + 800a778: f8d8 3024 ldr.w r3, [r8, #36] ; 0x24 + 800a77c: ea36 0303 bics.w r3, r6, r3 + 800a780: d1e7 bne.n 800a752 + } + } + } + } + return HAL_OK; + 800a782: 2000 movs r0, #0 +} + 800a784: e8bd 83f8 ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, pc} + 800a788: 50060400 .word 0x50060400 + +0800a78c : +} + 800a78c: 4770 bx lr + ... + +0800a790 : +{ + 800a790: b538 push {r3, r4, r5, lr} + if(hhash == NULL) + 800a792: 4604 mov r4, r0 + 800a794: b328 cbz r0, 800a7e2 + if(hhash->State == HAL_HASH_STATE_RESET) + 800a796: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + 800a79a: f003 02ff and.w r2, r3, #255 ; 0xff + 800a79e: b91b cbnz r3, 800a7a8 + hhash->Lock = HAL_UNLOCKED; + 800a7a0: f880 2034 strb.w r2, [r0, #52] ; 0x34 + HAL_HASH_MspInit(hhash); + 800a7a4: f7ff fff2 bl 800a78c + hhash->HashInCount = 0; + 800a7a8: 2000 movs r0, #0 + MODIFY_REG(HASH->CR, HASH_CR_DATATYPE, hhash->Init.DataType); + 800a7aa: 4a0f ldr r2, [pc, #60] ; (800a7e8 ) + hhash->HashBuffSize = 0; + 800a7ac: 61e0 str r0, [r4, #28] + hhash->State = HAL_HASH_STATE_BUSY; + 800a7ae: 2302 movs r3, #2 + hhash->Phase = HAL_HASH_PHASE_READY; + 800a7b0: 2101 movs r1, #1 + hhash->State = HAL_HASH_STATE_BUSY; + 800a7b2: f884 3035 strb.w r3, [r4, #53] ; 0x35 + hhash->Phase = HAL_HASH_PHASE_READY; + 800a7b6: f884 102d strb.w r1, [r4, #45] ; 0x2d + hhash->HashInCount = 0; + 800a7ba: 6220 str r0, [r4, #32] + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a7bc: 86e0 strh r0, [r4, #54] ; 0x36 + hhash->HashITCounter = 0; + 800a7be: 6260 str r0, [r4, #36] ; 0x24 + hhash->NbWordsAlreadyPushed = 0; + 800a7c0: 63a0 str r0, [r4, #56] ; 0x38 + MODIFY_REG(HASH->CR, HASH_CR_DATATYPE, hhash->Init.DataType); + 800a7c2: 6813 ldr r3, [r2, #0] + 800a7c4: 6825 ldr r5, [r4, #0] + 800a7c6: f023 0330 bic.w r3, r3, #48 ; 0x30 + 800a7ca: 432b orrs r3, r5 + 800a7cc: 6013 str r3, [r2, #0] +__HAL_HASH_RESET_MDMAT(); + 800a7ce: 6813 ldr r3, [r2, #0] + 800a7d0: f423 5300 bic.w r3, r3, #8192 ; 0x2000 + 800a7d4: 6013 str r3, [r2, #0] + hhash->State = HAL_HASH_STATE_READY; + 800a7d6: f884 1035 strb.w r1, [r4, #53] ; 0x35 + hhash->Status = HAL_OK; + 800a7da: f884 002c strb.w r0, [r4, #44] ; 0x2c + hhash->ErrorCode = HAL_HASH_ERROR_NONE; + 800a7de: 63e0 str r0, [r4, #60] ; 0x3c +} + 800a7e0: bd38 pop {r3, r4, r5, pc} + return HAL_ERROR; + 800a7e2: 2001 movs r0, #1 + 800a7e4: e7fc b.n 800a7e0 + 800a7e6: bf00 nop + 800a7e8: 50060400 .word 0x50060400 + +0800a7ec : + 800a7ec: 4770 bx lr + +0800a7ee : + 800a7ee: 4770 bx lr + +0800a7f0 : + 800a7f0: 4770 bx lr + +0800a7f2 : + 800a7f2: 4770 bx lr + +0800a7f4 : +{ + 800a7f4: b570 push {r4, r5, r6, lr} + * suspension time is stored in the handle for resumption later on. + * @retval HAL status + */ +static HAL_StatusTypeDef HASH_IT(HASH_HandleTypeDef *hhash) +{ + if (hhash->State == HAL_HASH_STATE_BUSY) + 800a7f6: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + 800a7fa: 2b02 cmp r3, #2 +{ + 800a7fc: 4604 mov r4, r0 + if (hhash->State == HAL_HASH_STATE_BUSY) + 800a7fe: b2da uxtb r2, r3 + 800a800: f040 80e7 bne.w 800a9d2 + { + /* ITCounter must not be equal to 0 at this point. Report an error if this is the case. */ + if(hhash->HashITCounter == 0U) + 800a804: 6a43 ldr r3, [r0, #36] ; 0x24 + 800a806: 4d74 ldr r5, [pc, #464] ; (800a9d8 ) + 800a808: b94b cbnz r3, 800a81e + { + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a80a: 6a2b ldr r3, [r5, #32] + 800a80c: f023 0303 bic.w r3, r3, #3 + 800a810: 622b str r3, [r5, #32] + /* HASH state set back to Ready to prevent any issue in user code + present in HAL_HASH_ErrorCallback() */ + hhash->State = HAL_HASH_STATE_READY; + 800a812: 2301 movs r3, #1 + 800a814: f880 3035 strb.w r3, [r0, #53] ; 0x35 + hhash->Status = HASH_IT(hhash); + 800a818: f884 302c strb.w r3, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800a81c: e099 b.n 800a952 + return HAL_ERROR; + } + else if (hhash->HashITCounter == 1U) + 800a81e: 6a43 ldr r3, [r0, #36] ; 0x24 + 800a820: 2b01 cmp r3, #1 + } + else + { + /* Cruise speed reached, HashITCounter remains equal to 3 until the end of + the HASH processing or the end of the current step for HMAC processing. */ + hhash->HashITCounter = 3U; + 800a822: bf16 itet ne + 800a824: 2303 movne r3, #3 + hhash->HashITCounter = 2U; + 800a826: 6242 streq r2, [r0, #36] ; 0x24 + hhash->HashITCounter = 3U; + 800a828: 6243 strne r3, [r0, #36] ; 0x24 + } + + /* If digest is ready */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DCIS)) + 800a82a: 6a6b ldr r3, [r5, #36] ; 0x24 + 800a82c: f013 0302 ands.w r3, r3, #2 + 800a830: d022 beq.n 800a878 + { + /* Read the digest */ + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800a832: 682a ldr r2, [r5, #0] + 800a834: 4b69 ldr r3, [pc, #420] ; (800a9dc ) + 800a836: 6900 ldr r0, [r0, #16] + 800a838: 421a tst r2, r3 + 800a83a: d019 beq.n 800a870 + 800a83c: 682a ldr r2, [r5, #0] + 800a83e: 401a ands r2, r3 + 800a840: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800a844: d016 beq.n 800a874 + 800a846: 682a ldr r2, [r5, #0] + 800a848: 4393 bics r3, r2 + 800a84a: bf0c ite eq + 800a84c: 2120 moveq r1, #32 + 800a84e: 2110 movne r1, #16 + 800a850: f7ff ff00 bl 800a654 + + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a854: 6a2b ldr r3, [r5, #32] + 800a856: f023 0303 bic.w r3, r3, #3 + 800a85a: 622b str r3, [r5, #32] + /* Change the HASH state */ + hhash->State = HAL_HASH_STATE_READY; + 800a85c: 2301 movs r3, #1 + 800a85e: f884 3035 strb.w r3, [r4, #53] ; 0x35 + /* Reset HASH state machine */ + hhash->Phase = HAL_HASH_PHASE_READY; + 800a862: f884 302d strb.w r3, [r4, #45] ; 0x2d + /* Call digest computation complete call back */ +#if (USE_HAL_HASH_REGISTER_CALLBACKS == 1) + hhash->DgstCpltCallback(hhash); +#else + HAL_HASH_DgstCpltCallback(hhash); + 800a866: 4620 mov r0, r4 + 800a868: f7ff ffc2 bl 800a7f0 + hhash->Status = HAL_OK; + 800a86c: 2300 movs r3, #0 + 800a86e: e015 b.n 800a89c + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800a870: 2114 movs r1, #20 + 800a872: e7ed b.n 800a850 + 800a874: 211c movs r1, #28 + 800a876: e7eb b.n 800a850 + + return HAL_OK; + } + + /* If Peripheral ready to accept new data */ + if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800a878: 6a6a ldr r2, [r5, #36] ; 0x24 + 800a87a: 07d2 lsls r2, r2, #31 + 800a87c: d5f6 bpl.n 800a86c + { + + /* If the suspension flag has been raised and if the processing is not about + to end, suspend processing */ + if ( (hhash->HashInCount != 0U) && (hhash->SuspendRequest == HAL_HASH_SUSPEND)) + 800a87e: 6a02 ldr r2, [r0, #32] + 800a880: b17a cbz r2, 800a8a2 + 800a882: f890 2036 ldrb.w r2, [r0, #54] ; 0x36 + 800a886: 2a01 cmp r2, #1 + 800a888: d10b bne.n 800a8a2 + { + /* Disable Interrupts */ + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a88a: 6a2a ldr r2, [r5, #32] + 800a88c: f022 0203 bic.w r2, r2, #3 + 800a890: 622a str r2, [r5, #32] + + /* Reset SuspendRequest */ + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + + /* Change the HASH state */ + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a892: 2208 movs r2, #8 + hhash->SuspendRequest = HAL_HASH_SUSPEND_NONE; + 800a894: f880 3036 strb.w r3, [r0, #54] ; 0x36 + hhash->State = HAL_HASH_STATE_SUSPENDED; + 800a898: f880 2035 strb.w r2, [r0, #53] ; 0x35 + hhash->Status = HAL_OK; + 800a89c: f884 302c strb.w r3, [r4, #44] ; 0x2c +} + 800a8a0: e076 b.n 800a990 + uint32_t buffercounter; + uint32_t inputcounter; + uint32_t ret = HASH_DIGEST_CALCULATION_NOT_STARTED; + + /* If there are more than 64 bytes remaining to be entered */ + if(hhash->HashInCount > 64U) + 800a8a2: 6a21 ldr r1, [r4, #32] + { + inputaddr = (uint32_t)hhash->pHashInBuffPtr; + 800a8a4: 68e3 ldr r3, [r4, #12] + if(hhash->HashInCount > 64U) + 800a8a6: 2940 cmp r1, #64 ; 0x40 + inputaddr = (uint32_t)hhash->pHashInBuffPtr; + 800a8a8: 461a mov r2, r3 + if(hhash->HashInCount > 64U) + 800a8aa: d91c bls.n 800a8e6 + 800a8ac: f103 0140 add.w r1, r3, #64 ; 0x40 + /* Write the Input block in the Data IN register + (16 32-bit words, or 64 bytes are entered) */ + for(buffercounter = 0U; buffercounter < 64U; buffercounter+=4U) + { + HASH->DIN = *(uint32_t*)inputaddr; + 800a8b0: f853 0b04 ldr.w r0, [r3], #4 + 800a8b4: 6068 str r0, [r5, #4] + for(buffercounter = 0U; buffercounter < 64U; buffercounter+=4U) + 800a8b6: 4299 cmp r1, r3 + 800a8b8: d1fa bne.n 800a8b0 + inputaddr+=4U; + } + /* If this is the start of input data entering, an additional word + must be entered to start up the HASH processing */ + if(hhash->HashITCounter == 2U) + 800a8ba: 6a63 ldr r3, [r4, #36] ; 0x24 + 800a8bc: 2b02 cmp r3, #2 + 800a8be: d10d bne.n 800a8dc + { + HASH->DIN = *(uint32_t*)inputaddr; + 800a8c0: 680b ldr r3, [r1, #0] + 800a8c2: 606b str r3, [r5, #4] + if(hhash->HashInCount >= 68U) + 800a8c4: 6a23 ldr r3, [r4, #32] + 800a8c6: 2b43 cmp r3, #67 ; 0x43 + 800a8c8: d905 bls.n 800a8d6 + { + /* There are still data waiting to be entered in the Peripheral. + Decrement buffer counter and set pointer to the proper + memory location for the next data entering round. */ + hhash->HashInCount -= 68U; + 800a8ca: 6a23 ldr r3, [r4, #32] + 800a8cc: 3b44 subs r3, #68 ; 0x44 + 800a8ce: 6223 str r3, [r4, #32] + hhash->pHashInBuffPtr+= 68U; + 800a8d0: 3244 adds r2, #68 ; 0x44 + { + /* 64 bytes have been entered and there are still some remaining: + Decrement buffer counter and set pointer to the proper + memory location for the next data entering round.*/ + hhash->HashInCount -= 64U; + hhash->pHashInBuffPtr+= 64U; + 800a8d2: 60e2 str r2, [r4, #12] + /* Reset buffer counter */ + hhash->HashInCount = 0; + } + + /* Return whether or digest calculation has started */ + return ret; + 800a8d4: e7ca b.n 800a86c + hhash->HashInCount = 0U; + 800a8d6: 2300 movs r3, #0 + 800a8d8: 6223 str r3, [r4, #32] + return ret; + 800a8da: e7c7 b.n 800a86c + hhash->HashInCount -= 64U; + 800a8dc: 6a23 ldr r3, [r4, #32] + 800a8de: 3b40 subs r3, #64 ; 0x40 + 800a8e0: 6223 str r3, [r4, #32] + hhash->pHashInBuffPtr+= 64U; + 800a8e2: 3240 adds r2, #64 ; 0x40 + 800a8e4: e7f5 b.n 800a8d2 + inputcounter = hhash->HashInCount; + 800a8e6: 6a22 ldr r2, [r4, #32] + __HAL_HASH_DISABLE_IT(HASH_IT_DINI); + 800a8e8: 6a29 ldr r1, [r5, #32] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a8ea: 3203 adds r2, #3 + __HAL_HASH_DISABLE_IT(HASH_IT_DINI); + 800a8ec: f021 0101 bic.w r1, r1, #1 + 800a8f0: f022 0203 bic.w r2, r2, #3 + 800a8f4: 6229 str r1, [r5, #32] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a8f6: 441a add r2, r3 + 800a8f8: 4293 cmp r3, r2 + 800a8fa: d10b bne.n 800a914 + if (hhash->Accumulation == 1U) + 800a8fc: 6c23 ldr r3, [r4, #64] ; 0x40 + 800a8fe: 2b01 cmp r3, #1 + 800a900: d10c bne.n 800a91c + hhash->Accumulation = 0U; + 800a902: 2500 movs r5, #0 + 800a904: 6425 str r5, [r4, #64] ; 0x40 + HAL_HASH_InCpltCallback(hhash); + 800a906: 4620 mov r0, r4 + hhash->State = HAL_HASH_STATE_READY; + 800a908: f884 3035 strb.w r3, [r4, #53] ; 0x35 + HAL_HASH_InCpltCallback(hhash); + 800a90c: f7ff ff6f bl 800a7ee + hhash->HashInCount = 0; + 800a910: 6225 str r5, [r4, #32] + return ret; + 800a912: e7ab b.n 800a86c + HASH->DIN = *(uint32_t*)inputaddr; + 800a914: f853 1b04 ldr.w r1, [r3], #4 + 800a918: 6069 str r1, [r5, #4] + for(buffercounter = 0U; buffercounter < ((inputcounter+3U)/4U); buffercounter++) + 800a91a: e7ed b.n 800a8f8 + __HAL_HASH_START_DIGEST(); + 800a91c: 68ab ldr r3, [r5, #8] + 800a91e: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800a922: 60ab str r3, [r5, #8] + hhash->HashInCount = 0; + 800a924: 2300 movs r3, #0 + 800a926: 6223 str r3, [r4, #32] + HAL_HASH_InCpltCallback(hhash); + 800a928: 4620 mov r0, r4 + 800a92a: f7ff ff60 bl 800a7ee + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) + 800a92e: f894 602d ldrb.w r6, [r4, #45] ; 0x2d + 800a932: 2e03 cmp r6, #3 + 800a934: d12d bne.n 800a992 + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, HASH_TIMEOUTVALUE) != HAL_OK) + 800a936: f44f 737a mov.w r3, #1000 ; 0x3e8 + 800a93a: 2201 movs r2, #1 + 800a93c: 2108 movs r1, #8 + 800a93e: 4620 mov r0, r4 + 800a940: f7ff feea bl 800a718 + 800a944: b168 cbz r0, 800a962 + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a946: 6a2b ldr r3, [r5, #32] + 800a948: f023 0303 bic.w r3, r3, #3 + 800a94c: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a94e: f884 602c strb.w r6, [r4, #44] ; 0x2c + hhash->ErrorCode |= HAL_HASH_ERROR_IT; + 800a952: 6be3 ldr r3, [r4, #60] ; 0x3c + 800a954: f043 0301 orr.w r3, r3, #1 + 800a958: 63e3 str r3, [r4, #60] ; 0x3c + HAL_HASH_ErrorCallback(hhash); + 800a95a: 4620 mov r0, r4 + 800a95c: f7ff ff49 bl 800a7f2 + 800a960: e784 b.n 800a86c + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_2; /* Move phase from Step 1 to Step 2 */ + 800a962: 2304 movs r3, #4 + 800a964: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); /* Set NBLW for the input message */ + 800a968: 68ab ldr r3, [r5, #8] + 800a96a: 69e2 ldr r2, [r4, #28] + 800a96c: f023 031f bic.w r3, r3, #31 + 800a970: f002 0103 and.w r1, r2, #3 + 800a974: ea43 03c1 orr.w r3, r3, r1, lsl #3 + 800a978: 60ab str r3, [r5, #8] + hhash->pHashInBuffPtr = hhash->pHashMsgBuffPtr; /* Set the input data address */ + 800a97a: 69a3 ldr r3, [r4, #24] + hhash->HashInCount = hhash->HashBuffSize; /* Set the input data size (in bytes) */ + 800a97c: 6222 str r2, [r4, #32] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a97e: 60e3 str r3, [r4, #12] + hhash->HashITCounter = 1; /* Set ITCounter to 1 to indicate the start of a new phase */ + 800a980: 2301 movs r3, #1 + 800a982: 6263 str r3, [r4, #36] ; 0x24 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI); /* Enable IT (was disabled in HASH_Write_Block_Data) */ + 800a984: 6a2b ldr r3, [r5, #32] + 800a986: f043 0301 orr.w r3, r3, #1 + 800a98a: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a98c: f884 002c strb.w r0, [r4, #44] ; 0x2c +} + 800a990: bd70 pop {r4, r5, r6, pc} + else if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2) + 800a992: 2e04 cmp r6, #4 + 800a994: f47f af6a bne.w 800a86c + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, HASH_TIMEOUTVALUE) != HAL_OK) + 800a998: f44f 737a mov.w r3, #1000 ; 0x3e8 + 800a99c: 2201 movs r2, #1 + 800a99e: 2108 movs r1, #8 + 800a9a0: 4620 mov r0, r4 + 800a9a2: f7ff feb9 bl 800a718 + 800a9a6: b128 cbz r0, 800a9b4 + __HAL_HASH_DISABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800a9a8: 6a2b ldr r3, [r5, #32] + 800a9aa: f023 0303 bic.w r3, r3, #3 + 800a9ae: 622b str r3, [r5, #32] + hhash->Status = HASH_IT(hhash); + 800a9b0: 2303 movs r3, #3 + 800a9b2: e731 b.n 800a818 + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_3; /* Move phase from Step 2 to Step 3 */ + 800a9b4: 2305 movs r3, #5 + 800a9b6: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); /* Set NBLW for the key */ + 800a9ba: 68ab ldr r3, [r5, #8] + 800a9bc: 6862 ldr r2, [r4, #4] + 800a9be: f023 031f bic.w r3, r3, #31 + 800a9c2: f002 0103 and.w r1, r2, #3 + 800a9c6: ea43 03c1 orr.w r3, r3, r1, lsl #3 + 800a9ca: 60ab str r3, [r5, #8] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a9cc: 68a3 ldr r3, [r4, #8] + hhash->HashInCount = hhash->Init.KeySize; /* Set the key size (in bytes) */ + 800a9ce: 6222 str r2, [r4, #32] + hhash->pHashInBuffPtr = hhash->Init.pKey; /* Set the key address */ + 800a9d0: e7d5 b.n 800a97e + hhash->Status = HASH_IT(hhash); + 800a9d2: 2302 movs r3, #2 + 800a9d4: e720 b.n 800a818 + 800a9d6: bf00 nop + 800a9d8: 50060400 .word 0x50060400 + 800a9dc: 00040080 .word 0x00040080 + +0800a9e0 : + return hhash->State; + 800a9e0: f890 0035 ldrb.w r0, [r0, #53] ; 0x35 +} + 800a9e4: 4770 bx lr + +0800a9e6 : +} + 800a9e6: f890 002c ldrb.w r0, [r0, #44] ; 0x2c + 800a9ea: 4770 bx lr + +0800a9ec : + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->IMR,HASH_IT_DINI|HASH_IT_DCI); + 800a9ec: 4b0e ldr r3, [pc, #56] ; (800aa28 ) + 800a9ee: 6a1a ldr r2, [r3, #32] + 800a9f0: f002 0203 and.w r2, r2, #3 + 800a9f4: 600a str r2, [r1, #0] + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->STR,HASH_STR_NBLW); + 800a9f6: 689a ldr r2, [r3, #8] + 800a9f8: f002 021f and.w r2, r2, #31 + 800a9fc: 604a str r2, [r1, #4] + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->CR,HASH_CR_DMAE|HASH_CR_DATATYPE|HASH_CR_MODE|HASH_CR_ALGO|HASH_CR_LKEY|HASH_CR_MDMAT); + 800a9fe: 681b ldr r3, [r3, #0] + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800aa00: 4a0a ldr r2, [pc, #40] ; (800aa2c ) + *(uint32_t*)(mem_ptr) = READ_BIT(HASH->CR,HASH_CR_DMAE|HASH_CR_DATATYPE|HASH_CR_MODE|HASH_CR_ALGO|HASH_CR_LKEY|HASH_CR_MDMAT); + 800aa02: f023 437f bic.w r3, r3, #4278190080 ; 0xff000000 + 800aa06: f423 037a bic.w r3, r3, #16384000 ; 0xfa0000 + 800aa0a: f423 435f bic.w r3, r3, #57088 ; 0xdf00 + 800aa0e: f023 0307 bic.w r3, r3, #7 + 800aa12: 608b str r3, [r1, #8] + uint32_t csr_ptr = (uint32_t)HASH->CSR; + 800aa14: 4b06 ldr r3, [pc, #24] ; (800aa30 ) + mem_ptr+=4U; + 800aa16: 310c adds r1, #12 + *(uint32_t*)(mem_ptr) = *(uint32_t*)(csr_ptr); + 800aa18: f853 0b04 ldr.w r0, [r3], #4 + 800aa1c: f841 0b04 str.w r0, [r1], #4 + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800aa20: 4293 cmp r3, r2 + 800aa22: d1f9 bne.n 800aa18 +} + 800aa24: 4770 bx lr + 800aa26: bf00 nop + 800aa28: 50060400 .word 0x50060400 + 800aa2c: 500605d0 .word 0x500605d0 + 800aa30: 500604f8 .word 0x500604f8 + +0800aa34 : + WRITE_REG(HASH->IMR, (*(uint32_t*)(mem_ptr))); + 800aa34: 4b0a ldr r3, [pc, #40] ; (800aa60 ) + 800aa36: 680a ldr r2, [r1, #0] + 800aa38: 621a str r2, [r3, #32] + WRITE_REG(HASH->STR, (*(uint32_t*)(mem_ptr))); + 800aa3a: 684a ldr r2, [r1, #4] + 800aa3c: 609a str r2, [r3, #8] + WRITE_REG(HASH->CR, (*(uint32_t*)(mem_ptr))); + 800aa3e: 688a ldr r2, [r1, #8] + 800aa40: 601a str r2, [r3, #0] + __HAL_HASH_INIT(); + 800aa42: 681a ldr r2, [r3, #0] + 800aa44: f042 0204 orr.w r2, r2, #4 + 800aa48: 601a str r2, [r3, #0] + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800aa4a: 4a06 ldr r2, [pc, #24] ; (800aa64 ) + mem_ptr+=4U; + 800aa4c: 310c adds r1, #12 + uint32_t csr_ptr = (uint32_t)HASH->CSR; + 800aa4e: 33f8 adds r3, #248 ; 0xf8 + WRITE_REG((*(uint32_t*)(csr_ptr)), (*(uint32_t*)(mem_ptr))); + 800aa50: f851 0b04 ldr.w r0, [r1], #4 + 800aa54: f843 0b04 str.w r0, [r3], #4 + for (i = HASH_NUMBER_OF_CSR_REGISTERS; i >0U; i--) + 800aa58: 4293 cmp r3, r2 + 800aa5a: d1f9 bne.n 800aa50 +} + 800aa5c: 4770 bx lr + 800aa5e: bf00 nop + 800aa60: 50060400 .word 0x50060400 + 800aa64: 500605d0 .word 0x500605d0 + +0800aa68 : + hhash->SuspendRequest = HAL_HASH_SUSPEND; + 800aa68: 2301 movs r3, #1 + 800aa6a: f880 3036 strb.w r3, [r0, #54] ; 0x36 +} + 800aa6e: 4770 bx lr + +0800aa70 : + return hhash->ErrorCode; + 800aa70: 6bc0 ldr r0, [r0, #60] ; 0x3c +} + 800aa72: 4770 bx lr + +0800aa74 : + * @param Timeout Timeout value. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout, uint32_t Algorithm) +{ + 800aa74: b5f8 push {r3, r4, r5, r6, r7, lr} + 800aa76: 461e mov r6, r3 + uint8_t *pInBuffer_tmp; /* input data address, input parameter of HASH_WriteData() */ + uint32_t Size_tmp; /* input data size (in bytes), input parameter of HASH_WriteData() */ + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800aa78: f890 3035 ldrb.w r3, [r0, #53] ; 0x35 + + + /* Initiate HASH processing in case of start or resumption */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800aa7c: 2b01 cmp r3, #1 +{ + 800aa7e: 4604 mov r4, r0 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800aa80: b2d8 uxtb r0, r3 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800aa82: d001 beq.n 800aa88 + 800aa84: 2808 cmp r0, #8 + 800aa86: d17a bne.n 800ab7e + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (pOutBuffer == NULL)) + 800aa88: b101 cbz r1, 800aa8c + 800aa8a: b926 cbnz r6, 800aa96 + { + hhash->State = HAL_HASH_STATE_READY; + 800aa8c: 2501 movs r5, #1 + 800aa8e: f884 5035 strb.w r5, [r4, #53] ; 0x35 + } + else + { + return HAL_BUSY; + } +} + 800aa92: 4628 mov r0, r5 + 800aa94: bdf8 pop {r3, r4, r5, r6, r7, pc} + __HAL_LOCK(hhash); + 800aa96: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800aa9a: 2b01 cmp r3, #1 + 800aa9c: d06f beq.n 800ab7e + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aa9e: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + __HAL_LOCK(hhash); + 800aaa2: 2501 movs r5, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aaa4: 42ab cmp r3, r5 + __HAL_LOCK(hhash); + 800aaa6: f884 5034 strb.w r5, [r4, #52] ; 0x34 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800aaaa: d148 bne.n 800ab3e + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800aaac: 4f36 ldr r7, [pc, #216] ; (800ab88 ) + 800aaae: 9b07 ldr r3, [sp, #28] + hhash->State = HAL_HASH_STATE_BUSY; + 800aab0: f04f 0c02 mov.w ip, #2 + 800aab4: f884 c035 strb.w ip, [r4, #53] ; 0x35 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800aab8: 683d ldr r5, [r7, #0] + 800aaba: f425 25a0 bic.w r5, r5, #327680 ; 0x50000 + 800aabe: f025 05c4 bic.w r5, r5, #196 ; 0xc4 + 800aac2: 431d orrs r5, r3 + 800aac4: f045 0504 orr.w r5, r5, #4 + 800aac8: 603d str r5, [r7, #0] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800aaca: 68b8 ldr r0, [r7, #8] + 800aacc: f002 0303 and.w r3, r2, #3 + 800aad0: f020 001f bic.w r0, r0, #31 + 800aad4: ea40 03c3 orr.w r3, r0, r3, lsl #3 + 800aad8: 60bb str r3, [r7, #8] + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800aada: f884 c02d strb.w ip, [r4, #45] ; 0x2d + hhash->Status = HASH_WriteData(hhash, pInBuffer_tmp, Size_tmp); + 800aade: 4620 mov r0, r4 + 800aae0: f7ff fd78 bl 800a5d4 + 800aae4: 4605 mov r5, r0 + 800aae6: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800aaea: 2800 cmp r0, #0 + 800aaec: d1d1 bne.n 800aa92 + if (hhash->State != HAL_HASH_STATE_SUSPENDED) + 800aaee: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800aaf2: 2b08 cmp r3, #8 + 800aaf4: d03b beq.n 800ab6e + __HAL_HASH_START_DIGEST(); + 800aaf6: 4f24 ldr r7, [pc, #144] ; (800ab88 ) + 800aaf8: 68bb ldr r3, [r7, #8] + 800aafa: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800aafe: 60bb str r3, [r7, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800ab00: 4602 mov r2, r0 + 800ab02: 9b06 ldr r3, [sp, #24] + 800ab04: 2102 movs r1, #2 + 800ab06: 4620 mov r0, r4 + 800ab08: f7ff fe06 bl 800a718 + 800ab0c: 2800 cmp r0, #0 + 800ab0e: d138 bne.n 800ab82 + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800ab10: 683a ldr r2, [r7, #0] + 800ab12: 4b1e ldr r3, [pc, #120] ; (800ab8c ) + 800ab14: 421a tst r2, r3 + 800ab16: d02e beq.n 800ab76 + 800ab18: 683a ldr r2, [r7, #0] + 800ab1a: 401a ands r2, r3 + 800ab1c: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800ab20: d02b beq.n 800ab7a + 800ab22: 683a ldr r2, [r7, #0] + 800ab24: 4393 bics r3, r2 + 800ab26: bf0c ite eq + 800ab28: 2120 moveq r1, #32 + 800ab2a: 2110 movne r1, #16 + 800ab2c: 4630 mov r0, r6 + 800ab2e: f7ff fd91 bl 800a654 + hhash->State = HAL_HASH_STATE_READY; + 800ab32: 2301 movs r3, #1 + 800ab34: f884 3035 strb.w r3, [r4, #53] ; 0x35 + hhash->Phase = HAL_HASH_PHASE_READY; + 800ab38: f884 302d strb.w r3, [r4, #45] ; 0x2d + 800ab3c: e017 b.n 800ab6e + else if (hhash->Phase == HAL_HASH_PHASE_PROCESS) + 800ab3e: 2b02 cmp r3, #2 + 800ab40: d113 bne.n 800ab6a + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800ab42: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800ab46: 2b08 cmp r3, #8 + __HAL_HASH_SET_NBVALIDBITS(Size); + 800ab48: bf15 itete ne + 800ab4a: 4d0f ldrne r5, [pc, #60] ; (800ab88 ) + Size_tmp = hhash->HashInCount; + 800ab4c: 6a22 ldreq r2, [r4, #32] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800ab4e: 68a8 ldrne r0, [r5, #8] + pInBuffer_tmp = hhash->pHashInBuffPtr; + 800ab50: 68e1 ldreq r1, [r4, #12] + __HAL_HASH_SET_NBVALIDBITS(Size); + 800ab52: bf1f itttt ne + 800ab54: f002 0303 andne.w r3, r2, #3 + 800ab58: f020 001f bicne.w r0, r0, #31 + 800ab5c: ea40 03c3 orrne.w r3, r0, r3, lsl #3 + 800ab60: 60ab strne r3, [r5, #8] + hhash->State = HAL_HASH_STATE_BUSY; + 800ab62: 2302 movs r3, #2 + 800ab64: f884 3035 strb.w r3, [r4, #53] ; 0x35 + 800ab68: e7b9 b.n 800aade + hhash->State = HAL_HASH_STATE_READY; + 800ab6a: f884 5035 strb.w r5, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800ab6e: 2300 movs r3, #0 + 800ab70: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800ab74: e78d b.n 800aa92 + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800ab76: 2114 movs r1, #20 + 800ab78: e7d8 b.n 800ab2c + 800ab7a: 211c movs r1, #28 + 800ab7c: e7d6 b.n 800ab2c + return HAL_BUSY; + 800ab7e: 2502 movs r5, #2 + 800ab80: e787 b.n 800aa92 + return HAL_TIMEOUT; + 800ab82: 2503 movs r5, #3 + 800ab84: e785 b.n 800aa92 + 800ab86: bf00 nop + 800ab88: 50060400 .word 0x50060400 + 800ab8c: 00040080 .word 0x00040080 + +0800ab90 : +{ + 800ab90: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_MD5); + 800ab92: 2480 movs r4, #128 ; 0x80 + 800ab94: 9401 str r4, [sp, #4] + 800ab96: 9c04 ldr r4, [sp, #16] + 800ab98: 9400 str r4, [sp, #0] + 800ab9a: f7ff ff6b bl 800aa74 +} + 800ab9e: b002 add sp, #8 + 800aba0: bd10 pop {r4, pc} + +0800aba2 : + 800aba2: f7ff bff5 b.w 800ab90 + +0800aba6 : +{ + 800aba6: b513 push {r0, r1, r4, lr} + return HASH_Start(hhash, pInBuffer, Size, pOutBuffer, Timeout, HASH_ALGOSELECTION_SHA1); + 800aba8: 2400 movs r4, #0 + 800abaa: 9401 str r4, [sp, #4] + 800abac: 9c04 ldr r4, [sp, #16] + 800abae: 9400 str r4, [sp, #0] + 800abb0: f7ff ff60 bl 800aa74 +} + 800abb4: b002 add sp, #8 + 800abb6: bd10 pop {r4, pc} + +0800abb8 : + 800abb8: f7ff bff5 b.w 800aba6 + +0800abbc : + * @param Size length of the input buffer in bytes, must be a multiple of 4. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Accumulate(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint32_t Algorithm) +{ + 800abbc: b570 push {r4, r5, r6, lr} + uint8_t *pInBuffer_tmp; /* input data address, input parameter of HASH_WriteData() */ + uint32_t Size_tmp; /* input data size (in bytes), input parameter of HASH_WriteData() */ + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800abbe: f890 5035 ldrb.w r5, [r0, #53] ; 0x35 +{ + 800abc2: 4604 mov r4, r0 + + /* Make sure the input buffer size (in bytes) is a multiple of 4 */ + if ((Size % 4U) != 0U) + 800abc4: 0790 lsls r0, r2, #30 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800abc6: b2ed uxtb r5, r5 + if ((Size % 4U) != 0U) + 800abc8: d13e bne.n 800ac48 + { + return HAL_ERROR; + } + + /* Initiate HASH processing in case of start or resumption */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800abca: 2d01 cmp r5, #1 + 800abcc: d001 beq.n 800abd2 + 800abce: 2d08 cmp r5, #8 + 800abd0: d13c bne.n 800ac4c + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U)) + 800abd2: b101 cbz r1, 800abd6 + 800abd4: b91a cbnz r2, 800abde + { + hhash->State = HAL_HASH_STATE_READY; + 800abd6: 2001 movs r0, #1 + 800abd8: f884 0035 strb.w r0, [r4, #53] ; 0x35 + { + return HAL_BUSY; + } + + +} + 800abdc: bd70 pop {r4, r5, r6, pc} + __HAL_LOCK(hhash); + 800abde: f894 0034 ldrb.w r0, [r4, #52] ; 0x34 + 800abe2: 2801 cmp r0, #1 + 800abe4: d032 beq.n 800ac4c + 800abe6: 2001 movs r0, #1 + 800abe8: f884 0034 strb.w r0, [r4, #52] ; 0x34 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800abec: f894 0035 ldrb.w r0, [r4, #53] ; 0x35 + 800abf0: 2808 cmp r0, #8 + 800abf2: f04f 0002 mov.w r0, #2 + hhash->State = HAL_HASH_STATE_BUSY; + 800abf6: f884 0035 strb.w r0, [r4, #53] ; 0x35 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800abfa: d113 bne.n 800ac24 + pInBuffer_tmp = hhash->pHashInBuffPtr; /* pInBuffer_tmp is set to the input data address */ + 800abfc: 68e1 ldr r1, [r4, #12] + Size_tmp = hhash->HashInCount; /* Size_tmp contains the input data size in bytes */ + 800abfe: 6a22 ldr r2, [r4, #32] + hhash->Status = HASH_WriteData(hhash, pInBuffer_tmp, Size_tmp); + 800ac00: 4620 mov r0, r4 + 800ac02: f7ff fce7 bl 800a5d4 + 800ac06: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800ac0a: 2800 cmp r0, #0 + 800ac0c: d1e6 bne.n 800abdc + if (hhash->State != HAL_HASH_STATE_SUSPENDED) + 800ac0e: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800ac12: 2b08 cmp r3, #8 + hhash->State = HAL_HASH_STATE_READY; + 800ac14: bf1c itt ne + 800ac16: 2301 movne r3, #1 + 800ac18: f884 3035 strbne.w r3, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800ac1c: 2300 movs r3, #0 + 800ac1e: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800ac22: e7db b.n 800abdc + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ac24: f894 002d ldrb.w r0, [r4, #45] ; 0x2d + 800ac28: 2801 cmp r0, #1 + 800ac2a: d109 bne.n 800ac40 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800ac2c: 4e08 ldr r6, [pc, #32] ; (800ac50 ) + 800ac2e: 6830 ldr r0, [r6, #0] + 800ac30: f420 20a0 bic.w r0, r0, #327680 ; 0x50000 + 800ac34: f020 00c4 bic.w r0, r0, #196 ; 0xc4 + 800ac38: 4318 orrs r0, r3 + 800ac3a: f040 0004 orr.w r0, r0, #4 + 800ac3e: 6030 str r0, [r6, #0] + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800ac40: 2302 movs r3, #2 + 800ac42: f884 302d strb.w r3, [r4, #45] ; 0x2d + 800ac46: e7db b.n 800ac00 + return HAL_ERROR; + 800ac48: 2001 movs r0, #1 + 800ac4a: e7c7 b.n 800abdc + return HAL_BUSY; + 800ac4c: 2002 movs r0, #2 + 800ac4e: e7c5 b.n 800abdc + 800ac50: 50060400 .word 0x50060400 + +0800ac54 : + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_MD5); + 800ac54: 2380 movs r3, #128 ; 0x80 + 800ac56: f7ff bfb1 b.w 800abbc + +0800ac5a : + return HASH_Accumulate(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA1); + 800ac5a: 2300 movs r3, #0 + 800ac5c: f7ff bfae b.w 800abbc + +0800ac60 : + * @param Size length of the input buffer in bytes, must be a multiple of 4. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Accumulate_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint32_t Algorithm) +{ + 800ac60: b567 push {r0, r1, r2, r5, r6, lr} + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ac62: f890 5035 ldrb.w r5, [r0, #53] ; 0x35 + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800ac66: 9101 str r1, [sp, #4] + uint32_t SizeVar = Size; + + /* Make sure the input buffer size (in bytes) is a multiple of 4 */ + if ((Size % 4U) != 0U) + 800ac68: 0796 lsls r6, r2, #30 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ac6a: b2ed uxtb r5, r5 + if ((Size % 4U) != 0U) + 800ac6c: d154 bne.n 800ad18 + { + return HAL_ERROR; + } + + /* Initiate HASH processing in case of start or resumption */ + if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800ac6e: 2d01 cmp r5, #1 + 800ac70: d001 beq.n 800ac76 + 800ac72: 2d08 cmp r5, #8 + 800ac74: d152 bne.n 800ad1c + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U)) + 800ac76: b101 cbz r1, 800ac7a + 800ac78: b92a cbnz r2, 800ac86 + { + hhash->State = HAL_HASH_STATE_READY; + 800ac7a: 2301 movs r3, #1 + 800ac7c: f880 3035 strb.w r3, [r0, #53] ; 0x35 + else + { + return HAL_BUSY; + } + +} + 800ac80: 4618 mov r0, r3 + 800ac82: b003 add sp, #12 + 800ac84: bd60 pop {r5, r6, pc} + __HAL_LOCK(hhash); + 800ac86: f890 1034 ldrb.w r1, [r0, #52] ; 0x34 + 800ac8a: 2901 cmp r1, #1 + 800ac8c: d046 beq.n 800ad1c + 800ac8e: 2101 movs r1, #1 + 800ac90: f880 1034 strb.w r1, [r0, #52] ; 0x34 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800ac94: f890 1035 ldrb.w r1, [r0, #53] ; 0x35 + 800ac98: 4d21 ldr r5, [pc, #132] ; (800ad20 ) + 800ac9a: 2908 cmp r1, #8 + 800ac9c: f04f 0102 mov.w r1, #2 + hhash->State = HAL_HASH_STATE_BUSY; + 800aca0: f880 1035 strb.w r1, [r0, #53] ; 0x35 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800aca4: d109 bne.n 800acba + hhash->Accumulation = 1U; + 800aca6: 2301 movs r3, #1 + 800aca8: 6403 str r3, [r0, #64] ; 0x40 + __HAL_UNLOCK(hhash); + 800acaa: 2300 movs r3, #0 + 800acac: f880 3034 strb.w r3, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI); + 800acb0: 6a2a ldr r2, [r5, #32] + 800acb2: f042 0201 orr.w r2, r2, #1 + 800acb6: 622a str r2, [r5, #32] + return HAL_OK; + 800acb8: e7e2 b.n 800ac80 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800acba: f890 602d ldrb.w r6, [r0, #45] ; 0x2d + 800acbe: 2e01 cmp r6, #1 + 800acc0: d11b bne.n 800acfa + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800acc2: 6829 ldr r1, [r5, #0] + 800acc4: f421 21a0 bic.w r1, r1, #327680 ; 0x50000 + 800acc8: f021 01c4 bic.w r1, r1, #196 ; 0xc4 + 800accc: 4319 orrs r1, r3 + 800acce: f041 0104 orr.w r1, r1, #4 + 800acd2: 6029 str r1, [r5, #0] + hhash->HashITCounter = 1; + 800acd4: 6246 str r6, [r0, #36] ; 0x24 + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800acd6: 2302 movs r3, #2 + 800acd8: f880 302d strb.w r3, [r0, #45] ; 0x2d + while((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) && (SizeVar > 0U)) + 800acdc: 6a6b ldr r3, [r5, #36] ; 0x24 + 800acde: 07d9 lsls r1, r3, #31 + 800ace0: d400 bmi.n 800ace4 + 800ace2: b96a cbnz r2, 800ad00 + if ((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) || (SizeVar == 0U)) + 800ace4: 6a6b ldr r3, [r5, #36] ; 0x24 + 800ace6: 07db lsls r3, r3, #31 + 800ace8: d500 bpl.n 800acec + 800acea: b98a cbnz r2, 800ad10 + hhash->State = HAL_HASH_STATE_READY; + 800acec: 2301 movs r3, #1 + 800acee: f880 3035 strb.w r3, [r0, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800acf2: 2300 movs r3, #0 + 800acf4: f880 3034 strb.w r3, [r0, #52] ; 0x34 + return HAL_OK; + 800acf8: e7c2 b.n 800ac80 + hhash->HashITCounter = 3; /* 'cruise-speed' reached during a previous buffer processing */ + 800acfa: 2303 movs r3, #3 + 800acfc: 6243 str r3, [r0, #36] ; 0x24 + 800acfe: e7ea b.n 800acd6 + HASH->DIN = *(uint32_t*)inputaddr; + 800ad00: 9b01 ldr r3, [sp, #4] + 800ad02: 681b ldr r3, [r3, #0] + 800ad04: 606b str r3, [r5, #4] + inputaddr+=4U; + 800ad06: 9b01 ldr r3, [sp, #4] + 800ad08: 3304 adds r3, #4 + 800ad0a: 9301 str r3, [sp, #4] + SizeVar-=4U; + 800ad0c: 3a04 subs r2, #4 + 800ad0e: e7e5 b.n 800acdc + hhash->HashInCount = SizeVar; /* Counter used to keep track of number of data + 800ad10: 6202 str r2, [r0, #32] + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; /* Points at data which will be fed to the Peripheral at + 800ad12: 9b01 ldr r3, [sp, #4] + 800ad14: 60c3 str r3, [r0, #12] + 800ad16: e7c6 b.n 800aca6 + return HAL_ERROR; + 800ad18: 2301 movs r3, #1 + 800ad1a: e7b1 b.n 800ac80 + return HAL_BUSY; + 800ad1c: 2302 movs r3, #2 + 800ad1e: e7af b.n 800ac80 + 800ad20: 50060400 .word 0x50060400 + +0800ad24 : + return HASH_Accumulate_IT(hhash, pInBuffer, Size,HASH_ALGOSELECTION_MD5); + 800ad24: 2380 movs r3, #128 ; 0x80 + 800ad26: f7ff bf9b b.w 800ac60 + +0800ad2a : + return HASH_Accumulate_IT(hhash, pInBuffer, Size,HASH_ALGOSELECTION_SHA1); + 800ad2a: 2300 movs r3, #0 + 800ad2c: f7ff bf98 b.w 800ac60 + +0800ad30 : + * @param pOutBuffer pointer to the computed digest. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Start_IT(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Algorithm) +{ + 800ad30: b573 push {r0, r1, r4, r5, r6, lr} + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ad32: f890 4035 ldrb.w r4, [r0, #53] ; 0x35 + __IO uint32_t inputaddr = (uint32_t) pInBuffer; + 800ad36: 9101 str r1, [sp, #4] + uint32_t polling_step = 0U; + uint32_t initialization_skipped = 0U; + uint32_t SizeVar = Size; + + /* If State is ready or suspended, start or resume IT-based HASH processing */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800ad38: 2c01 cmp r4, #1 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800ad3a: b2e5 uxtb r5, r4 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800ad3c: d001 beq.n 800ad42 + 800ad3e: 2d08 cmp r5, #8 + 800ad40: d17f bne.n 800ae42 + { + /* Check input parameters */ + if ((pInBuffer == NULL) || (Size == 0U) || (pOutBuffer == NULL)) + 800ad42: b109 cbz r1, 800ad48 + 800ad44: b102 cbz r2, 800ad48 + 800ad46: b92b cbnz r3, 800ad54 + { + hhash->State = HAL_HASH_STATE_READY; + 800ad48: 2201 movs r2, #1 + 800ad4a: f880 2035 strb.w r2, [r0, #53] ; 0x35 + else + { + return HAL_BUSY; + } + +} + 800ad4e: 4610 mov r0, r2 + 800ad50: b002 add sp, #8 + 800ad52: bd70 pop {r4, r5, r6, pc} + __HAL_LOCK(hhash); + 800ad54: f890 4034 ldrb.w r4, [r0, #52] ; 0x34 + 800ad58: 2c01 cmp r4, #1 + 800ad5a: f04f 0402 mov.w r4, #2 + 800ad5e: d072 beq.n 800ae46 + hhash->State = HAL_HASH_STATE_BUSY; + 800ad60: f880 4035 strb.w r4, [r0, #53] ; 0x35 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ad64: f890 402d ldrb.w r4, [r0, #45] ; 0x2d + __HAL_LOCK(hhash); + 800ad68: 2601 movs r6, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ad6a: 42b4 cmp r4, r6 + __HAL_LOCK(hhash); + 800ad6c: f880 6034 strb.w r6, [r0, #52] ; 0x34 + hhash->HashITCounter = 1; + 800ad70: 4c36 ldr r4, [pc, #216] ; (800ae4c ) + 800ad72: 6246 str r6, [r0, #36] ; 0x24 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800ad74: d115 bne.n 800ada2 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_CR_INIT); + 800ad76: 6825 ldr r5, [r4, #0] + 800ad78: 9e06 ldr r6, [sp, #24] + 800ad7a: f425 25a0 bic.w r5, r5, #327680 ; 0x50000 + 800ad7e: f025 05c4 bic.w r5, r5, #196 ; 0xc4 + 800ad82: 4335 orrs r5, r6 + 800ad84: f045 0504 orr.w r5, r5, #4 + 800ad88: 6025 str r5, [r4, #0] + __HAL_HASH_SET_NBVALIDBITS(SizeVar); + 800ad8a: 68a6 ldr r6, [r4, #8] + 800ad8c: f002 0503 and.w r5, r2, #3 + 800ad90: f026 061f bic.w r6, r6, #31 + 800ad94: ea46 05c5 orr.w r5, r6, r5, lsl #3 + 800ad98: 60a5 str r5, [r4, #8] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800ad9a: e9c0 1303 strd r1, r3, [r0, #12] + hhash->HashInCount = SizeVar; /* Counter used to keep track of number of data + 800ad9e: 6202 str r2, [r0, #32] + uint32_t initialization_skipped = 0U; + 800ada0: 2600 movs r6, #0 + hhash->Phase = HAL_HASH_PHASE_PROCESS; + 800ada2: 2102 movs r1, #2 + 800ada4: f880 102d strb.w r1, [r0, #45] ; 0x2d + uint32_t polling_step = 0U; + 800ada8: 2100 movs r1, #0 + while((!(__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS))) && (SizeVar > 3U)) + 800adaa: 6a65 ldr r5, [r4, #36] ; 0x24 + 800adac: 07ed lsls r5, r5, #31 + 800adae: d401 bmi.n 800adb4 + 800adb0: 2a03 cmp r2, #3 + 800adb2: d80d bhi.n 800add0 + if (polling_step == 1U) + 800adb4: b349 cbz r1, 800ae0a + if (SizeVar == 0U) + 800adb6: b9a2 cbnz r2, 800ade2 + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800adb8: 6103 str r3, [r0, #16] + __HAL_HASH_START_DIGEST(); + 800adba: 68a3 ldr r3, [r4, #8] + 800adbc: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800adc0: 60a3 str r3, [r4, #8] + __HAL_UNLOCK(hhash); + 800adc2: f880 2034 strb.w r2, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DCI); + 800adc6: 6a23 ldr r3, [r4, #32] + 800adc8: f043 0302 orr.w r3, r3, #2 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800adcc: 6223 str r3, [r4, #32] + return HAL_OK; + 800adce: e7be b.n 800ad4e + HASH->DIN = *(uint32_t*)inputaddr; + 800add0: 9901 ldr r1, [sp, #4] + 800add2: 6809 ldr r1, [r1, #0] + 800add4: 6061 str r1, [r4, #4] + inputaddr+=4U; + 800add6: 9901 ldr r1, [sp, #4] + 800add8: 3104 adds r1, #4 + 800adda: 9101 str r1, [sp, #4] + SizeVar-=4U; + 800addc: 3a04 subs r2, #4 + polling_step = 1U; /* note that some words are entered before enabling the interrupt */ + 800adde: 2101 movs r1, #1 + 800ade0: e7e3 b.n 800adaa + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800ade2: 6a61 ldr r1, [r4, #36] ; 0x24 + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800ade4: f002 0503 and.w r5, r2, #3 + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800ade8: f011 0101 ands.w r1, r1, #1 + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800adec: ea4f 05c5 mov.w r5, r5, lsl #3 + else if (__HAL_HASH_GET_FLAG(HASH_FLAG_DINIS)) + 800adf0: d012 beq.n 800ae18 + hhash->HashInCount = SizeVar; + 800adf2: 6202 str r2, [r0, #32] + hhash->pHashInBuffPtr = (uint8_t *)inputaddr; + 800adf4: 9a01 ldr r2, [sp, #4] + 800adf6: 60c2 str r2, [r0, #12] + __HAL_HASH_SET_NBVALIDBITS(SizeVar); /* Update the configuration of the number of valid bits in last word of the message */ + 800adf8: 68a2 ldr r2, [r4, #8] + 800adfa: f022 021f bic.w r2, r2, #31 + 800adfe: 432a orrs r2, r5 + 800ae00: 60a2 str r2, [r4, #8] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800ae02: 6103 str r3, [r0, #16] + if (initialization_skipped == 1U) + 800ae04: b10e cbz r6, 800ae0a + hhash->HashITCounter = 3; /* 'cruise-speed' reached during a previous buffer processing */ + 800ae06: 2303 movs r3, #3 + 800ae08: 6243 str r3, [r0, #36] ; 0x24 + __HAL_UNLOCK(hhash); + 800ae0a: 2200 movs r2, #0 + 800ae0c: f880 2034 strb.w r2, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DINI|HASH_IT_DCI); + 800ae10: 6a23 ldr r3, [r4, #32] + 800ae12: f043 0303 orr.w r3, r3, #3 + 800ae16: e7d9 b.n 800adcc + __HAL_HASH_SET_NBVALIDBITS(SizeVar); + 800ae18: 68a2 ldr r2, [r4, #8] + 800ae1a: f022 021f bic.w r2, r2, #31 + 800ae1e: 432a orrs r2, r5 + 800ae20: 60a2 str r2, [r4, #8] + HASH->DIN = *(uint32_t*)inputaddr; + 800ae22: 9a01 ldr r2, [sp, #4] + 800ae24: 6812 ldr r2, [r2, #0] + 800ae26: 6062 str r2, [r4, #4] + hhash->pHashOutBuffPtr = pOutBuffer; /* Points at the computed digest */ + 800ae28: 6103 str r3, [r0, #16] + __HAL_HASH_START_DIGEST(); + 800ae2a: 68a3 ldr r3, [r4, #8] + 800ae2c: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800ae30: 60a3 str r3, [r4, #8] + __HAL_UNLOCK(hhash); + 800ae32: f880 1034 strb.w r1, [r0, #52] ; 0x34 + __HAL_HASH_ENABLE_IT(HASH_IT_DCI); + 800ae36: 6a23 ldr r3, [r4, #32] + 800ae38: f043 0302 orr.w r3, r3, #2 + 800ae3c: 6223 str r3, [r4, #32] + return HAL_OK; + 800ae3e: 460a mov r2, r1 + 800ae40: e785 b.n 800ad4e + return HAL_BUSY; + 800ae42: 2202 movs r2, #2 + 800ae44: e783 b.n 800ad4e + 800ae46: 4622 mov r2, r4 + 800ae48: e781 b.n 800ad4e + 800ae4a: bf00 nop + 800ae4c: 50060400 .word 0x50060400 + +0800ae50 : +{ + 800ae50: b513 push {r0, r1, r4, lr} + return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_MD5); + 800ae52: 2480 movs r4, #128 ; 0x80 + 800ae54: 9400 str r4, [sp, #0] + 800ae56: f7ff ff6b bl 800ad30 +} + 800ae5a: b002 add sp, #8 + 800ae5c: bd10 pop {r4, pc} + +0800ae5e : + 800ae5e: f7ff bff7 b.w 800ae50 + +0800ae62 : +{ + 800ae62: b513 push {r0, r1, r4, lr} + return HASH_Start_IT(hhash, pInBuffer, Size, pOutBuffer,HASH_ALGOSELECTION_SHA1); + 800ae64: 2400 movs r4, #0 + 800ae66: 9400 str r4, [sp, #0] + 800ae68: f7ff ff62 bl 800ad30 +} + 800ae6c: b002 add sp, #8 + 800ae6e: bd10 pop {r4, pc} + +0800ae70 : + 800ae70: f7ff bff7 b.w 800ae62 + +0800ae74 : + * @param pOutBuffer pointer to the computed digest. + * @param Timeout Timeout value. + * @retval HAL status + */ +HAL_StatusTypeDef HASH_Finish(HASH_HandleTypeDef *hhash, uint8_t* pOutBuffer, uint32_t Timeout) +{ + 800ae74: b570 push {r4, r5, r6, lr} + 800ae76: 4613 mov r3, r2 + + if(hhash->State == HAL_HASH_STATE_READY) + 800ae78: f890 2035 ldrb.w r2, [r0, #53] ; 0x35 + 800ae7c: 2a01 cmp r2, #1 +{ + 800ae7e: 4605 mov r5, r0 + 800ae80: 460e mov r6, r1 + if(hhash->State == HAL_HASH_STATE_READY) + 800ae82: b2d4 uxtb r4, r2 + 800ae84: d12f bne.n 800aee6 + { + /* Check parameter */ + if (pOutBuffer == NULL) + 800ae86: b341 cbz r1, 800aeda + { + return HAL_ERROR; + } + + /* Process Locked */ + __HAL_LOCK(hhash); + 800ae88: f890 2034 ldrb.w r2, [r0, #52] ; 0x34 + 800ae8c: 2a01 cmp r2, #1 + 800ae8e: f04f 0102 mov.w r1, #2 + 800ae92: d028 beq.n 800aee6 + 800ae94: f880 4034 strb.w r4, [r0, #52] ; 0x34 + + /* Change the HASH state to busy */ + hhash->State = HAL_HASH_STATE_BUSY; + 800ae98: f880 1035 strb.w r1, [r0, #53] ; 0x35 + + /* Wait for DCIS flag to be set */ + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800ae9c: 2200 movs r2, #0 + 800ae9e: f7ff fc3b bl 800a718 + 800aea2: 4604 mov r4, r0 + 800aea4: bb08 cbnz r0, 800aeea + { + return HAL_TIMEOUT; + } + + /* Read the message digest */ + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800aea6: 4a12 ldr r2, [pc, #72] ; (800aef0 ) + 800aea8: 4b12 ldr r3, [pc, #72] ; (800aef4 ) + 800aeaa: 6811 ldr r1, [r2, #0] + 800aeac: 4219 tst r1, r3 + 800aeae: d016 beq.n 800aede + 800aeb0: 6811 ldr r1, [r2, #0] + 800aeb2: 4019 ands r1, r3 + 800aeb4: f5b1 2f80 cmp.w r1, #262144 ; 0x40000 + 800aeb8: d013 beq.n 800aee2 + 800aeba: 6812 ldr r2, [r2, #0] + 800aebc: 4393 bics r3, r2 + 800aebe: bf0c ite eq + 800aec0: 2120 moveq r1, #32 + 800aec2: 2110 movne r1, #16 + 800aec4: 4630 mov r0, r6 + 800aec6: f7ff fbc5 bl 800a654 + + /* Change the HASH state to ready */ + hhash->State = HAL_HASH_STATE_READY; + 800aeca: 2301 movs r3, #1 + 800aecc: f885 3035 strb.w r3, [r5, #53] ; 0x35 + + /* Reset HASH state machine */ + hhash->Phase = HAL_HASH_PHASE_READY; + 800aed0: f885 302d strb.w r3, [r5, #45] ; 0x2d + + /* Process UnLock */ + __HAL_UNLOCK(hhash); + 800aed4: 2300 movs r3, #0 + 800aed6: f885 3034 strb.w r3, [r5, #52] ; 0x34 + else + { + return HAL_BUSY; + } + +} + 800aeda: 4620 mov r0, r4 + 800aedc: bd70 pop {r4, r5, r6, pc} + HASH_GetDigest(pOutBuffer, HASH_DIGEST_LENGTH()); + 800aede: 2114 movs r1, #20 + 800aee0: e7f0 b.n 800aec4 + 800aee2: 211c movs r1, #28 + 800aee4: e7ee b.n 800aec4 + return HAL_BUSY; + 800aee6: 2402 movs r4, #2 + 800aee8: e7f7 b.n 800aeda + return HAL_TIMEOUT; + 800aeea: 2403 movs r4, #3 + 800aeec: e7f5 b.n 800aeda + 800aeee: bf00 nop + 800aef0: 50060400 .word 0x50060400 + 800aef4: 00040080 .word 0x00040080 + +0800aef8 : + * @param Timeout Timeout value. + * @param Algorithm HASH algorithm. + * @retval HAL status + */ +HAL_StatusTypeDef HMAC_Start(HASH_HandleTypeDef *hhash, uint8_t *pInBuffer, uint32_t Size, uint8_t* pOutBuffer, uint32_t Timeout, uint32_t Algorithm) +{ + 800aef8: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800aefc: 4604 mov r4, r0 + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800aefe: f890 0035 ldrb.w r0, [r0, #53] ; 0x35 + + /* If State is ready or suspended, start or resume polling-based HASH processing */ +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800af02: 2801 cmp r0, #1 +{ + 800af04: e9dd 7e06 ldrd r7, lr, [sp, #24] + HAL_HASH_StateTypeDef State_tmp = hhash->State; + 800af08: b2c5 uxtb r5, r0 +if((State_tmp == HAL_HASH_STATE_READY) || (State_tmp == HAL_HASH_STATE_SUSPENDED)) + 800af0a: d002 beq.n 800af12 + 800af0c: 2d08 cmp r5, #8 + 800af0e: f040 80df bne.w 800b0d0 + { + /* Check input parameters */ + if ((pInBuffer == NULL) || /*(Size == 0U) ||*/ (hhash->Init.pKey == NULL) || (hhash->Init.KeySize == 0U) || (pOutBuffer == NULL)) + 800af12: b139 cbz r1, 800af24 + 800af14: f8d4 c008 ldr.w ip, [r4, #8] + 800af18: f1bc 0f00 cmp.w ip, #0 + 800af1c: d002 beq.n 800af24 + 800af1e: 6865 ldr r5, [r4, #4] + 800af20: b105 cbz r5, 800af24 + 800af22: b923 cbnz r3, 800af2e + { + hhash->State = HAL_HASH_STATE_READY; + 800af24: 2001 movs r0, #1 + 800af26: f884 0035 strb.w r0, [r4, #53] ; 0x35 + return HMAC_Processing(hhash, Timeout); + + } + else + { + return HAL_BUSY; + 800af2a: 4605 mov r5, r0 + 800af2c: e05b b.n 800afe6 + __HAL_LOCK(hhash); + 800af2e: f894 0034 ldrb.w r0, [r4, #52] ; 0x34 + 800af32: 2801 cmp r0, #1 + 800af34: f04f 0002 mov.w r0, #2 + 800af38: d0f7 beq.n 800af2a + hhash->State = HAL_HASH_STATE_BUSY; + 800af3a: f884 0035 strb.w r0, [r4, #53] ; 0x35 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800af3e: f894 002d ldrb.w r0, [r4, #45] ; 0x2d + __HAL_LOCK(hhash); + 800af42: 2601 movs r6, #1 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800af44: 42b0 cmp r0, r6 + __HAL_LOCK(hhash); + 800af46: f884 6034 strb.w r6, [r4, #52] ; 0x34 + if(hhash->Phase == HAL_HASH_PHASE_READY) + 800af4a: d118 bne.n 800af7e + if(hhash->Init.KeySize > 64U) + 800af4c: 4e61 ldr r6, [pc, #388] ; (800b0d4 ) + 800af4e: f8df 818c ldr.w r8, [pc, #396] ; 800b0dc + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_HMAC_KEYTYPE_LONGKEY | HASH_CR_INIT); + 800af52: 6830 ldr r0, [r6, #0] + 800af54: ea00 0008 and.w r0, r0, r8 + 800af58: ea40 000e orr.w r0, r0, lr + if(hhash->Init.KeySize > 64U) + 800af5c: 2d40 cmp r5, #64 ; 0x40 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_HMAC_KEYTYPE_LONGKEY | HASH_CR_INIT); + 800af5e: bf88 it hi + 800af60: f440 3080 orrhi.w r0, r0, #65536 ; 0x10000 + MODIFY_REG(HASH->CR, HASH_CR_LKEY|HASH_CR_ALGO|HASH_CR_MODE|HASH_CR_INIT, Algorithm | HASH_ALGOMODE_HMAC | HASH_CR_INIT); + 800af64: f040 0044 orr.w r0, r0, #68 ; 0x44 + 800af68: 6030 str r0, [r6, #0] + hhash->pHashInBuffPtr = pInBuffer; /* Input data address, HMAC_Processing input parameter for Step 2 */ + 800af6a: e9c4 1303 strd r1, r3, [r4, #12] + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_1; + 800af6e: 2003 movs r0, #3 + hhash->HashInCount = Size; /* Input data size, HMAC_Processing input parameter for Step 2 */ + 800af70: 6222 str r2, [r4, #32] + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_1; + 800af72: f884 002d strb.w r0, [r4, #45] ; 0x2d + hhash->HashBuffSize = Size; /* Store the input buffer size for the whole HMAC process */ + 800af76: 61e2 str r2, [r4, #28] + hhash->pHashKeyBuffPtr = hhash->Init.pKey; /* Key address, HMAC_Processing input parameter for Step 1 and Step 3 */ + 800af78: f8c4 c014 str.w ip, [r4, #20] + hhash->HashKeyCount = hhash->Init.KeySize; /* Key size, HMAC_Processing input parameter for Step 1 and Step 3 */ + 800af7c: 62a5 str r5, [r4, #40] ; 0x28 + if ((hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_1) && (hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_2) && (hhash->Phase != HAL_HASH_PHASE_HMAC_STEP_3)) + 800af7e: f894 302d ldrb.w r3, [r4, #45] ; 0x2d + 800af82: 1eda subs r2, r3, #3 + 800af84: 2a02 cmp r2, #2 + 800af86: d906 bls.n 800af96 + hhash->State = HAL_HASH_STATE_READY; + 800af88: 2001 movs r0, #1 + __HAL_UNLOCK(hhash); + 800af8a: 2300 movs r3, #0 + hhash->State = HAL_HASH_STATE_READY; + 800af8c: f884 0035 strb.w r0, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800af90: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_ERROR; + 800af94: e7c9 b.n 800af2a + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_1) + 800af96: 2b03 cmp r3, #3 + 800af98: 4e4e ldr r6, [pc, #312] ; (800b0d4 ) + 800af9a: d155 bne.n 800b048 + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800af9c: 68b3 ldr r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800af9e: 6961 ldr r1, [r4, #20] + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800afa0: f023 031f bic.w r3, r3, #31 + 800afa4: f005 0503 and.w r5, r5, #3 + 800afa8: ea43 05c5 orr.w r5, r3, r5, lsl #3 + 800afac: 60b5 str r5, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800afae: 6aa2 ldr r2, [r4, #40] ; 0x28 + 800afb0: 4620 mov r0, r4 + 800afb2: f7ff fb0f bl 800a5d4 + 800afb6: 4605 mov r5, r0 + 800afb8: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800afbc: b998 cbnz r0, 800afe6 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800afbe: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800afc2: 2b08 cmp r3, #8 + 800afc4: d103 bne.n 800afce + __HAL_UNLOCK(hhash); + 800afc6: 2000 movs r0, #0 + 800afc8: f884 0034 strb.w r0, [r4, #52] ; 0x34 + return HAL_OK; + 800afcc: e7ad b.n 800af2a + __HAL_HASH_START_DIGEST(); + 800afce: 68b3 ldr r3, [r6, #8] + 800afd0: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800afd4: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, Timeout) != HAL_OK) + 800afd6: 2201 movs r2, #1 + 800afd8: 463b mov r3, r7 + 800afda: 2108 movs r1, #8 + 800afdc: 4620 mov r0, r4 + 800afde: f7ff fb9b bl 800a718 + 800afe2: b118 cbz r0, 800afec + return HAL_TIMEOUT; + 800afe4: 2503 movs r5, #3 + } +} + 800afe6: 4628 mov r0, r5 + 800afe8: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_2; + 800afec: 2304 movs r3, #4 + 800afee: f884 302d strb.w r3, [r4, #45] ; 0x2d + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); + 800aff2: 68b3 ldr r3, [r6, #8] + 800aff4: 69e2 ldr r2, [r4, #28] + hhash->Status = HASH_WriteData(hhash, hhash->pHashInBuffPtr, hhash->HashInCount); + 800aff6: 68e1 ldr r1, [r4, #12] + __HAL_HASH_SET_NBVALIDBITS(hhash->HashBuffSize); + 800aff8: f002 0203 and.w r2, r2, #3 + 800affc: f023 031f bic.w r3, r3, #31 + 800b000: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 800b004: 60b3 str r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashInBuffPtr, hhash->HashInCount); + 800b006: 6a22 ldr r2, [r4, #32] + 800b008: 4620 mov r0, r4 + 800b00a: f7ff fae3 bl 800a5d4 + 800b00e: 4605 mov r5, r0 + 800b010: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800b014: 2800 cmp r0, #0 + 800b016: d1e6 bne.n 800afe6 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800b018: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800b01c: 2b08 cmp r3, #8 + 800b01e: d0d2 beq.n 800afc6 + __HAL_HASH_START_DIGEST(); + 800b020: 68b3 ldr r3, [r6, #8] + 800b022: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800b026: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_BUSY, SET, Timeout) != HAL_OK) + 800b028: 2201 movs r2, #1 + 800b02a: 463b mov r3, r7 + 800b02c: 2108 movs r1, #8 + 800b02e: 4620 mov r0, r4 + 800b030: f7ff fb72 bl 800a718 + 800b034: 2800 cmp r0, #0 + 800b036: d1d5 bne.n 800afe4 + hhash->Phase = HAL_HASH_PHASE_HMAC_STEP_3; + 800b038: 2305 movs r3, #5 + 800b03a: f884 302d strb.w r3, [r4, #45] ; 0x2d + hhash->pHashKeyBuffPtr = hhash->Init.pKey; + 800b03e: 68a3 ldr r3, [r4, #8] + 800b040: 6163 str r3, [r4, #20] + hhash->HashKeyCount = hhash->Init.KeySize; + 800b042: 6863 ldr r3, [r4, #4] + 800b044: 62a3 str r3, [r4, #40] ; 0x28 + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_3) + 800b046: e001 b.n 800b04c + if (hhash->Phase == HAL_HASH_PHASE_HMAC_STEP_2) + 800b048: 2b04 cmp r3, #4 + 800b04a: d0d2 beq.n 800aff2 + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800b04c: 68b3 ldr r3, [r6, #8] + 800b04e: 6862 ldr r2, [r4, #4] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800b050: 6961 ldr r1, [r4, #20] + __HAL_HASH_SET_NBVALIDBITS(hhash->Init.KeySize); + 800b052: f002 0203 and.w r2, r2, #3 + 800b056: f023 031f bic.w r3, r3, #31 + 800b05a: ea43 03c2 orr.w r3, r3, r2, lsl #3 + 800b05e: 60b3 str r3, [r6, #8] + hhash->Status = HASH_WriteData(hhash, hhash->pHashKeyBuffPtr, hhash->HashKeyCount); + 800b060: 6aa2 ldr r2, [r4, #40] ; 0x28 + 800b062: 4620 mov r0, r4 + 800b064: f7ff fab6 bl 800a5d4 + 800b068: 4605 mov r5, r0 + 800b06a: f884 002c strb.w r0, [r4, #44] ; 0x2c + if (hhash->Status != HAL_OK) + 800b06e: 2800 cmp r0, #0 + 800b070: d1b9 bne.n 800afe6 + if (hhash->State == HAL_HASH_STATE_SUSPENDED) + 800b072: f894 3035 ldrb.w r3, [r4, #53] ; 0x35 + 800b076: 2b08 cmp r3, #8 + 800b078: d0a5 beq.n 800afc6 + __HAL_HASH_START_DIGEST(); + 800b07a: 68b3 ldr r3, [r6, #8] + 800b07c: f443 7380 orr.w r3, r3, #256 ; 0x100 + 800b080: 60b3 str r3, [r6, #8] + if (HASH_WaitOnFlagUntilTimeout(hhash, HASH_FLAG_DCIS, RESET, Timeout) != HAL_OK) + 800b082: 4602 mov r2, r0 + 800b084: 463b mov r3, r7 + 800b086: 2102 movs r1, #2 + 800b088: 4620 mov r0, r4 + 800b08a: f7ff fb45 bl 800a718 + 800b08e: 4605 mov r5, r0 + 800b090: 2800 cmp r0, #0 + 800b092: d1a7 bne.n 800afe4 + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800b094: 6832 ldr r2, [r6, #0] + 800b096: 4b10 ldr r3, [pc, #64] ; (800b0d8 ) + 800b098: 6920 ldr r0, [r4, #16] + 800b09a: 421a tst r2, r3 + 800b09c: d014 beq.n 800b0c8 + 800b09e: 6832 ldr r2, [r6, #0] + 800b0a0: 401a ands r2, r3 + 800b0a2: f5b2 2f80 cmp.w r2, #262144 ; 0x40000 + 800b0a6: d011 beq.n 800b0cc + 800b0a8: 6832 ldr r2, [r6, #0] + 800b0aa: 4393 bics r3, r2 + 800b0ac: bf0c ite eq + 800b0ae: 2120 moveq r1, #32 + 800b0b0: 2110 movne r1, #16 + 800b0b2: f7ff facf bl 800a654 + hhash->Phase = HAL_HASH_PHASE_READY; + 800b0b6: 2301 movs r3, #1 + 800b0b8: f884 302d strb.w r3, [r4, #45] ; 0x2d + hhash->State = HAL_HASH_STATE_READY; + 800b0bc: f884 3035 strb.w r3, [r4, #53] ; 0x35 + __HAL_UNLOCK(hhash); + 800b0c0: 2300 movs r3, #0 + 800b0c2: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800b0c6: e78e b.n 800afe6 + HASH_GetDigest(hhash->pHashOutBuffPtr, HASH_DIGEST_LENGTH()); + 800b0c8: 2114 movs r1, #20 + 800b0ca: e7f2 b.n 800b0b2 + 800b0cc: 211c movs r1, #28 + 800b0ce: e7f0 b.n 800b0b2 + return HAL_BUSY; + 800b0d0: 2502 movs r5, #2 + 800b0d2: e788 b.n 800afe6 + 800b0d4: 50060400 .word 0x50060400 + 800b0d8: 00040080 .word 0x00040080 + 800b0dc: fffaff3b .word 0xfffaff3b + +0800b0e0 : + * @param Tickstart : Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef OSPI_WaitFlagStateUntilTimeout(OSPI_HandleTypeDef *hospi, uint32_t Flag, + FlagStatus State, uint32_t Tickstart, uint32_t Timeout) +{ + 800b0e0: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800b0e4: f8dd 8018 ldr.w r8, [sp, #24] + 800b0e8: 4604 mov r4, r0 + 800b0ea: 460e mov r6, r1 + 800b0ec: 4615 mov r5, r2 + 800b0ee: 461f mov r7, r3 + /* Wait until flag is in expected state */ + while((__HAL_OSPI_GET_FLAG(hospi, Flag)) != State) + 800b0f0: 6822 ldr r2, [r4, #0] + 800b0f2: 6a13 ldr r3, [r2, #32] + 800b0f4: 4233 tst r3, r6 + 800b0f6: bf14 ite ne + 800b0f8: 2301 movne r3, #1 + 800b0fa: 2300 moveq r3, #0 + 800b0fc: 42ab cmp r3, r5 + 800b0fe: d101 bne.n 800b104 + + return HAL_ERROR; + } + } + } + return HAL_OK; + 800b100: 2000 movs r0, #0 + 800b102: e012 b.n 800b12a + if (Timeout != HAL_MAX_DELAY) + 800b104: f1b8 3fff cmp.w r8, #4294967295 ; 0xffffffff + 800b108: d0f3 beq.n 800b0f2 + if(((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800b10a: f7fc f967 bl 80073dc + 800b10e: 1bc0 subs r0, r0, r7 + 800b110: 4540 cmp r0, r8 + 800b112: d802 bhi.n 800b11a + 800b114: f1b8 0f00 cmp.w r8, #0 + 800b118: d1ea bne.n 800b0f0 + hospi->State = HAL_OSPI_STATE_ERROR; + 800b11a: f44f 7300 mov.w r3, #512 ; 0x200 + 800b11e: 6463 str r3, [r4, #68] ; 0x44 + hospi->ErrorCode |= HAL_OSPI_ERROR_TIMEOUT; + 800b120: 6ca3 ldr r3, [r4, #72] ; 0x48 + 800b122: f043 0301 orr.w r3, r3, #1 + 800b126: 64a3 str r3, [r4, #72] ; 0x48 + 800b128: 2001 movs r0, #1 +} + 800b12a: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +0800b12e : +} + 800b12e: 4770 bx lr + +0800b130 : +{ + 800b130: b5f0 push {r4, r5, r6, r7, lr} + 800b132: b085 sub sp, #20 + 800b134: 4604 mov r4, r0 + uint32_t tickstart = HAL_GetTick(); + 800b136: f7fc f951 bl 80073dc + 800b13a: 4603 mov r3, r0 + if (hospi == NULL) + 800b13c: 2c00 cmp r4, #0 + 800b13e: d05d beq.n 800b1fc + hospi->ErrorCode = HAL_OSPI_ERROR_NONE; + 800b140: 2000 movs r0, #0 + 800b142: 64a0 str r0, [r4, #72] ; 0x48 + if (hospi->State == HAL_OSPI_STATE_RESET) + 800b144: 6c66 ldr r6, [r4, #68] ; 0x44 + 800b146: 2e00 cmp r6, #0 + 800b148: d156 bne.n 800b1f8 + HAL_OSPI_MspInit(hospi); + 800b14a: 4620 mov r0, r4 + 800b14c: 9303 str r3, [sp, #12] + 800b14e: f7ff ffee bl 800b12e + MODIFY_REG(hospi->Instance->DCR1, + 800b152: 6b20 ldr r0, [r4, #48] ; 0x30 + 800b154: 68e1 ldr r1, [r4, #12] + 800b156: 6825 ldr r5, [r4, #0] + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b158: 9b03 ldr r3, [sp, #12] + MODIFY_REG(hospi->Instance->DCR1, + 800b15a: 68af ldr r7, [r5, #8] + 800b15c: 4301 orrs r1, r0 + 800b15e: 69e0 ldr r0, [r4, #28] + 800b160: 4301 orrs r1, r0 + 800b162: 4827 ldr r0, [pc, #156] ; (800b200 ) + 800b164: 4038 ands r0, r7 + 800b166: 4301 orrs r1, r0 + 800b168: 6920 ldr r0, [r4, #16] + 800b16a: 3801 subs r0, #1 + 800b16c: ea41 4100 orr.w r1, r1, r0, lsl #16 + 800b170: 6960 ldr r0, [r4, #20] + 800b172: 3801 subs r0, #1 + hospi->Timeout = Timeout; + 800b174: f241 3288 movw r2, #5000 ; 0x1388 + MODIFY_REG(hospi->Instance->DCR1, + 800b178: ea41 2100 orr.w r1, r1, r0, lsl #8 + hospi->Timeout = Timeout; + 800b17c: 64e2 str r2, [r4, #76] ; 0x4c + MODIFY_REG(hospi->Instance->DCR1, + 800b17e: 60a9 str r1, [r5, #8] + hospi->Instance->DCR3 = (hospi->Init.ChipSelectBoundary << OCTOSPI_DCR3_CSBOUND_Pos); + 800b180: 6ae1 ldr r1, [r4, #44] ; 0x2c + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold - 1U) << OCTOSPI_CR_FTHRES_Pos)); + 800b182: 6860 ldr r0, [r4, #4] + hospi->Instance->DCR3 = (hospi->Init.ChipSelectBoundary << OCTOSPI_DCR3_CSBOUND_Pos); + 800b184: 0409 lsls r1, r1, #16 + 800b186: 6129 str r1, [r5, #16] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold - 1U) << OCTOSPI_CR_FTHRES_Pos)); + 800b188: 6829 ldr r1, [r5, #0] + 800b18a: 3801 subs r0, #1 + 800b18c: f421 51f8 bic.w r1, r1, #7936 ; 0x1f00 + 800b190: ea41 2100 orr.w r1, r1, r0, lsl #8 + 800b194: 6029 str r1, [r5, #0] + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b196: 4620 mov r0, r4 + 800b198: 9200 str r2, [sp, #0] + 800b19a: 2120 movs r1, #32 + 800b19c: 4632 mov r2, r6 + 800b19e: f7ff ff9f bl 800b0e0 + if (status == HAL_OK) + 800b1a2: bb48 cbnz r0, 800b1f8 + MODIFY_REG(hospi->Instance->DCR2, OCTOSPI_DCR2_PRESCALER, ((hospi->Init.ClockPrescaler - 1U) << OCTOSPI_DCR2_PRESCALER_Pos)); + 800b1a4: 6823 ldr r3, [r4, #0] + 800b1a6: 6a22 ldr r2, [r4, #32] + 800b1a8: 68d9 ldr r1, [r3, #12] + 800b1aa: 3a01 subs r2, #1 + 800b1ac: f021 01ff bic.w r1, r1, #255 ; 0xff + 800b1b0: 430a orrs r2, r1 + 800b1b2: 60da str r2, [r3, #12] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_DQM, hospi->Init.DualQuad); + 800b1b4: 681a ldr r2, [r3, #0] + 800b1b6: 68a1 ldr r1, [r4, #8] + 800b1b8: f022 0240 bic.w r2, r2, #64 ; 0x40 + 800b1bc: 430a orrs r2, r1 + 800b1be: 601a str r2, [r3, #0] + MODIFY_REG(hospi->Instance->TCR, (OCTOSPI_TCR_SSHIFT | OCTOSPI_TCR_DHQC), (hospi->Init.SampleShifting | hospi->Init.DelayHoldQuarterCycle)); + 800b1c0: e9d4 2509 ldrd r2, r5, [r4, #36] ; 0x24 + 800b1c4: f8d3 1108 ldr.w r1, [r3, #264] ; 0x108 + 800b1c8: 432a orrs r2, r5 + 800b1ca: f021 41a0 bic.w r1, r1, #1342177280 ; 0x50000000 + 800b1ce: 430a orrs r2, r1 + 800b1d0: f8c3 2108 str.w r2, [r3, #264] ; 0x108 + __HAL_OSPI_ENABLE(hospi); + 800b1d4: 681a ldr r2, [r3, #0] + 800b1d6: f042 0201 orr.w r2, r2, #1 + 800b1da: 601a str r2, [r3, #0] + if (hospi->Init.FreeRunningClock == HAL_OSPI_FREERUNCLK_ENABLE) + 800b1dc: 69a2 ldr r2, [r4, #24] + 800b1de: 2a02 cmp r2, #2 + SET_BIT(hospi->Instance->DCR1, OCTOSPI_DCR1_FRCK); + 800b1e0: bf02 ittt eq + 800b1e2: 689a ldreq r2, [r3, #8] + 800b1e4: f042 0202 orreq.w r2, r2, #2 + 800b1e8: 609a streq r2, [r3, #8] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b1ea: 68e3 ldr r3, [r4, #12] + 800b1ec: f1b3 6f80 cmp.w r3, #67108864 ; 0x4000000 + hospi->State = HAL_OSPI_STATE_HYPERBUS_INIT; + 800b1f0: bf0c ite eq + 800b1f2: 2301 moveq r3, #1 + hospi->State = HAL_OSPI_STATE_READY; + 800b1f4: 2302 movne r3, #2 + 800b1f6: 6463 str r3, [r4, #68] ; 0x44 +} + 800b1f8: b005 add sp, #20 + 800b1fa: bdf0 pop {r4, r5, r6, r7, pc} + status = HAL_ERROR; + 800b1fc: 2001 movs r0, #1 + 800b1fe: e7fb b.n 800b1f8 + 800b200: f8e0f8f4 .word 0xf8e0f8f4 + +0800b204 : +{ + 800b204: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800b208: 4605 mov r5, r0 + 800b20a: b085 sub sp, #20 + 800b20c: 460c mov r4, r1 + 800b20e: 9202 str r2, [sp, #8] + uint32_t tickstart = HAL_GetTick(); + 800b210: f7fc f8e4 bl 80073dc + state = hospi->State; + 800b214: 6c6a ldr r2, [r5, #68] ; 0x44 + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800b216: 2a02 cmp r2, #2 + uint32_t tickstart = HAL_GetTick(); + 800b218: ee07 0a90 vmov s15, r0 + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800b21c: d105 bne.n 800b22a + 800b21e: 68ea ldr r2, [r5, #12] + 800b220: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + 800b224: d107 bne.n 800b236 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b226: 2310 movs r3, #16 + 800b228: e109 b.n 800b43e + if (((state == HAL_OSPI_STATE_READY) && (hospi->Init.MemoryType != HAL_OSPI_MEMTYPE_HYPERBUS)) || + 800b22a: 2a14 cmp r2, #20 + 800b22c: f040 8084 bne.w 800b338 + ((state == HAL_OSPI_STATE_READ_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG)) || + 800b230: 6822 ldr r2, [r4, #0] + 800b232: 2a02 cmp r2, #2 + ((state == HAL_OSPI_STATE_WRITE_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG))) + 800b234: d1f7 bne.n 800b226 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, Timeout); + 800b236: 9a02 ldr r2, [sp, #8] + 800b238: 9200 str r2, [sp, #0] + 800b23a: ee17 3a90 vmov r3, s15 + 800b23e: 2200 movs r2, #0 + 800b240: 2120 movs r1, #32 + 800b242: 4628 mov r0, r5 + 800b244: edcd 7a03 vstr s15, [sp, #12] + 800b248: f7ff ff4a bl 800b0e0 + if (status == HAL_OK) + 800b24c: eddd 7a03 vldr s15, [sp, #12] + 800b250: 2800 cmp r0, #0 + 800b252: f040 80b9 bne.w 800b3c8 +{ + HAL_StatusTypeDef status = HAL_OK; + __IO uint32_t *ccr_reg, *tcr_reg, *ir_reg, *abr_reg; + + /* Re-initialize the value of the functional mode */ + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, 0U); + 800b256: 6829 ldr r1, [r5, #0] + hospi->ErrorCode = HAL_OSPI_ERROR_NONE; + 800b258: 64a8 str r0, [r5, #72] ; 0x48 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, 0U); + 800b25a: 680a ldr r2, [r1, #0] + 800b25c: f022 5240 bic.w r2, r2, #805306368 ; 0x30000000 + 800b260: 600a str r2, [r1, #0] + + /* Configure the flash ID */ + if (hospi->Init.DualQuad == HAL_OSPI_DUALQUAD_DISABLE) + 800b262: 68aa ldr r2, [r5, #8] + 800b264: b92a cbnz r2, 800b272 + { + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FSEL, cmd->FlashId); + 800b266: 680a ldr r2, [r1, #0] + 800b268: 6866 ldr r6, [r4, #4] + 800b26a: f022 0280 bic.w r2, r2, #128 ; 0x80 + 800b26e: 4332 orrs r2, r6 + 800b270: 600a str r2, [r1, #0] + } + + if (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG) + 800b272: 6822 ldr r2, [r4, #0] + ir_reg = &(hospi->Instance->IR); + abr_reg = &(hospi->Instance->ABR); + } + + /* Configure the CCR register with DQS and SIOO modes */ + *ccr_reg = (cmd->DQSMode | cmd->SIOOMode); + 800b274: e9d4 6712 ldrd r6, r7, [r4, #72] ; 0x48 + if (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG) + 800b278: 2a02 cmp r2, #2 + ccr_reg = &(hospi->Instance->WCCR); + 800b27a: bf0c ite eq + 800b27c: f501 72c0 addeq.w r2, r1, #384 ; 0x180 + ccr_reg = &(hospi->Instance->CCR); + 800b280: f501 7280 addne.w r2, r1, #256 ; 0x100 + *ccr_reg = (cmd->DQSMode | cmd->SIOOMode); + 800b284: ea46 0607 orr.w r6, r6, r7 + 800b288: 6016 str r6, [r2, #0] + + if (cmd->AlternateBytesMode != HAL_OSPI_ALTERNATE_BYTES_NONE) + 800b28a: 6ae6 ldr r6, [r4, #44] ; 0x2c + tcr_reg = &(hospi->Instance->WTCR); + 800b28c: bf03 ittte eq + 800b28e: f501 7cc4 addeq.w ip, r1, #392 ; 0x188 + ir_reg = &(hospi->Instance->WIR); + 800b292: f501 7ec8 addeq.w lr, r1, #400 ; 0x190 + abr_reg = &(hospi->Instance->WABR); + 800b296: f501 78d0 addeq.w r8, r1, #416 ; 0x1a0 + tcr_reg = &(hospi->Instance->TCR); + 800b29a: f501 7c84 addne.w ip, r1, #264 ; 0x108 + ir_reg = &(hospi->Instance->IR); + 800b29e: bf1c itt ne + 800b2a0: f501 7e88 addne.w lr, r1, #272 ; 0x110 + abr_reg = &(hospi->Instance->ABR); + 800b2a4: f501 7890 addne.w r8, r1, #288 ; 0x120 + if (cmd->AlternateBytesMode != HAL_OSPI_ALTERNATE_BYTES_NONE) + 800b2a8: b16e cbz r6, 800b2c6 + { + /* Configure the ABR register with alternate bytes value */ + *abr_reg = cmd->AlternateBytes; + 800b2aa: 6aa6 ldr r6, [r4, #40] ; 0x28 + 800b2ac: f8c8 6000 str.w r6, [r8] + + /* Configure the CCR register with alternate bytes communication parameters */ + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ABMODE | OCTOSPI_CCR_ABDTR | OCTOSPI_CCR_ABSIZE), + 800b2b0: 6ae3 ldr r3, [r4, #44] ; 0x2c + 800b2b2: 6b67 ldr r7, [r4, #52] ; 0x34 + 800b2b4: 6816 ldr r6, [r2, #0] + 800b2b6: 431f orrs r7, r3 + 800b2b8: 6b23 ldr r3, [r4, #48] ; 0x30 + 800b2ba: f426 187c bic.w r8, r6, #4128768 ; 0x3f0000 + 800b2be: 431f orrs r7, r3 + 800b2c0: ea47 0708 orr.w r7, r7, r8 + 800b2c4: 6017 str r7, [r2, #0] + (cmd->AlternateBytesMode | cmd->AlternateBytesDtrMode | cmd->AlternateBytesSize)); + } + + /* Configure the TCR register with the number of dummy cycles */ + MODIFY_REG((*tcr_reg), OCTOSPI_TCR_DCYC, cmd->DummyCycles); + 800b2c6: f8dc 7000 ldr.w r7, [ip] + 800b2ca: 6c66 ldr r6, [r4, #68] ; 0x44 + 800b2cc: f027 071f bic.w r7, r7, #31 + 800b2d0: 433e orrs r6, r7 + 800b2d2: f8cc 6000 str.w r6, [ip] + + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b2d6: f8d4 c038 ldr.w ip, [r4, #56] ; 0x38 + 800b2da: f1bc 0f00 cmp.w ip, #0 + 800b2de: d004 beq.n 800b2ea + { + if (cmd->OperationType == HAL_OSPI_OPTYPE_COMMON_CFG) + 800b2e0: 6827 ldr r7, [r4, #0] + 800b2e2: b917 cbnz r7, 800b2ea + { + /* Configure the DLR register with the number of data */ + hospi->Instance->DLR = (cmd->NbData - 1U); + 800b2e4: 6be7 ldr r7, [r4, #60] ; 0x3c + 800b2e6: 3f01 subs r7, #1 + 800b2e8: 640f str r7, [r1, #64] ; 0x40 + } + } + + if (cmd->InstructionMode != HAL_OSPI_INSTRUCTION_NONE) + 800b2ea: 68e6 ldr r6, [r4, #12] + { + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b2ec: 69e7 ldr r7, [r4, #28] + if (cmd->InstructionMode != HAL_OSPI_INSTRUCTION_NONE) + 800b2ee: 2e00 cmp r6, #0 + 800b2f0: f000 8082 beq.w 800b3f8 + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + { + /* ---- Command with instruction, address and data ---- */ + + /* Configure the CCR register with all communication parameters */ + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b2f4: e9d4 8904 ldrd r8, r9, [r4, #16] + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b2f8: 2f00 cmp r7, #0 + 800b2fa: d040 beq.n 800b37e + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b2fc: e9d4 ab08 ldrd sl, fp, [r4, #32] + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b300: f1bc 0f00 cmp.w ip, #0 + 800b304: d01e beq.n 800b344 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b306: ea4c 0606 orr.w r6, ip, r6 + 800b30a: 433e orrs r6, r7 + 800b30c: ea46 0909 orr.w r9, r6, r9 + 800b310: ea49 0808 orr.w r8, r9, r8 + 800b314: 6813 ldr r3, [r2, #0] + 800b316: 6c26 ldr r6, [r4, #64] ; 0x40 + 800b318: 4f52 ldr r7, [pc, #328] ; (800b464 ) + 800b31a: ea48 0b0b orr.w fp, r8, fp + 800b31e: ea4b 0b0a orr.w fp, fp, sl + 800b322: ea4b 0606 orr.w r6, fp, r6 + 800b326: 401f ands r7, r3 + 800b328: 433e orrs r6, r7 + + /* The DHQC bit is linked with DDTR bit which should be activated */ + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + (cmd->InstructionDtrMode == HAL_OSPI_INSTRUCTION_DTR_ENABLE)) + { + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b32a: 6016 str r6, [r2, #0] + } + } + + /* Configure the IR register with the instruction value */ + *ir_reg = cmd->Instruction; + 800b32c: 68a2 ldr r2, [r4, #8] + 800b32e: f8ce 2000 str.w r2, [lr] + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + (cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize)); + } + + /* Configure the AR register with the instruction value */ + hospi->Instance->AR = cmd->Address; + 800b332: 69a2 ldr r2, [r4, #24] + 800b334: 648a str r2, [r1, #72] ; 0x48 + if (status == HAL_OK) + 800b336: e038 b.n 800b3aa + ((state == HAL_OSPI_STATE_READ_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_WRITE_CFG)) || + 800b338: 2a24 cmp r2, #36 ; 0x24 + 800b33a: f47f af74 bne.w 800b226 + ((state == HAL_OSPI_STATE_WRITE_CMD_CFG) && (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG))) + 800b33e: 6822 ldr r2, [r4, #0] + 800b340: 2a01 cmp r2, #1 + 800b342: e777 b.n 800b234 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b344: 433e orrs r6, r7 + 800b346: f8d2 c000 ldr.w ip, [r2] + 800b34a: ea46 0609 orr.w r6, r6, r9 + 800b34e: ea46 0608 orr.w r6, r6, r8 + 800b352: ea46 060b orr.w r6, r6, fp + 800b356: f42c 5c7c bic.w ip, ip, #16128 ; 0x3f00 + 800b35a: ea46 060a orr.w r6, r6, sl + 800b35e: f02c 0c3f bic.w ip, ip, #63 ; 0x3f + 800b362: ea46 060c orr.w r6, r6, ip + 800b366: 6016 str r6, [r2, #0] + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + 800b368: 6aae ldr r6, [r5, #40] ; 0x28 + 800b36a: f1b6 5f80 cmp.w r6, #268435456 ; 0x10000000 + 800b36e: d1dd bne.n 800b32c + 800b370: 6966 ldr r6, [r4, #20] + 800b372: 2e08 cmp r6, #8 + 800b374: d1da bne.n 800b32c + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b376: 6816 ldr r6, [r2, #0] + 800b378: f046 6600 orr.w r6, r6, #134217728 ; 0x8000000 + 800b37c: e7d5 b.n 800b32a + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b37e: f1bc 0f00 cmp.w ip, #0 + 800b382: d024 beq.n 800b3ce + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b384: ea4c 0106 orr.w r1, ip, r6 + 800b388: 6817 ldr r7, [r2, #0] + 800b38a: 6c26 ldr r6, [r4, #64] ; 0x40 + 800b38c: ea41 0109 orr.w r1, r1, r9 + 800b390: ea41 0108 orr.w r1, r1, r8 + 800b394: f027 6a70 bic.w sl, r7, #251658240 ; 0xf000000 + 800b398: 4331 orrs r1, r6 + 800b39a: f02a 0a3f bic.w sl, sl, #63 ; 0x3f + 800b39e: ea41 010a orr.w r1, r1, sl + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b3a2: 6011 str r1, [r2, #0] + *ir_reg = cmd->Instruction; + 800b3a4: 68a2 ldr r2, [r4, #8] + 800b3a6: f8ce 2000 str.w r2, [lr] + if (cmd->DataMode == HAL_OSPI_DATA_NONE) + 800b3aa: 6ba2 ldr r2, [r4, #56] ; 0x38 + 800b3ac: 2a00 cmp r2, #0 + 800b3ae: d149 bne.n 800b444 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_TC, SET, tickstart, Timeout); + 800b3b0: 9b02 ldr r3, [sp, #8] + 800b3b2: 9300 str r3, [sp, #0] + 800b3b4: 2201 movs r2, #1 + 800b3b6: ee17 3a90 vmov r3, s15 + 800b3ba: 2102 movs r1, #2 + 800b3bc: 4628 mov r0, r5 + 800b3be: f7ff fe8f bl 800b0e0 + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TC); + 800b3c2: 682b ldr r3, [r5, #0] + 800b3c4: 2202 movs r2, #2 + 800b3c6: 625a str r2, [r3, #36] ; 0x24 +} + 800b3c8: b005 add sp, #20 + 800b3ca: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE), + 800b3ce: 6811 ldr r1, [r2, #0] + 800b3d0: ea46 0609 orr.w r6, r6, r9 + 800b3d4: ea46 0808 orr.w r8, r6, r8 + 800b3d8: f021 063f bic.w r6, r1, #63 ; 0x3f + 800b3dc: ea48 0606 orr.w r6, r8, r6 + 800b3e0: 6016 str r6, [r2, #0] + if ((hospi->Init.DelayHoldQuarterCycle == HAL_OSPI_DHQC_ENABLE) && + 800b3e2: 6aa9 ldr r1, [r5, #40] ; 0x28 + 800b3e4: f1b1 5f80 cmp.w r1, #268435456 ; 0x10000000 + 800b3e8: d1dc bne.n 800b3a4 + 800b3ea: 6961 ldr r1, [r4, #20] + 800b3ec: 2908 cmp r1, #8 + 800b3ee: d1d9 bne.n 800b3a4 + MODIFY_REG((*ccr_reg), OCTOSPI_CCR_DDTR, HAL_OSPI_DATA_DTR_ENABLE); + 800b3f0: 6811 ldr r1, [r2, #0] + 800b3f2: f041 6100 orr.w r1, r1, #134217728 ; 0x8000000 + 800b3f6: e7d4 b.n 800b3a2 + if (cmd->AddressMode != HAL_OSPI_ADDRESS_NONE) + 800b3f8: b307 cbz r7, 800b43c + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + 800b3fa: e9d4 9808 ldrd r9, r8, [r4, #32] + if (cmd->DataMode != HAL_OSPI_DATA_NONE) + 800b3fe: f1bc 0f00 cmp.w ip, #0 + 800b402: d011 beq.n 800b428 + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE | + 800b404: f8d2 e000 ldr.w lr, [r2] + 800b408: 6c23 ldr r3, [r4, #64] ; 0x40 + 800b40a: ea4c 0607 orr.w r6, ip, r7 + 800b40e: ea46 0608 orr.w r6, r6, r8 + 800b412: ea46 0609 orr.w r6, r6, r9 + 800b416: f02e 6e70 bic.w lr, lr, #251658240 ; 0xf000000 + 800b41a: 431e orrs r6, r3 + 800b41c: f42e 5e7c bic.w lr, lr, #16128 ; 0x3f00 + 800b420: ea46 060e orr.w r6, r6, lr + MODIFY_REG((*ccr_reg), (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + 800b424: 6016 str r6, [r2, #0] + 800b426: e784 b.n 800b332 + 800b428: f8d2 c000 ldr.w ip, [r2] + 800b42c: ea48 0607 orr.w r6, r8, r7 + 800b430: ea46 0609 orr.w r6, r6, r9 + 800b434: f42c 577c bic.w r7, ip, #16128 ; 0x3f00 + 800b438: 433e orrs r6, r7 + 800b43a: e7f3 b.n 800b424 + } + else + { + /* ---- Invalid command configuration (no instruction, no address) ---- */ + status = HAL_ERROR; + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_PARAM; + 800b43c: 2308 movs r3, #8 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b43e: 64ab str r3, [r5, #72] ; 0x48 + status = HAL_ERROR; + 800b440: 2001 movs r0, #1 + 800b442: e7c1 b.n 800b3c8 + if (cmd->OperationType == HAL_OSPI_OPTYPE_COMMON_CFG) + 800b444: 6823 ldr r3, [r4, #0] + 800b446: b90b cbnz r3, 800b44c + hospi->State = HAL_OSPI_STATE_CMD_CFG; + 800b448: 2304 movs r3, #4 + 800b44a: e005 b.n 800b458 + else if (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG) + 800b44c: 2b01 cmp r3, #1 + if (hospi->State == HAL_OSPI_STATE_WRITE_CMD_CFG) + 800b44e: 6c6b ldr r3, [r5, #68] ; 0x44 + else if (cmd->OperationType == HAL_OSPI_OPTYPE_READ_CFG) + 800b450: d104 bne.n 800b45c + if (hospi->State == HAL_OSPI_STATE_WRITE_CMD_CFG) + 800b452: 2b24 cmp r3, #36 ; 0x24 + 800b454: d0f8 beq.n 800b448 + hospi->State = HAL_OSPI_STATE_READ_CMD_CFG; + 800b456: 2314 movs r3, #20 + hospi->State = HAL_OSPI_STATE_WRITE_CMD_CFG; + 800b458: 646b str r3, [r5, #68] ; 0x44 + 800b45a: e7b5 b.n 800b3c8 + if (hospi->State == HAL_OSPI_STATE_READ_CMD_CFG) + 800b45c: 2b14 cmp r3, #20 + 800b45e: d0f3 beq.n 800b448 + hospi->State = HAL_OSPI_STATE_WRITE_CMD_CFG; + 800b460: 2324 movs r3, #36 ; 0x24 + 800b462: e7f9 b.n 800b458 + 800b464: f0ffc0c0 .word 0xf0ffc0c0 + +0800b468 : +{ + 800b468: b5f0 push {r4, r5, r6, r7, lr} + 800b46a: 4604 mov r4, r0 + 800b46c: b085 sub sp, #20 + 800b46e: 460f mov r7, r1 + 800b470: 4616 mov r6, r2 + uint32_t tickstart = HAL_GetTick(); + 800b472: f7fb ffb3 bl 80073dc + __IO uint32_t *data_reg = &hospi->Instance->DR; + 800b476: 6825 ldr r5, [r4, #0] + uint32_t tickstart = HAL_GetTick(); + 800b478: 4603 mov r3, r0 + uint32_t addr_reg = hospi->Instance->AR; + 800b47a: 6ca8 ldr r0, [r5, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b47c: f8d5 c110 ldr.w ip, [r5, #272] ; 0x110 + if (pData == NULL) + 800b480: b91f cbnz r7, 800b48a + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_PARAM; + 800b482: 2308 movs r3, #8 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b484: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b486: 2001 movs r0, #1 + 800b488: e034 b.n 800b4f4 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b48a: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b48c: 2a04 cmp r2, #4 + 800b48e: d13b bne.n 800b508 + hospi->XferCount = READ_REG(hospi->Instance->DLR) + 1U; + 800b490: 6c2a ldr r2, [r5, #64] ; 0x40 + hospi->pBuffPtr = pData; + 800b492: 6367 str r7, [r4, #52] ; 0x34 + hospi->XferCount = READ_REG(hospi->Instance->DLR) + 1U; + 800b494: 3201 adds r2, #1 + 800b496: 63e2 str r2, [r4, #60] ; 0x3c + hospi->XferSize = hospi->XferCount; + 800b498: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b49a: 63a2 str r2, [r4, #56] ; 0x38 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b49c: 6829 ldr r1, [r5, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b49e: 68e2 ldr r2, [r4, #12] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b4a0: f021 5140 bic.w r1, r1, #805306368 ; 0x30000000 + 800b4a4: f041 5180 orr.w r1, r1, #268435456 ; 0x10000000 + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b4a8: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + 800b4ac: 6029 str r1, [r5, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b4ae: d123 bne.n 800b4f8 + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b4b0: 64a8 str r0, [r5, #72] ; 0x48 + status = OSPI_WaitFlagStateUntilTimeout(hospi, (HAL_OSPI_FLAG_FT | HAL_OSPI_FLAG_TC), SET, tickstart, Timeout); + 800b4b2: 9600 str r6, [sp, #0] + 800b4b4: 2201 movs r2, #1 + 800b4b6: 2106 movs r1, #6 + 800b4b8: 4620 mov r0, r4 + 800b4ba: 9303 str r3, [sp, #12] + 800b4bc: f7ff fe10 bl 800b0e0 + if (status != HAL_OK) + 800b4c0: b9c0 cbnz r0, 800b4f4 + *hospi->pBuffPtr = *((__IO uint8_t *)data_reg); + 800b4c2: 6b62 ldr r2, [r4, #52] ; 0x34 + 800b4c4: f895 1050 ldrb.w r1, [r5, #80] ; 0x50 + 800b4c8: 7011 strb r1, [r2, #0] + hospi->pBuffPtr++; + 800b4ca: 6b62 ldr r2, [r4, #52] ; 0x34 + } while(hospi->XferCount > 0U); + 800b4cc: 9b03 ldr r3, [sp, #12] + hospi->pBuffPtr++; + 800b4ce: 3201 adds r2, #1 + 800b4d0: 6362 str r2, [r4, #52] ; 0x34 + hospi->XferCount--; + 800b4d2: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b4d4: 3a01 subs r2, #1 + 800b4d6: 63e2 str r2, [r4, #60] ; 0x3c + } while(hospi->XferCount > 0U); + 800b4d8: 6be2 ldr r2, [r4, #60] ; 0x3c + 800b4da: 2a00 cmp r2, #0 + 800b4dc: d1e9 bne.n 800b4b2 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_TC, SET, tickstart, Timeout); + 800b4de: 9600 str r6, [sp, #0] + 800b4e0: 2201 movs r2, #1 + 800b4e2: 2102 movs r1, #2 + 800b4e4: 4620 mov r0, r4 + 800b4e6: f7ff fdfb bl 800b0e0 + if (status == HAL_OK) + 800b4ea: b918 cbnz r0, 800b4f4 + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TC); + 800b4ec: 6822 ldr r2, [r4, #0] + 800b4ee: 2302 movs r3, #2 + 800b4f0: 6253 str r3, [r2, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_READY; + 800b4f2: 6463 str r3, [r4, #68] ; 0x44 +} + 800b4f4: b005 add sp, #20 + 800b4f6: bdf0 pop {r4, r5, r6, r7, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b4f8: f8d5 2100 ldr.w r2, [r5, #256] ; 0x100 + 800b4fc: f412 6fe0 tst.w r2, #1792 ; 0x700 + 800b500: d1d6 bne.n 800b4b0 + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b502: f8c5 c110 str.w ip, [r5, #272] ; 0x110 + 800b506: e7d4 b.n 800b4b2 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b508: 2310 movs r3, #16 + 800b50a: e7bb b.n 800b484 + +0800b50c : +{ + 800b50c: e92d 41ff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, lr} + 800b510: 4604 mov r4, r0 + 800b512: 4616 mov r6, r2 + 800b514: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800b516: f7fb ff61 bl 80073dc + uint32_t addr_reg = hospi->Instance->AR; + 800b51a: 6822 ldr r2, [r4, #0] + 800b51c: 6c97 ldr r7, [r2, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b51e: f8d2 8110 ldr.w r8, [r2, #272] ; 0x110 + if ((hospi->State == HAL_OSPI_STATE_CMD_CFG) && (cfg->AutomaticStop == HAL_OSPI_AUTOMATIC_STOP_ENABLE)) + 800b522: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b524: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b526: 4603 mov r3, r0 + if ((hospi->State == HAL_OSPI_STATE_CMD_CFG) && (cfg->AutomaticStop == HAL_OSPI_AUTOMATIC_STOP_ENABLE)) + 800b528: d13c bne.n 800b5a4 + 800b52a: 68ea ldr r2, [r5, #12] + 800b52c: f5b2 0f80 cmp.w r2, #4194304 ; 0x400000 + 800b530: d138 bne.n 800b5a4 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, Timeout); + 800b532: 9003 str r0, [sp, #12] + 800b534: 9600 str r6, [sp, #0] + 800b536: 2200 movs r2, #0 + 800b538: 2120 movs r1, #32 + 800b53a: 4620 mov r0, r4 + 800b53c: f7ff fdd0 bl 800b0e0 + if (status == HAL_OK) + 800b540: bb28 cbnz r0, 800b58e + WRITE_REG (hospi->Instance->PSMAR, cfg->Match); + 800b542: 6822 ldr r2, [r4, #0] + 800b544: 6829 ldr r1, [r5, #0] + 800b546: f8c2 1088 str.w r1, [r2, #136] ; 0x88 + WRITE_REG (hospi->Instance->PSMKR, cfg->Mask); + 800b54a: 6869 ldr r1, [r5, #4] + 800b54c: f8c2 1080 str.w r1, [r2, #128] ; 0x80 + WRITE_REG (hospi->Instance->PIR, cfg->Interval); + 800b550: 6929 ldr r1, [r5, #16] + 800b552: f8c2 1090 str.w r1, [r2, #144] ; 0x90 + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b556: e9d5 1502 ldrd r1, r5, [r5, #8] + 800b55a: 6810 ldr r0, [r2, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b55c: 9b03 ldr r3, [sp, #12] + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b55e: 4329 orrs r1, r5 + 800b560: f020 5043 bic.w r0, r0, #817889280 ; 0x30c00000 + 800b564: 4301 orrs r1, r0 + 800b566: f041 5100 orr.w r1, r1, #536870912 ; 0x20000000 + 800b56a: 6011 str r1, [r2, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b56c: 68e1 ldr r1, [r4, #12] + 800b56e: f1b1 6f80 cmp.w r1, #67108864 ; 0x4000000 + 800b572: d10f bne.n 800b594 + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b574: 6497 str r7, [r2, #72] ; 0x48 + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_SM, SET, tickstart, Timeout); + 800b576: 9600 str r6, [sp, #0] + 800b578: 2201 movs r2, #1 + 800b57a: 2108 movs r1, #8 + 800b57c: 4620 mov r0, r4 + 800b57e: f7ff fdaf bl 800b0e0 + if (status == HAL_OK) + 800b582: b920 cbnz r0, 800b58e + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_SM); + 800b584: 6823 ldr r3, [r4, #0] + 800b586: 2208 movs r2, #8 + 800b588: 625a str r2, [r3, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_READY; + 800b58a: 2302 movs r3, #2 + 800b58c: 6463 str r3, [r4, #68] ; 0x44 +} + 800b58e: b004 add sp, #16 + 800b590: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b594: f8d2 1100 ldr.w r1, [r2, #256] ; 0x100 + 800b598: f411 6fe0 tst.w r1, #1792 ; 0x700 + 800b59c: d1ea bne.n 800b574 + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b59e: f8c2 8110 str.w r8, [r2, #272] ; 0x110 + 800b5a2: e7e8 b.n 800b576 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b5a4: 2310 movs r3, #16 + 800b5a6: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b5a8: 2001 movs r0, #1 + 800b5aa: e7f0 b.n 800b58e + +0800b5ac : +{ + 800b5ac: b5f7 push {r0, r1, r2, r4, r5, r6, r7, lr} + 800b5ae: 4604 mov r4, r0 + 800b5b0: 460f mov r7, r1 + uint32_t tickstart = HAL_GetTick(); + 800b5b2: f7fb ff13 bl 80073dc + uint32_t addr_reg = hospi->Instance->AR; + 800b5b6: 6822 ldr r2, [r4, #0] + 800b5b8: 6c95 ldr r5, [r2, #72] ; 0x48 + uint32_t ir_reg = hospi->Instance->IR; + 800b5ba: f8d2 6110 ldr.w r6, [r2, #272] ; 0x110 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b5be: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b5c0: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b5c2: 4603 mov r3, r0 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b5c4: d132 bne.n 800b62c + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b5c6: 6ce2 ldr r2, [r4, #76] ; 0x4c + 800b5c8: 9200 str r2, [sp, #0] + 800b5ca: 2120 movs r1, #32 + 800b5cc: 2200 movs r2, #0 + 800b5ce: 4620 mov r0, r4 + 800b5d0: f7ff fd86 bl 800b0e0 + if (status == HAL_OK) + 800b5d4: bb00 cbnz r0, 800b618 + WRITE_REG (hospi->Instance->PSMAR, cfg->Match); + 800b5d6: 6823 ldr r3, [r4, #0] + 800b5d8: 683a ldr r2, [r7, #0] + 800b5da: f8c3 2088 str.w r2, [r3, #136] ; 0x88 + WRITE_REG (hospi->Instance->PSMKR, cfg->Mask); + 800b5de: 687a ldr r2, [r7, #4] + 800b5e0: f8c3 2080 str.w r2, [r3, #128] ; 0x80 + WRITE_REG (hospi->Instance->PIR, cfg->Interval); + 800b5e4: 693a ldr r2, [r7, #16] + 800b5e6: f8c3 2090 str.w r2, [r3, #144] ; 0x90 + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_PMM | OCTOSPI_CR_APMS | OCTOSPI_CR_FMODE), + 800b5ea: e9d7 2702 ldrd r2, r7, [r7, #8] + 800b5ee: 6819 ldr r1, [r3, #0] + 800b5f0: 433a orrs r2, r7 + 800b5f2: f021 5143 bic.w r1, r1, #817889280 ; 0x30c00000 + 800b5f6: 430a orrs r2, r1 + 800b5f8: f042 5200 orr.w r2, r2, #536870912 ; 0x20000000 + 800b5fc: 601a str r2, [r3, #0] + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TE | HAL_OSPI_FLAG_SM); + 800b5fe: 2209 movs r2, #9 + 800b600: 625a str r2, [r3, #36] ; 0x24 + hospi->State = HAL_OSPI_STATE_BUSY_AUTO_POLLING; + 800b602: 2248 movs r2, #72 ; 0x48 + 800b604: 6462 str r2, [r4, #68] ; 0x44 + __HAL_OSPI_ENABLE_IT(hospi, HAL_OSPI_IT_SM | HAL_OSPI_IT_TE); + 800b606: 681a ldr r2, [r3, #0] + 800b608: f442 2210 orr.w r2, r2, #589824 ; 0x90000 + 800b60c: 601a str r2, [r3, #0] + if (hospi->Init.MemoryType == HAL_OSPI_MEMTYPE_HYPERBUS) + 800b60e: 68e2 ldr r2, [r4, #12] + 800b610: f1b2 6f80 cmp.w r2, #67108864 ; 0x4000000 + 800b614: d102 bne.n 800b61c + WRITE_REG(hospi->Instance->AR, addr_reg); + 800b616: 649d str r5, [r3, #72] ; 0x48 +} + 800b618: b003 add sp, #12 + 800b61a: bdf0 pop {r4, r5, r6, r7, pc} + if (READ_BIT(hospi->Instance->CCR, OCTOSPI_CCR_ADMODE) != HAL_OSPI_ADDRESS_NONE) + 800b61c: f8d3 2100 ldr.w r2, [r3, #256] ; 0x100 + 800b620: f412 6fe0 tst.w r2, #1792 ; 0x700 + 800b624: d1f7 bne.n 800b616 + WRITE_REG(hospi->Instance->IR, ir_reg); + 800b626: f8c3 6110 str.w r6, [r3, #272] ; 0x110 + 800b62a: e7f5 b.n 800b618 + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b62c: 2310 movs r3, #16 + 800b62e: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b630: 2001 movs r0, #1 + 800b632: e7f1 b.n 800b618 + +0800b634 : +{ + 800b634: b573 push {r0, r1, r4, r5, r6, lr} + 800b636: 4604 mov r4, r0 + 800b638: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800b63a: f7fb fecf bl 80073dc + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b63e: 6c62 ldr r2, [r4, #68] ; 0x44 + 800b640: 2a04 cmp r2, #4 + uint32_t tickstart = HAL_GetTick(); + 800b642: 4603 mov r3, r0 + if (hospi->State == HAL_OSPI_STATE_CMD_CFG) + 800b644: d121 bne.n 800b68a + status = OSPI_WaitFlagStateUntilTimeout(hospi, HAL_OSPI_FLAG_BUSY, RESET, tickstart, hospi->Timeout); + 800b646: 6ce2 ldr r2, [r4, #76] ; 0x4c + 800b648: 9200 str r2, [sp, #0] + 800b64a: 2120 movs r1, #32 + 800b64c: 2200 movs r2, #0 + 800b64e: 4620 mov r0, r4 + 800b650: f7ff fd46 bl 800b0e0 + if (status == HAL_OK) + 800b654: b9b8 cbnz r0, 800b686 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b656: 682e ldr r6, [r5, #0] + WRITE_REG(hospi->Instance->LPTR, cfg->TimeOutPeriod); + 800b658: 6822 ldr r2, [r4, #0] + hospi->State = HAL_OSPI_STATE_BUSY_MEM_MAPPED; + 800b65a: 2388 movs r3, #136 ; 0x88 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b65c: 2e08 cmp r6, #8 + hospi->State = HAL_OSPI_STATE_BUSY_MEM_MAPPED; + 800b65e: 6463 str r3, [r4, #68] ; 0x44 + if (cfg->TimeOutActivation == HAL_OSPI_TIMEOUT_COUNTER_ENABLE) + 800b660: d108 bne.n 800b674 + WRITE_REG(hospi->Instance->LPTR, cfg->TimeOutPeriod); + 800b662: 686b ldr r3, [r5, #4] + 800b664: f8c2 3130 str.w r3, [r2, #304] ; 0x130 + __HAL_OSPI_CLEAR_FLAG(hospi, HAL_OSPI_FLAG_TO); + 800b668: 2310 movs r3, #16 + 800b66a: 6253 str r3, [r2, #36] ; 0x24 + __HAL_OSPI_ENABLE_IT(hospi, HAL_OSPI_IT_TO); + 800b66c: 6811 ldr r1, [r2, #0] + 800b66e: f441 1180 orr.w r1, r1, #1048576 ; 0x100000 + 800b672: 6011 str r1, [r2, #0] + MODIFY_REG(hospi->Instance->CR, (OCTOSPI_CR_TCEN | OCTOSPI_CR_FMODE), + 800b674: 6813 ldr r3, [r2, #0] + 800b676: f023 5340 bic.w r3, r3, #805306368 ; 0x30000000 + 800b67a: f023 0308 bic.w r3, r3, #8 + 800b67e: 4333 orrs r3, r6 + 800b680: f043 5340 orr.w r3, r3, #805306368 ; 0x30000000 + 800b684: 6013 str r3, [r2, #0] +} + 800b686: b002 add sp, #8 + 800b688: bd70 pop {r4, r5, r6, pc} + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b68a: 2310 movs r3, #16 + 800b68c: 64a3 str r3, [r4, #72] ; 0x48 + status = HAL_ERROR; + 800b68e: 2001 movs r0, #1 + 800b690: e7f9 b.n 800b686 + +0800b692 : + if ((hospi->State & OSPI_BUSY_STATE_MASK) == 0U) + 800b692: 6c43 ldr r3, [r0, #68] ; 0x44 + 800b694: f013 0308 ands.w r3, r3, #8 + 800b698: d10a bne.n 800b6b0 + hospi->Init.FifoThreshold = Threshold; + 800b69a: 6041 str r1, [r0, #4] + MODIFY_REG(hospi->Instance->CR, OCTOSPI_CR_FTHRES, ((hospi->Init.FifoThreshold-1U) << OCTOSPI_CR_FTHRES_Pos)); + 800b69c: 6800 ldr r0, [r0, #0] + 800b69e: 6802 ldr r2, [r0, #0] + 800b6a0: 3901 subs r1, #1 + 800b6a2: f422 52f8 bic.w r2, r2, #7936 ; 0x1f00 + 800b6a6: ea42 2101 orr.w r1, r2, r1, lsl #8 + 800b6aa: 6001 str r1, [r0, #0] + HAL_StatusTypeDef status = HAL_OK; + 800b6ac: 4618 mov r0, r3 + 800b6ae: 4770 bx lr + hospi->ErrorCode = HAL_OSPI_ERROR_INVALID_SEQUENCE; + 800b6b0: 2310 movs r3, #16 + 800b6b2: 6483 str r3, [r0, #72] ; 0x48 + status = HAL_ERROR; + 800b6b4: 2001 movs r0, #1 +} + 800b6b6: 4770 bx lr + +0800b6b8 : + return ((READ_BIT(hospi->Instance->CR, OCTOSPI_CR_FTHRES) >> OCTOSPI_CR_FTHRES_Pos) + 1U); + 800b6b8: 6803 ldr r3, [r0, #0] + 800b6ba: 6818 ldr r0, [r3, #0] + 800b6bc: f3c0 2004 ubfx r0, r0, #8, #5 +} + 800b6c0: 3001 adds r0, #1 + 800b6c2: 4770 bx lr + +0800b6c4 : + hospi->Timeout = Timeout; + 800b6c4: 64c1 str r1, [r0, #76] ; 0x4c +} + 800b6c6: 2000 movs r0, #0 + 800b6c8: 4770 bx lr + +0800b6ca : + return hospi->ErrorCode; + 800b6ca: 6c80 ldr r0, [r0, #72] ; 0x48 +} + 800b6cc: 4770 bx lr + +0800b6ce : + return hospi->State; + 800b6ce: 6c40 ldr r0, [r0, #68] ; 0x44 +} + 800b6d0: 4770 bx lr + ... + +0800b6d4 : +{ + 800b6d4: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + if (hospi->Instance == OCTOSPI1) + 800b6d8: 6802 ldr r2, [r0, #0] + other_instance = 0U; + 800b6da: 4bbf ldr r3, [pc, #764] ; (800b9d8 ) + * @retval HAL status + */ +static HAL_StatusTypeDef OSPIM_GetConfig(uint8_t instance_nb, OSPIM_CfgTypeDef *cfg) +{ + HAL_StatusTypeDef status = HAL_OK; + uint32_t reg, value = 0U; + 800b6dc: f8df 8304 ldr.w r8, [pc, #772] ; 800b9e4 + other_instance = 0U; + 800b6e0: 429a cmp r2, r3 +{ + 800b6e2: b08b sub sp, #44 ; 0x2c + } + + /* Get the information about the instance */ + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + { + reg = OCTOSPIM->PCR[index]; + 800b6e4: 4bbd ldr r3, [pc, #756] ; (800b9dc ) + other_instance = 0U; + 800b6e6: bf0b itete eq + 800b6e8: f04f 0a01 moveq.w sl, #1 + 800b6ec: f04f 0a00 movne.w sl, #0 + 800b6f0: 2000 moveq r0, #0 + 800b6f2: 2001 movne r0, #1 + for (index = 0U; index < OSPI_NB_INSTANCE; index++) + 800b6f4: 466a mov r2, sp + instance = 1U; + 800b6f6: 2501 movs r5, #1 + cfg->ClkPort = 0U; + 800b6f8: 2700 movs r7, #0 + cfg->DQSPort = 0U; + 800b6fa: e9c2 7700 strd r7, r7, [r2] + cfg->IOLowPort = 0U; + 800b6fe: e9c2 7702 strd r7, r7, [r2, #8] + uint32_t reg, value = 0U; + 800b702: 2d02 cmp r5, #2 + 800b704: bf0c ite eq + 800b706: 46c4 moveq ip, r8 + 800b708: f04f 0c00 movne.w ip, #0 + cfg->IOHighPort = 0U; + 800b70c: 6117 str r7, [r2, #16] + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + 800b70e: f04f 0e00 mov.w lr, #0 + reg = OCTOSPIM->PCR[index]; + 800b712: eb03 048e add.w r4, r3, lr, lsl #2 + { + /* The clock is enabled on this port */ + if ((reg & OCTOSPIM_PCR_CLKSRC) == (value & OCTOSPIM_PCR_CLKSRC)) + { + /* The clock correspond to the instance passed as parameter */ + cfg->ClkPort = index+1U; + 800b716: f10e 0601 add.w r6, lr, #1 + reg = OCTOSPIM->PCR[index]; + 800b71a: 6864 ldr r4, [r4, #4] + if ((reg & OCTOSPIM_PCR_CLKEN) != 0U) + 800b71c: f014 0f01 tst.w r4, #1 + 800b720: d005 beq.n 800b72e + if ((reg & OCTOSPIM_PCR_CLKSRC) == (value & OCTOSPIM_PCR_CLKSRC)) + 800b722: ea84 0e0c eor.w lr, r4, ip + 800b726: f01e 0f02 tst.w lr, #2 + cfg->ClkPort = index+1U; + 800b72a: bf08 it eq + 800b72c: 6016 streq r6, [r2, #0] + } + } + + if ((reg & OCTOSPIM_PCR_DQSEN) != 0U) + 800b72e: f014 0f10 tst.w r4, #16 + 800b732: d005 beq.n 800b740 + { + /* The DQS is enabled on this port */ + if ((reg & OCTOSPIM_PCR_DQSSRC) == (value & OCTOSPIM_PCR_DQSSRC)) + 800b734: ea84 0e0c eor.w lr, r4, ip + 800b738: f01e 0f20 tst.w lr, #32 + { + /* The DQS correspond to the instance passed as parameter */ + cfg->DQSPort = index+1U; + 800b73c: bf08 it eq + 800b73e: 6056 streq r6, [r2, #4] + } + } + + if ((reg & OCTOSPIM_PCR_NCSEN) != 0U) + 800b740: f414 7f80 tst.w r4, #256 ; 0x100 + 800b744: d005 beq.n 800b752 + { + /* The nCS is enabled on this port */ + if ((reg & OCTOSPIM_PCR_NCSSRC) == (value & OCTOSPIM_PCR_NCSSRC)) + 800b746: ea84 0e0c eor.w lr, r4, ip + 800b74a: f41e 7f00 tst.w lr, #512 ; 0x200 + { + /* The nCS correspond to the instance passed as parameter */ + cfg->NCSPort = index+1U; + 800b74e: bf08 it eq + 800b750: 6096 streq r6, [r2, #8] + } + } + + if ((reg & OCTOSPIM_PCR_IOLEN) != 0U) + 800b752: f414 3f80 tst.w r4, #65536 ; 0x10000 + 800b756: d00d beq.n 800b774 + { + /* The IO Low is enabled on this port */ + if ((reg & OCTOSPIM_PCR_IOLSRC_1) == (value & OCTOSPIM_PCR_IOLSRC_1)) + 800b758: ea84 0e0c eor.w lr, r4, ip + 800b75c: f41e 2f80 tst.w lr, #262144 ; 0x40000 + 800b760: d108 bne.n 800b774 + { + /* The IO Low correspond to the instance passed as parameter */ + if ((reg & OCTOSPIM_PCR_IOLSRC_0) == 0U) + 800b762: f414 3f00 tst.w r4, #131072 ; 0x20000 + { + cfg->IOLowPort = (OCTOSPIM_PCR_IOLEN | (index+1U)); + 800b766: bf0c ite eq + 800b768: f446 3e80 orreq.w lr, r6, #65536 ; 0x10000 + } + else + { + cfg->IOLowPort = (OCTOSPIM_PCR_IOHEN | (index+1U)); + 800b76c: f046 7e80 orrne.w lr, r6, #16777216 ; 0x1000000 + 800b770: f8c2 e00c str.w lr, [r2, #12] + } + } + } + + if ((reg & OCTOSPIM_PCR_IOHEN) != 0U) + 800b774: f014 7f80 tst.w r4, #16777216 ; 0x1000000 + 800b778: d00b beq.n 800b792 + { + /* The IO High is enabled on this port */ + if ((reg & OCTOSPIM_PCR_IOHSRC_1) == (value & OCTOSPIM_PCR_IOHSRC_1)) + 800b77a: ea84 0e0c eor.w lr, r4, ip + 800b77e: f01e 6f80 tst.w lr, #67108864 ; 0x4000000 + 800b782: d106 bne.n 800b792 + { + /* The IO High correspond to the instance passed as parameter */ + if ((reg & OCTOSPIM_PCR_IOHSRC_0) == 0U) + 800b784: 01a4 lsls r4, r4, #6 + { + cfg->IOHighPort = (OCTOSPIM_PCR_IOLEN | (index+1U)); + 800b786: bf54 ite pl + 800b788: f446 3480 orrpl.w r4, r6, #65536 ; 0x10000 + } + else + { + cfg->IOHighPort = (OCTOSPIM_PCR_IOHEN | (index+1U)); + 800b78c: f046 7480 orrmi.w r4, r6, #16777216 ; 0x1000000 + 800b790: 6114 str r4, [r2, #16] + for (index = 0U; index < OSPI_IOM_NB_PORTS; index ++) + 800b792: 2e02 cmp r6, #2 + 800b794: f04f 0e01 mov.w lr, #1 + 800b798: d1bb bne.n 800b712 + for (index = 0U; index < OSPI_NB_INSTANCE; index++) + 800b79a: 2d02 cmp r5, #2 + 800b79c: f102 0214 add.w r2, r2, #20 + 800b7a0: f040 8117 bne.w 800b9d2 + if ((OCTOSPI1->CR & OCTOSPI_CR_EN) != 0U) + 800b7a4: 4c8c ldr r4, [pc, #560] ; (800b9d8 ) + 800b7a6: 6825 ldr r5, [r4, #0] + 800b7a8: ea15 050e ands.w r5, r5, lr + CLEAR_BIT(OCTOSPI1->CR, OCTOSPI_CR_EN); + 800b7ac: bf1e ittt ne + 800b7ae: 6822 ldrne r2, [r4, #0] + 800b7b0: f022 0201 bicne.w r2, r2, #1 + 800b7b4: 6022 strne r2, [r4, #0] + if ((OCTOSPI2->CR & OCTOSPI_CR_EN) != 0U) + 800b7b6: 4a8a ldr r2, [pc, #552] ; (800b9e0 ) + 800b7b8: 6814 ldr r4, [r2, #0] + ospi_enabled |= 0x1U; + 800b7ba: bf18 it ne + 800b7bc: 4675 movne r5, lr + if ((OCTOSPI2->CR & OCTOSPI_CR_EN) != 0U) + 800b7be: 07e6 lsls r6, r4, #31 + CLEAR_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b7c0: bf42 ittt mi + 800b7c2: 6814 ldrmi r4, [r2, #0] + 800b7c4: f024 0401 bicmi.w r4, r4, #1 + 800b7c8: 6014 strmi r4, [r2, #0] + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b7ca: aa0a add r2, sp, #40 ; 0x28 + 800b7cc: f04f 0414 mov.w r4, #20 + 800b7d0: fb04 2400 mla r4, r4, r0, r2 + ospi_enabled |= 0x2U; + 800b7d4: bf48 it mi + 800b7d6: f045 0b02 orrmi.w fp, r5, #2 + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b7da: f854 2c20 ldr.w r2, [r4, #-32] + 800b7de: f102 32ff add.w r2, r2, #4294967295 ; 0xffffffff + 800b7e2: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b7e6: bf58 it pl + 800b7e8: 46ab movpl fp, r5 + 800b7ea: 6856 ldr r6, [r2, #4] + 800b7ec: f426 7680 bic.w r6, r6, #256 ; 0x100 + 800b7f0: 6056 str r6, [r2, #4] + if (IOM_cfg[instance].ClkPort != 0U) + 800b7f2: f854 2c28 ldr.w r2, [r4, #-40] + 800b7f6: b382 cbz r2, 800b85a + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].ClkPort-1U)], OCTOSPIM_PCR_CLKEN); + 800b7f8: 3a01 subs r2, #1 + 800b7fa: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b7fe: 6856 ldr r6, [r2, #4] + 800b800: f026 0601 bic.w r6, r6, #1 + 800b804: 6056 str r6, [r2, #4] + if (IOM_cfg[instance].DQSPort != 0U) + 800b806: f854 2c24 ldr.w r2, [r4, #-36] + 800b80a: b132 cbz r2, 800b81a + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[instance].DQSPort-1U)], OCTOSPIM_PCR_DQSEN); + 800b80c: 3a01 subs r2, #1 + 800b80e: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b812: 6854 ldr r4, [r2, #4] + 800b814: f024 0410 bic.w r4, r4, #16 + 800b818: 6054 str r4, [r2, #4] + if (IOM_cfg[instance].IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b81a: 2214 movs r2, #20 + 800b81c: ac0a add r4, sp, #40 ; 0x28 + 800b81e: fb02 4200 mla r2, r2, r0, r4 + 800b822: f852 2c1c ldr.w r2, [r2, #-28] + 800b826: b142 cbz r2, 800b83a + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[instance].IOLowPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOLEN); + 800b828: 3a01 subs r2, #1 + 800b82a: f002 0201 and.w r2, r2, #1 + 800b82e: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b832: 6854 ldr r4, [r2, #4] + 800b834: f424 3480 bic.w r4, r4, #65536 ; 0x10000 + 800b838: 6054 str r4, [r2, #4] + if (IOM_cfg[instance].IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800b83a: 2214 movs r2, #20 + 800b83c: ac0a add r4, sp, #40 ; 0x28 + 800b83e: fb02 4200 mla r2, r2, r0, r4 + 800b842: f852 2c18 ldr.w r2, [r2, #-24] + 800b846: b142 cbz r2, 800b85a + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[instance].IOHighPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOHEN); + 800b848: 3a01 subs r2, #1 + 800b84a: f002 0201 and.w r2, r2, #1 + 800b84e: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b852: 6854 ldr r4, [r2, #4] + 800b854: f024 7480 bic.w r4, r4, #16777216 ; 0x1000000 + 800b858: 6054 str r4, [r2, #4] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b85a: aa0a add r2, sp, #40 ; 0x28 + 800b85c: f04f 0914 mov.w r9, #20 + 800b860: fb09 290a mla r9, r9, sl, r2 + 800b864: f8d1 c000 ldr.w ip, [r1] + 800b868: f859 8c28 ldr.w r8, [r9, #-40] + (cfg->IOHighPort == IOM_cfg[other_instance].IOHighPort)) + 800b86c: f859 4c18 ldr.w r4, [r9, #-24] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b870: 45c4 cmp ip, r8 + (cfg->NCSPort == IOM_cfg[other_instance].NCSPort) || (cfg->IOLowPort == IOM_cfg[other_instance].IOLowPort) || + 800b872: e9d1 6e01 ldrd r6, lr, [r1, #4] + (cfg->IOHighPort == IOM_cfg[other_instance].IOHighPort)) + 800b876: e9d1 2103 ldrd r2, r1, [r1, #12] + if ((cfg->ClkPort == IOM_cfg[other_instance].ClkPort) || (cfg->DQSPort == IOM_cfg[other_instance].DQSPort) || + 800b87a: d00d beq.n 800b898 + 800b87c: f859 7c24 ldr.w r7, [r9, #-36] + 800b880: 42b7 cmp r7, r6 + 800b882: d009 beq.n 800b898 + 800b884: f859 7c20 ldr.w r7, [r9, #-32] + 800b888: 45be cmp lr, r7 + 800b88a: d005 beq.n 800b898 + (cfg->NCSPort == IOM_cfg[other_instance].NCSPort) || (cfg->IOLowPort == IOM_cfg[other_instance].IOLowPort) || + 800b88c: f859 7c1c ldr.w r7, [r9, #-28] + 800b890: 4297 cmp r7, r2 + 800b892: d001 beq.n 800b898 + 800b894: 428c cmp r4, r1 + 800b896: d142 bne.n 800b91e + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].ClkPort-1U)], OCTOSPIM_PCR_CLKEN); + 800b898: f108 38ff add.w r8, r8, #4294967295 ; 0xffffffff + 800b89c: eb03 0888 add.w r8, r3, r8, lsl #2 + 800b8a0: f8d8 7004 ldr.w r7, [r8, #4] + 800b8a4: f027 0701 bic.w r7, r7, #1 + 800b8a8: f8c8 7004 str.w r7, [r8, #4] + if (IOM_cfg[other_instance].DQSPort != 0U) + 800b8ac: 2714 movs r7, #20 + 800b8ae: f10d 0828 add.w r8, sp, #40 ; 0x28 + 800b8b2: fb07 870a mla r7, r7, sl, r8 + 800b8b6: f857 7c24 ldr.w r7, [r7, #-36] + 800b8ba: b147 cbz r7, 800b8ce + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].DQSPort-1U)], OCTOSPIM_PCR_DQSEN); + 800b8bc: 3f01 subs r7, #1 + 800b8be: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b8c2: f8d7 8004 ldr.w r8, [r7, #4] + 800b8c6: f028 0810 bic.w r8, r8, #16 + 800b8ca: f8c7 8004 str.w r8, [r7, #4] + CLEAR_BIT(OCTOSPIM->PCR[(IOM_cfg[other_instance].NCSPort-1U)], OCTOSPIM_PCR_NCSEN); + 800b8ce: 2714 movs r7, #20 + 800b8d0: f10d 0828 add.w r8, sp, #40 ; 0x28 + 800b8d4: fb07 8a0a mla sl, r7, sl, r8 + 800b8d8: f85a 7c20 ldr.w r7, [sl, #-32] + 800b8dc: 3f01 subs r7, #1 + 800b8de: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b8e2: f8d7 8004 ldr.w r8, [r7, #4] + 800b8e6: f428 7880 bic.w r8, r8, #256 ; 0x100 + 800b8ea: f8c7 8004 str.w r8, [r7, #4] + if (IOM_cfg[other_instance].IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b8ee: f85a 7c1c ldr.w r7, [sl, #-28] + 800b8f2: b157 cbz r7, 800b90a + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[other_instance].IOLowPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOLEN); + 800b8f4: 3f01 subs r7, #1 + 800b8f6: f007 0701 and.w r7, r7, #1 + 800b8fa: eb03 0787 add.w r7, r3, r7, lsl #2 + 800b8fe: f8d7 8004 ldr.w r8, [r7, #4] + 800b902: f428 3880 bic.w r8, r8, #65536 ; 0x10000 + 800b906: f8c7 8004 str.w r8, [r7, #4] + if (IOM_cfg[other_instance].IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800b90a: b144 cbz r4, 800b91e + CLEAR_BIT(OCTOSPIM->PCR[((IOM_cfg[other_instance].IOHighPort-1U)& OSPI_IOM_PORT_MASK)], OCTOSPIM_PCR_IOHEN); + 800b90c: 3c01 subs r4, #1 + 800b90e: f004 0401 and.w r4, r4, #1 + 800b912: eb03 0484 add.w r4, r3, r4, lsl #2 + 800b916: 6867 ldr r7, [r4, #4] + 800b918: f027 7780 bic.w r7, r7, #16777216 ; 0x1000000 + 800b91c: 6067 str r7, [r4, #4] + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b91e: f10e 3eff add.w lr, lr, #4294967295 ; 0xffffffff + 800b922: eb03 0e8e add.w lr, r3, lr, lsl #2 + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b926: f10c 3cff add.w ip, ip, #4294967295 ; 0xffffffff + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b92a: f8de 4004 ldr.w r4, [lr, #4] + 800b92e: f424 7440 bic.w r4, r4, #768 ; 0x300 + 800b932: ea44 2440 orr.w r4, r4, r0, lsl #9 + 800b936: f444 7480 orr.w r4, r4, #256 ; 0x100 + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b93a: eb03 0c8c add.w ip, r3, ip, lsl #2 + MODIFY_REG(OCTOSPIM->PCR[(cfg->NCSPort-1U)], (OCTOSPIM_PCR_NCSEN | OCTOSPIM_PCR_NCSSRC), (OCTOSPIM_PCR_NCSEN | (instance << OCTOSPIM_PCR_NCSSRC_Pos))); + 800b93e: f8ce 4004 str.w r4, [lr, #4] + MODIFY_REG(OCTOSPIM->PCR[(cfg->ClkPort-1U)], (OCTOSPIM_PCR_CLKEN | OCTOSPIM_PCR_CLKSRC), (OCTOSPIM_PCR_CLKEN | (instance << OCTOSPIM_PCR_CLKSRC_Pos))); + 800b942: f8dc 4004 ldr.w r4, [ip, #4] + 800b946: f024 0403 bic.w r4, r4, #3 + 800b94a: ea44 0440 orr.w r4, r4, r0, lsl #1 + 800b94e: f044 0401 orr.w r4, r4, #1 + 800b952: f8cc 4004 str.w r4, [ip, #4] + if (cfg->DQSPort != 0U) + 800b956: b156 cbz r6, 800b96e + MODIFY_REG(OCTOSPIM->PCR[(cfg->DQSPort-1U)], (OCTOSPIM_PCR_DQSEN | OCTOSPIM_PCR_DQSSRC), (OCTOSPIM_PCR_DQSEN | (instance << OCTOSPIM_PCR_DQSSRC_Pos))); + 800b958: 3e01 subs r6, #1 + 800b95a: eb03 0686 add.w r6, r3, r6, lsl #2 + 800b95e: 6874 ldr r4, [r6, #4] + 800b960: f024 0430 bic.w r4, r4, #48 ; 0x30 + 800b964: ea44 1440 orr.w r4, r4, r0, lsl #5 + 800b968: f044 0410 orr.w r4, r4, #16 + 800b96c: 6074 str r4, [r6, #4] + if ((cfg->IOLowPort & OCTOSPIM_PCR_IOLEN) != 0U) + 800b96e: 03d4 lsls r4, r2, #15 + 800b970: d53a bpl.n 800b9e8 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOLEN | OCTOSPIM_PCR_IOLSRC), + 800b972: 3a01 subs r2, #1 + 800b974: f002 0201 and.w r2, r2, #1 + 800b978: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b97c: 6854 ldr r4, [r2, #4] + 800b97e: f424 24e0 bic.w r4, r4, #458752 ; 0x70000 + 800b982: ea44 4480 orr.w r4, r4, r0, lsl #18 + 800b986: f444 3480 orr.w r4, r4, #65536 ; 0x10000 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b98a: 6054 str r4, [r2, #4] + if ((cfg->IOHighPort & OCTOSPIM_PCR_IOLEN) != 0U) + 800b98c: 03ca lsls r2, r1, #15 + 800b98e: d53a bpl.n 800ba06 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOLEN | OCTOSPIM_PCR_IOLSRC), + 800b990: 3901 subs r1, #1 + 800b992: f001 0101 and.w r1, r1, #1 + 800b996: eb03 0381 add.w r3, r3, r1, lsl #2 + 800b99a: 685a ldr r2, [r3, #4] + 800b99c: f422 22e0 bic.w r2, r2, #458752 ; 0x70000 + 800b9a0: ea42 4080 orr.w r0, r2, r0, lsl #18 + 800b9a4: f440 3040 orr.w r0, r0, #196608 ; 0x30000 + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b9a8: 6058 str r0, [r3, #4] + if ((ospi_enabled & 0x1U) != 0U) + 800b9aa: b125 cbz r5, 800b9b6 + SET_BIT(OCTOSPI1->CR, OCTOSPI_CR_EN); + 800b9ac: 4a0a ldr r2, [pc, #40] ; (800b9d8 ) + 800b9ae: 6813 ldr r3, [r2, #0] + 800b9b0: f043 0301 orr.w r3, r3, #1 + 800b9b4: 6013 str r3, [r2, #0] + if ((ospi_enabled & 0x2U) != 0U) + 800b9b6: f01b 0f02 tst.w fp, #2 + SET_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b9ba: bf1c itt ne + 800b9bc: 4a08 ldrne r2, [pc, #32] ; (800b9e0 ) + 800b9be: 6813 ldrne r3, [r2, #0] +} + 800b9c0: f04f 0000 mov.w r0, #0 + SET_BIT(OCTOSPI2->CR, OCTOSPI_CR_EN); + 800b9c4: bf1c itt ne + 800b9c6: f043 0301 orrne.w r3, r3, #1 + 800b9ca: 6013 strne r3, [r2, #0] +} + 800b9cc: b00b add sp, #44 ; 0x2c + 800b9ce: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + 800b9d2: 4635 mov r5, r6 + 800b9d4: e691 b.n 800b6fa + 800b9d6: bf00 nop + 800b9d8: a0001000 .word 0xa0001000 + 800b9dc: 50061c00 .word 0x50061c00 + 800b9e0: a0001400 .word 0xa0001400 + 800b9e4: 04040222 .word 0x04040222 + else if (cfg->IOLowPort != HAL_OSPIM_IOPORT_NONE) + 800b9e8: 2a00 cmp r2, #0 + 800b9ea: d0cf beq.n 800b98c + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOLowPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800b9ec: 3a01 subs r2, #1 + 800b9ee: f002 0201 and.w r2, r2, #1 + 800b9f2: eb03 0282 add.w r2, r3, r2, lsl #2 + 800b9f6: 6854 ldr r4, [r2, #4] + 800b9f8: f024 64e0 bic.w r4, r4, #117440512 ; 0x7000000 + 800b9fc: ea44 6480 orr.w r4, r4, r0, lsl #26 + 800ba00: f044 7480 orr.w r4, r4, #16777216 ; 0x1000000 + 800ba04: e7c1 b.n 800b98a + else if (cfg->IOHighPort != HAL_OSPIM_IOPORT_NONE) + 800ba06: 2900 cmp r1, #0 + 800ba08: d0cf beq.n 800b9aa + MODIFY_REG(OCTOSPIM->PCR[((cfg->IOHighPort-1U)& OSPI_IOM_PORT_MASK)], (OCTOSPIM_PCR_IOHEN | OCTOSPIM_PCR_IOHSRC), + 800ba0a: 3901 subs r1, #1 + 800ba0c: f001 0101 and.w r1, r1, #1 + 800ba10: eb03 0381 add.w r3, r3, r1, lsl #2 + 800ba14: 685a ldr r2, [r3, #4] + 800ba16: f022 62e0 bic.w r2, r2, #117440512 ; 0x7000000 + 800ba1a: ea42 6080 orr.w r0, r2, r0, lsl #26 + 800ba1e: f040 7040 orr.w r0, r0, #50331648 ; 0x3000000 + 800ba22: e7c1 b.n 800b9a8 + +0800ba24 : + * @arg @ref I2C_GENERATE_START_WRITE Generate Restart for write request. + * @retval None + */ +static void I2C_TransferConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t Size, uint32_t Mode, + uint32_t Request) +{ + 800ba24: b530 push {r4, r5, lr} + 800ba26: 9d03 ldr r5, [sp, #12] + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + assert_param(IS_TRANSFER_MODE(Mode)); + assert_param(IS_TRANSFER_REQUEST(Request)); + + /* update CR2 register */ + MODIFY_REG(hi2c->Instance->CR2, + 800ba28: 6804 ldr r4, [r0, #0] + 800ba2a: ea45 4202 orr.w r2, r5, r2, lsl #16 + 800ba2e: 431a orrs r2, r3 + 800ba30: 4b05 ldr r3, [pc, #20] ; (800ba48 ) + 800ba32: 6860 ldr r0, [r4, #4] + 800ba34: f3c1 0109 ubfx r1, r1, #0, #10 + 800ba38: ea43 5355 orr.w r3, r3, r5, lsr #21 + 800ba3c: 430a orrs r2, r1 + 800ba3e: ea20 0003 bic.w r0, r0, r3 + 800ba42: 4302 orrs r2, r0 + 800ba44: 6062 str r2, [r4, #4] + ((I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | \ + (I2C_CR2_RD_WRN & (uint32_t)(Request >> (31U - I2C_CR2_RD_WRN_Pos))) | I2C_CR2_START | I2C_CR2_STOP)), \ + (uint32_t)(((uint32_t)DevAddress & I2C_CR2_SADD) | + (((uint32_t)Size << I2C_CR2_NBYTES_Pos) & I2C_CR2_NBYTES) | (uint32_t)Mode | (uint32_t)Request)); +} + 800ba46: bd30 pop {r4, r5, pc} + 800ba48: 03ff63ff .word 0x03ff63ff + +0800ba4c : + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800ba4c: 6803 ldr r3, [r0, #0] +{ + 800ba4e: b570 push {r4, r5, r6, lr} + 800ba50: 4604 mov r4, r0 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800ba52: 6998 ldr r0, [r3, #24] + 800ba54: f010 0010 ands.w r0, r0, #16 +{ + 800ba58: 460d mov r5, r1 + 800ba5a: 4616 mov r6, r2 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) + 800ba5c: d116 bne.n 800ba8c +} + 800ba5e: bd70 pop {r4, r5, r6, pc} + if (Timeout != HAL_MAX_DELAY) + 800ba60: 1c6a adds r2, r5, #1 + 800ba62: d014 beq.n 800ba8e + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800ba64: f7fb fcba bl 80073dc + 800ba68: 1b80 subs r0, r0, r6 + 800ba6a: 4285 cmp r5, r0 + 800ba6c: d300 bcc.n 800ba70 + 800ba6e: b96d cbnz r5, 800ba8c + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800ba70: 6c63 ldr r3, [r4, #68] ; 0x44 + 800ba72: f043 0320 orr.w r3, r3, #32 + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + 800ba76: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800ba78: 2320 movs r3, #32 + 800ba7a: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800ba7e: 2300 movs r3, #0 + 800ba80: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800ba84: f884 3040 strb.w r3, [r4, #64] ; 0x40 + return HAL_ERROR; + 800ba88: 2001 movs r0, #1 + 800ba8a: e7e8 b.n 800ba5e + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET) + 800ba8c: 6823 ldr r3, [r4, #0] + 800ba8e: 699a ldr r2, [r3, #24] + 800ba90: 0690 lsls r0, r2, #26 + 800ba92: d5e5 bpl.n 800ba60 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); + 800ba94: 2210 movs r2, #16 + 800ba96: 61da str r2, [r3, #28] + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800ba98: 2220 movs r2, #32 + 800ba9a: 61da str r2, [r3, #28] + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) != RESET) + 800ba9c: 699a ldr r2, [r3, #24] + 800ba9e: 0791 lsls r1, r2, #30 + hi2c->Instance->TXDR = 0x00U; + 800baa0: bf44 itt mi + 800baa2: 2200 movmi r2, #0 + 800baa4: 629a strmi r2, [r3, #40] ; 0x28 + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXE) == RESET) + 800baa6: 699a ldr r2, [r3, #24] + 800baa8: 07d2 lsls r2, r2, #31 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_TXE); + 800baaa: bf5e ittt pl + 800baac: 699a ldrpl r2, [r3, #24] + 800baae: f042 0201 orrpl.w r2, r2, #1 + 800bab2: 619a strpl r2, [r3, #24] + I2C_RESET_CR2(hi2c); + 800bab4: 685a ldr r2, [r3, #4] + 800bab6: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800baba: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800babe: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800bac2: f022 0201 bic.w r2, r2, #1 + 800bac6: 605a str r2, [r3, #4] + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + 800bac8: 6c63 ldr r3, [r4, #68] ; 0x44 + 800baca: f043 0304 orr.w r3, r3, #4 + 800bace: e7d2 b.n 800ba76 + +0800bad0 : +{ + 800bad0: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800bad4: 9f06 ldr r7, [sp, #24] + 800bad6: 4604 mov r4, r0 + 800bad8: 4688 mov r8, r1 + 800bada: 4616 mov r6, r2 + 800badc: 461d mov r5, r3 + while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status) + 800bade: 6822 ldr r2, [r4, #0] + 800bae0: 6993 ldr r3, [r2, #24] + 800bae2: ea38 0303 bics.w r3, r8, r3 + 800bae6: bf0c ite eq + 800bae8: 2301 moveq r3, #1 + 800baea: 2300 movne r3, #0 + 800baec: 42b3 cmp r3, r6 + 800baee: d001 beq.n 800baf4 + return HAL_OK; + 800baf0: 2000 movs r0, #0 + 800baf2: e015 b.n 800bb20 + if (Timeout != HAL_MAX_DELAY) + 800baf4: 1c6b adds r3, r5, #1 + 800baf6: d0f3 beq.n 800bae0 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800baf8: f7fb fc70 bl 80073dc + 800bafc: 1bc0 subs r0, r0, r7 + 800bafe: 42a8 cmp r0, r5 + 800bb00: d801 bhi.n 800bb06 + 800bb02: 2d00 cmp r5, #0 + 800bb04: d1eb bne.n 800bade + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800bb06: 6c63 ldr r3, [r4, #68] ; 0x44 + 800bb08: f043 0320 orr.w r3, r3, #32 + 800bb0c: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bb0e: 2320 movs r3, #32 + 800bb10: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bb14: 2300 movs r3, #0 + 800bb16: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800bb1a: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800bb1e: 2001 movs r0, #1 +} + 800bb20: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + +0800bb24 : +{ + 800bb24: b570 push {r4, r5, r6, lr} + 800bb26: 4604 mov r4, r0 + 800bb28: 460d mov r5, r1 + 800bb2a: 4616 mov r6, r2 + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET) + 800bb2c: 6823 ldr r3, [r4, #0] + 800bb2e: 699b ldr r3, [r3, #24] + 800bb30: 069b lsls r3, r3, #26 + 800bb32: d501 bpl.n 800bb38 + return HAL_OK; + 800bb34: 2000 movs r0, #0 +} + 800bb36: bd70 pop {r4, r5, r6, pc} + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800bb38: 4632 mov r2, r6 + 800bb3a: 4629 mov r1, r5 + 800bb3c: 4620 mov r0, r4 + 800bb3e: f7ff ff85 bl 800ba4c + 800bb42: b990 cbnz r0, 800bb6a + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800bb44: f7fb fc4a bl 80073dc + 800bb48: 1b80 subs r0, r0, r6 + 800bb4a: 42a8 cmp r0, r5 + 800bb4c: d801 bhi.n 800bb52 + 800bb4e: 2d00 cmp r5, #0 + 800bb50: d1ec bne.n 800bb2c + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800bb52: 6c63 ldr r3, [r4, #68] ; 0x44 + 800bb54: f043 0320 orr.w r3, r3, #32 + 800bb58: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bb5a: 2320 movs r3, #32 + 800bb5c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bb60: 2300 movs r3, #0 + 800bb62: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800bb66: f884 3040 strb.w r3, [r4, #64] ; 0x40 + return HAL_ERROR; + 800bb6a: 2001 movs r0, #1 + 800bb6c: e7e3 b.n 800bb36 + +0800bb6e : +} + 800bb6e: 4770 bx lr + +0800bb70 : +{ + 800bb70: b510 push {r4, lr} + if (hi2c == NULL) + 800bb72: 4604 mov r4, r0 + 800bb74: 2800 cmp r0, #0 + 800bb76: d04a beq.n 800bc0e + if (hi2c->State == HAL_I2C_STATE_RESET) + 800bb78: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 + 800bb7c: f003 02ff and.w r2, r3, #255 ; 0xff + 800bb80: b91b cbnz r3, 800bb8a + hi2c->Lock = HAL_UNLOCKED; + 800bb82: f880 2040 strb.w r2, [r0, #64] ; 0x40 + HAL_I2C_MspInit(hi2c); + 800bb86: f7ff fff2 bl 800bb6e + hi2c->State = HAL_I2C_STATE_BUSY; + 800bb8a: 2324 movs r3, #36 ; 0x24 + 800bb8c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + __HAL_I2C_DISABLE(hi2c); + 800bb90: 6823 ldr r3, [r4, #0] + 800bb92: 681a ldr r2, [r3, #0] + 800bb94: f022 0201 bic.w r2, r2, #1 + 800bb98: 601a str r2, [r3, #0] + hi2c->Instance->TIMINGR = hi2c->Init.Timing & TIMING_CLEAR_MASK; + 800bb9a: 6862 ldr r2, [r4, #4] + 800bb9c: f022 6270 bic.w r2, r2, #251658240 ; 0xf000000 + 800bba0: 611a str r2, [r3, #16] + hi2c->Instance->OAR1 &= ~I2C_OAR1_OA1EN; + 800bba2: 689a ldr r2, [r3, #8] + 800bba4: f422 4200 bic.w r2, r2, #32768 ; 0x8000 + 800bba8: 609a str r2, [r3, #8] + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | hi2c->Init.OwnAddress1); + 800bbaa: e9d4 2102 ldrd r2, r1, [r4, #8] + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) + 800bbae: 2901 cmp r1, #1 + 800bbb0: d124 bne.n 800bbfc + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | hi2c->Init.OwnAddress1); + 800bbb2: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 800bbb6: 609a str r2, [r3, #8] + hi2c->Instance->CR2 |= (I2C_CR2_AUTOEND | I2C_CR2_NACK); + 800bbb8: 685a ldr r2, [r3, #4] + 800bbba: f042 7200 orr.w r2, r2, #33554432 ; 0x2000000 + 800bbbe: f442 4200 orr.w r2, r2, #32768 ; 0x8000 + 800bbc2: 605a str r2, [r3, #4] + hi2c->Instance->OAR2 &= ~I2C_DUALADDRESS_ENABLE; + 800bbc4: 68da ldr r2, [r3, #12] + 800bbc6: f422 4200 bic.w r2, r2, #32768 ; 0x8000 + 800bbca: 60da str r2, [r3, #12] + hi2c->Instance->OAR2 = (hi2c->Init.DualAddressMode | hi2c->Init.OwnAddress2 | (hi2c->Init.OwnAddress2Masks << 8)); + 800bbcc: e9d4 2104 ldrd r2, r1, [r4, #16] + 800bbd0: 430a orrs r2, r1 + 800bbd2: 69a1 ldr r1, [r4, #24] + 800bbd4: ea42 2201 orr.w r2, r2, r1, lsl #8 + 800bbd8: 60da str r2, [r3, #12] + hi2c->Instance->CR1 = (hi2c->Init.GeneralCallMode | hi2c->Init.NoStretchMode); + 800bbda: e9d4 2107 ldrd r2, r1, [r4, #28] + 800bbde: 430a orrs r2, r1 + 800bbe0: 601a str r2, [r3, #0] + __HAL_I2C_ENABLE(hi2c); + 800bbe2: 681a ldr r2, [r3, #0] + 800bbe4: f042 0201 orr.w r2, r2, #1 + 800bbe8: 601a str r2, [r3, #0] + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800bbea: 2000 movs r0, #0 + hi2c->State = HAL_I2C_STATE_READY; + 800bbec: 2320 movs r3, #32 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800bbee: 6460 str r0, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bbf0: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->PreviousState = I2C_STATE_NONE; + 800bbf4: 6320 str r0, [r4, #48] ; 0x30 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bbf6: f884 0042 strb.w r0, [r4, #66] ; 0x42 +} + 800bbfa: bd10 pop {r4, pc} + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE | hi2c->Init.OwnAddress1); + 800bbfc: f442 4204 orr.w r2, r2, #33792 ; 0x8400 + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_10BIT) + 800bc00: 2902 cmp r1, #2 + hi2c->Instance->OAR1 = (I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE | hi2c->Init.OwnAddress1); + 800bc02: 609a str r2, [r3, #8] + hi2c->Instance->CR2 = (I2C_CR2_ADD10); + 800bc04: bf04 itt eq + 800bc06: f44f 6200 moveq.w r2, #2048 ; 0x800 + 800bc0a: 605a streq r2, [r3, #4] + 800bc0c: e7d4 b.n 800bbb8 + return HAL_ERROR; + 800bc0e: 2001 movs r0, #1 + 800bc10: e7f3 b.n 800bbfa + +0800bc12 : + 800bc12: 4770 bx lr + +0800bc14 : +{ + 800bc14: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + 800bc18: 4698 mov r8, r3 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bc1a: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 +{ + 800bc1e: 9f0a ldr r7, [sp, #40] ; 0x28 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bc20: 2b20 cmp r3, #32 +{ + 800bc22: 4604 mov r4, r0 + 800bc24: 460e mov r6, r1 + 800bc26: 4691 mov r9, r2 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bc28: f040 80a3 bne.w 800bd72 + __HAL_LOCK(hi2c); + 800bc2c: f890 3040 ldrb.w r3, [r0, #64] ; 0x40 + 800bc30: 2b01 cmp r3, #1 + 800bc32: f000 809e beq.w 800bd72 + 800bc36: f04f 0a01 mov.w sl, #1 + 800bc3a: f880 a040 strb.w sl, [r0, #64] ; 0x40 + tickstart = HAL_GetTick(); + 800bc3e: f7fb fbcd bl 80073dc + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800bc42: 2319 movs r3, #25 + tickstart = HAL_GetTick(); + 800bc44: 4605 mov r5, r0 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800bc46: 9000 str r0, [sp, #0] + 800bc48: 4652 mov r2, sl + 800bc4a: f44f 4100 mov.w r1, #32768 ; 0x8000 + 800bc4e: 4620 mov r0, r4 + 800bc50: f7ff ff3e bl 800bad0 + 800bc54: b118 cbz r0, 800bc5e + return HAL_ERROR; + 800bc56: 2001 movs r0, #1 +} + 800bc58: b002 add sp, #8 + 800bc5a: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hi2c->State = HAL_I2C_STATE_BUSY_TX; + 800bc5e: 2321 movs r3, #33 ; 0x21 + 800bc60: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_MASTER; + 800bc64: 2310 movs r3, #16 + 800bc66: f884 3042 strb.w r3, [r4, #66] ; 0x42 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800bc6a: 6460 str r0, [r4, #68] ; 0x44 + hi2c->XferCount = Size; + 800bc6c: f8a4 802a strh.w r8, [r4, #42] ; 0x2a + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bc70: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->pBuffPtr = pData; + 800bc72: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bc76: b29b uxth r3, r3 + 800bc78: 2bff cmp r3, #255 ; 0xff + hi2c->XferISR = NULL; + 800bc7a: 6360 str r0, [r4, #52] ; 0x34 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bc7c: 4b3e ldr r3, [pc, #248] ; (800bd78 ) + 800bc7e: d927 bls.n 800bcd0 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800bc80: 22ff movs r2, #255 ; 0xff + 800bc82: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE); + 800bc84: 9300 str r3, [sp, #0] + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bc86: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bc8a: 4631 mov r1, r6 + 800bc8c: 4620 mov r0, r4 + 800bc8e: f7ff fec9 bl 800ba24 + while (hi2c->XferCount > 0U) + 800bc92: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bc94: b29b uxth r3, r3 + 800bc96: 2b00 cmp r3, #0 + 800bc98: d13e bne.n 800bd18 + if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + 800bc9a: 462a mov r2, r5 + 800bc9c: 4639 mov r1, r7 + 800bc9e: 4620 mov r0, r4 + 800bca0: f7ff ff40 bl 800bb24 + 800bca4: 2800 cmp r0, #0 + 800bca6: d1d6 bne.n 800bc56 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800bca8: 6823 ldr r3, [r4, #0] + 800bcaa: 2120 movs r1, #32 + 800bcac: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800bcae: 685a ldr r2, [r3, #4] + 800bcb0: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800bcb4: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800bcb8: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800bcbc: f022 0201 bic.w r2, r2, #1 + 800bcc0: 605a str r2, [r3, #4] + hi2c->State = HAL_I2C_STATE_READY; + 800bcc2: f884 1041 strb.w r1, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800bcc6: f884 0040 strb.w r0, [r4, #64] ; 0x40 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bcca: f884 0042 strb.w r0, [r4, #66] ; 0x42 + return HAL_OK; + 800bcce: e7c3 b.n 800bc58 + hi2c->XferSize = hi2c->XferCount; + 800bcd0: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_WRITE); + 800bcd2: 9300 str r3, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800bcd4: b292 uxth r2, r2 + 800bcd6: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bcd8: f04f 7300 mov.w r3, #33554432 ; 0x2000000 + 800bcdc: b2d2 uxtb r2, r2 + 800bcde: e7d4 b.n 800bc8a + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800bce0: 462a mov r2, r5 + 800bce2: 4639 mov r1, r7 + 800bce4: 4620 mov r0, r4 + 800bce6: f7ff feb1 bl 800ba4c + 800bcea: 2800 cmp r0, #0 + 800bcec: d1b3 bne.n 800bc56 + if (Timeout != HAL_MAX_DELAY) + 800bcee: 1c7a adds r2, r7, #1 + 800bcf0: d012 beq.n 800bd18 + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800bcf2: f7fb fb73 bl 80073dc + 800bcf6: 1b40 subs r0, r0, r5 + 800bcf8: 4287 cmp r7, r0 + 800bcfa: d300 bcc.n 800bcfe + 800bcfc: b967 cbnz r7, 800bd18 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800bcfe: 6c63 ldr r3, [r4, #68] ; 0x44 + 800bd00: f043 0320 orr.w r3, r3, #32 + 800bd04: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800bd06: 2320 movs r3, #32 + 800bd08: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800bd0c: 2300 movs r3, #0 + 800bd0e: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800bd12: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800bd16: e79e b.n 800bc56 + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) == RESET) + 800bd18: 6822 ldr r2, [r4, #0] + 800bd1a: 6993 ldr r3, [r2, #24] + 800bd1c: 079b lsls r3, r3, #30 + 800bd1e: d5df bpl.n 800bce0 + hi2c->Instance->TXDR = *hi2c->pBuffPtr; + 800bd20: 6a63 ldr r3, [r4, #36] ; 0x24 + 800bd22: f813 1b01 ldrb.w r1, [r3], #1 + 800bd26: 6291 str r1, [r2, #40] ; 0x28 + hi2c->pBuffPtr++; + 800bd28: 6263 str r3, [r4, #36] ; 0x24 + hi2c->XferCount--; + 800bd2a: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800bd2c: 8d22 ldrh r2, [r4, #40] ; 0x28 + hi2c->XferCount--; + 800bd2e: 3b01 subs r3, #1 + 800bd30: b29b uxth r3, r3 + 800bd32: 8563 strh r3, [r4, #42] ; 0x2a + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bd34: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800bd36: 3a01 subs r2, #1 + 800bd38: b292 uxth r2, r2 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bd3a: b29b uxth r3, r3 + hi2c->XferSize--; + 800bd3c: 8522 strh r2, [r4, #40] ; 0x28 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bd3e: 2b00 cmp r3, #0 + 800bd40: d0a7 beq.n 800bc92 + 800bd42: 2a00 cmp r2, #0 + 800bd44: d1a5 bne.n 800bc92 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK) + 800bd46: 9500 str r5, [sp, #0] + 800bd48: 463b mov r3, r7 + 800bd4a: 2180 movs r1, #128 ; 0x80 + 800bd4c: 4620 mov r0, r4 + 800bd4e: f7ff febf bl 800bad0 + 800bd52: 2800 cmp r0, #0 + 800bd54: f47f af7f bne.w 800bc56 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bd58: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bd5a: b29b uxth r3, r3 + 800bd5c: 2bff cmp r3, #255 ; 0xff + 800bd5e: d903 bls.n 800bd68 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800bd60: 22ff movs r2, #255 ; 0xff + 800bd62: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bd64: 9000 str r0, [sp, #0] + 800bd66: e78e b.n 800bc86 + hi2c->XferSize = hi2c->XferCount; + 800bd68: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bd6a: 9000 str r0, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800bd6c: b292 uxth r2, r2 + 800bd6e: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bd70: e7b2 b.n 800bcd8 + return HAL_BUSY; + 800bd72: 2002 movs r0, #2 + 800bd74: e770 b.n 800bc58 + 800bd76: bf00 nop + 800bd78: 80002000 .word 0x80002000 + +0800bd7c : +{ + 800bd7c: e92d 47f3 stmdb sp!, {r0, r1, r4, r5, r6, r7, r8, r9, sl, lr} + 800bd80: 4698 mov r8, r3 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bd82: f890 3041 ldrb.w r3, [r0, #65] ; 0x41 +{ + 800bd86: 9f0a ldr r7, [sp, #40] ; 0x28 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bd88: 2b20 cmp r3, #32 +{ + 800bd8a: 4604 mov r4, r0 + 800bd8c: 460e mov r6, r1 + 800bd8e: 4691 mov r9, r2 + if (hi2c->State == HAL_I2C_STATE_READY) + 800bd90: f040 80be bne.w 800bf10 + __HAL_LOCK(hi2c); + 800bd94: f890 3040 ldrb.w r3, [r0, #64] ; 0x40 + 800bd98: 2b01 cmp r3, #1 + 800bd9a: f000 80b9 beq.w 800bf10 + 800bd9e: f04f 0a01 mov.w sl, #1 + 800bda2: f880 a040 strb.w sl, [r0, #64] ; 0x40 + tickstart = HAL_GetTick(); + 800bda6: f7fb fb19 bl 80073dc + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800bdaa: 2319 movs r3, #25 + tickstart = HAL_GetTick(); + 800bdac: 4605 mov r5, r0 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK) + 800bdae: 9000 str r0, [sp, #0] + 800bdb0: 4652 mov r2, sl + 800bdb2: f44f 4100 mov.w r1, #32768 ; 0x8000 + 800bdb6: 4620 mov r0, r4 + 800bdb8: f7ff fe8a bl 800bad0 + 800bdbc: b118 cbz r0, 800bdc6 + return HAL_ERROR; + 800bdbe: 2001 movs r0, #1 +} + 800bdc0: b002 add sp, #8 + 800bdc2: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hi2c->State = HAL_I2C_STATE_BUSY_RX; + 800bdc6: 2322 movs r3, #34 ; 0x22 + 800bdc8: f884 3041 strb.w r3, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_MASTER; + 800bdcc: 2310 movs r3, #16 + 800bdce: f884 3042 strb.w r3, [r4, #66] ; 0x42 + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800bdd2: 6460 str r0, [r4, #68] ; 0x44 + hi2c->XferCount = Size; + 800bdd4: f8a4 802a strh.w r8, [r4, #42] ; 0x2a + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bdd8: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->pBuffPtr = pData; + 800bdda: f8c4 9024 str.w r9, [r4, #36] ; 0x24 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bdde: b29b uxth r3, r3 + 800bde0: 2bff cmp r3, #255 ; 0xff + hi2c->XferISR = NULL; + 800bde2: 6360 str r0, [r4, #52] ; 0x34 + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bde4: 4b4b ldr r3, [pc, #300] ; (800bf14 ) + 800bde6: d909 bls.n 800bdfc + hi2c->XferSize = MAX_NBYTE_SIZE; + 800bde8: 22ff movs r2, #255 ; 0xff + 800bdea: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_READ); + 800bdec: 9300 str r3, [sp, #0] + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bdee: f04f 7380 mov.w r3, #16777216 ; 0x1000000 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bdf2: 4631 mov r1, r6 + 800bdf4: 4620 mov r0, r4 + 800bdf6: f7ff fe15 bl 800ba24 + 800bdfa: e052 b.n 800bea2 + hi2c->XferSize = hi2c->XferCount; + 800bdfc: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_READ); + 800bdfe: 9300 str r3, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800be00: b292 uxth r2, r2 + 800be02: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800be04: f04f 7300 mov.w r3, #33554432 ; 0x2000000 + 800be08: b2d2 uxtb r2, r2 + 800be0a: e7f2 b.n 800bdf2 + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800be0c: 2120 movs r1, #32 + 800be0e: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800be10: 685a ldr r2, [r3, #4] + 800be12: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800be16: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800be1a: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800be1e: f022 0201 bic.w r2, r2, #1 + 800be22: 605a str r2, [r3, #4] + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + 800be24: 2300 movs r3, #0 + 800be26: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800be28: f884 1041 strb.w r1, [r4, #65] ; 0x41 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800be2c: f884 3042 strb.w r3, [r4, #66] ; 0x42 + __HAL_UNLOCK(hi2c); + 800be30: f884 3040 strb.w r3, [r4, #64] ; 0x40 + 800be34: e7c3 b.n 800bdbe + if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U)) + 800be36: f7fb fad1 bl 80073dc + 800be3a: 1b40 subs r0, r0, r5 + 800be3c: 4287 cmp r7, r0 + 800be3e: d300 bcc.n 800be42 + 800be40: b947 cbnz r7, 800be54 + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + 800be42: 6c63 ldr r3, [r4, #68] ; 0x44 + 800be44: f043 0320 orr.w r3, r3, #32 + 800be48: 6463 str r3, [r4, #68] ; 0x44 + hi2c->State = HAL_I2C_STATE_READY; + 800be4a: 2320 movs r3, #32 + 800be4c: f884 3041 strb.w r3, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800be50: 2300 movs r3, #0 + 800be52: e7ed b.n 800be30 + while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == RESET) + 800be54: 6823 ldr r3, [r4, #0] + 800be56: 699b ldr r3, [r3, #24] + 800be58: 075b lsls r3, r3, #29 + 800be5a: d410 bmi.n 800be7e + if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK) + 800be5c: 462a mov r2, r5 + 800be5e: 4639 mov r1, r7 + 800be60: 4620 mov r0, r4 + 800be62: f7ff fdf3 bl 800ba4c + 800be66: 2800 cmp r0, #0 + 800be68: d1a9 bne.n 800bdbe + if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == SET) + 800be6a: 6823 ldr r3, [r4, #0] + 800be6c: 699a ldr r2, [r3, #24] + 800be6e: 0691 lsls r1, r2, #26 + 800be70: d5e1 bpl.n 800be36 + if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == SET) && (hi2c->XferSize > 0U)) + 800be72: 699a ldr r2, [r3, #24] + 800be74: 0752 lsls r2, r2, #29 + 800be76: d5c9 bpl.n 800be0c + 800be78: 8d22 ldrh r2, [r4, #40] ; 0x28 + 800be7a: 2a00 cmp r2, #0 + 800be7c: d0c6 beq.n 800be0c + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->RXDR; + 800be7e: 6823 ldr r3, [r4, #0] + 800be80: 6a5a ldr r2, [r3, #36] ; 0x24 + 800be82: 6a63 ldr r3, [r4, #36] ; 0x24 + 800be84: 701a strb r2, [r3, #0] + hi2c->pBuffPtr++; + 800be86: 6a63 ldr r3, [r4, #36] ; 0x24 + hi2c->XferSize--; + 800be88: 8d22 ldrh r2, [r4, #40] ; 0x28 + hi2c->pBuffPtr++; + 800be8a: 3301 adds r3, #1 + 800be8c: 6263 str r3, [r4, #36] ; 0x24 + hi2c->XferCount--; + 800be8e: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800be90: 3b01 subs r3, #1 + 800be92: b29b uxth r3, r3 + 800be94: 8563 strh r3, [r4, #42] ; 0x2a + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800be96: 8d63 ldrh r3, [r4, #42] ; 0x2a + hi2c->XferSize--; + 800be98: 3a01 subs r2, #1 + 800be9a: b292 uxth r2, r2 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800be9c: b29b uxth r3, r3 + hi2c->XferSize--; + 800be9e: 8522 strh r2, [r4, #40] ; 0x28 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bea0: b9f3 cbnz r3, 800bee0 + while (hi2c->XferCount > 0U) + 800bea2: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bea4: b29b uxth r3, r3 + 800bea6: 2b00 cmp r3, #0 + 800bea8: d1d4 bne.n 800be54 + if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + 800beaa: 462a mov r2, r5 + 800beac: 4639 mov r1, r7 + 800beae: 4620 mov r0, r4 + 800beb0: f7ff fe38 bl 800bb24 + 800beb4: 2800 cmp r0, #0 + 800beb6: d182 bne.n 800bdbe + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + 800beb8: 6823 ldr r3, [r4, #0] + 800beba: 2120 movs r1, #32 + 800bebc: 61d9 str r1, [r3, #28] + I2C_RESET_CR2(hi2c); + 800bebe: 685a ldr r2, [r3, #4] + 800bec0: f022 72ff bic.w r2, r2, #33423360 ; 0x1fe0000 + 800bec4: f422 328b bic.w r2, r2, #71168 ; 0x11600 + 800bec8: f422 72ff bic.w r2, r2, #510 ; 0x1fe + 800becc: f022 0201 bic.w r2, r2, #1 + 800bed0: 605a str r2, [r3, #4] + hi2c->State = HAL_I2C_STATE_READY; + 800bed2: f884 1041 strb.w r1, [r4, #65] ; 0x41 + __HAL_UNLOCK(hi2c); + 800bed6: f884 0040 strb.w r0, [r4, #64] ; 0x40 + hi2c->Mode = HAL_I2C_MODE_NONE; + 800beda: f884 0042 strb.w r0, [r4, #66] ; 0x42 + return HAL_OK; + 800bede: e76f b.n 800bdc0 + if ((hi2c->XferCount != 0U) && (hi2c->XferSize == 0U)) + 800bee0: 2a00 cmp r2, #0 + 800bee2: d1de bne.n 800bea2 + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK) + 800bee4: 9500 str r5, [sp, #0] + 800bee6: 463b mov r3, r7 + 800bee8: 2180 movs r1, #128 ; 0x80 + 800beea: 4620 mov r0, r4 + 800beec: f7ff fdf0 bl 800bad0 + 800bef0: 2800 cmp r0, #0 + 800bef2: f47f af64 bne.w 800bdbe + if (hi2c->XferCount > MAX_NBYTE_SIZE) + 800bef6: 8d63 ldrh r3, [r4, #42] ; 0x2a + 800bef8: b29b uxth r3, r3 + 800befa: 2bff cmp r3, #255 ; 0xff + 800befc: d903 bls.n 800bf06 + hi2c->XferSize = MAX_NBYTE_SIZE; + 800befe: 22ff movs r2, #255 ; 0xff + 800bf00: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + 800bf02: 9000 str r0, [sp, #0] + 800bf04: e773 b.n 800bdee + hi2c->XferSize = hi2c->XferCount; + 800bf06: 8d62 ldrh r2, [r4, #42] ; 0x2a + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bf08: 9000 str r0, [sp, #0] + hi2c->XferSize = hi2c->XferCount; + 800bf0a: b292 uxth r2, r2 + 800bf0c: 8522 strh r2, [r4, #40] ; 0x28 + I2C_TransferConfig(hi2c, DevAddress, (uint8_t)hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + 800bf0e: e779 b.n 800be04 + return HAL_BUSY; + 800bf10: 2002 movs r0, #2 + 800bf12: e755 b.n 800bdc0 + 800bf14: 80002400 .word 0x80002400 + +0800bf18 : + * @param hsd Pointer to SD handle + * @param pSCR pointer to the buffer that will contain the SCR value + * @retval error state + */ +static uint32_t SD_FindSCR(SD_HandleTypeDef *hsd, uint32_t *pSCR) +{ + 800bf18: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + 800bf1c: b086 sub sp, #24 + 800bf1e: 4605 mov r5, r0 + 800bf20: 4688 mov r8, r1 + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t tickstart = HAL_GetTick(); + 800bf22: f7fb fa5b bl 80073dc + uint32_t index = 0U; + uint32_t tempscr[2U] = {0UL, 0UL}; + uint32_t *scr = pSCR; + + /* Set Block Size To 8 Bytes */ + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 8U); + 800bf26: 2108 movs r1, #8 + uint32_t tickstart = HAL_GetTick(); + 800bf28: 4681 mov r9, r0 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 8U); + 800bf2a: 6828 ldr r0, [r5, #0] + 800bf2c: f001 f996 bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800bf30: 4604 mov r4, r0 + 800bf32: bb48 cbnz r0, 800bf88 + { + return errorstate; + } + + /* Send CMD55 APP_CMD with argument as card's RCA */ + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)((hsd->SdCard.RelCardAdd) << 16U)); + 800bf34: 6ca9 ldr r1, [r5, #72] ; 0x48 + 800bf36: 6828 ldr r0, [r5, #0] + 800bf38: 0409 lsls r1, r1, #16 + 800bf3a: f001 fac8 bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800bf3e: 4604 mov r4, r0 + 800bf40: bb10 cbnz r0, 800bf88 + { + return errorstate; + } + + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = 8U; + 800bf42: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff + 800bf46: 2308 movs r3, #8 + 800bf48: e9cd 0300 strd r0, r3, [sp] + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_8B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800bf4c: 2630 movs r6, #48 ; 0x30 + 800bf4e: 2302 movs r3, #2 + 800bf50: e9cd 6302 strd r6, r3, [sp, #8] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_ENABLE; + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bf54: 4669 mov r1, sp + config.DPSM = SDMMC_DPSM_ENABLE; + 800bf56: 2301 movs r3, #1 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bf58: 6828 ldr r0, [r5, #0] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800bf5a: 9404 str r4, [sp, #16] + config.DPSM = SDMMC_DPSM_ENABLE; + 800bf5c: 9305 str r3, [sp, #20] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800bf5e: f001 f8a1 bl 800d0a4 + + /* Send ACMD51 SD_APP_SEND_SCR with argument as 0 */ + errorstate = SDMMC_CmdSendSCR(hsd->Instance); + 800bf62: 6828 ldr r0, [r5, #0] + 800bf64: f001 fae7 bl 800d536 + if(errorstate != HAL_SD_ERROR_NONE) + 800bf68: 4604 mov r4, r0 + 800bf6a: b968 cbnz r0, 800bf88 + uint32_t tempscr[2U] = {0UL, 0UL}; + 800bf6c: 4607 mov r7, r0 + 800bf6e: 4606 mov r6, r0 + { + return errorstate; + } + +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND | SDMMC_FLAG_DATAEND)) + 800bf70: f240 5a2a movw sl, #1322 ; 0x52a + 800bf74: 6828 ldr r0, [r5, #0] + 800bf76: 6b42 ldr r2, [r0, #52] ; 0x34 + 800bf78: ea12 0f0a tst.w r2, sl + { + if((!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOE)) && (index == 0U)) + 800bf7c: 6b42 ldr r2, [r0, #52] ; 0x34 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND | SDMMC_FLAG_DATAEND)) + 800bf7e: d007 beq.n 800bf90 + return HAL_SD_ERROR_TIMEOUT; + } + } +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800bf80: 0712 lsls r2, r2, #28 + 800bf82: d519 bpl.n 800bfb8 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800bf84: 2408 movs r4, #8 + + return HAL_SD_ERROR_DATA_CRC_FAIL; + } + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bf86: 6384 str r4, [r0, #56] ; 0x38 + ((tempscr[0] & SDMMC_16TO23BITS) >> 8) | ((tempscr[0] & SDMMC_24TO31BITS) >> 24)); + + } + + return HAL_SD_ERROR_NONE; +} + 800bf88: 4620 mov r0, r4 + 800bf8a: b006 add sp, #24 + 800bf8c: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + if((!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOE)) && (index == 0U)) + 800bf90: 0311 lsls r1, r2, #12 + 800bf92: d408 bmi.n 800bfa6 + 800bf94: b93c cbnz r4, 800bfa6 + tempscr[0] = SDMMC_ReadFIFO(hsd->Instance); + 800bf96: f001 f849 bl 800d02c + 800bf9a: 4606 mov r6, r0 + tempscr[1] = SDMMC_ReadFIFO(hsd->Instance); + 800bf9c: 6828 ldr r0, [r5, #0] + 800bf9e: f001 f845 bl 800d02c + index++; + 800bfa2: 2401 movs r4, #1 + tempscr[1] = SDMMC_ReadFIFO(hsd->Instance); + 800bfa4: 4607 mov r7, r0 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800bfa6: f7fb fa19 bl 80073dc + 800bfaa: eba0 0009 sub.w r0, r0, r9 + 800bfae: 3001 adds r0, #1 + 800bfb0: d1e0 bne.n 800bf74 + return HAL_SD_ERROR_TIMEOUT; + 800bfb2: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + 800bfb6: e7e7 b.n 800bf88 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800bfb8: 6b42 ldr r2, [r0, #52] ; 0x34 + 800bfba: 0793 lsls r3, r2, #30 + 800bfbc: d501 bpl.n 800bfc2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800bfbe: 2402 movs r4, #2 + 800bfc0: e7e1 b.n 800bf86 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800bfc2: 6b44 ldr r4, [r0, #52] ; 0x34 + 800bfc4: f014 0420 ands.w r4, r4, #32 + 800bfc8: d001 beq.n 800bfce + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800bfca: 2420 movs r4, #32 + 800bfcc: e7db b.n 800bf86 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800bfce: 4a04 ldr r2, [pc, #16] ; (800bfe0 ) + 800bfd0: 6382 str r2, [r0, #56] ; 0x38 + *scr = (((tempscr[1] & SDMMC_0TO7BITS) << 24) | ((tempscr[1] & SDMMC_8TO15BITS) << 8) |\ + 800bfd2: ba3f rev r7, r7 + 800bfd4: ba36 rev r6, r6 + 800bfd6: f8c8 7000 str.w r7, [r8] + *scr = (((tempscr[0] & SDMMC_0TO7BITS) << 24) | ((tempscr[0] & SDMMC_8TO15BITS) << 8) |\ + 800bfda: f8c8 6004 str.w r6, [r8, #4] + return HAL_SD_ERROR_NONE; + 800bfde: e7d3 b.n 800bf88 + 800bfe0: 18000f3a .word 0x18000f3a + +0800bfe4 : + * of PLL to have SDMMCCK clock between 50 and 120 MHz + * @param hsd SD handle + * @retval SD Card error state + */ +static uint32_t SD_UltraHighSpeed(SD_HandleTypeDef *hsd) +{ + 800bfe4: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + uint32_t errorstate = HAL_SD_ERROR_NONE; + SDMMC_DataInitTypeDef sdmmc_datainitstructure; + uint32_t SD_hs[16] = {0}; + 800bfe8: 2640 movs r6, #64 ; 0x40 +{ + 800bfea: b096 sub sp, #88 ; 0x58 + 800bfec: 4605 mov r5, r0 + uint32_t SD_hs[16] = {0}; + 800bfee: 4632 mov r2, r6 + 800bff0: 2100 movs r1, #0 + 800bff2: a806 add r0, sp, #24 + 800bff4: f001 fc96 bl 800d924 + uint32_t count, loop = 0 ; + uint32_t Timeout = HAL_GetTick(); + 800bff8: f7fb f9f0 bl 80073dc + + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800bffc: 6deb ldr r3, [r5, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800bffe: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800c000: 2b00 cmp r3, #0 + 800c002: d067 beq.n 800c0d4 + { + /* Standard Speed Card <= 12.5Mhz */ + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + } + + if((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) && + 800c004: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800c008: d167 bne.n 800c0da + 800c00a: 69af ldr r7, [r5, #24] + 800c00c: 2f01 cmp r7, #1 + 800c00e: d164 bne.n 800c0da + (hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE)) + { + /* Initialize the Data control register */ + hsd->Instance->DCTRL = 0; + 800c010: 6828 ldr r0, [r5, #0] + 800c012: 2300 movs r3, #0 + 800c014: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800c016: 4631 mov r1, r6 + 800c018: f001 f920 bl 800d25c + + if (errorstate != HAL_SD_ERROR_NONE) + 800c01c: 4604 mov r4, r0 + 800c01e: 2800 cmp r0, #0 + 800c020: d13e bne.n 800c0a0 + { + return errorstate; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800c022: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800c026: e9cd 3600 strd r3, r6, [sp] + sdmmc_datainitstructure.DataBlockSize = SDMMC_DATABLOCK_SIZE_64B ; + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800c02a: e9cd 0704 strd r0, r7, [sp, #16] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800c02e: 2660 movs r6, #96 ; 0x60 + 800c030: 2302 movs r3, #2 + + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800c032: 6828 ldr r0, [r5, #0] + 800c034: 4669 mov r1, sp + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800c036: e9cd 6302 strd r6, r3, [sp, #8] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800c03a: f001 f833 bl 800d0a4 + 800c03e: 2800 cmp r0, #0 + 800c040: d14d bne.n 800c0de + { + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + } + + errorstate = SDMMC_CmdSwitch(hsd->Instance, SDMMC_SDR104_SWITCH_PATTERN); + 800c042: 492a ldr r1, [pc, #168] ; (800c0ec ) + 800c044: 6828 ldr r0, [r5, #0] + 800c046: f001 fa74 bl 800d532 + if(errorstate != HAL_SD_ERROR_NONE) + 800c04a: 4604 mov r4, r0 + 800c04c: bb40 cbnz r0, 800c0a0 + uint32_t count, loop = 0 ; + 800c04e: 4607 mov r7, r0 + { + return errorstate; + } + + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800c050: f240 592a movw r9, #1322 ; 0x52a + 800c054: 682b ldr r3, [r5, #0] + 800c056: 6b5e ldr r6, [r3, #52] ; 0x34 + 800c058: ea16 0609 ands.w r6, r6, r9 + 800c05c: d005 beq.n 800c06a + hsd->State= HAL_SD_STATE_READY; + return HAL_SD_ERROR_TIMEOUT; + } + } + + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c05e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c060: 0711 lsls r1, r2, #28 + 800c062: d521 bpl.n 800c0a8 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800c064: 2208 movs r2, #8 + 800c066: 639a str r2, [r3, #56] ; 0x38 + + return errorstate; + 800c068: e01a b.n 800c0a0 + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800c06a: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c06c: 0418 lsls r0, r3, #16 + 800c06e: d50b bpl.n 800c088 + 800c070: ab06 add r3, sp, #24 + 800c072: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800c076: 6828 ldr r0, [r5, #0] + 800c078: f000 ffd8 bl 800d02c + for (count = 0U; count < 8U; count++) + 800c07c: 3601 adds r6, #1 + 800c07e: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800c080: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800c084: d1f7 bne.n 800c076 + loop ++; + 800c086: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800c088: f7fb f9a8 bl 80073dc + 800c08c: eba0 0008 sub.w r0, r0, r8 + 800c090: 3001 adds r0, #1 + 800c092: d1df bne.n 800c054 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800c094: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + hsd->State= HAL_SD_STATE_READY; + 800c098: 2301 movs r3, #1 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800c09a: 63ac str r4, [r5, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800c09c: f885 3034 strb.w r3, [r5, #52] ; 0x34 +#endif /* (DLYB_SDMMC1) || (DLYB_SDMMC2) */ + } + } + + return errorstate; +} + 800c0a0: 4620 mov r0, r4 + 800c0a2: b016 add sp, #88 ; 0x58 + 800c0a4: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c0a8: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c0aa: 0792 lsls r2, r2, #30 + 800c0ac: d502 bpl.n 800c0b4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800c0ae: 2402 movs r4, #2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800c0b0: 639c str r4, [r3, #56] ; 0x38 + return errorstate; + 800c0b2: e7f5 b.n 800c0a0 + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800c0b4: 6b5c ldr r4, [r3, #52] ; 0x34 + 800c0b6: f014 0420 ands.w r4, r4, #32 + 800c0ba: d001 beq.n 800c0c0 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800c0bc: 2420 movs r4, #32 + 800c0be: e7f7 b.n 800c0b0 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c0c0: 4a0b ldr r2, [pc, #44] ; (800c0f0 ) + 800c0c2: 639a str r2, [r3, #56] ; 0x38 + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800c0c4: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800c0c8: 079b lsls r3, r3, #30 + 800c0ca: d50b bpl.n 800c0e4 + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800c0cc: 2001 movs r0, #1 + 800c0ce: f7fb f9f5 bl 80074bc + 800c0d2: e7e5 b.n 800c0a0 + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800c0d4: f04f 6480 mov.w r4, #67108864 ; 0x4000000 + 800c0d8: e7e2 b.n 800c0a0 + uint32_t errorstate = HAL_SD_ERROR_NONE; + 800c0da: 2400 movs r4, #0 + 800c0dc: e7e0 b.n 800c0a0 + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + 800c0de: f44f 3480 mov.w r4, #65536 ; 0x10000 + 800c0e2: e7dd b.n 800c0a0 + errorstate = SDMMC_ERROR_UNSUPPORTED_FEATURE; + 800c0e4: f04f 5480 mov.w r4, #268435456 ; 0x10000000 + 800c0e8: e7da b.n 800c0a0 + 800c0ea: bf00 nop + 800c0ec: 80ff1f03 .word 0x80ff1f03 + 800c0f0: 18000f3a .word 0x18000f3a + +0800c0f4 : +} + 800c0f4: 4770 bx lr + +0800c0f6 : + 800c0f6: 4770 bx lr + +0800c0f8 : +{ + 800c0f8: b510 push {r4, lr} + if(hsd == NULL) + 800c0fa: 4604 mov r4, r0 + 800c0fc: b198 cbz r0, 800c126 + hsd->State = HAL_SD_STATE_BUSY; + 800c0fe: 2303 movs r3, #3 + 800c100: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c104: 6983 ldr r3, [r0, #24] + 800c106: 2b01 cmp r3, #1 + 800c108: d102 bne.n 800c110 + HAL_SDEx_DriveTransceiver_1_8V_Callback(RESET); + 800c10a: 2000 movs r0, #0 + 800c10c: f7fb f9d6 bl 80074bc + (void)SDMMC_PowerState_OFF(hsd->Instance); + 800c110: 6820 ldr r0, [r4, #0] + 800c112: f000 ffa3 bl 800d05c + HAL_SD_MspDeInit(hsd); + 800c116: 4620 mov r0, r4 + 800c118: f7ff ffed bl 800c0f6 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c11c: 2000 movs r0, #0 + 800c11e: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_RESET; + 800c120: f884 0034 strb.w r0, [r4, #52] ; 0x34 +} + 800c124: bd10 pop {r4, pc} + return HAL_ERROR; + 800c126: 2001 movs r0, #1 + 800c128: e7fc b.n 800c124 + ... + +0800c12c : +{ + 800c12c: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800c130: b087 sub sp, #28 + 800c132: 4604 mov r4, r0 + 800c134: 460e mov r6, r1 + 800c136: 4692 mov sl, r2 + 800c138: 461f mov r7, r3 + uint32_t tickstart = HAL_GetTick(); + 800c13a: f7fb f94f bl 80073dc + 800c13e: 4681 mov r9, r0 + if(NULL == pData) + 800c140: b936 cbnz r6, 800c150 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800c142: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c144: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c148: 63a3 str r3, [r4, #56] ; 0x38 + return HAL_ERROR; + 800c14a: f04f 0801 mov.w r8, #1 + 800c14e: e011 b.n 800c174 + if(hsd->State == HAL_SD_STATE_READY) + 800c150: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800c154: 2b01 cmp r3, #1 + 800c156: fa5f f883 uxtb.w r8, r3 + 800c15a: f040 80c3 bne.w 800c2e4 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c15e: 6d62 ldr r2, [r4, #84] ; 0x54 + 800c160: eb0a 0307 add.w r3, sl, r7 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c164: 2100 movs r1, #0 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c166: 4293 cmp r3, r2 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c168: 63a1 str r1, [r4, #56] ; 0x38 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c16a: d907 bls.n 800c17c + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + 800c16c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c16e: f043 7300 orr.w r3, r3, #33554432 ; 0x2000000 + 800c172: 63a3 str r3, [r4, #56] ; 0x38 +} + 800c174: 4640 mov r0, r8 + 800c176: b007 add sp, #28 + 800c178: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + hsd->State = HAL_SD_STATE_BUSY; + 800c17c: 2303 movs r3, #3 + 800c17e: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c182: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->Instance->DCTRL = 0U; + 800c184: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c186: 2b01 cmp r3, #1 + config.DataTimeOut = SDMMC_DATATIMEOUT; + 800c188: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800c18c: 9300 str r3, [sp, #0] + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800c18e: ea4f 2347 mov.w r3, r7, lsl #9 + 800c192: 9301 str r3, [sp, #4] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800c194: f04f 0502 mov.w r5, #2 + 800c198: f04f 0390 mov.w r3, #144 ; 0x90 + 800c19c: e9cd 3502 strd r3, r5, [sp, #8] + hsd->Instance->DCTRL = 0U; + 800c1a0: 62c1 str r1, [r0, #44] ; 0x2c + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800c1a2: f04f 0300 mov.w r3, #0 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c1a6: 4669 mov r1, sp + config.DPSM = SDMMC_DPSM_DISABLE; + 800c1a8: e9cd 3304 strd r3, r3, [sp, #16] + add *= 512U; + 800c1ac: bf18 it ne + 800c1ae: ea4f 2a4a movne.w sl, sl, lsl #9 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c1b2: f000 ff77 bl 800d0a4 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c1b6: 6820 ldr r0, [r4, #0] + 800c1b8: 68c3 ldr r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c1ba: 2f01 cmp r7, #1 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c1bc: f043 0340 orr.w r3, r3, #64 ; 0x40 + 800c1c0: 60c3 str r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c1c2: d910 bls.n 800c1e6 + hsd->Context = SD_CONTEXT_READ_MULTIPLE_BLOCK; + 800c1c4: 6325 str r5, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add); + 800c1c6: 4651 mov r1, sl + 800c1c8: f001 f87a bl 800d2c0 + if(errorstate != HAL_SD_ERROR_NONE) + 800c1cc: b188 cbz r0, 800c1f2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c1ce: 6823 ldr r3, [r4, #0] + 800c1d0: 4a46 ldr r2, [pc, #280] ; (800c2ec ) + 800c1d2: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c1d4: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c1d6: 4318 orrs r0, r3 + 800c1d8: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c1da: 2301 movs r3, #1 + 800c1dc: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c1e0: 2300 movs r3, #0 + 800c1e2: 6323 str r3, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c1e4: e7c6 b.n 800c174 + hsd->Context = SD_CONTEXT_READ_SINGLE_BLOCK; + 800c1e6: 2301 movs r3, #1 + 800c1e8: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, add); + 800c1ea: 4651 mov r1, sl + 800c1ec: f001 f84f bl 800d28e + 800c1f0: e7ec b.n 800c1cc + dataremaining = config.DataLength; + 800c1f2: 9d01 ldr r5, [sp, #4] + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800c1f4: 6820 ldr r0, [r4, #0] + 800c1f6: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c1f8: f413 7f95 tst.w r3, #298 ; 0x12a + 800c1fc: d01b beq.n 800c236 + __SDMMC_CMDTRANS_DISABLE( hsd->Instance); + 800c1fe: 68c3 ldr r3, [r0, #12] + 800c200: f023 0340 bic.w r3, r3, #64 ; 0x40 + 800c204: 60c3 str r3, [r0, #12] + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U)) + 800c206: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c208: 05db lsls r3, r3, #23 + 800c20a: d508 bpl.n 800c21e + 800c20c: 2f01 cmp r7, #1 + 800c20e: d906 bls.n 800c21e + if(hsd->SdCard.CardType != CARD_SECURED) + 800c210: 6be3 ldr r3, [r4, #60] ; 0x3c + 800c212: 2b03 cmp r3, #3 + 800c214: d003 beq.n 800c21e + errorstate = SDMMC_CmdStopTransfer(hsd->Instance); + 800c216: f001 f91b bl 800d450 + if(errorstate != HAL_SD_ERROR_NONE) + 800c21a: 2800 cmp r0, #0 + 800c21c: d1d7 bne.n 800c1ce + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c21e: 6823 ldr r3, [r4, #0] + 800c220: 6b58 ldr r0, [r3, #52] ; 0x34 + 800c222: f010 0008 ands.w r0, r0, #8 + 800c226: d038 beq.n 800c29a + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c228: 4a30 ldr r2, [pc, #192] ; (800c2ec ) + 800c22a: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT; + 800c22c: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c22e: f043 0308 orr.w r3, r3, #8 + 800c232: 63a3 str r3, [r4, #56] ; 0x38 + 800c234: e7d1 b.n 800c1da + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF) && (dataremaining > 0U)) + 800c236: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c238: 041a lsls r2, r3, #16 + 800c23a: d518 bpl.n 800c26e + 800c23c: b1bd cbz r5, 800c26e + 800c23e: f106 0a04 add.w sl, r6, #4 + 800c242: f106 0b24 add.w fp, r6, #36 ; 0x24 + data = SDMMC_ReadFIFO(hsd->Instance); + 800c246: 6820 ldr r0, [r4, #0] + 800c248: f000 fef0 bl 800d02c + *tempbuff = (uint8_t)((data >> 8U) & 0xFFU); + 800c24c: 0a02 lsrs r2, r0, #8 + 800c24e: f80a 2c03 strb.w r2, [sl, #-3] + *tempbuff = (uint8_t)((data >> 16U) & 0xFFU); + 800c252: 0c02 lsrs r2, r0, #16 + 800c254: f80a 2c02 strb.w r2, [sl, #-2] + *tempbuff = (uint8_t)((data >> 24U) & 0xFFU); + 800c258: 0e02 lsrs r2, r0, #24 + *tempbuff = (uint8_t)(data & 0xFFU); + 800c25a: f80a 0c04 strb.w r0, [sl, #-4] + *tempbuff = (uint8_t)((data >> 24U) & 0xFFU); + 800c25e: f80a 2c01 strb.w r2, [sl, #-1] + for(count = 0U; count < 8U; count++) + 800c262: f10a 0a04 add.w sl, sl, #4 + 800c266: 45d3 cmp fp, sl + 800c268: d1ed bne.n 800c246 + tempbuff++; + 800c26a: 3620 adds r6, #32 + dataremaining--; + 800c26c: 3d20 subs r5, #32 + if(((HAL_GetTick()-tickstart) >= Timeout) || (Timeout == 0U)) + 800c26e: f7fb f8b5 bl 80073dc + 800c272: 9b10 ldr r3, [sp, #64] ; 0x40 + 800c274: eba0 0009 sub.w r0, r0, r9 + 800c278: 4298 cmp r0, r3 + 800c27a: d3bb bcc.n 800c1f4 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c27c: 6823 ldr r3, [r4, #0] + 800c27e: 4a1b ldr r2, [pc, #108] ; (800c2ec ) + 800c280: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT; + 800c282: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c284: f043 4300 orr.w r3, r3, #2147483648 ; 0x80000000 + 800c288: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800c28a: 2301 movs r3, #1 + 800c28c: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c290: 2300 movs r3, #0 + 800c292: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800c294: f04f 0803 mov.w r8, #3 + 800c298: e76c b.n 800c174 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c29a: 6b59 ldr r1, [r3, #52] ; 0x34 + 800c29c: f011 0102 ands.w r1, r1, #2 + 800c2a0: d00a beq.n 800c2b8 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c2a2: 4a12 ldr r2, [pc, #72] ; (800c2ec ) + 800c2a4: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL; + 800c2a6: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c2a8: f043 0302 orr.w r3, r3, #2 + 800c2ac: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c2ae: 2301 movs r3, #1 + 800c2b0: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c2b4: 6320 str r0, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c2b6: e75d b.n 800c174 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800c2b8: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c2ba: f012 0220 ands.w r2, r2, #32 + 800c2be: d00a beq.n 800c2d6 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c2c0: 4a0a ldr r2, [pc, #40] ; (800c2ec ) + 800c2c2: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN; + 800c2c4: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c2c6: f043 0320 orr.w r3, r3, #32 + 800c2ca: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c2cc: 2301 movs r3, #1 + 800c2ce: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c2d2: 6321 str r1, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c2d4: e74e b.n 800c174 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c2d6: 4906 ldr r1, [pc, #24] ; (800c2f0 ) + 800c2d8: 6399 str r1, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c2da: 2301 movs r3, #1 + 800c2dc: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800c2e0: 4690 mov r8, r2 + 800c2e2: e747 b.n 800c174 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c2e4: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c2e6: f043 5300 orr.w r3, r3, #536870912 ; 0x20000000 + 800c2ea: e72d b.n 800c148 + 800c2ec: 1fe00fff .word 0x1fe00fff + 800c2f0: 18000f3a .word 0x18000f3a + +0800c2f4 : +{ + 800c2f4: e92d 4ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} + 800c2f8: b089 sub sp, #36 ; 0x24 + 800c2fa: 4604 mov r4, r0 + 800c2fc: 460d mov r5, r1 + 800c2fe: 4692 mov sl, r2 + 800c300: 461f mov r7, r3 + uint32_t tickstart = HAL_GetTick(); + 800c302: f7fb f86b bl 80073dc + 800c306: 4681 mov r9, r0 + if(NULL == pData) + 800c308: b935 cbnz r5, 800c318 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800c30a: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c30c: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c310: 63a3 str r3, [r4, #56] ; 0x38 + return HAL_ERROR; + 800c312: f04f 0801 mov.w r8, #1 + 800c316: e011 b.n 800c33c + if(hsd->State == HAL_SD_STATE_READY) + 800c318: f894 3034 ldrb.w r3, [r4, #52] ; 0x34 + 800c31c: 2b01 cmp r3, #1 + 800c31e: fa5f f883 uxtb.w r8, r3 + 800c322: f040 80b4 bne.w 800c48e + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c326: 6d62 ldr r2, [r4, #84] ; 0x54 + 800c328: eb0a 0307 add.w r3, sl, r7 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c32c: 2100 movs r1, #0 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c32e: 4293 cmp r3, r2 + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800c330: 63a1 str r1, [r4, #56] ; 0x38 + if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + 800c332: d907 bls.n 800c344 + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + 800c334: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c336: f043 7300 orr.w r3, r3, #33554432 ; 0x2000000 + 800c33a: 63a3 str r3, [r4, #56] ; 0x38 +} + 800c33c: 4640 mov r0, r8 + 800c33e: b009 add sp, #36 ; 0x24 + 800c340: e8bd 8ff0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} + hsd->State = HAL_SD_STATE_BUSY; + 800c344: 2303 movs r3, #3 + 800c346: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c34a: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->Instance->DCTRL = 0U; + 800c34c: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SDHC_SDXC) + 800c34e: 2b01 cmp r3, #1 + config.DataTimeOut = SDMMC_DATATIMEOUT; + 800c350: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + 800c354: 9302 str r3, [sp, #8] + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800c356: ea4f 2347 mov.w r3, r7, lsl #9 + hsd->Instance->DCTRL = 0U; + 800c35a: 62c1 str r1, [r0, #44] ; 0x2c + config.DataLength = NumberOfBlocks * BLOCKSIZE; + 800c35c: 9303 str r3, [sp, #12] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + 800c35e: f04f 0190 mov.w r1, #144 ; 0x90 + 800c362: f04f 0300 mov.w r3, #0 + 800c366: e9cd 1304 strd r1, r3, [sp, #16] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c36a: a902 add r1, sp, #8 + config.DPSM = SDMMC_DPSM_DISABLE; + 800c36c: e9cd 3306 strd r3, r3, [sp, #24] + add *= 512U; + 800c370: bf18 it ne + 800c372: ea4f 2a4a movne.w sl, sl, lsl #9 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c376: f000 fe95 bl 800d0a4 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c37a: 6820 ldr r0, [r4, #0] + 800c37c: 68c3 ldr r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c37e: 2f01 cmp r7, #1 + __SDMMC_CMDTRANS_ENABLE( hsd->Instance); + 800c380: f043 0340 orr.w r3, r3, #64 ; 0x40 + 800c384: 60c3 str r3, [r0, #12] + if(NumberOfBlocks > 1U) + 800c386: d911 bls.n 800c3ac + hsd->Context = SD_CONTEXT_WRITE_MULTIPLE_BLOCK; + 800c388: 2320 movs r3, #32 + 800c38a: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add); + 800c38c: 4651 mov r1, sl + 800c38e: f000 ffc9 bl 800d324 + if(errorstate != HAL_SD_ERROR_NONE) + 800c392: b188 cbz r0, 800c3b8 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c394: 6823 ldr r3, [r4, #0] + 800c396: 4a40 ldr r2, [pc, #256] ; (800c498 ) + 800c398: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c39a: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c39c: 4318 orrs r0, r3 + 800c39e: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c3a0: 2301 movs r3, #1 + 800c3a2: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c3a6: 2300 movs r3, #0 + 800c3a8: 6323 str r3, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c3aa: e7c7 b.n 800c33c + hsd->Context = SD_CONTEXT_WRITE_SINGLE_BLOCK; + 800c3ac: 2310 movs r3, #16 + 800c3ae: 6323 str r3, [r4, #48] ; 0x30 + errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, add); + 800c3b0: 4651 mov r1, sl + 800c3b2: f000 ff9e bl 800d2f2 + 800c3b6: e7ec b.n 800c392 + dataremaining = config.DataLength; + 800c3b8: 9e03 ldr r6, [sp, #12] + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800c3ba: 6820 ldr r0, [r4, #0] + 800c3bc: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c3be: f413 7f8d tst.w r3, #282 ; 0x11a + 800c3c2: d01b beq.n 800c3fc + __SDMMC_CMDTRANS_DISABLE( hsd->Instance); + 800c3c4: 68c3 ldr r3, [r0, #12] + 800c3c6: f023 0340 bic.w r3, r3, #64 ; 0x40 + 800c3ca: 60c3 str r3, [r0, #12] + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U)) + 800c3cc: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c3ce: 05db lsls r3, r3, #23 + 800c3d0: d508 bpl.n 800c3e4 + 800c3d2: 2f01 cmp r7, #1 + 800c3d4: d906 bls.n 800c3e4 + if(hsd->SdCard.CardType != CARD_SECURED) + 800c3d6: 6be3 ldr r3, [r4, #60] ; 0x3c + 800c3d8: 2b03 cmp r3, #3 + 800c3da: d003 beq.n 800c3e4 + errorstate = SDMMC_CmdStopTransfer(hsd->Instance); + 800c3dc: f001 f838 bl 800d450 + if(errorstate != HAL_SD_ERROR_NONE) + 800c3e0: 2800 cmp r0, #0 + 800c3e2: d1d7 bne.n 800c394 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c3e4: 6823 ldr r3, [r4, #0] + 800c3e6: 6b58 ldr r0, [r3, #52] ; 0x34 + 800c3e8: f010 0008 ands.w r0, r0, #8 + 800c3ec: d02a beq.n 800c444 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c3ee: 4a2a ldr r2, [pc, #168] ; (800c498 ) + 800c3f0: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT; + 800c3f2: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c3f4: f043 0308 orr.w r3, r3, #8 + 800c3f8: 63a3 str r3, [r4, #56] ; 0x38 + 800c3fa: e7d1 b.n 800c3a0 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXFIFOHE) && (dataremaining > 0U)) + 800c3fc: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c3fe: 045a lsls r2, r3, #17 + 800c400: d50c bpl.n 800c41c + 800c402: b15e cbz r6, 800c41c + 800c404: f105 0b20 add.w fp, r5, #32 + data |= ((uint32_t)(*tempbuff) << 24U); + 800c408: f855 3b04 ldr.w r3, [r5], #4 + (void)SDMMC_WriteFIFO(hsd->Instance, &data); + 800c40c: 6820 ldr r0, [r4, #0] + data |= ((uint32_t)(*tempbuff) << 24U); + 800c40e: 9301 str r3, [sp, #4] + (void)SDMMC_WriteFIFO(hsd->Instance, &data); + 800c410: a901 add r1, sp, #4 + 800c412: f000 fe0e bl 800d032 + for(count = 0U; count < 8U; count++) + 800c416: 45ab cmp fp, r5 + 800c418: d1f6 bne.n 800c408 + dataremaining--; + 800c41a: 3e20 subs r6, #32 + if(((HAL_GetTick()-tickstart) >= Timeout) || (Timeout == 0U)) + 800c41c: f7fa ffde bl 80073dc + 800c420: 9b12 ldr r3, [sp, #72] ; 0x48 + 800c422: eba0 0009 sub.w r0, r0, r9 + 800c426: 4298 cmp r0, r3 + 800c428: d3c7 bcc.n 800c3ba + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c42a: 6823 ldr r3, [r4, #0] + 800c42c: 4a1a ldr r2, [pc, #104] ; (800c498 ) + 800c42e: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c430: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c432: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c434: 2301 movs r3, #1 + 800c436: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c43a: 2300 movs r3, #0 + 800c43c: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800c43e: f04f 0803 mov.w r8, #3 + 800c442: e77b b.n 800c33c + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c444: 6b59 ldr r1, [r3, #52] ; 0x34 + 800c446: f011 0102 ands.w r1, r1, #2 + 800c44a: d00a beq.n 800c462 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c44c: 4a12 ldr r2, [pc, #72] ; (800c498 ) + 800c44e: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL; + 800c450: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c452: f043 0302 orr.w r3, r3, #2 + 800c456: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c458: 2301 movs r3, #1 + 800c45a: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c45e: 6320 str r0, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c460: e76c b.n 800c33c + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR)) + 800c462: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c464: f012 0210 ands.w r2, r2, #16 + 800c468: d00a beq.n 800c480 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c46a: 4a0b ldr r2, [pc, #44] ; (800c498 ) + 800c46c: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_TX_UNDERRUN; + 800c46e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c470: f043 0310 orr.w r3, r3, #16 + 800c474: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c476: 2301 movs r3, #1 + 800c478: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800c47c: 6321 str r1, [r4, #48] ; 0x30 + return HAL_ERROR; + 800c47e: e75d b.n 800c33c + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c480: 4906 ldr r1, [pc, #24] ; (800c49c ) + 800c482: 6399 str r1, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c484: 2301 movs r3, #1 + 800c486: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800c48a: 4690 mov r8, r2 + 800c48c: e756 b.n 800c33c + hsd->ErrorCode |= HAL_SD_ERROR_BUSY; + 800c48e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c490: f043 5300 orr.w r3, r3, #536870912 ; 0x20000000 + 800c494: e73c b.n 800c310 + 800c496: bf00 nop + 800c498: 1fe00fff .word 0x1fe00fff + 800c49c: 18000f3a .word 0x18000f3a + +0800c4a0 : + return hsd->State; + 800c4a0: f890 0034 ldrb.w r0, [r0, #52] ; 0x34 +} + 800c4a4: 4770 bx lr + +0800c4a6 : + return hsd->ErrorCode; + 800c4a6: 6b80 ldr r0, [r0, #56] ; 0x38 +} + 800c4a8: 4770 bx lr + +0800c4aa : + 800c4aa: 4770 bx lr + +0800c4ac : + 800c4ac: 4770 bx lr + +0800c4ae : + 800c4ae: 4770 bx lr + +0800c4b0 : + 800c4b0: 4770 bx lr + ... + +0800c4b4 : + pCSD->CSDStruct = (uint8_t)((hsd->CSD[0] & 0xC0000000U) >> 30U); + 800c4b4: 6e03 ldr r3, [r0, #96] ; 0x60 + 800c4b6: 0f9a lsrs r2, r3, #30 + 800c4b8: 700a strb r2, [r1, #0] + pCSD->SysSpecVersion = (uint8_t)((hsd->CSD[0] & 0x3C000000U) >> 26U); + 800c4ba: f3c3 6283 ubfx r2, r3, #26, #4 + 800c4be: 704a strb r2, [r1, #1] + pCSD->Reserved1 = (uint8_t)((hsd->CSD[0] & 0x03000000U) >> 24U); + 800c4c0: f3c3 6201 ubfx r2, r3, #24, #2 + 800c4c4: 708a strb r2, [r1, #2] + pCSD->TAAC = (uint8_t)((hsd->CSD[0] & 0x00FF0000U) >> 16U); + 800c4c6: f3c3 4207 ubfx r2, r3, #16, #8 + 800c4ca: 70ca strb r2, [r1, #3] + pCSD->NSAC = (uint8_t)((hsd->CSD[0] & 0x0000FF00U) >> 8U); + 800c4cc: f3c3 2207 ubfx r2, r3, #8, #8 + pCSD->MaxBusClkFrec = (uint8_t)(hsd->CSD[0] & 0x000000FFU); + 800c4d0: b2db uxtb r3, r3 + pCSD->NSAC = (uint8_t)((hsd->CSD[0] & 0x0000FF00U) >> 8U); + 800c4d2: 710a strb r2, [r1, #4] + pCSD->MaxBusClkFrec = (uint8_t)(hsd->CSD[0] & 0x000000FFU); + 800c4d4: 714b strb r3, [r1, #5] + pCSD->CardComdClasses = (uint16_t)((hsd->CSD[1] & 0xFFF00000U) >> 20U); + 800c4d6: 6e43 ldr r3, [r0, #100] ; 0x64 + 800c4d8: 0d1a lsrs r2, r3, #20 + 800c4da: 80ca strh r2, [r1, #6] + pCSD->RdBlockLen = (uint8_t)((hsd->CSD[1] & 0x000F0000U) >> 16U); + 800c4dc: f3c3 4203 ubfx r2, r3, #16, #4 + 800c4e0: 720a strb r2, [r1, #8] + pCSD->PartBlockRead = (uint8_t)((hsd->CSD[1] & 0x00008000U) >> 15U); + 800c4e2: f3c3 32c0 ubfx r2, r3, #15, #1 + 800c4e6: 724a strb r2, [r1, #9] + pCSD->WrBlockMisalign = (uint8_t)((hsd->CSD[1] & 0x00004000U) >> 14U); + 800c4e8: f3c3 3280 ubfx r2, r3, #14, #1 + 800c4ec: 728a strb r2, [r1, #10] + pCSD->RdBlockMisalign = (uint8_t)((hsd->CSD[1] & 0x00002000U) >> 13U); + 800c4ee: f3c3 3240 ubfx r2, r3, #13, #1 + 800c4f2: 72ca strb r2, [r1, #11] + pCSD->DSRImpl = (uint8_t)((hsd->CSD[1] & 0x00001000U) >> 12U); + 800c4f4: f3c3 3200 ubfx r2, r3, #12, #1 + 800c4f8: 730a strb r2, [r1, #12] + pCSD->Reserved2 = 0U; /*!< Reserved */ + 800c4fa: 2200 movs r2, #0 + 800c4fc: 734a strb r2, [r1, #13] + if(hsd->SdCard.CardType == CARD_SDSC) + 800c4fe: 6bc2 ldr r2, [r0, #60] ; 0x3c +{ + 800c500: b510 push {r4, lr} + if(hsd->SdCard.CardType == CARD_SDSC) + 800c502: 2a00 cmp r2, #0 + 800c504: d16c bne.n 800c5e0 + pCSD->DeviceSize = (((hsd->CSD[1] & 0x000003FFU) << 2U) | ((hsd->CSD[2] & 0xC0000000U) >> 30U)); + 800c506: 6e82 ldr r2, [r0, #104] ; 0x68 + 800c508: f640 74fc movw r4, #4092 ; 0xffc + 800c50c: ea04 0383 and.w r3, r4, r3, lsl #2 + 800c510: ea43 7392 orr.w r3, r3, r2, lsr #30 + 800c514: 610b str r3, [r1, #16] + pCSD->MaxRdCurrentVDDMin = (uint8_t)((hsd->CSD[2] & 0x38000000U) >> 27U); + 800c516: f3c2 63c2 ubfx r3, r2, #27, #3 + 800c51a: 750b strb r3, [r1, #20] + pCSD->MaxRdCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x07000000U) >> 24U); + 800c51c: f3c2 6302 ubfx r3, r2, #24, #3 + 800c520: 754b strb r3, [r1, #21] + pCSD->MaxWrCurrentVDDMin = (uint8_t)((hsd->CSD[2] & 0x00E00000U) >> 21U); + 800c522: f3c2 5342 ubfx r3, r2, #21, #3 + 800c526: 758b strb r3, [r1, #22] + pCSD->MaxWrCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x001C0000U) >> 18U); + 800c528: f3c2 4382 ubfx r3, r2, #18, #3 + pCSD->DeviceSizeMul = (uint8_t)((hsd->CSD[2] & 0x00038000U) >> 15U); + 800c52c: f3c2 32c2 ubfx r2, r2, #15, #3 + pCSD->MaxWrCurrentVDDMax = (uint8_t)((hsd->CSD[2] & 0x001C0000U) >> 18U); + 800c530: 75cb strb r3, [r1, #23] + pCSD->DeviceSizeMul = (uint8_t)((hsd->CSD[2] & 0x00038000U) >> 15U); + 800c532: 760a strb r2, [r1, #24] + hsd->SdCard.BlockNbr = (pCSD->DeviceSize + 1U) ; + 800c534: 690b ldr r3, [r1, #16] + hsd->SdCard.BlockNbr *= (1UL << ((pCSD->DeviceSizeMul & 0x07U) + 2U)); + 800c536: 7e0a ldrb r2, [r1, #24] + 800c538: f002 0207 and.w r2, r2, #7 + hsd->SdCard.BlockNbr = (pCSD->DeviceSize + 1U) ; + 800c53c: 3301 adds r3, #1 + hsd->SdCard.BlockNbr *= (1UL << ((pCSD->DeviceSizeMul & 0x07U) + 2U)); + 800c53e: 3202 adds r2, #2 + 800c540: fa03 f202 lsl.w r2, r3, r2 + 800c544: 64c2 str r2, [r0, #76] ; 0x4c + hsd->SdCard.BlockSize = (1UL << (pCSD->RdBlockLen & 0x0FU)); + 800c546: 7a0b ldrb r3, [r1, #8] + 800c548: f003 040f and.w r4, r3, #15 + 800c54c: 2301 movs r3, #1 + 800c54e: 40a3 lsls r3, r4 + 800c550: 6503 str r3, [r0, #80] ; 0x50 + hsd->SdCard.LogBlockNbr = (hsd->SdCard.BlockNbr) * ((hsd->SdCard.BlockSize) / 512U); + 800c552: 0a5b lsrs r3, r3, #9 + 800c554: 4353 muls r3, r2 + 800c556: 6543 str r3, [r0, #84] ; 0x54 + hsd->SdCard.LogBlockSize = 512U; + 800c558: f44f 7300 mov.w r3, #512 ; 0x200 + hsd->SdCard.LogBlockSize = hsd->SdCard.BlockSize; + 800c55c: 6583 str r3, [r0, #88] ; 0x58 + pCSD->EraseGrSize = (uint8_t)((hsd->CSD[2] & 0x00004000U) >> 14U); + 800c55e: 6e83 ldr r3, [r0, #104] ; 0x68 + 800c560: f3c3 3280 ubfx r2, r3, #14, #1 + 800c564: 764a strb r2, [r1, #25] + pCSD->EraseGrMul = (uint8_t)((hsd->CSD[2] & 0x00003F80U) >> 7U); + 800c566: f3c3 12c6 ubfx r2, r3, #7, #7 + pCSD->WrProtectGrSize = (uint8_t)(hsd->CSD[2] & 0x0000007FU); + 800c56a: f003 037f and.w r3, r3, #127 ; 0x7f + pCSD->EraseGrMul = (uint8_t)((hsd->CSD[2] & 0x00003F80U) >> 7U); + 800c56e: 768a strb r2, [r1, #26] + pCSD->WrProtectGrSize = (uint8_t)(hsd->CSD[2] & 0x0000007FU); + 800c570: 76cb strb r3, [r1, #27] + pCSD->WrProtectGrEnable = (uint8_t)((hsd->CSD[3] & 0x80000000U) >> 31U); + 800c572: 6ec3 ldr r3, [r0, #108] ; 0x6c + 800c574: 0fda lsrs r2, r3, #31 + 800c576: 770a strb r2, [r1, #28] + pCSD->ManDeflECC = (uint8_t)((hsd->CSD[3] & 0x60000000U) >> 29U); + 800c578: f3c3 7241 ubfx r2, r3, #29, #2 + 800c57c: 774a strb r2, [r1, #29] + pCSD->WrSpeedFact = (uint8_t)((hsd->CSD[3] & 0x1C000000U) >> 26U); + 800c57e: f3c3 6282 ubfx r2, r3, #26, #3 + 800c582: 778a strb r2, [r1, #30] + pCSD->MaxWrBlockLen= (uint8_t)((hsd->CSD[3] & 0x03C00000U) >> 22U); + 800c584: f3c3 5283 ubfx r2, r3, #22, #4 + 800c588: 77ca strb r2, [r1, #31] + pCSD->WriteBlockPaPartial = (uint8_t)((hsd->CSD[3] & 0x00200000U) >> 21U); + 800c58a: f3c3 5240 ubfx r2, r3, #21, #1 + 800c58e: f881 2020 strb.w r2, [r1, #32] + pCSD->Reserved3 = 0; + 800c592: 2000 movs r0, #0 + pCSD->ContentProtectAppli = (uint8_t)((hsd->CSD[3] & 0x00010000U) >> 16U); + 800c594: f3c3 4200 ubfx r2, r3, #16, #1 + pCSD->Reserved3 = 0; + 800c598: f881 0021 strb.w r0, [r1, #33] ; 0x21 + pCSD->ContentProtectAppli = (uint8_t)((hsd->CSD[3] & 0x00010000U) >> 16U); + 800c59c: f881 2022 strb.w r2, [r1, #34] ; 0x22 + pCSD->FileFormatGroup = (uint8_t)((hsd->CSD[3] & 0x00008000U) >> 15U); + 800c5a0: f3c3 32c0 ubfx r2, r3, #15, #1 + 800c5a4: f881 2023 strb.w r2, [r1, #35] ; 0x23 + pCSD->CopyFlag = (uint8_t)((hsd->CSD[3] & 0x00004000U) >> 14U); + 800c5a8: f3c3 3280 ubfx r2, r3, #14, #1 + 800c5ac: f881 2024 strb.w r2, [r1, #36] ; 0x24 + pCSD->PermWrProtect = (uint8_t)((hsd->CSD[3] & 0x00002000U) >> 13U); + 800c5b0: f3c3 3240 ubfx r2, r3, #13, #1 + 800c5b4: f881 2025 strb.w r2, [r1, #37] ; 0x25 + pCSD->TempWrProtect = (uint8_t)((hsd->CSD[3] & 0x00001000U) >> 12U); + 800c5b8: f3c3 3200 ubfx r2, r3, #12, #1 + 800c5bc: f881 2026 strb.w r2, [r1, #38] ; 0x26 + pCSD->FileFormat = (uint8_t)((hsd->CSD[3] & 0x00000C00U) >> 10U); + 800c5c0: f3c3 2281 ubfx r2, r3, #10, #2 + 800c5c4: f881 2027 strb.w r2, [r1, #39] ; 0x27 + pCSD->ECC= (uint8_t)((hsd->CSD[3] & 0x00000300U) >> 8U); + 800c5c8: f3c3 2201 ubfx r2, r3, #8, #2 + pCSD->CSD_CRC = (uint8_t)((hsd->CSD[3] & 0x000000FEU) >> 1U); + 800c5cc: f3c3 0346 ubfx r3, r3, #1, #7 + pCSD->ECC= (uint8_t)((hsd->CSD[3] & 0x00000300U) >> 8U); + 800c5d0: f881 2028 strb.w r2, [r1, #40] ; 0x28 + pCSD->CSD_CRC = (uint8_t)((hsd->CSD[3] & 0x000000FEU) >> 1U); + 800c5d4: f881 3029 strb.w r3, [r1, #41] ; 0x29 + pCSD->Reserved4 = 1; + 800c5d8: 2301 movs r3, #1 + 800c5da: f881 302a strb.w r3, [r1, #42] ; 0x2a +} + 800c5de: bd10 pop {r4, pc} + else if(hsd->SdCard.CardType == CARD_SDHC_SDXC) + 800c5e0: 2a01 cmp r2, #1 + 800c5e2: d10f bne.n 800c604 + pCSD->DeviceSize = (((hsd->CSD[1] & 0x0000003FU) << 16U) | ((hsd->CSD[2] & 0xFFFF0000U) >> 16U)); + 800c5e4: f8b0 206a ldrh.w r2, [r0, #106] ; 0x6a + 800c5e8: 041b lsls r3, r3, #16 + 800c5ea: f403 137c and.w r3, r3, #4128768 ; 0x3f0000 + 800c5ee: 4313 orrs r3, r2 + 800c5f0: 610b str r3, [r1, #16] + hsd->SdCard.BlockNbr = ((pCSD->DeviceSize + 1U) * 1024U); + 800c5f2: 690b ldr r3, [r1, #16] + 800c5f4: 3301 adds r3, #1 + 800c5f6: 029b lsls r3, r3, #10 + 800c5f8: 64c3 str r3, [r0, #76] ; 0x4c + hsd->SdCard.LogBlockNbr = hsd->SdCard.BlockNbr; + 800c5fa: 6543 str r3, [r0, #84] ; 0x54 + hsd->SdCard.BlockSize = 512U; + 800c5fc: f44f 7300 mov.w r3, #512 ; 0x200 + 800c600: 6503 str r3, [r0, #80] ; 0x50 + 800c602: e7ab b.n 800c55c + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c604: 6803 ldr r3, [r0, #0] + 800c606: 4a05 ldr r2, [pc, #20] ; (800c61c ) + 800c608: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c60a: 6b83 ldr r3, [r0, #56] ; 0x38 + 800c60c: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800c610: 6383 str r3, [r0, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c612: 2301 movs r3, #1 + 800c614: f880 3034 strb.w r3, [r0, #52] ; 0x34 + return HAL_ERROR; + 800c618: 4618 mov r0, r3 + 800c61a: e7e0 b.n 800c5de + 800c61c: 1fe00fff .word 0x1fe00fff + +0800c620 : +{ + 800c620: e92d 43f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, lr} + Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + 800c624: 2300 movs r3, #0 +{ + 800c626: b099 sub sp, #100 ; 0x64 + 800c628: 4604 mov r4, r0 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800c62a: f44f 2000 mov.w r0, #524288 ; 0x80000 + Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + 800c62e: e9cd 3307 strd r3, r3, [sp, #28] + Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + 800c632: e9cd 3309 strd r3, r3, [sp, #36] ; 0x24 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800c636: f7fd fb2d bl 8009c94 + if (sdmmc_clk == 0U) + 800c63a: 4605 mov r5, r0 + 800c63c: b948 cbnz r0, 800c652 + hsd->State = HAL_SD_STATE_READY; + 800c63e: 2501 movs r5, #1 + hsd->ErrorCode = SDMMC_ERROR_INVALID_PARAMETER; + 800c640: f04f 6300 mov.w r3, #134217728 ; 0x8000000 + hsd->State = HAL_SD_STATE_READY; + 800c644: f884 5034 strb.w r5, [r4, #52] ; 0x34 + hsd->ErrorCode = SDMMC_ERROR_INVALID_PARAMETER; + 800c648: 63a3 str r3, [r4, #56] ; 0x38 +} + 800c64a: 4628 mov r0, r5 + 800c64c: b019 add sp, #100 ; 0x64 + 800c64e: e8bd 83f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, pc} + Init.Transceiver = hsd->Init.Transceiver; + 800c652: 69a3 ldr r3, [r4, #24] + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c654: 6827 ldr r7, [r4, #0] + Init.Transceiver = hsd->Init.Transceiver; + 800c656: 930c str r3, [sp, #48] ; 0x30 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c658: 2b01 cmp r3, #1 + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c65a: bf08 it eq + 800c65c: 683b ldreq r3, [r7, #0] + Init.ClockDiv = sdmmc_clk / (2U * SD_INIT_FREQ); + 800c65e: 4e99 ldr r6, [pc, #612] ; (800c8c4 ) + 800c660: fbb0 f6f6 udiv r6, r0, r6 + hsd->Instance->POWER |= SDMMC_POWER_DIRPOL; + 800c664: bf04 itt eq + 800c666: f043 0310 orreq.w r3, r3, #16 + 800c66a: 603b streq r3, [r7, #0] + status = SDMMC_Init(hsd->Instance, Init); + 800c66c: 960b str r6, [sp, #44] ; 0x2c + 800c66e: ab0a add r3, sp, #40 ; 0x28 + 800c670: e893 0007 ldmia.w r3, {r0, r1, r2} + 800c674: e88d 0007 stmia.w sp, {r0, r1, r2} + 800c678: ab07 add r3, sp, #28 + 800c67a: cb0e ldmia r3, {r1, r2, r3} + 800c67c: 4638 mov r0, r7 + 800c67e: f000 fcbb bl 800cff8 + if(status != HAL_OK) + 800c682: b108 cbz r0, 800c688 + return HAL_ERROR; + 800c684: 2501 movs r5, #1 + 800c686: e7e0 b.n 800c64a + status = SDMMC_PowerState_ON(hsd->Instance); + 800c688: 6820 ldr r0, [r4, #0] + 800c68a: f000 fcd7 bl 800d03c + if(status != HAL_OK) + 800c68e: 4607 mov r7, r0 + 800c690: 2800 cmp r0, #0 + 800c692: d1f7 bne.n 800c684 + sdmmc_clk = sdmmc_clk/(2U*Init.ClockDiv); + 800c694: 0076 lsls r6, r6, #1 + HAL_Delay(1U+ (74U*1000U/(sdmmc_clk))); + 800c696: 488c ldr r0, [pc, #560] ; (800c8c8 ) + sdmmc_clk = sdmmc_clk/(2U*Init.ClockDiv); + 800c698: fbb5 f5f6 udiv r5, r5, r6 + HAL_Delay(1U+ (74U*1000U/(sdmmc_clk))); + 800c69c: fbb0 f0f5 udiv r0, r0, r5 + 800c6a0: 3001 adds r0, #1 + 800c6a2: f7f7 f9fc bl 8003a9e + __IO uint32_t count = 0U; + 800c6a6: 9706 str r7, [sp, #24] + uint32_t tickstart = HAL_GetTick(); + 800c6a8: f7fa fe98 bl 80073dc + 800c6ac: 4606 mov r6, r0 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c6ae: 6820 ldr r0, [r4, #0] + 800c6b0: f000 fd18 bl 800d0e4 + if(errorstate != HAL_SD_ERROR_NONE) + 800c6b4: 4605 mov r5, r0 + 800c6b6: b940 cbnz r0, 800c6ca + errorstate = SDMMC_CmdOperCond(hsd->Instance); + 800c6b8: 6820 ldr r0, [r4, #0] + 800c6ba: f001 f8fd bl 800d8b8 + if(errorstate != HAL_SD_ERROR_NONE) + 800c6be: b158 cbz r0, 800c6d8 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c6c0: 6820 ldr r0, [r4, #0] + hsd->SdCard.CardVersion = CARD_V1_X; + 800c6c2: 6425 str r5, [r4, #64] ; 0x40 + errorstate = SDMMC_CmdGoIdleState(hsd->Instance); + 800c6c4: f000 fd0e bl 800d0e4 + if(errorstate != HAL_SD_ERROR_NONE) + 800c6c8: b180 cbz r0, 800c6ec + hsd->State = HAL_SD_STATE_READY; + 800c6ca: 2501 movs r5, #1 + 800c6cc: f884 5034 strb.w r5, [r4, #52] ; 0x34 + hsd->ErrorCode |= errorstate; + 800c6d0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c6d2: 4318 orrs r0, r3 + 800c6d4: 63a0 str r0, [r4, #56] ; 0x38 + return HAL_ERROR; + 800c6d6: e7b8 b.n 800c64a + hsd->SdCard.CardVersion = CARD_V2_X; + 800c6d8: 2301 movs r3, #1 + 800c6da: 6423 str r3, [r4, #64] ; 0x40 + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c6dc: 6820 ldr r0, [r4, #0] + 800c6de: 2100 movs r1, #0 + 800c6e0: f000 fef5 bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800c6e4: b128 cbz r0, 800c6f2 + return HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800c6e6: f04f 5080 mov.w r0, #268435456 ; 0x10000000 + 800c6ea: e7ee b.n 800c6ca + if( hsd->SdCard.CardVersion == CARD_V2_X) + 800c6ec: 6c23 ldr r3, [r4, #64] ; 0x40 + 800c6ee: 2b01 cmp r3, #1 + 800c6f0: d0f4 beq.n 800c6dc + errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_VOLTAGE_WINDOW_SD | SDMMC_HIGH_CAPACITY | SD_SWITCH_1_8V_CAPACITY); + 800c6f2: f8df 91dc ldr.w r9, [pc, #476] ; 800c8d0 +{ + 800c6f6: 2700 movs r7, #0 + while((count < SDMMC_MAX_VOLT_TRIAL) && (validvoltage == 0U)) + 800c6f8: f64f 78fe movw r8, #65534 ; 0xfffe + 800c6fc: 9b06 ldr r3, [sp, #24] + 800c6fe: 4543 cmp r3, r8 + 800c700: d800 bhi.n 800c704 + 800c702: b12f cbz r7, 800c710 + if(count >= SDMMC_MAX_VOLT_TRIAL) + 800c704: 9b06 ldr r3, [sp, #24] + 800c706: 4543 cmp r3, r8 + 800c708: d918 bls.n 800c73c + return HAL_SD_ERROR_INVALID_VOLTRANGE; + 800c70a: f04f 7080 mov.w r0, #16777216 ; 0x1000000 + 800c70e: e7dc b.n 800c6ca + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c710: 6820 ldr r0, [r4, #0] + 800c712: 4639 mov r1, r7 + 800c714: f000 fedb bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800c718: 2800 cmp r0, #0 + 800c71a: d1d6 bne.n 800c6ca + errorstate = SDMMC_CmdAppOperCommand(hsd->Instance, SDMMC_VOLTAGE_WINDOW_SD | SDMMC_HIGH_CAPACITY | SD_SWITCH_1_8V_CAPACITY); + 800c71c: 6820 ldr r0, [r4, #0] + 800c71e: 4649 mov r1, r9 + 800c720: f001 f816 bl 800d750 + if(errorstate != HAL_SD_ERROR_NONE) + 800c724: 2800 cmp r0, #0 + 800c726: d1de bne.n 800c6e6 + response = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c728: 4639 mov r1, r7 + 800c72a: 6820 ldr r0, [r4, #0] + 800c72c: f000 fcb7 bl 800d09e + count++; + 800c730: 9b06 ldr r3, [sp, #24] + 800c732: 3301 adds r3, #1 + response = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c734: 4605 mov r5, r0 + validvoltage = (((response >> 31U) == 1U) ? 1U : 0U); + 800c736: 0fc7 lsrs r7, r0, #31 + count++; + 800c738: 9306 str r3, [sp, #24] + 800c73a: e7df b.n 800c6fc + if((response & SDMMC_HIGH_CAPACITY) == SDMMC_HIGH_CAPACITY) /* (response &= SD_HIGH_CAPACITY) */ + 800c73c: f015 4380 ands.w r3, r5, #1073741824 ; 0x40000000 + 800c740: d04b beq.n 800c7da + hsd->SdCard.CardType = CARD_SDHC_SDXC; + 800c742: 2301 movs r3, #1 + 800c744: 63e3 str r3, [r4, #60] ; 0x3c + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c746: 69a3 ldr r3, [r4, #24] + errorstate = SDMMC_CmdAppCommand(hsd->Instance, 0); + 800c748: 6820 ldr r0, [r4, #0] + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800c74a: 2b01 cmp r3, #1 + 800c74c: d12d bne.n 800c7aa + if((response & SD_SWITCH_1_8V_CAPACITY) == SD_SWITCH_1_8V_CAPACITY) + 800c74e: 01ef lsls r7, r5, #7 + 800c750: d52b bpl.n 800c7aa + hsd->SdCard.CardSpeed = CARD_ULTRA_HIGH_SPEED; + 800c752: f44f 7300 mov.w r3, #512 ; 0x200 + 800c756: 65e3 str r3, [r4, #92] ; 0x5c + hsd->Instance->POWER |= SDMMC_POWER_VSWITCHEN; + 800c758: 6803 ldr r3, [r0, #0] + 800c75a: f043 0308 orr.w r3, r3, #8 + 800c75e: 6003 str r3, [r0, #0] + errorstate = SDMMC_CmdVoltageSwitch(hsd->Instance); + 800c760: f000 ff4e bl 800d600 + if(errorstate != HAL_SD_ERROR_NONE) + 800c764: 2800 cmp r0, #0 + 800c766: d1b0 bne.n 800c6ca + while(( hsd->Instance->STA & SDMMC_FLAG_CKSTOP) != SDMMC_FLAG_CKSTOP) + 800c768: 6823 ldr r3, [r4, #0] + 800c76a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c76c: 0155 lsls r5, r2, #5 + 800c76e: d526 bpl.n 800c7be + hsd->Instance->ICR = SDMMC_FLAG_CKSTOP; + 800c770: f04f 6280 mov.w r2, #67108864 ; 0x4000000 + 800c774: 639a str r2, [r3, #56] ; 0x38 + if(( hsd->Instance->STA & SDMMC_FLAG_BUSYD0) != SDMMC_FLAG_BUSYD0) + 800c776: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c778: 02d8 lsls r0, r3, #11 + 800c77a: d5b4 bpl.n 800c6e6 + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800c77c: 2001 movs r0, #1 + 800c77e: f7fa fe9d bl 80074bc + hsd->Instance->POWER |= SDMMC_POWER_VSWITCH; + 800c782: 6822 ldr r2, [r4, #0] + 800c784: 6813 ldr r3, [r2, #0] + 800c786: f043 0304 orr.w r3, r3, #4 + 800c78a: 6013 str r3, [r2, #0] + while(( hsd->Instance->STA & SDMMC_FLAG_VSWEND) != SDMMC_FLAG_VSWEND) + 800c78c: 6823 ldr r3, [r4, #0] + 800c78e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c790: 0191 lsls r1, r2, #6 + 800c792: d51c bpl.n 800c7ce + hsd->Instance->ICR = SDMMC_FLAG_VSWEND; + 800c794: f04f 7200 mov.w r2, #33554432 ; 0x2000000 + 800c798: 639a str r2, [r3, #56] ; 0x38 + if(( hsd->Instance->STA & SDMMC_FLAG_BUSYD0) == SDMMC_FLAG_BUSYD0) + 800c79a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c79c: 02d2 lsls r2, r2, #11 + 800c79e: d4b4 bmi.n 800c70a + hsd->Instance->POWER = 0x13U; + 800c7a0: 2213 movs r2, #19 + 800c7a2: 601a str r2, [r3, #0] + hsd->Instance->ICR = 0xFFFFFFFFU; + 800c7a4: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff + 800c7a8: 639a str r2, [r3, #56] ; 0x38 + uint16_t sd_rca = 1U; + 800c7aa: 2301 movs r3, #1 + if(SDMMC_GetPowerState(hsd->Instance) == 0U) + 800c7ac: 6820 ldr r0, [r4, #0] + uint16_t sd_rca = 1U; + 800c7ae: f8ad 3016 strh.w r3, [sp, #22] + if(SDMMC_GetPowerState(hsd->Instance) == 0U) + 800c7b2: f000 fc59 bl 800d068 + 800c7b6: b990 cbnz r0, 800c7de + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800c7b8: f04f 6080 mov.w r0, #67108864 ; 0x4000000 + 800c7bc: e785 b.n 800c6ca + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c7be: f7fa fe0d bl 80073dc + 800c7c2: 1b80 subs r0, r0, r6 + 800c7c4: 3001 adds r0, #1 + 800c7c6: d1cf bne.n 800c768 + return HAL_SD_ERROR_TIMEOUT; + 800c7c8: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800c7cc: e77d b.n 800c6ca + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c7ce: f7fa fe05 bl 80073dc + 800c7d2: 1b80 subs r0, r0, r6 + 800c7d4: 3001 adds r0, #1 + 800c7d6: d1d9 bne.n 800c78c + 800c7d8: e7f6 b.n 800c7c8 + hsd->SdCard.CardType = CARD_SDSC; + 800c7da: 63e3 str r3, [r4, #60] ; 0x3c + if(errorstate != HAL_SD_ERROR_NONE) + 800c7dc: e7e5 b.n 800c7aa + if(hsd->SdCard.CardType != CARD_SECURED) + 800c7de: 6be3 ldr r3, [r4, #60] ; 0x3c + 800c7e0: 2b03 cmp r3, #3 + 800c7e2: d045 beq.n 800c870 + errorstate = SDMMC_CmdSendCID(hsd->Instance); + 800c7e4: 6820 ldr r0, [r4, #0] + 800c7e6: f000 ff65 bl 800d6b4 + if(errorstate != HAL_SD_ERROR_NONE) + 800c7ea: 2800 cmp r0, #0 + 800c7ec: f47f af6d bne.w 800c6ca + hsd->CID[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c7f0: 4601 mov r1, r0 + 800c7f2: 6820 ldr r0, [r4, #0] + 800c7f4: f000 fc53 bl 800d09e + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c7f8: 2104 movs r1, #4 + hsd->CID[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c7fa: 6720 str r0, [r4, #112] ; 0x70 + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c7fc: 6820 ldr r0, [r4, #0] + 800c7fe: f000 fc4e bl 800d09e + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c802: 2108 movs r1, #8 + hsd->CID[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c804: 6760 str r0, [r4, #116] ; 0x74 + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c806: 6820 ldr r0, [r4, #0] + 800c808: f000 fc49 bl 800d09e + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c80c: 210c movs r1, #12 + hsd->CID[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c80e: 67a0 str r0, [r4, #120] ; 0x78 + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c810: 6820 ldr r0, [r4, #0] + 800c812: f000 fc44 bl 800d09e + if(hsd->SdCard.CardType != CARD_SECURED) + 800c816: 6be3 ldr r3, [r4, #60] ; 0x3c + hsd->CID[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c818: 67e0 str r0, [r4, #124] ; 0x7c + if(hsd->SdCard.CardType != CARD_SECURED) + 800c81a: 2b03 cmp r3, #3 + 800c81c: d028 beq.n 800c870 + errorstate = SDMMC_CmdSetRelAdd(hsd->Instance, &sd_rca); + 800c81e: 6820 ldr r0, [r4, #0] + 800c820: f10d 0116 add.w r1, sp, #22 + 800c824: f001 f804 bl 800d830 + if(errorstate != HAL_SD_ERROR_NONE) + 800c828: 2800 cmp r0, #0 + 800c82a: f47f af4e bne.w 800c6ca + if(hsd->SdCard.CardType != CARD_SECURED) + 800c82e: 6be3 ldr r3, [r4, #60] ; 0x3c + errorstate = SDMMC_CmdSendCSD(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c830: 6820 ldr r0, [r4, #0] + if(hsd->SdCard.CardType != CARD_SECURED) + 800c832: 2b03 cmp r3, #3 + 800c834: d01c beq.n 800c870 + hsd->SdCard.RelCardAdd = sd_rca; + 800c836: f8bd 1016 ldrh.w r1, [sp, #22] + 800c83a: 64a1 str r1, [r4, #72] ; 0x48 + errorstate = SDMMC_CmdSendCSD(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c83c: 0409 lsls r1, r1, #16 + 800c83e: f000 ff4f bl 800d6e0 + if(errorstate != HAL_SD_ERROR_NONE) + 800c842: 2800 cmp r0, #0 + 800c844: f47f af41 bne.w 800c6ca + hsd->CSD[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c848: 4601 mov r1, r0 + 800c84a: 6820 ldr r0, [r4, #0] + 800c84c: f000 fc27 bl 800d09e + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c850: 2104 movs r1, #4 + hsd->CSD[0U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800c852: 6620 str r0, [r4, #96] ; 0x60 + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c854: 6820 ldr r0, [r4, #0] + 800c856: f000 fc22 bl 800d09e + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c85a: 2108 movs r1, #8 + hsd->CSD[1U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2); + 800c85c: 6660 str r0, [r4, #100] ; 0x64 + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c85e: 6820 ldr r0, [r4, #0] + 800c860: f000 fc1d bl 800d09e + hsd->CSD[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c864: 210c movs r1, #12 + hsd->CSD[2U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP3); + 800c866: 66a0 str r0, [r4, #104] ; 0x68 + hsd->CSD[3U] = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP4); + 800c868: 6820 ldr r0, [r4, #0] + 800c86a: f000 fc18 bl 800d09e + 800c86e: 66e0 str r0, [r4, #108] ; 0x6c + hsd->SdCard.Class = (SDMMC_GetResponse(hsd->Instance, SDMMC_RESP2) >> 20U); + 800c870: 2104 movs r1, #4 + 800c872: 6820 ldr r0, [r4, #0] + 800c874: f000 fc13 bl 800d09e + 800c878: 0d00 lsrs r0, r0, #20 + 800c87a: 6460 str r0, [r4, #68] ; 0x44 + if (HAL_SD_GetCardCSD(hsd, &CSD) != HAL_OK) + 800c87c: a90d add r1, sp, #52 ; 0x34 + 800c87e: 4620 mov r0, r4 + 800c880: f7ff fe18 bl 800c4b4 + 800c884: 4605 mov r5, r0 + 800c886: 2800 cmp r0, #0 + 800c888: f47f af2d bne.w 800c6e6 + errorstate = SDMMC_CmdSelDesel(hsd->Instance, (uint32_t)(((uint32_t)hsd->SdCard.RelCardAdd) << 16U)); + 800c88c: 6ca2 ldr r2, [r4, #72] ; 0x48 + 800c88e: 4603 mov r3, r0 + 800c890: 0412 lsls r2, r2, #16 + 800c892: 6820 ldr r0, [r4, #0] + 800c894: f000 fe02 bl 800d49c + if(errorstate != HAL_SD_ERROR_NONE) + 800c898: 2800 cmp r0, #0 + 800c89a: f47f af16 bne.w 800c6ca + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800c89e: 6820 ldr r0, [r4, #0] + 800c8a0: f44f 7100 mov.w r1, #512 ; 0x200 + 800c8a4: f000 fcda bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800c8a8: 2800 cmp r0, #0 + 800c8aa: f43f aece beq.w 800c64a + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c8ae: 6823 ldr r3, [r4, #0] + 800c8b0: 4a06 ldr r2, [pc, #24] ; (800c8cc ) + 800c8b2: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c8b4: 6ba3 ldr r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c8b6: 2501 movs r5, #1 + hsd->ErrorCode |= errorstate; + 800c8b8: 4318 orrs r0, r3 + 800c8ba: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c8bc: f884 5034 strb.w r5, [r4, #52] ; 0x34 + return HAL_ERROR; + 800c8c0: e6c3 b.n 800c64a + 800c8c2: bf00 nop + 800c8c4: 000c3500 .word 0x000c3500 + 800c8c8: 00012110 .word 0x00012110 + 800c8cc: 1fe00fff .word 0x1fe00fff + 800c8d0: c1100000 .word 0xc1100000 + +0800c8d4 : +{ + 800c8d4: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} + 800c8d8: b096 sub sp, #88 ; 0x58 + 800c8da: 4604 mov r4, r0 + 800c8dc: 460d mov r5, r1 + uint32_t tickstart = HAL_GetTick(); + 800c8de: f7fa fd7d bl 80073dc + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c8e2: 2100 movs r1, #0 + uint32_t tickstart = HAL_GetTick(); + 800c8e4: 4606 mov r6, r0 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800c8e6: 6820 ldr r0, [r4, #0] + 800c8e8: f000 fbd9 bl 800d09e + 800c8ec: 0183 lsls r3, r0, #6 + 800c8ee: d50b bpl.n 800c908 + return HAL_SD_ERROR_LOCK_UNLOCK_FAILED; + 800c8f0: f44f 6000 mov.w r0, #2048 ; 0x800 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800c8f4: 6823 ldr r3, [r4, #0] + 800c8f6: 4a54 ldr r2, [pc, #336] ; (800ca48 ) + 800c8f8: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800c8fa: 6ba3 ldr r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c8fc: 2501 movs r5, #1 + hsd->ErrorCode |= errorstate; + 800c8fe: 4318 orrs r0, r3 + 800c900: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800c902: f884 5034 strb.w r5, [r4, #52] ; 0x34 + status = HAL_ERROR; + 800c906: e08a b.n 800ca1e + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800c908: 6820 ldr r0, [r4, #0] + 800c90a: 2140 movs r1, #64 ; 0x40 + 800c90c: f000 fca6 bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800c910: b110 cbz r0, 800c918 + hsd->ErrorCode |= HAL_SD_ERROR_NONE; + 800c912: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800c914: 63a3 str r3, [r4, #56] ; 0x38 + return errorstate; + 800c916: e7ed b.n 800c8f4 + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800c918: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800c91a: 6820 ldr r0, [r4, #0] + 800c91c: 0409 lsls r1, r1, #16 + 800c91e: f000 fdd6 bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800c922: 2800 cmp r0, #0 + 800c924: d1f5 bne.n 800c912 + config.DataLength = 64U; + 800c926: 2340 movs r3, #64 ; 0x40 + 800c928: f04f 37ff mov.w r7, #4294967295 ; 0xffffffff + 800c92c: e9cd 7300 strd r7, r3, [sp] + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800c930: f04f 0c60 mov.w ip, #96 ; 0x60 + 800c934: 2302 movs r3, #2 + 800c936: e9cd c302 strd ip, r3, [sp, #8] + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800c93a: 9004 str r0, [sp, #16] + config.DPSM = SDMMC_DPSM_ENABLE; + 800c93c: 2301 movs r3, #1 + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c93e: 6820 ldr r0, [r4, #0] + config.DPSM = SDMMC_DPSM_ENABLE; + 800c940: 9305 str r3, [sp, #20] + (void)SDMMC_ConfigData(hsd->Instance, &config); + 800c942: 4669 mov r1, sp + 800c944: f000 fbae bl 800d0a4 + errorstate = SDMMC_CmdStatusRegister(hsd->Instance); + 800c948: 6820 ldr r0, [r4, #0] + 800c94a: f000 fe40 bl 800d5ce + if(errorstate != HAL_SD_ERROR_NONE) + 800c94e: 2800 cmp r0, #0 + 800c950: d1df bne.n 800c912 + uint32_t *pData = pSDstatus; + 800c952: af06 add r7, sp, #24 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND)) + 800c954: 6823 ldr r3, [r4, #0] + 800c956: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c958: f412 7f95 tst.w r2, #298 ; 0x12a + 800c95c: d00a beq.n 800c974 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800c95e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c960: 0711 lsls r1, r2, #28 + 800c962: d46f bmi.n 800ca44 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800c964: 6b5a ldr r2, [r3, #52] ; 0x34 + 800c966: 0792 lsls r2, r2, #30 + 800c968: d46a bmi.n 800ca40 + else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800c96a: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c96c: 069b lsls r3, r3, #26 + 800c96e: d51e bpl.n 800c9ae + return HAL_SD_ERROR_RX_OVERRUN; + 800c970: 2020 movs r0, #32 + 800c972: e7bf b.n 800c8f4 + if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800c974: 6b5b ldr r3, [r3, #52] ; 0x34 + 800c976: 0418 lsls r0, r3, #16 + 800c978: d508 bpl.n 800c98c + 800c97a: f107 0820 add.w r8, r7, #32 + *pData = SDMMC_ReadFIFO(hsd->Instance); + 800c97e: 6820 ldr r0, [r4, #0] + 800c980: f000 fb54 bl 800d02c + 800c984: f847 0b04 str.w r0, [r7], #4 + for(count = 0U; count < 8U; count++) + 800c988: 45b8 cmp r8, r7 + 800c98a: d1f8 bne.n 800c97e + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c98c: f7fa fd26 bl 80073dc + 800c990: 1b80 subs r0, r0, r6 + 800c992: 3001 adds r0, #1 + 800c994: d1de bne.n 800c954 + return HAL_SD_ERROR_TIMEOUT; + 800c996: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + if(errorstate != HAL_SD_ERROR_NONE) + 800c99a: e7ab b.n 800c8f4 + *pData = SDMMC_ReadFIFO(hsd->Instance); + 800c99c: f000 fb46 bl 800d02c + 800c9a0: f847 0b04 str.w r0, [r7], #4 + if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800c9a4: f7fa fd1a bl 80073dc + 800c9a8: 1b80 subs r0, r0, r6 + 800c9aa: 3001 adds r0, #1 + 800c9ac: d0f3 beq.n 800c996 + while ((__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DPSMACT))) + 800c9ae: 6820 ldr r0, [r4, #0] + 800c9b0: 6b43 ldr r3, [r0, #52] ; 0x34 + 800c9b2: f413 5380 ands.w r3, r3, #4096 ; 0x1000 + 800c9b6: d1f1 bne.n 800c99c + pStatus->DataBusWidth = (uint8_t)((sd_status[0] & 0xC0U) >> 6U); + 800c9b8: 9906 ldr r1, [sp, #24] + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800c9ba: 4a24 ldr r2, [pc, #144] ; (800ca4c ) + 800c9bc: 6382 str r2, [r0, #56] ; 0x38 + pStatus->DataBusWidth = (uint8_t)((sd_status[0] & 0xC0U) >> 6U); + 800c9be: f3c1 1281 ubfx r2, r1, #6, #2 + 800c9c2: 702a strb r2, [r5, #0] + pStatus->SecuredMode = (uint8_t)((sd_status[0] & 0x20U) >> 5U); + 800c9c4: f3c1 1240 ubfx r2, r1, #5, #1 + 800c9c8: 706a strb r2, [r5, #1] + pStatus->CardType = (uint16_t)(((sd_status[0] & 0x00FF0000U) >> 8U) | ((sd_status[0] & 0xFF000000U) >> 24U)); + 800c9ca: 0a0a lsrs r2, r1, #8 + 800c9cc: f022 02ff bic.w r2, r2, #255 ; 0xff + 800c9d0: ea42 6211 orr.w r2, r2, r1, lsr #24 + 800c9d4: b292 uxth r2, r2 + 800c9d6: 806a strh r2, [r5, #2] + pStatus->ProtectedAreaSize = (((sd_status[1] & 0xFFU) << 24U) | ((sd_status[1] & 0xFF00U) << 8U) | + 800c9d8: 9a07 ldr r2, [sp, #28] + 800c9da: ba12 rev r2, r2 + 800c9dc: 606a str r2, [r5, #4] + pStatus->SpeedClass = (uint8_t)(sd_status[2] & 0xFFU); + 800c9de: 9a08 ldr r2, [sp, #32] + 800c9e0: b2d1 uxtb r1, r2 + 800c9e2: 7229 strb r1, [r5, #8] + pStatus->PerformanceMove = (uint8_t)((sd_status[2] & 0xFF00U) >> 8U); + 800c9e4: f3c2 2107 ubfx r1, r2, #8, #8 + 800c9e8: 7269 strb r1, [r5, #9] + pStatus->AllocationUnitSize = (uint8_t)((sd_status[2] & 0xF00000U) >> 20U); + 800c9ea: f3c2 5103 ubfx r1, r2, #20, #4 + 800c9ee: 72a9 strb r1, [r5, #10] + pStatus->EraseSize = (uint16_t)(((sd_status[2] & 0xFF000000U) >> 16U) | (sd_status[3] & 0xFFU)); + 800c9f0: 9909 ldr r1, [sp, #36] ; 0x24 + 800c9f2: 0c12 lsrs r2, r2, #16 + 800c9f4: b2c8 uxtb r0, r1 + 800c9f6: f022 02ff bic.w r2, r2, #255 ; 0xff + 800c9fa: 4302 orrs r2, r0 + 800c9fc: 81aa strh r2, [r5, #12] + pStatus->EraseTimeout = (uint8_t)((sd_status[3] & 0xFC00U) >> 10U); + 800c9fe: f3c1 2285 ubfx r2, r1, #10, #6 + 800ca02: 73aa strb r2, [r5, #14] + pStatus->EraseOffset = (uint8_t)((sd_status[3] & 0x0300U) >> 8U); + 800ca04: f3c1 2201 ubfx r2, r1, #8, #2 + 800ca08: 73ea strb r2, [r5, #15] + pStatus->UhsSpeedGrade = (uint8_t)((sd_status[3] & 0x00F0U) >> 4U); + 800ca0a: f3c1 1203 ubfx r2, r1, #4, #4 + 800ca0e: 742a strb r2, [r5, #16] + pStatus->UhsAllocationUnitSize = (uint8_t)(sd_status[3] & 0x000FU) ; + 800ca10: f001 010f and.w r1, r1, #15 + pStatus->VideoSpeedClass = (uint8_t)((sd_status[4] & 0xFF000000U) >> 24U); + 800ca14: f89d 202b ldrb.w r2, [sp, #43] ; 0x2b + pStatus->UhsAllocationUnitSize = (uint8_t)(sd_status[3] & 0x000FU) ; + 800ca18: 7469 strb r1, [r5, #17] + pStatus->VideoSpeedClass = (uint8_t)((sd_status[4] & 0xFF000000U) >> 24U); + 800ca1a: 74aa strb r2, [r5, #18] + HAL_StatusTypeDef status = HAL_OK; + 800ca1c: 461d mov r5, r3 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800ca1e: 6820 ldr r0, [r4, #0] + 800ca20: f44f 7100 mov.w r1, #512 ; 0x200 + 800ca24: f000 fc1a bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800ca28: b130 cbz r0, 800ca38 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800ca2a: 6823 ldr r3, [r4, #0] + 800ca2c: 4a06 ldr r2, [pc, #24] ; (800ca48 ) + 800ca2e: 639a str r2, [r3, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800ca30: 2501 movs r5, #1 + hsd->ErrorCode = errorstate; + 800ca32: 63a0 str r0, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800ca34: f884 5034 strb.w r5, [r4, #52] ; 0x34 +} + 800ca38: 4628 mov r0, r5 + 800ca3a: b016 add sp, #88 ; 0x58 + 800ca3c: e8bd 81f0 ldmia.w sp!, {r4, r5, r6, r7, r8, pc} + return HAL_SD_ERROR_DATA_CRC_FAIL; + 800ca40: 2002 movs r0, #2 + 800ca42: e757 b.n 800c8f4 + return HAL_SD_ERROR_DATA_TIMEOUT; + 800ca44: 2008 movs r0, #8 + 800ca46: e755 b.n 800c8f4 + 800ca48: 1fe00fff .word 0x1fe00fff + 800ca4c: 18000f3a .word 0x18000f3a + +0800ca50 : + pCardInfo->CardType = (uint32_t)(hsd->SdCard.CardType); + 800ca50: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800ca52: 600b str r3, [r1, #0] + pCardInfo->CardVersion = (uint32_t)(hsd->SdCard.CardVersion); + 800ca54: 6c03 ldr r3, [r0, #64] ; 0x40 + 800ca56: 604b str r3, [r1, #4] + pCardInfo->Class = (uint32_t)(hsd->SdCard.Class); + 800ca58: 6c43 ldr r3, [r0, #68] ; 0x44 + 800ca5a: 608b str r3, [r1, #8] + pCardInfo->RelCardAdd = (uint32_t)(hsd->SdCard.RelCardAdd); + 800ca5c: 6c83 ldr r3, [r0, #72] ; 0x48 + 800ca5e: 60cb str r3, [r1, #12] + pCardInfo->BlockNbr = (uint32_t)(hsd->SdCard.BlockNbr); + 800ca60: 6cc3 ldr r3, [r0, #76] ; 0x4c + 800ca62: 610b str r3, [r1, #16] + pCardInfo->BlockSize = (uint32_t)(hsd->SdCard.BlockSize); + 800ca64: 6d03 ldr r3, [r0, #80] ; 0x50 + 800ca66: 614b str r3, [r1, #20] + pCardInfo->LogBlockNbr = (uint32_t)(hsd->SdCard.LogBlockNbr); + 800ca68: 6d43 ldr r3, [r0, #84] ; 0x54 + 800ca6a: 618b str r3, [r1, #24] + pCardInfo->LogBlockSize = (uint32_t)(hsd->SdCard.LogBlockSize); + 800ca6c: 6d83 ldr r3, [r0, #88] ; 0x58 + 800ca6e: 61cb str r3, [r1, #28] +} + 800ca70: 2000 movs r0, #0 + 800ca72: 4770 bx lr + +0800ca74 : +{ + 800ca74: b530 push {r4, r5, lr} + hsd->State = HAL_SD_STATE_BUSY; + 800ca76: 2303 movs r3, #3 + 800ca78: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->SdCard.CardType != CARD_SECURED) + 800ca7c: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800ca7e: 2b03 cmp r3, #3 +{ + 800ca80: b08b sub sp, #44 ; 0x2c + 800ca82: 4604 mov r4, r0 + 800ca84: 460d mov r5, r1 + if(hsd->SdCard.CardType != CARD_SECURED) + 800ca86: d002 beq.n 800ca8e + if(WideMode == SDMMC_BUS_WIDE_8B) + 800ca88: f5b1 4f00 cmp.w r1, #32768 ; 0x8000 + 800ca8c: d103 bne.n 800ca96 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800ca8e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800ca90: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + 800ca94: e049 b.n 800cb2a + else if(WideMode == SDMMC_BUS_WIDE_4B) + 800ca96: f5b1 4f80 cmp.w r1, #16384 ; 0x4000 + 800ca9a: d123 bne.n 800cae4 + uint32_t scr[2U] = {0UL, 0UL}; + 800ca9c: 2100 movs r1, #0 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800ca9e: 6800 ldr r0, [r0, #0] + uint32_t scr[2U] = {0UL, 0UL}; + 800caa0: e9cd 1104 strd r1, r1, [sp, #16] + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800caa4: f000 fafb bl 800d09e + 800caa8: 0180 lsls r0, r0, #6 + 800caaa: d435 bmi.n 800cb18 + errorstate = SD_FindSCR(hsd, scr); + 800caac: a904 add r1, sp, #16 + 800caae: 4620 mov r0, r4 + 800cab0: f7ff fa32 bl 800bf18 + if(errorstate != HAL_SD_ERROR_NONE) + 800cab4: b960 cbnz r0, 800cad0 + if((scr[1U] & SDMMC_WIDE_BUS_SUPPORT) != SDMMC_ALLZERO) + 800cab6: 9b05 ldr r3, [sp, #20] + 800cab8: 0359 lsls r1, r3, #13 + 800caba: d530 bpl.n 800cb1e + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800cabc: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800cabe: 6820 ldr r0, [r4, #0] + 800cac0: 0409 lsls r1, r1, #16 + 800cac2: f000 fd04 bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800cac6: b918 cbnz r0, 800cad0 + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 2U); + 800cac8: 2102 movs r1, #2 + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 0U); + 800caca: 6820 ldr r0, [r4, #0] + 800cacc: f000 fd18 bl 800d500 + hsd->ErrorCode |= errorstate; + 800cad0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cad2: 4318 orrs r0, r3 + 800cad4: 63a0 str r0, [r4, #56] ; 0x38 + if(hsd->ErrorCode != HAL_SD_ERROR_NONE) + 800cad6: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cad8: b34b cbz r3, 800cb2e + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800cada: 6823 ldr r3, [r4, #0] + 800cadc: 4a42 ldr r2, [pc, #264] ; (800cbe8 ) + 800cade: 639a str r2, [r3, #56] ; 0x38 + status = HAL_ERROR; + 800cae0: 2501 movs r5, #1 + 800cae2: e054 b.n 800cb8e + else if(WideMode == SDMMC_BUS_WIDE_1B) + 800cae4: b9f1 cbnz r1, 800cb24 + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800cae6: 6800 ldr r0, [r0, #0] + uint32_t scr[2U] = {0UL, 0UL}; + 800cae8: e9cd 1104 strd r1, r1, [sp, #16] + if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED) + 800caec: f000 fad7 bl 800d09e + 800caf0: 0182 lsls r2, r0, #6 + 800caf2: d411 bmi.n 800cb18 + errorstate = SD_FindSCR(hsd, scr); + 800caf4: a904 add r1, sp, #16 + 800caf6: 4620 mov r0, r4 + 800caf8: f7ff fa0e bl 800bf18 + if(errorstate != HAL_SD_ERROR_NONE) + 800cafc: 2800 cmp r0, #0 + 800cafe: d1e7 bne.n 800cad0 + if((scr[1U] & SDMMC_SINGLE_BUS_SUPPORT) != SDMMC_ALLZERO) + 800cb00: 9b05 ldr r3, [sp, #20] + 800cb02: 03db lsls r3, r3, #15 + 800cb04: d50b bpl.n 800cb1e + errorstate = SDMMC_CmdAppCommand(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800cb06: 6ca1 ldr r1, [r4, #72] ; 0x48 + 800cb08: 6820 ldr r0, [r4, #0] + 800cb0a: 0409 lsls r1, r1, #16 + 800cb0c: f000 fcdf bl 800d4ce + if(errorstate != HAL_SD_ERROR_NONE) + 800cb10: 2800 cmp r0, #0 + 800cb12: d1dd bne.n 800cad0 + errorstate = SDMMC_CmdBusWidth(hsd->Instance, 0U); + 800cb14: 4601 mov r1, r0 + 800cb16: e7d8 b.n 800caca + return HAL_SD_ERROR_LOCK_UNLOCK_FAILED; + 800cb18: f44f 6000 mov.w r0, #2048 ; 0x800 + 800cb1c: e7d8 b.n 800cad0 + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800cb1e: f04f 6080 mov.w r0, #67108864 ; 0x4000000 + 800cb22: e7d5 b.n 800cad0 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800cb24: 6b83 ldr r3, [r0, #56] ; 0x38 + 800cb26: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800cb2a: 63a3 str r3, [r4, #56] ; 0x38 + 800cb2c: e7d3 b.n 800cad6 + sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC1); + 800cb2e: f44f 2000 mov.w r0, #524288 ; 0x80000 + 800cb32: f7fd f8af bl 8009c94 + if (sdmmc_clk != 0U) + 800cb36: 2800 cmp r0, #0 + 800cb38: d051 beq.n 800cbde + Init.ClockEdge = hsd->Init.ClockEdge; + 800cb3a: 6863 ldr r3, [r4, #4] + 800cb3c: 9304 str r3, [sp, #16] + Init.ClockPowerSave = hsd->Init.ClockPowerSave; + 800cb3e: 68a3 ldr r3, [r4, #8] + if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ))) + 800cb40: 492a ldr r1, [pc, #168] ; (800cbec ) + 800cb42: fbb0 f2f1 udiv r2, r0, r1 + Init.BusWide = WideMode; + 800cb46: e9cd 3505 strd r3, r5, [sp, #20] + Init.HardwareFlowControl = hsd->Init.HardwareFlowControl; + 800cb4a: 6923 ldr r3, [r4, #16] + 800cb4c: 9307 str r3, [sp, #28] + if (hsd->Init.ClockDiv >= (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ))) + 800cb4e: 6963 ldr r3, [r4, #20] + 800cb50: 4293 cmp r3, r2 + 800cb52: d301 bcc.n 800cb58 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800cb54: 9308 str r3, [sp, #32] + 800cb56: e00d b.n 800cb74 + else if (hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) + 800cb58: 6de5 ldr r5, [r4, #92] ; 0x5c + 800cb5a: f5b5 7f00 cmp.w r5, #512 ; 0x200 + 800cb5e: d0f9 beq.n 800cb54 + else if (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800cb60: f5b5 7f80 cmp.w r5, #256 ; 0x100 + 800cb64: d12e bne.n 800cbc4 + if (hsd->Init.ClockDiv == 0U) + 800cb66: bb3b cbnz r3, 800cbb8 + if (sdmmc_clk > SD_HIGH_SPEED_FREQ) + 800cb68: 4288 cmp r0, r1 + 800cb6a: d923 bls.n 800cbb4 + Init.ClockDiv = sdmmc_clk / (2U * SD_HIGH_SPEED_FREQ); + 800cb6c: 4b20 ldr r3, [pc, #128] ; (800cbf0 ) + 800cb6e: fbb0 f0f3 udiv r0, r0, r3 + 800cb72: 9008 str r0, [sp, #32] + Init.Transceiver = hsd->Init.Transceiver; + 800cb74: 69a3 ldr r3, [r4, #24] + 800cb76: 9309 str r3, [sp, #36] ; 0x24 + (void)SDMMC_Init(hsd->Instance, Init); + 800cb78: ab0a add r3, sp, #40 ; 0x28 + 800cb7a: e913 0007 ldmdb r3, {r0, r1, r2} + 800cb7e: e88d 0007 stmia.w sp, {r0, r1, r2} + 800cb82: ab04 add r3, sp, #16 + 800cb84: cb0e ldmia r3, {r1, r2, r3} + 800cb86: 6820 ldr r0, [r4, #0] + 800cb88: f000 fa36 bl 800cff8 + HAL_StatusTypeDef status = HAL_OK; + 800cb8c: 2500 movs r5, #0 + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800cb8e: 6820 ldr r0, [r4, #0] + 800cb90: f44f 7100 mov.w r1, #512 ; 0x200 + 800cb94: f000 fb62 bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800cb98: b130 cbz r0, 800cba8 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800cb9a: 6823 ldr r3, [r4, #0] + 800cb9c: 4a12 ldr r2, [pc, #72] ; (800cbe8 ) + 800cb9e: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800cba0: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cba2: 4318 orrs r0, r3 + 800cba4: 63a0 str r0, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800cba6: 2501 movs r5, #1 + hsd->State = HAL_SD_STATE_READY; + 800cba8: 2301 movs r3, #1 +} + 800cbaa: 4628 mov r0, r5 + hsd->State = HAL_SD_STATE_READY; + 800cbac: f884 3034 strb.w r3, [r4, #52] ; 0x34 +} + 800cbb0: b00b add sp, #44 ; 0x2c + 800cbb2: bd30 pop {r4, r5, pc} + Init.ClockDiv = hsd->Init.ClockDiv; + 800cbb4: 2300 movs r3, #0 + 800cbb6: e7cd b.n 800cb54 + if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_HIGH_SPEED_FREQ) + 800cbb8: 005a lsls r2, r3, #1 + 800cbba: fbb0 f2f2 udiv r2, r0, r2 + 800cbbe: 428a cmp r2, r1 + 800cbc0: d9c8 bls.n 800cb54 + 800cbc2: e7d3 b.n 800cb6c + if (hsd->Init.ClockDiv == 0U) + 800cbc4: 490b ldr r1, [pc, #44] ; (800cbf4 ) + 800cbc6: b91b cbnz r3, 800cbd0 + if (sdmmc_clk > SD_NORMAL_SPEED_FREQ) + 800cbc8: 4288 cmp r0, r1 + 800cbca: d9f3 bls.n 800cbb4 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800cbcc: 9208 str r2, [sp, #32] + 800cbce: e7d1 b.n 800cb74 + if ((sdmmc_clk/(2U * hsd->Init.ClockDiv)) > SD_NORMAL_SPEED_FREQ) + 800cbd0: 005d lsls r5, r3, #1 + 800cbd2: fbb0 f0f5 udiv r0, r0, r5 + Init.ClockDiv = sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ); + 800cbd6: 4288 cmp r0, r1 + 800cbd8: bf88 it hi + 800cbda: 4613 movhi r3, r2 + 800cbdc: e7ba b.n 800cb54 + hsd->ErrorCode |= SDMMC_ERROR_INVALID_PARAMETER; + 800cbde: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cbe0: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + 800cbe4: 63a3 str r3, [r4, #56] ; 0x38 + 800cbe6: e77b b.n 800cae0 + 800cbe8: 1fe00fff .word 0x1fe00fff + 800cbec: 02faf080 .word 0x02faf080 + 800cbf0: 05f5e100 .word 0x05f5e100 + 800cbf4: 017d7840 .word 0x017d7840 + +0800cbf8 : + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800cbf8: 6c81 ldr r1, [r0, #72] ; 0x48 +{ + 800cbfa: b510 push {r4, lr} + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800cbfc: 0409 lsls r1, r1, #16 +{ + 800cbfe: 4604 mov r4, r0 + errorstate = SDMMC_CmdSendStatus(hsd->Instance, (uint32_t)(hsd->SdCard.RelCardAdd << 16U)); + 800cc00: 6800 ldr r0, [r0, #0] + 800cc02: f000 fccb bl 800d59c + if(errorstate != HAL_SD_ERROR_NONE) + 800cc06: 4601 mov r1, r0 + 800cc08: b928 cbnz r0, 800cc16 + *pCardStatus = SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1); + 800cc0a: 6820 ldr r0, [r4, #0] + 800cc0c: f000 fa47 bl 800d09e +} + 800cc10: f3c0 2043 ubfx r0, r0, #9, #4 + 800cc14: bd10 pop {r4, pc} + hsd->ErrorCode |= errorstate; + 800cc16: 6ba0 ldr r0, [r4, #56] ; 0x38 + 800cc18: 4308 orrs r0, r1 + 800cc1a: 63a0 str r0, [r4, #56] ; 0x38 + uint32_t resp1 = 0; + 800cc1c: 2000 movs r0, #0 + 800cc1e: e7f7 b.n 800cc10 + +0800cc20 : +{ + 800cc20: b570 push {r4, r5, r6, lr} + if(hsd == NULL) + 800cc22: 4604 mov r4, r0 +{ + 800cc24: b086 sub sp, #24 + if(hsd == NULL) + 800cc26: b918 cbnz r0, 800cc30 + return HAL_ERROR; + 800cc28: 2501 movs r5, #1 +} + 800cc2a: 4628 mov r0, r5 + 800cc2c: b006 add sp, #24 + 800cc2e: bd70 pop {r4, r5, r6, pc} + if(hsd->State == HAL_SD_STATE_RESET) + 800cc30: f890 3034 ldrb.w r3, [r0, #52] ; 0x34 + 800cc34: f003 02ff and.w r2, r3, #255 ; 0xff + 800cc38: b913 cbnz r3, 800cc40 + hsd->Lock = HAL_UNLOCKED; + 800cc3a: 7702 strb r2, [r0, #28] + HAL_SD_MspInit(hsd); + 800cc3c: f7ff fa5a bl 800c0f4 + hsd->State = HAL_SD_STATE_BUSY; + 800cc40: 2303 movs r3, #3 + 800cc42: f884 3034 strb.w r3, [r4, #52] ; 0x34 + if (HAL_SD_InitCard(hsd) != HAL_OK) + 800cc46: 4620 mov r0, r4 + 800cc48: f7ff fcea bl 800c620 + 800cc4c: 2800 cmp r0, #0 + 800cc4e: d1eb bne.n 800cc28 + if( HAL_SD_GetCardStatus(hsd, &CardStatus) != HAL_OK) + 800cc50: a901 add r1, sp, #4 + 800cc52: 4620 mov r0, r4 + 800cc54: f7ff fe3e bl 800c8d4 + 800cc58: 2800 cmp r0, #0 + 800cc5a: d1e5 bne.n 800cc28 + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800cc5c: 6be1 ldr r1, [r4, #60] ; 0x3c + speedgrade = CardStatus.UhsSpeedGrade; + 800cc5e: f89d 2014 ldrb.w r2, [sp, #20] + unitsize = CardStatus.UhsAllocationUnitSize; + 800cc62: f89d 3015 ldrb.w r3, [sp, #21] + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800cc66: 2901 cmp r1, #1 + speedgrade = CardStatus.UhsSpeedGrade; + 800cc68: b2d2 uxtb r2, r2 + unitsize = CardStatus.UhsAllocationUnitSize; + 800cc6a: b2db uxtb r3, r3 + if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U))) + 800cc6c: d11c bne.n 800cca8 + 800cc6e: 4313 orrs r3, r2 + hsd->SdCard.CardSpeed = CARD_ULTRA_HIGH_SPEED; + 800cc70: bf14 ite ne + 800cc72: f44f 7300 movne.w r3, #512 ; 0x200 + hsd->SdCard.CardSpeed = CARD_HIGH_SPEED; + 800cc76: f44f 7380 moveq.w r3, #256 ; 0x100 + 800cc7a: 65e3 str r3, [r4, #92] ; 0x5c + if(HAL_SD_ConfigWideBusOperation(hsd, hsd->Init.BusWide) != HAL_OK) + 800cc7c: 68e1 ldr r1, [r4, #12] + 800cc7e: 4620 mov r0, r4 + 800cc80: f7ff fef8 bl 800ca74 + 800cc84: 4605 mov r5, r0 + 800cc86: 2800 cmp r0, #0 + 800cc88: d1ce bne.n 800cc28 + tickstart = HAL_GetTick(); + 800cc8a: f7fa fba7 bl 80073dc + 800cc8e: 4606 mov r6, r0 + while((HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)) + 800cc90: 4620 mov r0, r4 + 800cc92: f7ff ffb1 bl 800cbf8 + 800cc96: 2804 cmp r0, #4 + 800cc98: d108 bne.n 800ccac + hsd->ErrorCode = HAL_SD_ERROR_NONE; + 800cc9a: 2300 movs r3, #0 + 800cc9c: 63a3 str r3, [r4, #56] ; 0x38 + hsd->Context = SD_CONTEXT_NONE; + 800cc9e: 6323 str r3, [r4, #48] ; 0x30 + hsd->State = HAL_SD_STATE_READY; + 800cca0: 2301 movs r3, #1 + 800cca2: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_OK; + 800cca6: e7c0 b.n 800cc2a + hsd->SdCard.CardSpeed = CARD_NORMAL_SPEED; + 800cca8: 65e0 str r0, [r4, #92] ; 0x5c + 800ccaa: e7e7 b.n 800cc7c + if((HAL_GetTick()-tickstart) >= SDMMC_DATATIMEOUT) + 800ccac: f7fa fb96 bl 80073dc + 800ccb0: 1b80 subs r0, r0, r6 + 800ccb2: 3001 adds r0, #1 + 800ccb4: d1ec bne.n 800cc90 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800ccb6: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800ccba: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800ccbc: 2301 movs r3, #1 + 800ccbe: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->Context = SD_CONTEXT_NONE; + 800ccc2: 2300 movs r3, #0 + 800ccc4: 6323 str r3, [r4, #48] ; 0x30 + return HAL_TIMEOUT; + 800ccc6: 2503 movs r5, #3 + 800ccc8: e7af b.n 800cc2a + ... + +0800cccc : +{ + 800cccc: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + uint32_t SD_hs[16] = {0}; + 800ccd0: 2640 movs r6, #64 ; 0x40 +{ + 800ccd2: b096 sub sp, #88 ; 0x58 + 800ccd4: 4605 mov r5, r0 + uint32_t SD_hs[16] = {0}; + 800ccd6: 4632 mov r2, r6 + 800ccd8: 2100 movs r1, #0 + 800ccda: a806 add r0, sp, #24 + 800ccdc: f000 fe22 bl 800d924 + uint32_t Timeout = HAL_GetTick(); + 800cce0: f7fa fb7c bl 80073dc + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800cce4: 6deb ldr r3, [r5, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800cce6: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800cce8: 2b00 cmp r3, #0 + 800ccea: d066 beq.n 800cdba + if(hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800ccec: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800ccf0: d004 beq.n 800ccfc + uint32_t errorstate = HAL_SD_ERROR_NONE; + 800ccf2: 2400 movs r4, #0 +} + 800ccf4: 4620 mov r0, r4 + 800ccf6: b016 add sp, #88 ; 0x58 + 800ccf8: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + hsd->Instance->DCTRL = 0; + 800ccfc: 6828 ldr r0, [r5, #0] + 800ccfe: 2300 movs r3, #0 + 800cd00: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800cd02: 4631 mov r1, r6 + 800cd04: f000 faaa bl 800d25c + if (errorstate != HAL_SD_ERROR_NONE) + 800cd08: 4604 mov r4, r0 + 800cd0a: 2800 cmp r0, #0 + 800cd0c: d1f2 bne.n 800ccf4 + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800cd0e: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800cd12: e9cd 3600 strd r3, r6, [sp] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800cd16: 2260 movs r2, #96 ; 0x60 + 800cd18: 2302 movs r3, #2 + 800cd1a: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + 800cd1e: 9004 str r0, [sp, #16] + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800cd20: 2301 movs r3, #1 + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800cd22: 6828 ldr r0, [r5, #0] + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800cd24: 9305 str r3, [sp, #20] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800cd26: 4669 mov r1, sp + 800cd28: f000 f9bc bl 800d0a4 + 800cd2c: 2800 cmp r0, #0 + 800cd2e: d147 bne.n 800cdc0 + errorstate = SDMMC_CmdSwitch(hsd->Instance,SDMMC_SDR25_SWITCH_PATTERN); + 800cd30: 4925 ldr r1, [pc, #148] ; (800cdc8 ) + 800cd32: 6828 ldr r0, [r5, #0] + 800cd34: f000 fbfd bl 800d532 + if(errorstate != HAL_SD_ERROR_NONE) + 800cd38: 4604 mov r4, r0 + 800cd3a: 2800 cmp r0, #0 + 800cd3c: d1da bne.n 800ccf4 + uint32_t count, loop = 0 ; + 800cd3e: 4607 mov r7, r0 + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800cd40: f240 592a movw r9, #1322 ; 0x52a + 800cd44: 682b ldr r3, [r5, #0] + 800cd46: 6b5e ldr r6, [r3, #52] ; 0x34 + 800cd48: ea16 0609 ands.w r6, r6, r9 + 800cd4c: d005 beq.n 800cd5a + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800cd4e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cd50: 0710 lsls r0, r2, #28 + 800cd52: d51e bpl.n 800cd92 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800cd54: 2208 movs r2, #8 + 800cd56: 639a str r2, [r3, #56] ; 0x38 + return errorstate; + 800cd58: e7cc b.n 800ccf4 + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800cd5a: 6b5b ldr r3, [r3, #52] ; 0x34 + 800cd5c: 041b lsls r3, r3, #16 + 800cd5e: d50b bpl.n 800cd78 + 800cd60: ab06 add r3, sp, #24 + 800cd62: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cd66: 6828 ldr r0, [r5, #0] + 800cd68: f000 f960 bl 800d02c + for (count = 0U; count < 8U; count++) + 800cd6c: 3601 adds r6, #1 + 800cd6e: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cd70: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800cd74: d1f7 bne.n 800cd66 + loop ++; + 800cd76: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800cd78: f7fa fb30 bl 80073dc + 800cd7c: eba0 0008 sub.w r0, r0, r8 + 800cd80: 3001 adds r0, #1 + 800cd82: d1df bne.n 800cd44 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800cd84: f04f 4400 mov.w r4, #2147483648 ; 0x80000000 + hsd->State= HAL_SD_STATE_READY; + 800cd88: 2301 movs r3, #1 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800cd8a: 63ac str r4, [r5, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800cd8c: f885 3034 strb.w r3, [r5, #52] ; 0x34 + return HAL_SD_ERROR_TIMEOUT; + 800cd90: e7b0 b.n 800ccf4 + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800cd92: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cd94: 0791 lsls r1, r2, #30 + 800cd96: d502 bpl.n 800cd9e + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800cd98: 2402 movs r4, #2 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cd9a: 639c str r4, [r3, #56] ; 0x38 + return errorstate; + 800cd9c: e7aa b.n 800ccf4 + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800cd9e: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cda0: 0692 lsls r2, r2, #26 + 800cda2: d501 bpl.n 800cda8 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cda4: 2420 movs r4, #32 + 800cda6: e7f8 b.n 800cd9a + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800cda8: 4a08 ldr r2, [pc, #32] ; (800cdcc ) + 800cdaa: 639a str r2, [r3, #56] ; 0x38 + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800cdac: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800cdb0: 079b lsls r3, r3, #30 + 800cdb2: d49e bmi.n 800ccf2 + errorstate = SDMMC_ERROR_UNSUPPORTED_FEATURE; + 800cdb4: f04f 5480 mov.w r4, #268435456 ; 0x10000000 + 800cdb8: e79c b.n 800ccf4 + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + 800cdba: f04f 6480 mov.w r4, #67108864 ; 0x4000000 + 800cdbe: e799 b.n 800ccf4 + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + 800cdc0: f44f 3480 mov.w r4, #65536 ; 0x10000 + 800cdc4: e796 b.n 800ccf4 + 800cdc6: bf00 nop + 800cdc8: 80ffff01 .word 0x80ffff01 + 800cdcc: 18000f3a .word 0x18000f3a + +0800cdd0 : +{ + 800cdd0: e92d 47f0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, lr} + hsd->State = HAL_SD_STATE_BUSY; + 800cdd4: 2303 movs r3, #3 + 800cdd6: f880 3034 strb.w r3, [r0, #52] ; 0x34 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800cdda: 6983 ldr r3, [r0, #24] + 800cddc: 2b01 cmp r3, #1 +{ + 800cdde: b096 sub sp, #88 ; 0x58 + 800cde0: 4604 mov r4, r0 + if(hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE) + 800cde2: f040 80cf bne.w 800cf84 + switch (SpeedMode) + 800cde6: 2904 cmp r1, #4 + 800cde8: f200 80eb bhi.w 800cfc2 + 800cdec: e8df f011 tbh [pc, r1, lsl #1] + 800cdf0: 00150005 .word 0x00150005 + 800cdf4: 001e00dc .word 0x001e00dc + 800cdf8: 0031 .short 0x0031 + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cdfa: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800cdfc: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ce00: d002 beq.n 800ce08 + 800ce02: 6bc2 ldr r2, [r0, #60] ; 0x3c + 800ce04: 2a01 cmp r2, #1 + 800ce06: d10a bne.n 800ce1e + hsd->Instance->CLKCR |= 0x00100000U; + 800ce08: 6822 ldr r2, [r4, #0] + 800ce0a: 6853 ldr r3, [r2, #4] + 800ce0c: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800ce10: 6053 str r3, [r2, #4] + if (SD_UltraHighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800ce12: 4620 mov r0, r4 + 800ce14: f7ff f8e6 bl 800bfe4 + 800ce18: b920 cbnz r0, 800ce24 + switch (SpeedMode) + 800ce1a: 2500 movs r5, #0 + 800ce1c: e063 b.n 800cee6 + else if (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) + 800ce1e: f5b3 7f80 cmp.w r3, #256 ; 0x100 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800ce22: d1fa bne.n 800ce1a + if (SD_HighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800ce24: 4620 mov r0, r4 + 800ce26: f7ff ff51 bl 800cccc + 800ce2a: e00f b.n 800ce4c + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800ce2c: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800ce2e: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ce32: d003 beq.n 800ce3c + 800ce34: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800ce36: 2b01 cmp r3, #1 + 800ce38: f040 8089 bne.w 800cf4e + hsd->Instance->CLKCR |= 0x00100000U; + 800ce3c: 6822 ldr r2, [r4, #0] + 800ce3e: 6853 ldr r3, [r2, #4] + 800ce40: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800ce44: 6053 str r3, [r2, #4] + if (SD_UltraHighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800ce46: 4620 mov r0, r4 + 800ce48: f7ff f8cc bl 800bfe4 + if (SD_HighSpeed(hsd) != HAL_SD_ERROR_NONE) + 800ce4c: 2800 cmp r0, #0 + 800ce4e: d0e4 beq.n 800ce1a + 800ce50: e07d b.n 800cf4e + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800ce52: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800ce54: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ce58: d002 beq.n 800ce60 + 800ce5a: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800ce5c: 2b01 cmp r3, #1 + 800ce5e: d176 bne.n 800cf4e + hsd->Instance->CLKCR |= 0x00100000U; + 800ce60: 6822 ldr r2, [r4, #0] + 800ce62: 6853 ldr r3, [r2, #4] + */ +static uint32_t SD_DDR_Mode(SD_HandleTypeDef *hsd) +{ + uint32_t errorstate = HAL_SD_ERROR_NONE; + SDMMC_DataInitTypeDef sdmmc_datainitstructure; + uint32_t SD_hs[16] = {0}; + 800ce64: 2540 movs r5, #64 ; 0x40 + hsd->Instance->CLKCR |= 0x00100000U; + 800ce66: f443 1380 orr.w r3, r3, #1048576 ; 0x100000 + 800ce6a: 6053 str r3, [r2, #4] + uint32_t SD_hs[16] = {0}; + 800ce6c: 2100 movs r1, #0 + 800ce6e: 462a mov r2, r5 + 800ce70: a806 add r0, sp, #24 + 800ce72: f000 fd57 bl 800d924 + uint32_t count, loop = 0 ; + uint32_t Timeout = HAL_GetTick(); + 800ce76: f7fa fab1 bl 80073dc + + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800ce7a: 6de3 ldr r3, [r4, #92] ; 0x5c + uint32_t Timeout = HAL_GetTick(); + 800ce7c: 4680 mov r8, r0 + if(hsd->SdCard.CardSpeed == CARD_NORMAL_SPEED) + 800ce7e: 2b00 cmp r3, #0 + 800ce80: d065 beq.n 800cf4e + { + /* Standard Speed Card <= 12.5Mhz */ + return HAL_SD_ERROR_REQUEST_NOT_APPLICABLE; + } + + if((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) && + 800ce82: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800ce86: d1c8 bne.n 800ce1a + 800ce88: 69a6 ldr r6, [r4, #24] + 800ce8a: 2e01 cmp r6, #1 + 800ce8c: d1c5 bne.n 800ce1a + (hsd->Init.Transceiver == SDMMC_TRANSCEIVER_ENABLE)) + { + /* Initialize the Data control register */ + hsd->Instance->DCTRL = 0; + 800ce8e: 6820 ldr r0, [r4, #0] + 800ce90: 2300 movs r3, #0 + 800ce92: 62c3 str r3, [r0, #44] ; 0x2c + errorstate = SDMMC_CmdBlockLength(hsd->Instance, 64U); + 800ce94: 4629 mov r1, r5 + 800ce96: f000 f9e1 bl 800d25c + + if (errorstate != HAL_SD_ERROR_NONE) + 800ce9a: 2800 cmp r0, #0 + 800ce9c: d157 bne.n 800cf4e + { + return errorstate; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + sdmmc_datainitstructure.DataTimeOut = SDMMC_DATATIMEOUT; + 800ce9e: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff + sdmmc_datainitstructure.DataLength = 64U; + 800cea2: e9cd 3500 strd r3, r5, [sp] + sdmmc_datainitstructure.DataBlockSize = SDMMC_DATABLOCK_SIZE_64B ; + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + sdmmc_datainitstructure.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + sdmmc_datainitstructure.DPSM = SDMMC_DPSM_ENABLE; + 800cea6: e9cd 0604 strd r0, r6, [sp, #16] + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800ceaa: 2260 movs r2, #96 ; 0x60 + 800ceac: 2302 movs r3, #2 + + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800ceae: 6820 ldr r0, [r4, #0] + 800ceb0: 4669 mov r1, sp + sdmmc_datainitstructure.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + 800ceb2: e9cd 2302 strd r2, r3, [sp, #8] + if ( SDMMC_ConfigData(hsd->Instance, &sdmmc_datainitstructure) != HAL_OK) + 800ceb6: f000 f8f5 bl 800d0a4 + 800ceba: 4605 mov r5, r0 + 800cebc: 2800 cmp r0, #0 + 800cebe: d146 bne.n 800cf4e + { + return (HAL_SD_ERROR_GENERAL_UNKNOWN_ERR); + } + + errorstate = SDMMC_CmdSwitch(hsd->Instance, SDMMC_DDR50_SWITCH_PATTERN); + 800cec0: 494a ldr r1, [pc, #296] ; (800cfec ) + 800cec2: 6820 ldr r0, [r4, #0] + 800cec4: f000 fb35 bl 800d532 + if(errorstate != HAL_SD_ERROR_NONE) + 800cec8: 4607 mov r7, r0 + 800ceca: 2800 cmp r0, #0 + 800cecc: d13f bne.n 800cf4e + { + return errorstate; + } + + while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND| SDMMC_FLAG_DATAEND )) + 800cece: f240 592a movw r9, #1322 ; 0x52a + 800ced2: 6823 ldr r3, [r4, #0] + 800ced4: 6b5e ldr r6, [r3, #52] ; 0x34 + 800ced6: ea16 0609 ands.w r6, r6, r9 + 800ceda: d01d beq.n 800cf18 + hsd->State= HAL_SD_STATE_READY; + return HAL_SD_ERROR_TIMEOUT; + } + } + + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT)) + 800cedc: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cede: 0710 lsls r0, r2, #28 + 800cee0: d53b bpl.n 800cf5a + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DTIMEOUT); + 800cee2: 2208 movs r2, #8 + 800cee4: 639a str r2, [r3, #56] ; 0x38 + tickstart = HAL_GetTick(); + 800cee6: f7fa fa79 bl 80073dc + 800ceea: 4606 mov r6, r0 + while ((HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)) + 800ceec: 4620 mov r0, r4 + 800ceee: f7ff fe83 bl 800cbf8 + 800cef2: 2804 cmp r0, #4 + 800cef4: d169 bne.n 800cfca + errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE); + 800cef6: 6820 ldr r0, [r4, #0] + 800cef8: f44f 7100 mov.w r1, #512 ; 0x200 + 800cefc: f000 f9ae bl 800d25c + if(errorstate != HAL_SD_ERROR_NONE) + 800cf00: b130 cbz r0, 800cf10 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS); + 800cf02: 6823 ldr r3, [r4, #0] + 800cf04: 4a3a ldr r2, [pc, #232] ; (800cff0 ) + 800cf06: 639a str r2, [r3, #56] ; 0x38 + hsd->ErrorCode |= errorstate; + 800cf08: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cf0a: 4318 orrs r0, r3 + 800cf0c: 63a0 str r0, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800cf0e: 2501 movs r5, #1 + hsd->State = HAL_SD_STATE_READY; + 800cf10: 2301 movs r3, #1 + 800cf12: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return status; + 800cf16: e064 b.n 800cfe2 + if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF)) + 800cf18: 6b5b ldr r3, [r3, #52] ; 0x34 + 800cf1a: 041b lsls r3, r3, #16 + 800cf1c: d50b bpl.n 800cf36 + 800cf1e: ab06 add r3, sp, #24 + 800cf20: eb03 1a47 add.w sl, r3, r7, lsl #5 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cf24: 6820 ldr r0, [r4, #0] + 800cf26: f000 f881 bl 800d02c + for (count = 0U; count < 8U; count++) + 800cf2a: 3601 adds r6, #1 + 800cf2c: 2e08 cmp r6, #8 + SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); + 800cf2e: f84a 0b04 str.w r0, [sl], #4 + for (count = 0U; count < 8U; count++) + 800cf32: d1f7 bne.n 800cf24 + loop ++; + 800cf34: 3701 adds r7, #1 + if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) + 800cf36: f7fa fa51 bl 80073dc + 800cf3a: eba0 0008 sub.w r0, r0, r8 + 800cf3e: 3001 adds r0, #1 + 800cf40: d1c7 bne.n 800ced2 + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800cf42: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800cf46: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State= HAL_SD_STATE_READY; + 800cf48: 2301 movs r3, #1 + 800cf4a: f884 3034 strb.w r3, [r4, #52] ; 0x34 + hsd->ErrorCode |= HAL_SD_ERROR_UNSUPPORTED_FEATURE; + 800cf4e: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cf50: f043 5380 orr.w r3, r3, #268435456 ; 0x10000000 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800cf54: 63a3 str r3, [r4, #56] ; 0x38 + status = HAL_ERROR; + 800cf56: 2501 movs r5, #1 + break; + 800cf58: e7c5 b.n 800cee6 + + return errorstate; + } + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL)) + 800cf5a: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cf5c: 0791 lsls r1, r2, #30 + 800cf5e: d502 bpl.n 800cf66 + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DCRCFAIL); + 800cf60: 2202 movs r2, #2 + + return errorstate; + } + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + { + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cf62: 639a str r2, [r3, #56] ; 0x38 + + errorstate = SDMMC_ERROR_RX_OVERRUN; + + return errorstate; + 800cf64: e7f3 b.n 800cf4e + else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR)) + 800cf66: 6b5a ldr r2, [r3, #52] ; 0x34 + 800cf68: 0692 lsls r2, r2, #26 + 800cf6a: d501 bpl.n 800cf70 + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_RXOVERR); + 800cf6c: 2220 movs r2, #32 + 800cf6e: e7f8 b.n 800cf62 + { + /* No error flag set */ + } + + /* Clear all the static flags */ + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + 800cf70: 4a20 ldr r2, [pc, #128] ; (800cff4 ) + 800cf72: 639a str r2, [r3, #56] ; 0x38 + + /* Test if the switch mode is ok */ + if ((((uint8_t*)SD_hs)[13] & 2U) != 2U) + 800cf74: f89d 3025 ldrb.w r3, [sp, #37] ; 0x25 + 800cf78: 079b lsls r3, r3, #30 + 800cf7a: d5e8 bpl.n 800cf4e + else + { +#if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U) + hsd->DriveTransceiver_1_8V_Callback(SET); +#else + HAL_SDEx_DriveTransceiver_1_8V_Callback(SET); + 800cf7c: 2001 movs r0, #1 + 800cf7e: f7fa fa9d bl 80074bc + 800cf82: e7b0 b.n 800cee6 + switch (SpeedMode) + 800cf84: 2901 cmp r1, #1 + 800cf86: f43f af48 beq.w 800ce1a + 800cf8a: 2902 cmp r1, #2 + 800cf8c: d00c beq.n 800cfa8 + 800cf8e: b9c1 cbnz r1, 800cfc2 + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cf90: 6dc3 ldr r3, [r0, #92] ; 0x5c + 800cf92: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cf96: f43f af45 beq.w 800ce24 + 800cf9a: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800cf9e: f43f af41 beq.w 800ce24 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800cfa2: 6bc3 ldr r3, [r0, #60] ; 0x3c + 800cfa4: 2b01 cmp r3, #1 + 800cfa6: e73c b.n 800ce22 + if ((hsd->SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED) || + 800cfa8: 6de3 ldr r3, [r4, #92] ; 0x5c + 800cfaa: f5b3 7f00 cmp.w r3, #512 ; 0x200 + 800cfae: f43f af39 beq.w 800ce24 + 800cfb2: f5b3 7f80 cmp.w r3, #256 ; 0x100 + 800cfb6: f43f af35 beq.w 800ce24 + (hsd->SdCard.CardSpeed == CARD_HIGH_SPEED) || + 800cfba: 6be3 ldr r3, [r4, #60] ; 0x3c + 800cfbc: 2b01 cmp r3, #1 + 800cfbe: d1c6 bne.n 800cf4e + 800cfc0: e730 b.n 800ce24 + hsd->ErrorCode |= HAL_SD_ERROR_PARAM; + 800cfc2: 6ba3 ldr r3, [r4, #56] ; 0x38 + 800cfc4: f043 6300 orr.w r3, r3, #134217728 ; 0x8000000 + 800cfc8: e7c4 b.n 800cf54 + if ((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT) + 800cfca: f7fa fa07 bl 80073dc + 800cfce: 1b80 subs r0, r0, r6 + 800cfd0: 3001 adds r0, #1 + 800cfd2: d18b bne.n 800ceec + hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT; + 800cfd4: f04f 4300 mov.w r3, #2147483648 ; 0x80000000 + 800cfd8: 63a3 str r3, [r4, #56] ; 0x38 + hsd->State = HAL_SD_STATE_READY; + 800cfda: 2301 movs r3, #1 + 800cfdc: f884 3034 strb.w r3, [r4, #52] ; 0x34 + return HAL_TIMEOUT; + 800cfe0: 2503 movs r5, #3 +} + 800cfe2: 4628 mov r0, r5 + 800cfe4: b016 add sp, #88 ; 0x58 + 800cfe6: e8bd 87f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, pc} + 800cfea: bf00 nop + 800cfec: 80ffff04 .word 0x80ffff04 + 800cff0: 1fe00fff .word 0x1fe00fff + 800cff4: 18000f3a .word 0x18000f3a + +0800cff8 : + * @param SDMMCx Pointer to SDMMC register base + * @param Init SDMMC initialization structure + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_Init(SDMMC_TypeDef *SDMMCx, SDMMC_InitTypeDef Init) +{ + 800cff8: b084 sub sp, #16 + 800cffa: b510 push {r4, lr} + 800cffc: ac03 add r4, sp, #12 + 800cffe: e884 000e stmia.w r4, {r1, r2, r3} + + /* Set SDMMC configuration parameters */ +#if !defined(STM32L4P5xx) && !defined(STM32L4Q5xx) && !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx) + tmpreg |= Init.ClockBypass; +#endif + tmpreg |= (Init.ClockEdge |\ + 800d002: 9b03 ldr r3, [sp, #12] + Init.HardwareFlowControl |\ + Init.ClockDiv + ); + + /* Write to SDMMC CLKCR */ + MODIFY_REG(SDMMCx->CLKCR, CLKCR_CLEAR_MASK, tmpreg); + 800d004: 6841 ldr r1, [r0, #4] + tmpreg |= (Init.ClockEdge |\ + 800d006: 4313 orrs r3, r2 + Init.ClockPowerSave |\ + 800d008: 9a05 ldr r2, [sp, #20] + 800d00a: 4313 orrs r3, r2 + Init.BusWide |\ + 800d00c: 9a06 ldr r2, [sp, #24] + 800d00e: 4313 orrs r3, r2 + Init.HardwareFlowControl |\ + 800d010: 9a07 ldr r2, [sp, #28] + + return HAL_OK; +} + 800d012: e8bd 4010 ldmia.w sp!, {r4, lr} + Init.HardwareFlowControl |\ + 800d016: 4313 orrs r3, r2 + MODIFY_REG(SDMMCx->CLKCR, CLKCR_CLEAR_MASK, tmpreg); + 800d018: 4a03 ldr r2, [pc, #12] ; (800d028 ) + 800d01a: 400a ands r2, r1 + 800d01c: 4313 orrs r3, r2 + 800d01e: 6043 str r3, [r0, #4] +} + 800d020: b004 add sp, #16 + 800d022: 2000 movs r0, #0 + 800d024: 4770 bx lr + 800d026: bf00 nop + 800d028: ffc02c00 .word 0xffc02c00 + +0800d02c : + * @retval HAL status + */ +uint32_t SDMMC_ReadFIFO(SDMMC_TypeDef *SDMMCx) +{ + /* Read data from Rx FIFO */ + return (SDMMCx->FIFO); + 800d02c: f8d0 0080 ldr.w r0, [r0, #128] ; 0x80 +} + 800d030: 4770 bx lr + +0800d032 : + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_WriteFIFO(SDMMC_TypeDef *SDMMCx, uint32_t *pWriteData) +{ + /* Write data to FIFO */ + SDMMCx->FIFO = *pWriteData; + 800d032: 680b ldr r3, [r1, #0] + 800d034: f8c0 3080 str.w r3, [r0, #128] ; 0x80 + + return HAL_OK; +} + 800d038: 2000 movs r0, #0 + 800d03a: 4770 bx lr + +0800d03c : + * @brief Set SDMMC Power state to ON. + * @param SDMMCx Pointer to SDMMC register base + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_PowerState_ON(SDMMC_TypeDef *SDMMCx) +{ + 800d03c: b508 push {r3, lr} + /* Set power state to ON */ +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + SDMMCx->POWER |= SDMMC_POWER_PWRCTRL; + 800d03e: 6803 ldr r3, [r0, #0] + 800d040: f043 0303 orr.w r3, r3, #3 + 800d044: 6003 str r3, [r0, #0] + SDMMCx->POWER = SDMMC_POWER_PWRCTRL; +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + /* 1ms: required power up waiting time before starting the SD initialization + sequence */ + HAL_Delay(2); + 800d046: 2002 movs r0, #2 + 800d048: f7f6 fd29 bl 8003a9e + + return HAL_OK; +} + 800d04c: 2000 movs r0, #0 + 800d04e: bd08 pop {r3, pc} + +0800d050 : + * @retval HAL status + */ +HAL_StatusTypeDef SDMMC_PowerState_Cycle(SDMMC_TypeDef *SDMMCx) +{ + /* Set power state to Power Cycle*/ + SDMMCx->POWER |= SDMMC_POWER_PWRCTRL_1; + 800d050: 6803 ldr r3, [r0, #0] + 800d052: f043 0302 orr.w r3, r3, #2 + 800d056: 6003 str r3, [r0, #0] + + return HAL_OK; +} + 800d058: 2000 movs r0, #0 + 800d05a: 4770 bx lr + +0800d05c : + */ +HAL_StatusTypeDef SDMMC_PowerState_OFF(SDMMC_TypeDef *SDMMCx) +{ + /* Set power state to OFF */ +#if defined(STM32L4P5xx) || defined(STM32L4Q5xx) || defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || defined(STM32L4S7xx) || defined(STM32L4S9xx) + SDMMCx->POWER &= ~(SDMMC_POWER_PWRCTRL); + 800d05c: 6803 ldr r3, [r0, #0] + 800d05e: f023 0303 bic.w r3, r3, #3 + 800d062: 6003 str r3, [r0, #0] +#else + SDMMCx->POWER = (uint32_t)0x00000000; +#endif /* STM32L4P5xx || STM32L4Q5xx || STM32L4R5xx || STM32L4R7xx || STM32L4R9xx || STM32L4S5xx || STM32L4S7xx || STM32L4S9xx */ + + return HAL_OK; +} + 800d064: 2000 movs r0, #0 + 800d066: 4770 bx lr + +0800d068 : + * - 0x02: Power UP + * - 0x03: Power ON + */ +uint32_t SDMMC_GetPowerState(SDMMC_TypeDef *SDMMCx) +{ + return (SDMMCx->POWER & SDMMC_POWER_PWRCTRL); + 800d068: 6800 ldr r0, [r0, #0] +} + 800d06a: f000 0003 and.w r0, r0, #3 + 800d06e: 4770 bx lr + +0800d070 : + assert_param(IS_SDMMC_RESPONSE(Command->Response)); + assert_param(IS_SDMMC_WAIT(Command->WaitForInterrupt)); + assert_param(IS_SDMMC_CPSM(Command->CPSM)); + + /* Set the SDMMC Argument value */ + SDMMCx->ARG = Command->Argument; + 800d070: 680b ldr r3, [r1, #0] +{ + 800d072: b510 push {r4, lr} + SDMMCx->ARG = Command->Argument; + 800d074: 6083 str r3, [r0, #8] + + /* Set SDMMC command parameters */ + tmpreg |= (uint32_t)(Command->CmdIndex |\ + 800d076: e9d1 3201 ldrd r3, r2, [r1, #4] + 800d07a: 4313 orrs r3, r2 + Command->Response |\ + 800d07c: 68ca ldr r2, [r1, #12] + Command->WaitForInterrupt |\ + Command->CPSM); + + /* Write to SDMMC CMD register */ + MODIFY_REG(SDMMCx->CMD, CMD_CLEAR_MASK, tmpreg); + 800d07e: 68c4 ldr r4, [r0, #12] + Command->Response |\ + 800d080: 4313 orrs r3, r2 + Command->WaitForInterrupt |\ + 800d082: 690a ldr r2, [r1, #16] + 800d084: 4313 orrs r3, r2 + MODIFY_REG(SDMMCx->CMD, CMD_CLEAR_MASK, tmpreg); + 800d086: 4a03 ldr r2, [pc, #12] ; (800d094 ) + 800d088: 4022 ands r2, r4 + 800d08a: 4313 orrs r3, r2 + 800d08c: 60c3 str r3, [r0, #12] + + return HAL_OK; +} + 800d08e: 2000 movs r0, #0 + 800d090: bd10 pop {r4, pc} + 800d092: bf00 nop + 800d094: fffee0c0 .word 0xfffee0c0 + +0800d098 : + * @param SDMMCx Pointer to SDMMC register base + * @retval Command index of the last command response received + */ +uint8_t SDMMC_GetCommandResponse(SDMMC_TypeDef *SDMMCx) +{ + return (uint8_t)(SDMMCx->RESPCMD); + 800d098: 6900 ldr r0, [r0, #16] +} + 800d09a: b2c0 uxtb r0, r0 + 800d09c: 4770 bx lr + +0800d09e : + + /* Check the parameters */ + assert_param(IS_SDMMC_RESP(Response)); + + /* Get the response */ + tmp = (uint32_t)(&(SDMMCx->RESP1)) + Response; + 800d09e: 3014 adds r0, #20 + + return (*(__IO uint32_t *) tmp); + 800d0a0: 5840 ldr r0, [r0, r1] +} + 800d0a2: 4770 bx lr + +0800d0a4 : + assert_param(IS_SDMMC_TRANSFER_DIR(Data->TransferDir)); + assert_param(IS_SDMMC_TRANSFER_MODE(Data->TransferMode)); + assert_param(IS_SDMMC_DPSM(Data->DPSM)); + + /* Set the SDMMC Data TimeOut value */ + SDMMCx->DTIMER = Data->DataTimeOut; + 800d0a4: 680b ldr r3, [r1, #0] +{ + 800d0a6: b510 push {r4, lr} + SDMMCx->DTIMER = Data->DataTimeOut; + 800d0a8: 6243 str r3, [r0, #36] ; 0x24 + + /* Set the SDMMC DataLength value */ + SDMMCx->DLEN = Data->DataLength; + 800d0aa: 684b ldr r3, [r1, #4] + 800d0ac: 6283 str r3, [r0, #40] ; 0x28 + + /* Set the SDMMC data configuration parameters */ + tmpreg |= (uint32_t)(Data->DataBlockSize |\ + 800d0ae: e9d1 3402 ldrd r3, r4, [r1, #8] + 800d0b2: 4323 orrs r3, r4 + Data->TransferDir |\ + 800d0b4: 690c ldr r4, [r1, #16] + Data->TransferMode |\ + Data->DPSM); + + /* Write to SDMMC DCTRL */ + MODIFY_REG(SDMMCx->DCTRL, DCTRL_CLEAR_MASK, tmpreg); + 800d0b6: 6ac2 ldr r2, [r0, #44] ; 0x2c + Data->TransferMode |\ + 800d0b8: 6949 ldr r1, [r1, #20] + Data->TransferDir |\ + 800d0ba: 4323 orrs r3, r4 + Data->TransferMode |\ + 800d0bc: 430b orrs r3, r1 + MODIFY_REG(SDMMCx->DCTRL, DCTRL_CLEAR_MASK, tmpreg); + 800d0be: f022 02ff bic.w r2, r2, #255 ; 0xff + 800d0c2: 4313 orrs r3, r2 + 800d0c4: 62c3 str r3, [r0, #44] ; 0x2c + + return HAL_OK; + +} + 800d0c6: 2000 movs r0, #0 + 800d0c8: bd10 pop {r4, pc} + +0800d0ca : + * @param SDMMCx Pointer to SDMMC register base + * @retval Number of remaining data bytes to be transferred + */ +uint32_t SDMMC_GetDataCounter(SDMMC_TypeDef *SDMMCx) +{ + return (SDMMCx->DCOUNT); + 800d0ca: 6b00 ldr r0, [r0, #48] ; 0x30 +} + 800d0cc: 4770 bx lr + +0800d0ce : + 800d0ce: f8d0 0080 ldr.w r0, [r0, #128] ; 0x80 + 800d0d2: 4770 bx lr + +0800d0d4 : +{ + /* Check the parameters */ + assert_param(IS_SDMMC_READWAIT_MODE(SDMMC_ReadWaitMode)); + + /* Set SDMMC read wait mode */ + MODIFY_REG(SDMMCx->DCTRL, SDMMC_DCTRL_RWMOD, SDMMC_ReadWaitMode); + 800d0d4: 6ac3 ldr r3, [r0, #44] ; 0x2c + 800d0d6: f423 6380 bic.w r3, r3, #1024 ; 0x400 + 800d0da: 4319 orrs r1, r3 + 800d0dc: 62c1 str r1, [r0, #44] ; 0x2c + + return HAL_OK; +} + 800d0de: 2000 movs r0, #0 + 800d0e0: 4770 bx lr + ... + +0800d0e4 : + * @brief Send the Go Idle State command and check the response. + * @param SDMMCx Pointer to SDMMC register base + * @retval HAL status + */ +uint32_t SDMMC_CmdGoIdleState(SDMMC_TypeDef *SDMMCx) +{ + 800d0e4: b510 push {r4, lr} + SDMMC_CmdInitTypeDef sdmmc_cmdinit; + uint32_t errorstate; + + sdmmc_cmdinit.Argument = 0U; + 800d0e6: 2300 movs r3, #0 +{ + 800d0e8: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_GO_IDLE_STATE; + 800d0ea: e9cd 3301 strd r3, r3, [sp, #4] + sdmmc_cmdinit.Response = SDMMC_RESPONSE_NO; + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d0ee: e9cd 3303 strd r3, r3, [sp, #12] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0f2: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d0f4: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d0f8: 9305 str r3, [sp, #20] +{ + 800d0fa: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d0fc: f7ff ffb8 bl 800d070 + */ +static uint32_t SDMMC_GetCmdError(SDMMC_TypeDef *SDMMCx) +{ + /* 8 is the number of required instructions cycles for the below loop statement. + The SDMMC_CMDTIMEOUT is expressed in ms */ + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d100: 4b0a ldr r3, [pc, #40] ; (800d12c ) + 800d102: f44f 52fa mov.w r2, #8000 ; 0x1f40 + 800d106: 681b ldr r3, [r3, #0] + 800d108: fbb3 f3f2 udiv r3, r3, r2 + 800d10c: f241 3288 movw r2, #5000 ; 0x1388 + 800d110: 4353 muls r3, r2 + + do + { + if (count-- == 0U) + 800d112: 3b01 subs r3, #1 + 800d114: d307 bcc.n 800d126 + { + return SDMMC_ERROR_TIMEOUT; + } + + }while(!__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CMDSENT)); + 800d116: 6b62 ldr r2, [r4, #52] ; 0x34 + 800d118: 0612 lsls r2, r2, #24 + 800d11a: d5fa bpl.n 800d112 + + /* Clear all the static flags */ + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d11c: 4b04 ldr r3, [pc, #16] ; (800d130 ) + 800d11e: 63a3 str r3, [r4, #56] ; 0x38 + + return SDMMC_ERROR_NONE; + 800d120: 2000 movs r0, #0 +} + 800d122: b006 add sp, #24 + 800d124: bd10 pop {r4, pc} + return SDMMC_ERROR_TIMEOUT; + 800d126: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + return errorstate; + 800d12a: e7fa b.n 800d122 + 800d12c: 2009e2ac .word 0x2009e2ac + 800d130: 002000c5 .word 0x002000c5 + +0800d134 : + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800d134: 4b45 ldr r3, [pc, #276] ; (800d24c ) +{ + 800d136: b510 push {r4, lr} + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800d138: 681b ldr r3, [r3, #0] +{ + 800d13a: 4604 mov r4, r0 + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800d13c: f44f 50fa mov.w r0, #8000 ; 0x1f40 + 800d140: fbb3 f3f0 udiv r3, r3, r0 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || + 800d144: 4842 ldr r0, [pc, #264] ; (800d250 ) + uint32_t count = Timeout * (SystemCoreClock / 8U /1000U); + 800d146: 435a muls r2, r3 + if (count-- == 0U) + 800d148: 2a00 cmp r2, #0 + 800d14a: d048 beq.n 800d1de + sta_reg = SDMMCx->STA; + 800d14c: 6b63 ldr r3, [r4, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d14e: 4203 tst r3, r0 + 800d150: d007 beq.n 800d162 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || + 800d152: 049b lsls r3, r3, #18 + 800d154: d405 bmi.n 800d162 + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d156: 6b63 ldr r3, [r4, #52] ; 0x34 + 800d158: 0758 lsls r0, r3, #29 + 800d15a: d504 bpl.n 800d166 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d15c: 2004 movs r0, #4 + 800d15e: 63a0 str r0, [r4, #56] ; 0x38 +} + 800d160: bd10 pop {r4, pc} + 800d162: 3a01 subs r2, #1 + 800d164: e7f0 b.n 800d148 + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d166: 6b60 ldr r0, [r4, #52] ; 0x34 + 800d168: f010 0001 ands.w r0, r0, #1 + 800d16c: d002 beq.n 800d174 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d16e: 2301 movs r3, #1 + 800d170: 63a3 str r3, [r4, #56] ; 0x38 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d172: e7f5 b.n 800d160 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d174: 4b37 ldr r3, [pc, #220] ; (800d254 ) + 800d176: 63a3 str r3, [r4, #56] ; 0x38 + return (uint8_t)(SDMMCx->RESPCMD); + 800d178: 6923 ldr r3, [r4, #16] + if(SDMMC_GetCommandResponse(SDMMCx) != SD_CMD) + 800d17a: b2db uxtb r3, r3 + 800d17c: 4299 cmp r1, r3 + 800d17e: d131 bne.n 800d1e4 + return (*(__IO uint32_t *) tmp); + 800d180: 6963 ldr r3, [r4, #20] + if((response_r1 & SDMMC_OCR_ERRORBITS) == SDMMC_ALLZERO) + 800d182: 4835 ldr r0, [pc, #212] ; (800d258 ) + 800d184: 4018 ands r0, r3 + 800d186: 2800 cmp r0, #0 + 800d188: d0ea beq.n 800d160 + else if((response_r1 & SDMMC_OCR_ADDR_OUT_OF_RANGE) == SDMMC_OCR_ADDR_OUT_OF_RANGE) + 800d18a: 2b00 cmp r3, #0 + 800d18c: db2c blt.n 800d1e8 + else if((response_r1 & SDMMC_OCR_ADDR_MISALIGNED) == SDMMC_OCR_ADDR_MISALIGNED) + 800d18e: 005a lsls r2, r3, #1 + 800d190: d42d bmi.n 800d1ee + else if((response_r1 & SDMMC_OCR_BLOCK_LEN_ERR) == SDMMC_OCR_BLOCK_LEN_ERR) + 800d192: 009c lsls r4, r3, #2 + 800d194: d42d bmi.n 800d1f2 + else if((response_r1 & SDMMC_OCR_ERASE_SEQ_ERR) == SDMMC_OCR_ERASE_SEQ_ERR) + 800d196: 00d9 lsls r1, r3, #3 + 800d198: d42d bmi.n 800d1f6 + else if((response_r1 & SDMMC_OCR_BAD_ERASE_PARAM) == SDMMC_OCR_BAD_ERASE_PARAM) + 800d19a: 011a lsls r2, r3, #4 + 800d19c: d42e bmi.n 800d1fc + else if((response_r1 & SDMMC_OCR_WRITE_PROT_VIOLATION) == SDMMC_OCR_WRITE_PROT_VIOLATION) + 800d19e: 015c lsls r4, r3, #5 + 800d1a0: d42f bmi.n 800d202 + else if((response_r1 & SDMMC_OCR_LOCK_UNLOCK_FAILED) == SDMMC_OCR_LOCK_UNLOCK_FAILED) + 800d1a2: 01d9 lsls r1, r3, #7 + 800d1a4: d430 bmi.n 800d208 + else if((response_r1 & SDMMC_OCR_COM_CRC_FAILED) == SDMMC_OCR_COM_CRC_FAILED) + 800d1a6: 021a lsls r2, r3, #8 + 800d1a8: d431 bmi.n 800d20e + else if((response_r1 & SDMMC_OCR_ILLEGAL_CMD) == SDMMC_OCR_ILLEGAL_CMD) + 800d1aa: 025c lsls r4, r3, #9 + 800d1ac: d432 bmi.n 800d214 + else if((response_r1 & SDMMC_OCR_CARD_ECC_FAILED) == SDMMC_OCR_CARD_ECC_FAILED) + 800d1ae: 0299 lsls r1, r3, #10 + 800d1b0: d433 bmi.n 800d21a + else if((response_r1 & SDMMC_OCR_CC_ERROR) == SDMMC_OCR_CC_ERROR) + 800d1b2: 02da lsls r2, r3, #11 + 800d1b4: d434 bmi.n 800d220 + else if((response_r1 & SDMMC_OCR_STREAM_READ_UNDERRUN) == SDMMC_OCR_STREAM_READ_UNDERRUN) + 800d1b6: 035c lsls r4, r3, #13 + 800d1b8: d435 bmi.n 800d226 + else if((response_r1 & SDMMC_OCR_STREAM_WRITE_OVERRUN) == SDMMC_OCR_STREAM_WRITE_OVERRUN) + 800d1ba: 0399 lsls r1, r3, #14 + 800d1bc: d436 bmi.n 800d22c + else if((response_r1 & SDMMC_OCR_CID_CSD_OVERWRITE) == SDMMC_OCR_CID_CSD_OVERWRITE) + 800d1be: 03da lsls r2, r3, #15 + 800d1c0: d437 bmi.n 800d232 + else if((response_r1 & SDMMC_OCR_WP_ERASE_SKIP) == SDMMC_OCR_WP_ERASE_SKIP) + 800d1c2: 041c lsls r4, r3, #16 + 800d1c4: d438 bmi.n 800d238 + else if((response_r1 & SDMMC_OCR_CARD_ECC_DISABLED) == SDMMC_OCR_CARD_ECC_DISABLED) + 800d1c6: 0459 lsls r1, r3, #17 + 800d1c8: d439 bmi.n 800d23e + else if((response_r1 & SDMMC_OCR_ERASE_RESET) == SDMMC_OCR_ERASE_RESET) + 800d1ca: 049a lsls r2, r3, #18 + 800d1cc: d43a bmi.n 800d244 + return SDMMC_ERROR_GENERAL_UNKNOWN_ERR; + 800d1ce: f013 0f08 tst.w r3, #8 + 800d1d2: bf14 ite ne + 800d1d4: f44f 0000 movne.w r0, #8388608 ; 0x800000 + 800d1d8: f44f 3080 moveq.w r0, #65536 ; 0x10000 + 800d1dc: e7c0 b.n 800d160 + return SDMMC_ERROR_TIMEOUT; + 800d1de: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800d1e2: e7bd b.n 800d160 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d1e4: 2001 movs r0, #1 + 800d1e6: e7bb b.n 800d160 + return SDMMC_ERROR_ADDR_OUT_OF_RANGE; + 800d1e8: f04f 7000 mov.w r0, #33554432 ; 0x2000000 + 800d1ec: e7b8 b.n 800d160 + return SDMMC_ERROR_ADDR_MISALIGNED; + 800d1ee: 2040 movs r0, #64 ; 0x40 + 800d1f0: e7b6 b.n 800d160 + return SDMMC_ERROR_BLOCK_LEN_ERR; + 800d1f2: 2080 movs r0, #128 ; 0x80 + 800d1f4: e7b4 b.n 800d160 + return SDMMC_ERROR_ERASE_SEQ_ERR; + 800d1f6: f44f 7080 mov.w r0, #256 ; 0x100 + 800d1fa: e7b1 b.n 800d160 + return SDMMC_ERROR_BAD_ERASE_PARAM; + 800d1fc: f44f 7000 mov.w r0, #512 ; 0x200 + 800d200: e7ae b.n 800d160 + return SDMMC_ERROR_WRITE_PROT_VIOLATION; + 800d202: f44f 6080 mov.w r0, #1024 ; 0x400 + 800d206: e7ab b.n 800d160 + return SDMMC_ERROR_LOCK_UNLOCK_FAILED; + 800d208: f44f 6000 mov.w r0, #2048 ; 0x800 + 800d20c: e7a8 b.n 800d160 + return SDMMC_ERROR_COM_CRC_FAILED; + 800d20e: f44f 5080 mov.w r0, #4096 ; 0x1000 + 800d212: e7a5 b.n 800d160 + return SDMMC_ERROR_ILLEGAL_CMD; + 800d214: f44f 5000 mov.w r0, #8192 ; 0x2000 + 800d218: e7a2 b.n 800d160 + return SDMMC_ERROR_CARD_ECC_FAILED; + 800d21a: f44f 4080 mov.w r0, #16384 ; 0x4000 + 800d21e: e79f b.n 800d160 + return SDMMC_ERROR_CC_ERR; + 800d220: f44f 4000 mov.w r0, #32768 ; 0x8000 + 800d224: e79c b.n 800d160 + return SDMMC_ERROR_STREAM_READ_UNDERRUN; + 800d226: f44f 3000 mov.w r0, #131072 ; 0x20000 + 800d22a: e799 b.n 800d160 + return SDMMC_ERROR_STREAM_WRITE_OVERRUN; + 800d22c: f44f 2080 mov.w r0, #262144 ; 0x40000 + 800d230: e796 b.n 800d160 + return SDMMC_ERROR_CID_CSD_OVERWRITE; + 800d232: f44f 2000 mov.w r0, #524288 ; 0x80000 + 800d236: e793 b.n 800d160 + return SDMMC_ERROR_WP_ERASE_SKIP; + 800d238: f44f 1080 mov.w r0, #1048576 ; 0x100000 + 800d23c: e790 b.n 800d160 + return SDMMC_ERROR_CARD_ECC_DISABLED; + 800d23e: f44f 1000 mov.w r0, #2097152 ; 0x200000 + 800d242: e78d b.n 800d160 + return SDMMC_ERROR_ERASE_RESET; + 800d244: f44f 0080 mov.w r0, #4194304 ; 0x400000 + 800d248: e78a b.n 800d160 + 800d24a: bf00 nop + 800d24c: 2009e2ac .word 0x2009e2ac + 800d250: 00200045 .word 0x00200045 + 800d254: 002000c5 .word 0x002000c5 + 800d258: fdffe008 .word 0xfdffe008 + +0800d25c : +{ + 800d25c: b530 push {r4, r5, lr} + 800d25e: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d260: 2510 movs r5, #16 + 800d262: f44f 7380 mov.w r3, #256 ; 0x100 + 800d266: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d26a: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d26c: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)BlockSize; + 800d270: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d272: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d274: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d276: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d27a: f7ff fef9 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SET_BLOCKLEN, SDMMC_CMDTIMEOUT); + 800d27e: f241 3288 movw r2, #5000 ; 0x1388 + 800d282: 4629 mov r1, r5 + 800d284: 4620 mov r0, r4 + 800d286: f7ff ff55 bl 800d134 +} + 800d28a: b007 add sp, #28 + 800d28c: bd30 pop {r4, r5, pc} + +0800d28e : +{ + 800d28e: b530 push {r4, r5, lr} + 800d290: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d292: 2511 movs r5, #17 + 800d294: f44f 7380 mov.w r3, #256 ; 0x100 + 800d298: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d29c: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d29e: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)ReadAdd; + 800d2a2: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2a4: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2a6: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2a8: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2ac: f7ff fee0 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_READ_SINGLE_BLOCK, SDMMC_CMDTIMEOUT); + 800d2b0: f241 3288 movw r2, #5000 ; 0x1388 + 800d2b4: 4629 mov r1, r5 + 800d2b6: 4620 mov r0, r4 + 800d2b8: f7ff ff3c bl 800d134 +} + 800d2bc: b007 add sp, #28 + 800d2be: bd30 pop {r4, r5, pc} + +0800d2c0 : +{ + 800d2c0: b530 push {r4, r5, lr} + 800d2c2: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d2c4: 2512 movs r5, #18 + 800d2c6: f44f 7380 mov.w r3, #256 ; 0x100 + 800d2ca: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d2ce: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2d0: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)ReadAdd; + 800d2d4: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2d6: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2d8: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d2da: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d2de: f7ff fec7 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_READ_MULT_BLOCK, SDMMC_CMDTIMEOUT); + 800d2e2: f241 3288 movw r2, #5000 ; 0x1388 + 800d2e6: 4629 mov r1, r5 + 800d2e8: 4620 mov r0, r4 + 800d2ea: f7ff ff23 bl 800d134 +} + 800d2ee: b007 add sp, #28 + 800d2f0: bd30 pop {r4, r5, pc} + +0800d2f2 : +{ + 800d2f2: b530 push {r4, r5, lr} + 800d2f4: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d2f6: 2518 movs r5, #24 + 800d2f8: f44f 7380 mov.w r3, #256 ; 0x100 + 800d2fc: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d300: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d302: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)WriteAdd; + 800d306: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d308: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d30a: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d30c: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d310: f7ff feae bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_WRITE_SINGLE_BLOCK, SDMMC_CMDTIMEOUT); + 800d314: f241 3288 movw r2, #5000 ; 0x1388 + 800d318: 4629 mov r1, r5 + 800d31a: 4620 mov r0, r4 + 800d31c: f7ff ff0a bl 800d134 +} + 800d320: b007 add sp, #28 + 800d322: bd30 pop {r4, r5, pc} + +0800d324 : +{ + 800d324: b530 push {r4, r5, lr} + 800d326: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d328: 2519 movs r5, #25 + 800d32a: f44f 7380 mov.w r3, #256 ; 0x100 + 800d32e: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d332: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d334: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)WriteAdd; + 800d338: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d33a: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d33c: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d33e: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d342: f7ff fe95 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_WRITE_MULT_BLOCK, SDMMC_CMDTIMEOUT); + 800d346: f241 3288 movw r2, #5000 ; 0x1388 + 800d34a: 4629 mov r1, r5 + 800d34c: 4620 mov r0, r4 + 800d34e: f7ff fef1 bl 800d134 +} + 800d352: b007 add sp, #28 + 800d354: bd30 pop {r4, r5, pc} + +0800d356 : +{ + 800d356: b530 push {r4, r5, lr} + 800d358: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d35a: 2520 movs r5, #32 + 800d35c: f44f 7380 mov.w r3, #256 ; 0x100 + 800d360: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d364: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d366: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)StartAdd; + 800d36a: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d36c: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d36e: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d370: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d374: f7ff fe7c bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_ERASE_GRP_START, SDMMC_CMDTIMEOUT); + 800d378: f241 3288 movw r2, #5000 ; 0x1388 + 800d37c: 4629 mov r1, r5 + 800d37e: 4620 mov r0, r4 + 800d380: f7ff fed8 bl 800d134 +} + 800d384: b007 add sp, #28 + 800d386: bd30 pop {r4, r5, pc} + +0800d388 : +{ + 800d388: b530 push {r4, r5, lr} + 800d38a: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d38c: 2521 movs r5, #33 ; 0x21 + 800d38e: f44f 7380 mov.w r3, #256 ; 0x100 + 800d392: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d396: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d398: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)EndAdd; + 800d39c: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d39e: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3a0: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3a2: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3a6: f7ff fe63 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_ERASE_GRP_END, SDMMC_CMDTIMEOUT); + 800d3aa: f241 3288 movw r2, #5000 ; 0x1388 + 800d3ae: 4629 mov r1, r5 + 800d3b0: 4620 mov r0, r4 + 800d3b2: f7ff febf bl 800d134 +} + 800d3b6: b007 add sp, #28 + 800d3b8: bd30 pop {r4, r5, pc} + +0800d3ba : +{ + 800d3ba: b530 push {r4, r5, lr} + 800d3bc: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d3be: 2523 movs r5, #35 ; 0x23 + 800d3c0: f44f 7380 mov.w r3, #256 ; 0x100 + 800d3c4: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d3c8: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3ca: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)StartAdd; + 800d3ce: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3d0: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3d2: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3d4: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d3d8: f7ff fe4a bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE_GRP_START, SDMMC_CMDTIMEOUT); + 800d3dc: f241 3288 movw r2, #5000 ; 0x1388 + 800d3e0: 4629 mov r1, r5 + 800d3e2: 4620 mov r0, r4 + 800d3e4: f7ff fea6 bl 800d134 +} + 800d3e8: b007 add sp, #28 + 800d3ea: bd30 pop {r4, r5, pc} + +0800d3ec : +{ + 800d3ec: b530 push {r4, r5, lr} + 800d3ee: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d3f0: 2524 movs r5, #36 ; 0x24 + 800d3f2: f44f 7380 mov.w r3, #256 ; 0x100 + 800d3f6: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d3fa: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d3fc: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)EndAdd; + 800d400: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d402: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d404: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d406: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d40a: f7ff fe31 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE_GRP_END, SDMMC_CMDTIMEOUT); + 800d40e: f241 3288 movw r2, #5000 ; 0x1388 + 800d412: 4629 mov r1, r5 + 800d414: 4620 mov r0, r4 + 800d416: f7ff fe8d bl 800d134 +} + 800d41a: b007 add sp, #28 + 800d41c: bd30 pop {r4, r5, pc} + +0800d41e : +{ + 800d41e: b530 push {r4, r5, lr} + 800d420: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d422: 2526 movs r5, #38 ; 0x26 + 800d424: f44f 7380 mov.w r3, #256 ; 0x100 + 800d428: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d42c: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d42e: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = EraseType; + 800d432: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d434: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d436: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d438: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d43c: f7ff fe18 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_ERASE, SDMMC_MAXERASETIMEOUT); + 800d440: f24f 6218 movw r2, #63000 ; 0xf618 + 800d444: 4629 mov r1, r5 + 800d446: 4620 mov r0, r4 + 800d448: f7ff fe74 bl 800d134 +} + 800d44c: b007 add sp, #28 + 800d44e: bd30 pop {r4, r5, pc} + +0800d450 : +{ + 800d450: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d452: 2300 movs r3, #0 +{ + 800d454: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d456: 250c movs r5, #12 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d458: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d45c: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_STOP_TRANSMISSION; + 800d460: e9cd 3501 strd r3, r5, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d464: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d468: 9305 str r3, [sp, #20] + __SDMMC_CMDSTOP_ENABLE(SDMMCx); + 800d46a: 68c3 ldr r3, [r0, #12] + 800d46c: f043 0380 orr.w r3, r3, #128 ; 0x80 + 800d470: 60c3 str r3, [r0, #12] + __SDMMC_CMDTRANS_DISABLE(SDMMCx); + 800d472: 68c3 ldr r3, [r0, #12] + 800d474: f023 0340 bic.w r3, r3, #64 ; 0x40 +{ + 800d478: 4604 mov r4, r0 + __SDMMC_CMDTRANS_DISABLE(SDMMCx); + 800d47a: 60c3 str r3, [r0, #12] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d47c: a901 add r1, sp, #4 + 800d47e: f7ff fdf7 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_STOP_TRANSMISSION, SDMMC_STOPTRANSFERTIMEOUT); + 800d482: 4a05 ldr r2, [pc, #20] ; (800d498 ) + 800d484: 4629 mov r1, r5 + 800d486: 4620 mov r0, r4 + 800d488: f7ff fe54 bl 800d134 + __SDMMC_CMDSTOP_DISABLE(SDMMCx); + 800d48c: 68e3 ldr r3, [r4, #12] + 800d48e: f023 0380 bic.w r3, r3, #128 ; 0x80 + 800d492: 60e3 str r3, [r4, #12] +} + 800d494: b007 add sp, #28 + 800d496: bd30 pop {r4, r5, pc} + 800d498: 05f5e100 .word 0x05f5e100 + +0800d49c : +{ + 800d49c: b530 push {r4, r5, lr} + 800d49e: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d4a0: 2507 movs r5, #7 + 800d4a2: f44f 7380 mov.w r3, #256 ; 0x100 + 800d4a6: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d4aa: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4ac: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)Addr; + 800d4b0: 9201 str r2, [sp, #4] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d4b2: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4b4: 2200 movs r2, #0 + 800d4b6: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d4ba: f7ff fdd9 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SEL_DESEL_CARD, SDMMC_CMDTIMEOUT); + 800d4be: f241 3288 movw r2, #5000 ; 0x1388 + 800d4c2: 4629 mov r1, r5 + 800d4c4: 4620 mov r0, r4 + 800d4c6: f7ff fe35 bl 800d134 +} + 800d4ca: b007 add sp, #28 + 800d4cc: bd30 pop {r4, r5, pc} + +0800d4ce : +{ + 800d4ce: b530 push {r4, r5, lr} + 800d4d0: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d4d2: 2537 movs r5, #55 ; 0x37 + 800d4d4: f44f 7380 mov.w r3, #256 ; 0x100 + 800d4d8: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d4dc: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4de: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)Argument; + 800d4e2: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4e4: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d4e6: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d4e8: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d4ec: f7ff fdc0 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_APP_CMD, SDMMC_CMDTIMEOUT); + 800d4f0: f241 3288 movw r2, #5000 ; 0x1388 + 800d4f4: 4629 mov r1, r5 + 800d4f6: 4620 mov r0, r4 + 800d4f8: f7ff fe1c bl 800d134 +} + 800d4fc: b007 add sp, #28 + 800d4fe: bd30 pop {r4, r5, pc} + +0800d500 : +{ + 800d500: b530 push {r4, r5, lr} + 800d502: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d504: 2506 movs r5, #6 + 800d506: f44f 7380 mov.w r3, #256 ; 0x100 + 800d50a: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d50e: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d510: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = (uint32_t)BusWidth; + 800d514: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d516: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d518: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d51a: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d51e: f7ff fda7 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_APP_SD_SET_BUSWIDTH, SDMMC_CMDTIMEOUT); + 800d522: f241 3288 movw r2, #5000 ; 0x1388 + 800d526: 4629 mov r1, r5 + 800d528: 4620 mov r0, r4 + 800d52a: f7ff fe03 bl 800d134 +} + 800d52e: b007 add sp, #28 + 800d530: bd30 pop {r4, r5, pc} + +0800d532 : + 800d532: f7ff bfe5 b.w 800d500 + +0800d536 : +{ + 800d536: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d538: 2300 movs r3, #0 +{ + 800d53a: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d53c: 2533 movs r5, #51 ; 0x33 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d53e: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d542: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_SEND_SCR; + 800d546: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d54a: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d54c: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d550: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d552: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d554: f7ff fd8c bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_APP_SEND_SCR, SDMMC_CMDTIMEOUT); + 800d558: f241 3288 movw r2, #5000 ; 0x1388 + 800d55c: 4629 mov r1, r5 + 800d55e: 4620 mov r0, r4 + 800d560: f7ff fde8 bl 800d134 +} + 800d564: b007 add sp, #28 + 800d566: bd30 pop {r4, r5, pc} + +0800d568 : +{ + 800d568: b530 push {r4, r5, lr} + 800d56a: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d56c: 2503 movs r5, #3 + sdmmc_cmdinit.Argument = ((uint32_t)RCA << 16U); + 800d56e: 0409 lsls r1, r1, #16 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d570: f44f 7380 mov.w r3, #256 ; 0x100 + 800d574: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d578: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d57a: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = ((uint32_t)RCA << 16U); + 800d57e: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d580: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d582: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d584: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d588: f7ff fd72 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SET_REL_ADDR, SDMMC_CMDTIMEOUT); + 800d58c: f241 3288 movw r2, #5000 ; 0x1388 + 800d590: 4629 mov r1, r5 + 800d592: 4620 mov r0, r4 + 800d594: f7ff fdce bl 800d134 +} + 800d598: b007 add sp, #28 + 800d59a: bd30 pop {r4, r5, pc} + +0800d59c : +{ + 800d59c: b530 push {r4, r5, lr} + 800d59e: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d5a0: 250d movs r5, #13 + 800d5a2: f44f 7380 mov.w r3, #256 ; 0x100 + 800d5a6: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d5aa: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5ac: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = Argument; + 800d5b0: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5b2: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5b4: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5b6: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5ba: f7ff fd59 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SEND_STATUS, SDMMC_CMDTIMEOUT); + 800d5be: f241 3288 movw r2, #5000 ; 0x1388 + 800d5c2: 4629 mov r1, r5 + 800d5c4: 4620 mov r0, r4 + 800d5c6: f7ff fdb5 bl 800d134 +} + 800d5ca: b007 add sp, #28 + 800d5cc: bd30 pop {r4, r5, pc} + +0800d5ce : +{ + 800d5ce: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d5d0: 2300 movs r3, #0 +{ + 800d5d2: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d5d4: 250d movs r5, #13 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d5d6: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d5da: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SD_APP_STATUS; + 800d5de: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d5e2: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5e4: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5e8: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d5ea: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d5ec: f7ff fd40 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_SD_APP_STATUS, SDMMC_CMDTIMEOUT); + 800d5f0: f241 3288 movw r2, #5000 ; 0x1388 + 800d5f4: 4629 mov r1, r5 + 800d5f6: 4620 mov r0, r4 + 800d5f8: f7ff fd9c bl 800d134 +} + 800d5fc: b007 add sp, #28 + 800d5fe: bd30 pop {r4, r5, pc} + +0800d600 : +{ + 800d600: b530 push {r4, r5, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d602: 2300 movs r3, #0 +{ + 800d604: b087 sub sp, #28 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d606: 250b movs r5, #11 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d608: f44f 7280 mov.w r2, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d60c: e9cd 2303 strd r2, r3, [sp, #12] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_VOLTAGE_SWITCH; + 800d610: e9cd 3501 strd r3, r5, [sp, #4] +{ + 800d614: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d616: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d61a: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d61c: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d61e: f7ff fd27 bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_VOLTAGE_SWITCH, SDMMC_CMDTIMEOUT); + 800d622: f241 3288 movw r2, #5000 ; 0x1388 + 800d626: 4629 mov r1, r5 + 800d628: 4620 mov r0, r4 + 800d62a: f7ff fd83 bl 800d134 +} + 800d62e: b007 add sp, #28 + 800d630: bd30 pop {r4, r5, pc} + +0800d632 : +{ + 800d632: b530 push {r4, r5, lr} + 800d634: b087 sub sp, #28 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d636: 2508 movs r5, #8 + 800d638: f44f 7380 mov.w r3, #256 ; 0x100 + 800d63c: e9cd 5302 strd r5, r3, [sp, #8] +{ + 800d640: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d642: f44f 5380 mov.w r3, #4096 ; 0x1000 + sdmmc_cmdinit.Argument = Argument; + 800d646: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d648: 2200 movs r2, #0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d64a: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d64c: e9cd 2304 strd r2, r3, [sp, #16] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d650: f7ff fd0e bl 800d070 + errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_HS_SEND_EXT_CSD,SDMMC_CMDTIMEOUT); + 800d654: f241 3288 movw r2, #5000 ; 0x1388 + 800d658: 4629 mov r1, r5 + 800d65a: 4620 mov r0, r4 + 800d65c: f7ff fd6a bl 800d134 +} + 800d660: b007 add sp, #28 + 800d662: bd30 pop {r4, r5, pc} + +0800d664 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d664: 4b11 ldr r3, [pc, #68] ; (800d6ac ) + 800d666: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d66a: 681b ldr r3, [r3, #0] + 800d66c: fbb3 f3f1 udiv r3, r3, r1 + 800d670: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d674: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d676: 434b muls r3, r1 + if (count-- == 0U) + 800d678: 3b01 subs r3, #1 + 800d67a: d313 bcc.n 800d6a4 + sta_reg = SDMMCx->STA; + 800d67c: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d67e: f011 0f45 tst.w r1, #69 ; 0x45 + 800d682: d0f9 beq.n 800d678 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d684: 0489 lsls r1, r1, #18 + 800d686: d4f7 bmi.n 800d678 + if (__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d688: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d68a: 075b lsls r3, r3, #29 + 800d68c: d502 bpl.n 800d694 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d68e: 2004 movs r0, #4 + 800d690: 6390 str r0, [r2, #56] ; 0x38 + return SDMMC_ERROR_CMD_RSP_TIMEOUT; + 800d692: 4770 bx lr + else if (__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d694: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d696: f010 0001 ands.w r0, r0, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d69a: bf0c ite eq + 800d69c: 4b04 ldreq r3, [pc, #16] ; (800d6b0 ) + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d69e: 2301 movne r3, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d6a0: 6393 str r3, [r2, #56] ; 0x38 + return SDMMC_ERROR_NONE; + 800d6a2: 4770 bx lr + return SDMMC_ERROR_TIMEOUT; + 800d6a4: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d6a8: 4770 bx lr + 800d6aa: bf00 nop + 800d6ac: 2009e2ac .word 0x2009e2ac + 800d6b0: 002000c5 .word 0x002000c5 + +0800d6b4 : +{ + 800d6b4: b510 push {r4, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_ALL_SEND_CID; + 800d6b6: 2300 movs r3, #0 +{ + 800d6b8: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_ALL_SEND_CID; + 800d6ba: 2202 movs r2, #2 + 800d6bc: e9cd 3201 strd r3, r2, [sp, #4] + sdmmc_cmdinit.Response = SDMMC_RESPONSE_LONG; + 800d6c0: f44f 7240 mov.w r2, #768 ; 0x300 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d6c4: e9cd 2303 strd r2, r3, [sp, #12] +{ + 800d6c8: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d6ca: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d6ce: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d6d0: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d6d2: f7ff fccd bl 800d070 + errorstate = SDMMC_GetCmdResp2(SDMMCx); + 800d6d6: 4620 mov r0, r4 + 800d6d8: f7ff ffc4 bl 800d664 +} + 800d6dc: b006 add sp, #24 + 800d6de: bd10 pop {r4, pc} + +0800d6e0 : +{ + 800d6e0: b510 push {r4, lr} + 800d6e2: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_LONG; + 800d6e4: 2209 movs r2, #9 + 800d6e6: f44f 7340 mov.w r3, #768 ; 0x300 + 800d6ea: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d6ee: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d6f0: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d6f4: 2100 movs r1, #0 + 800d6f6: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d6fa: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d6fc: a901 add r1, sp, #4 + 800d6fe: f7ff fcb7 bl 800d070 + errorstate = SDMMC_GetCmdResp2(SDMMCx); + 800d702: 4620 mov r0, r4 + 800d704: f7ff ffae bl 800d664 +} + 800d708: b006 add sp, #24 + 800d70a: bd10 pop {r4, pc} + +0800d70c : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d70c: 4b0e ldr r3, [pc, #56] ; (800d748 ) + 800d70e: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d712: 681b ldr r3, [r3, #0] + 800d714: fbb3 f3f1 udiv r3, r3, r1 + 800d718: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d71c: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d71e: 434b muls r3, r1 + if (count-- == 0U) + 800d720: 3b01 subs r3, #1 + 800d722: d30e bcc.n 800d742 + sta_reg = SDMMCx->STA; + 800d724: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d726: f011 0f45 tst.w r1, #69 ; 0x45 + 800d72a: d0f9 beq.n 800d720 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d72c: 0489 lsls r1, r1, #18 + 800d72e: d4f7 bmi.n 800d720 + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d730: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d732: f010 0004 ands.w r0, r0, #4 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d736: bf15 itete ne + 800d738: 2004 movne r0, #4 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d73a: 4b04 ldreq r3, [pc, #16] ; (800d74c ) + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d73c: 6390 strne r0, [r2, #56] ; 0x38 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d73e: 6393 streq r3, [r2, #56] ; 0x38 + return SDMMC_ERROR_NONE; + 800d740: 4770 bx lr + return SDMMC_ERROR_TIMEOUT; + 800d742: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d746: 4770 bx lr + 800d748: 2009e2ac .word 0x2009e2ac + 800d74c: 002000c5 .word 0x002000c5 + +0800d750 : +{ + 800d750: b510 push {r4, lr} + 800d752: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d754: 2229 movs r2, #41 ; 0x29 + 800d756: f44f 7380 mov.w r3, #256 ; 0x100 + 800d75a: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d75e: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d760: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d764: 2100 movs r1, #0 + 800d766: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d76a: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d76c: a901 add r1, sp, #4 + 800d76e: f7ff fc7f bl 800d070 + errorstate = SDMMC_GetCmdResp3(SDMMCx); + 800d772: 4620 mov r0, r4 + 800d774: f7ff ffca bl 800d70c +} + 800d778: b006 add sp, #24 + 800d77a: bd10 pop {r4, pc} + +0800d77c : +{ + 800d77c: b510 push {r4, lr} + 800d77e: b086 sub sp, #24 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d780: 2201 movs r2, #1 + 800d782: f44f 7380 mov.w r3, #256 ; 0x100 + 800d786: e9cd 2302 strd r2, r3, [sp, #8] + sdmmc_cmdinit.Argument = Argument; + 800d78a: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d78c: f44f 5380 mov.w r3, #4096 ; 0x1000 + 800d790: 2100 movs r1, #0 + 800d792: e9cd 1304 strd r1, r3, [sp, #16] +{ + 800d796: 4604 mov r4, r0 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d798: a901 add r1, sp, #4 + 800d79a: f7ff fc69 bl 800d070 + errorstate = SDMMC_GetCmdResp3(SDMMCx); + 800d79e: 4620 mov r0, r4 + 800d7a0: f7ff ffb4 bl 800d70c +} + 800d7a4: b006 add sp, #24 + 800d7a6: bd10 pop {r4, pc} + +0800d7a8 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d7a8: 4b1f ldr r3, [pc, #124] ; (800d828 ) +{ + 800d7aa: b510 push {r4, lr} + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d7ac: 681b ldr r3, [r3, #0] +{ + 800d7ae: 4604 mov r4, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d7b0: f44f 50fa mov.w r0, #8000 ; 0x1f40 + 800d7b4: fbb3 f3f0 udiv r3, r3, r0 + 800d7b8: f241 3088 movw r0, #5000 ; 0x1388 + 800d7bc: 4343 muls r3, r0 + if (count-- == 0U) + 800d7be: 3b01 subs r3, #1 + 800d7c0: d329 bcc.n 800d816 + sta_reg = SDMMCx->STA; + 800d7c2: 6b60 ldr r0, [r4, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d7c4: f010 0f45 tst.w r0, #69 ; 0x45 + 800d7c8: d0f9 beq.n 800d7be + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d7ca: 0480 lsls r0, r0, #18 + 800d7cc: d4f7 bmi.n 800d7be + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d7ce: 6b63 ldr r3, [r4, #52] ; 0x34 + 800d7d0: 0758 lsls r0, r3, #29 + 800d7d2: d502 bpl.n 800d7da + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d7d4: 2004 movs r0, #4 + 800d7d6: 63a0 str r0, [r4, #56] ; 0x38 +} + 800d7d8: bd10 pop {r4, pc} + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d7da: 6b60 ldr r0, [r4, #52] ; 0x34 + 800d7dc: f010 0001 ands.w r0, r0, #1 + 800d7e0: d002 beq.n 800d7e8 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d7e2: 2301 movs r3, #1 + 800d7e4: 63a3 str r3, [r4, #56] ; 0x38 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d7e6: e7f7 b.n 800d7d8 + return (uint8_t)(SDMMCx->RESPCMD); + 800d7e8: 6923 ldr r3, [r4, #16] + if(SDMMC_GetCommandResponse(SDMMCx) != SD_CMD) + 800d7ea: b2db uxtb r3, r3 + 800d7ec: 4299 cmp r1, r3 + 800d7ee: d115 bne.n 800d81c + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_STATIC_CMD_FLAGS); + 800d7f0: 4b0e ldr r3, [pc, #56] ; (800d82c ) + 800d7f2: 63a3 str r3, [r4, #56] ; 0x38 + return (*(__IO uint32_t *) tmp); + 800d7f4: 6963 ldr r3, [r4, #20] + if((response_r1 & (SDMMC_R6_GENERAL_UNKNOWN_ERROR | SDMMC_R6_ILLEGAL_CMD | SDMMC_R6_COM_CRC_FAILED)) == SDMMC_ALLZERO) + 800d7f6: f413 4060 ands.w r0, r3, #57344 ; 0xe000 + 800d7fa: d102 bne.n 800d802 + *pRCA = (uint16_t) (response_r1 >> 16); + 800d7fc: 0c1b lsrs r3, r3, #16 + 800d7fe: 8013 strh r3, [r2, #0] + return SDMMC_ERROR_NONE; + 800d800: e7ea b.n 800d7d8 + else if((response_r1 & SDMMC_R6_ILLEGAL_CMD) == SDMMC_R6_ILLEGAL_CMD) + 800d802: 045a lsls r2, r3, #17 + 800d804: d40c bmi.n 800d820 + return SDMMC_ERROR_GENERAL_UNKNOWN_ERR; + 800d806: f413 4f00 tst.w r3, #32768 ; 0x8000 + 800d80a: bf14 ite ne + 800d80c: f44f 5080 movne.w r0, #4096 ; 0x1000 + 800d810: f44f 3080 moveq.w r0, #65536 ; 0x10000 + 800d814: e7e0 b.n 800d7d8 + return SDMMC_ERROR_TIMEOUT; + 800d816: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 + 800d81a: e7dd b.n 800d7d8 + return SDMMC_ERROR_CMD_CRC_FAIL; + 800d81c: 2001 movs r0, #1 + 800d81e: e7db b.n 800d7d8 + return SDMMC_ERROR_ILLEGAL_CMD; + 800d820: f44f 5000 mov.w r0, #8192 ; 0x2000 + 800d824: e7d8 b.n 800d7d8 + 800d826: bf00 nop + 800d828: 2009e2ac .word 0x2009e2ac + 800d82c: 002000c5 .word 0x002000c5 + +0800d830 : +{ + 800d830: b530 push {r4, r5, lr} + 800d832: b089 sub sp, #36 ; 0x24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d834: 2300 movs r3, #0 +{ + 800d836: 9101 str r1, [sp, #4] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d838: 2503 movs r5, #3 + sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT; + 800d83a: f44f 7180 mov.w r1, #256 ; 0x100 + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d83e: e9cd 1305 strd r1, r3, [sp, #20] + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_SET_REL_ADDR; + 800d842: e9cd 3503 strd r3, r5, [sp, #12] +{ + 800d846: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d848: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d84c: a903 add r1, sp, #12 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d84e: 9307 str r3, [sp, #28] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d850: f7ff fc0e bl 800d070 + errorstate = SDMMC_GetCmdResp6(SDMMCx, SDMMC_CMD_SET_REL_ADDR, pRCA); + 800d854: 9a01 ldr r2, [sp, #4] + 800d856: 4629 mov r1, r5 + 800d858: 4620 mov r0, r4 + 800d85a: f7ff ffa5 bl 800d7a8 +} + 800d85e: b009 add sp, #36 ; 0x24 + 800d860: bd30 pop {r4, r5, pc} + ... + +0800d864 : + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d864: 4b13 ldr r3, [pc, #76] ; (800d8b4 ) + 800d866: f44f 51fa mov.w r1, #8000 ; 0x1f40 + 800d86a: 681b ldr r3, [r3, #0] + 800d86c: fbb3 f3f1 udiv r3, r3, r1 + 800d870: f241 3188 movw r1, #5000 ; 0x1388 +{ + 800d874: 4602 mov r2, r0 + uint32_t count = SDMMC_CMDTIMEOUT * (SystemCoreClock / 8U /1000U); + 800d876: 434b muls r3, r1 + if (count-- == 0U) + 800d878: 3b01 subs r3, #1 + 800d87a: d317 bcc.n 800d8ac + sta_reg = SDMMCx->STA; + 800d87c: 6b51 ldr r1, [r2, #52] ; 0x34 + ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); + 800d87e: f011 0f45 tst.w r1, #69 ; 0x45 + 800d882: d0f9 beq.n 800d878 + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT)) == 0U) || + 800d884: 0488 lsls r0, r1, #18 + 800d886: d4f7 bmi.n 800d878 + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) + 800d888: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d88a: 0759 lsls r1, r3, #29 + 800d88c: d502 bpl.n 800d894 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); + 800d88e: 2004 movs r0, #4 + 800d890: 6390 str r0, [r2, #56] ; 0x38 + return SDMMC_ERROR_CMD_RSP_TIMEOUT; + 800d892: 4770 bx lr + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + 800d894: 6b50 ldr r0, [r2, #52] ; 0x34 + 800d896: f010 0001 ands.w r0, r0, #1 + 800d89a: d002 beq.n 800d8a2 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); + 800d89c: 2301 movs r3, #1 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CMDREND); + 800d89e: 6393 str r3, [r2, #56] ; 0x38 + 800d8a0: 4770 bx lr + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CMDREND)) + 800d8a2: 6b53 ldr r3, [r2, #52] ; 0x34 + 800d8a4: 065b lsls r3, r3, #25 + 800d8a6: d503 bpl.n 800d8b0 + __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CMDREND); + 800d8a8: 2340 movs r3, #64 ; 0x40 + 800d8aa: e7f8 b.n 800d89e + return SDMMC_ERROR_TIMEOUT; + 800d8ac: f04f 4000 mov.w r0, #2147483648 ; 0x80000000 +} + 800d8b0: 4770 bx lr + 800d8b2: bf00 nop + 800d8b4: 2009e2ac .word 0x2009e2ac + +0800d8b8 : +{ + 800d8b8: b510 push {r4, lr} + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_HS_SEND_EXT_CSD; + 800d8ba: f44f 72d5 mov.w r2, #426 ; 0x1aa +{ + 800d8be: b086 sub sp, #24 + sdmmc_cmdinit.CmdIndex = SDMMC_CMD_HS_SEND_EXT_CSD; + 800d8c0: 2308 movs r3, #8 + 800d8c2: e9cd 2301 strd r2, r3, [sp, #4] + sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO; + 800d8c6: f44f 7180 mov.w r1, #256 ; 0x100 + 800d8ca: 2300 movs r3, #0 + 800d8cc: e9cd 1303 strd r1, r3, [sp, #12] +{ + 800d8d0: 4604 mov r4, r0 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d8d2: f44f 5380 mov.w r3, #4096 ; 0x1000 + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d8d6: a901 add r1, sp, #4 + sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE; + 800d8d8: 9305 str r3, [sp, #20] + (void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit); + 800d8da: f7ff fbc9 bl 800d070 + errorstate = SDMMC_GetCmdResp7(SDMMCx); + 800d8de: 4620 mov r0, r4 + 800d8e0: f7ff ffc0 bl 800d864 +} + 800d8e4: b006 add sp, #24 + 800d8e6: bd10 pop {r4, pc} + +0800d8e8 : + 800d8e8: b510 push {r4, lr} + 800d8ea: 3901 subs r1, #1 + 800d8ec: 4402 add r2, r0 + 800d8ee: 4290 cmp r0, r2 + 800d8f0: d101 bne.n 800d8f6 + 800d8f2: 2000 movs r0, #0 + 800d8f4: e005 b.n 800d902 + 800d8f6: 7803 ldrb r3, [r0, #0] + 800d8f8: f811 4f01 ldrb.w r4, [r1, #1]! + 800d8fc: 42a3 cmp r3, r4 + 800d8fe: d001 beq.n 800d904 + 800d900: 1b18 subs r0, r3, r4 + 800d902: bd10 pop {r4, pc} + 800d904: 3001 adds r0, #1 + 800d906: e7f2 b.n 800d8ee + +0800d908 : + 800d908: 440a add r2, r1 + 800d90a: 4291 cmp r1, r2 + 800d90c: f100 33ff add.w r3, r0, #4294967295 ; 0xffffffff + 800d910: d100 bne.n 800d914 + 800d912: 4770 bx lr + 800d914: b510 push {r4, lr} + 800d916: f811 4b01 ldrb.w r4, [r1], #1 + 800d91a: f803 4f01 strb.w r4, [r3, #1]! + 800d91e: 4291 cmp r1, r2 + 800d920: d1f9 bne.n 800d916 + 800d922: bd10 pop {r4, pc} + +0800d924 : + 800d924: 4402 add r2, r0 + 800d926: 4603 mov r3, r0 + 800d928: 4293 cmp r3, r2 + 800d92a: d100 bne.n 800d92e + 800d92c: 4770 bx lr + 800d92e: f803 1b01 strb.w r1, [r3], #1 + 800d932: e7f9 b.n 800d928 + +0800d934 : + 800d934: 46ec mov ip, sp + 800d936: e8a0 5ff0 stmia.w r0!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} + 800d93a: f04f 0000 mov.w r0, #0 + 800d93e: 4770 bx lr + +0800d940 : + 800d940: e8b0 5ff0 ldmia.w r0!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr} + 800d944: 46e5 mov sp, ip + 800d946: 0008 movs r0, r1 + 800d948: bf08 it eq + 800d94a: 2001 moveq r0, #1 + 800d94c: 4770 bx lr + 800d94e: bf00 nop + +0800d950 : + 800d950: b510 push {r4, lr} + 800d952: 460b mov r3, r1 + 800d954: b162 cbz r2, 800d970 + 800d956: 3a01 subs r2, #1 + 800d958: d008 beq.n 800d96c + 800d95a: f813 4b01 ldrb.w r4, [r3], #1 + 800d95e: f800 4b01 strb.w r4, [r0], #1 + 800d962: 2c00 cmp r4, #0 + 800d964: d1f7 bne.n 800d956 + 800d966: 1a58 subs r0, r3, r1 + 800d968: 3801 subs r0, #1 + 800d96a: bd10 pop {r4, pc} + 800d96c: 2200 movs r2, #0 + 800d96e: 7002 strb r2, [r0, #0] + 800d970: f813 2b01 ldrb.w r2, [r3], #1 + 800d974: 2a00 cmp r2, #0 + 800d976: d1fb bne.n 800d970 + 800d978: e7f5 b.n 800d966 + +0800d97a : + 800d97a: 4603 mov r3, r0 + 800d97c: f813 2b01 ldrb.w r2, [r3], #1 + 800d980: 2a00 cmp r2, #0 + 800d982: d1fb bne.n 800d97c + 800d984: 1a18 subs r0, r3, r0 + 800d986: 3801 subs r0, #1 + 800d988: 4770 bx lr + 800d98a: 0000 movs r0, r0 + 800d98c: 0000 movs r0, r0 + ... + +0800d990 <__flash_burn_veneer>: + 800d990: f85f f000 ldr.w pc, [pc] ; 800d994 <__flash_burn_veneer+0x4> + 800d994: 2009e001 .word 0x2009e001 + +0800d998 <__flash_page_erase_veneer>: + 800d998: f85f f000 ldr.w pc, [pc] ; 800d99c <__flash_page_erase_veneer+0x4> + 800d99c: 2009e08d .word 0x2009e08d + 800d9a0: 6f636e69 .word 0x6f636e69 + 800d9a4: 006e .short 0x006e + 800d9a6: 6944 .short 0x6944 + 800d9a8: 44203a65 .word 0x44203a65 + 800d9ac: 44005546 .word 0x44005546 + 800d9b0: 203a6569 .word 0x203a6569 + 800d9b4: 6e776f44 .word 0x6e776f44 + 800d9b8: 64617267 .word 0x64617267 + 800d9bc: 69440065 .word 0x69440065 + 800d9c0: 42203a65 .word 0x42203a65 + 800d9c4: 6b6e616c .word 0x6b6e616c + 800d9c8: 00687369 .word 0x00687369 + 800d9cc: 3a656944 .word 0x3a656944 + 800d9d0: 69724220 .word 0x69724220 + 800d9d4: 42006b63 .word 0x42006b63 + 800d9d8: 32746f6f .word 0x32746f6f + 800d9dc: 00554644 .word 0x00554644 + 800d9e0: 524c .short 0x524c + 800d9e2: 00 .byte 0x00 + 800d9e3: 65 .byte 0x65 + 800d9e4: 7265746e .word 0x7265746e + 800d9e8: 7566645f .word 0x7566645f + 800d9ec: 2928 .short 0x2928 + 800d9ee: 00 .byte 0x00 + 800d9ef: 0d .byte 0x0d + 800d9f0: 31510a0a .word 0x31510a0a + 800d9f4: 6f6f4220 .word 0x6f6f4220 + 800d9f8: 616f6c74 .word 0x616f6c74 + 800d9fc: 3a726564 .word 0x3a726564 + 800da00: 463e0020 .word 0x463e0020 + 800da04: 57455249 .word 0x57455249 + 800da08: 454c4c41 .word 0x454c4c41 + 800da0c: 70003c44 .word 0x70003c44 + 800da10: 2d726961 .word 0x2d726961 + 800da14: 63697262 .word 0x63697262 + 800da18: 0064656b .word 0x0064656b + 800da1c: 69726556 .word 0x69726556 + 800da20: 203a7966 .word 0x203a7966 + 800da24: 00 .byte 0x00 + 800da25: 54 .byte 0x54 + 800da26: 4145 .short 0x4145 + 800da28: 69742052 .word 0x69742052 + 800da2c: 756f656d .word 0x756f656d + 800da30: 00000074 .word 0x00000074 + 800da34: 00000150 .word 0x00000150 + 800da38: 00000011 .word 0x00000011 + 800da3c: 00000001 .word 0x00000001 + 800da40: 00000001 .word 0x00000001 + 800da44: 00000000 .word 0x00000000 + +0800da48 : + 800da48: 0011627f 01001886 22180080 00188600 .b.........".... + 800da58: 18008001 00117d7f .....}... + +0800da61 : + 800da61: 000f577f 07e00182 1e408100 10018600 .W........@..... + 800da71: 01000200 40810003 0186001e 00020008 .......@........ + 800da81: 81000301 82001e40 00030801 00030181 ....@........... + 800da91: 001e4081 5c08018a 08c1010e 1e40071c .@.....\......@. + 800daa1: 08018a00 21020262 c0082210 018a001e ....b..!."...... + 800dab1: 040241f0 10412011 8a001e40 02400801 .A... A.@.....@. + 800dac1: 41400104 001e4010 4004018a c0010402 ..@A.@.....@.... + 800dad1: 1e40107f 04018a00 01040240 40104020 ..@.....@... @.@ + 800dae1: 018a001e 04024004 10401011 8a001e40 .....@....@.@... + 800daf1: 02400801 21082102 001ec008 40f0018a ..@..!.!.......@ + 800db01: 04c10102 7f40071e 00000f7d ......@.}... + +0800db0d : + 800db0d: 0016237f 00270881 00247f81 ff031f81 .#....'...$..... + 800db1d: 0023c081 e081ff04 07810022 e382ff03 ..#....."....... + 800db2d: 860022e0 ff0fff1f 0022f081 03c03f82 ."........"..?.. + 800db3d: 22f08100 04ff8100 21788100 fc018200 ..."......x!.... + 800db4d: 78810004 03820021 810004f0 83002178 ...x!.......x!.. + 800db5d: 030fe007 21788100 800f8700 0000803f ......x!....?... + 800db6d: 870021f8 80ff001f 21f00000 033e8300 .!.........!..>. + 800db7d: 810003ff 830021f0 03f8077c 21e08100 .....!..|......! + 800db8d: 0ff88700 010000c0 870021e0 00801ff0 .........!...... + 800db9d: 20e00100 e0018300 8200033e 0020e001 ... ....>..... . + 800dbad: 7ce00383 01820003 830020e0 04f8c003 ...|..... ...... + 800dbbd: 20e08100 81078300 810004f0 830020e0 ... ......... .. + 800dbcd: 04e08307 20e08100 030f8300 810004c0 ....... ........ + 800dbdd: 830020f0 04c0070f 20f08100 0f1e8300 . ......... .... + 800dbed: 81000480 820020f0 00050f1e 0020f081 ..... ........ . + 800dbfd: 051f1e82 20f08100 1e1e8200 f0810005 ....... ........ + 800dc0d: 1c820020 8100051e 820020f0 00051c3c ........ ..<... + 800dc1d: 0020f081 053c3c82 20f08100 1c3c8200 .. ..<<.... ..<. + 800dc2d: f0810005 3c810020 e0810006 3c810020 .... ..<.... ..< + 800dc3d: 01820005 810020e0 8200053c 0020e001 ..... ..<..... . + 800dc4d: 00053c81 20e00182 053c8100 c0018200 .<..... ..<..... + 800dc5d: 3c810020 03820005 810020c0 8200053c ..<..... ..<... + 800dc6d: 0020c003 00053c81 20800782 051c8100 .. ..<..... .... + 800dc7d: 80078200 1c810020 0f810005 1c810021 .... .......!... + 800dc8d: 1f810005 1e810021 1e810005 1e810021 ....!.......!... + 800dc9d: 3c810005 1e810021 7c810005 1c810021 ..... + 800dcfd: 22e0ffc3 1f1f8200 8081ff03 1f810022 ..."........"... + 800dd0d: fc81ff03 0f810023 e081ff03 03820023 ....#.......#... + 800dd1d: 810027f8 24297f40 f00f8200 03820008 .'..@.)$........ + 800dd2d: 83001cc0 07200008 20048200 0883001c ...... .... .... + 800dd3d: 00082000 001c1081 000a0881 001c1081 . .............. + 800dd4d: e000088c 08c83d5c 00075cf8 8c001c20 ....\=...\.. ... + 800dd5d: 6220c00f 04882822 40800862 088c001c .. b"(..b..@.... + 800dd6d: 22412000 41048828 1c804010 00088b00 . A"(..A.@...... + 800dd7d: 28224020 1040fc88 8b001d41 40200008 @"(..@.A..... @ + 800dd8d: 04892822 1dc11f40 00088a00 25224020 "(..@....... @"% + 800dd9d: 10400451 088a001e 22402000 40045125 Q.@...... @"%Q.@ + 800ddad: 8b001e10 40200008 0c512520 1d410840 ...... @ %Q.@.A. + 800ddbd: 00088b00 22204020 0740f420 0f4b7f81 .... @ " .@...K. + ... + +0800ddcf : + 800ddcf: 0010237f 00272081 00087081 f8e31f83 .#... '..p...... + 800dddf: 7181001c 3f830008 001bfeff 80730e83 ...q...?......s. + 800ddef: 7f830007 001bfeff 80770f83 fc830007 ..........w..... + 800ddff: 001b9fff 08ff0782 3ef08400 001a800f ...........>.... + 800de0f: 078e0382 f0018500 23800700 e0018500 ...........#.... + 800de1f: 1bc00300 e0078200 03850006 c00300c0 ................ + 800de2f: 0183001a 0006e087 00c00385 0019e001 ................ + 800de3f: c403f883 07850007 e0010080 01840018 ................ + 800de4f: 07ee07fc 803f8500 17fc0100 fb7f8500 ......?......... + 800de5f: 07cf0ffe 80ff8500 16ff0000 ff038700 ................ + 800de6f: 871fdfff 82000580 0003ff01 1580ff82 ................ + 800de7f: ff0f8700 03bf8fff 82000580 0003f803 ................ + 800de8f: 15c00f82 ff3f8500 07fe07ff e0038200 ......?......... + 800de9f: 03820003 850015e0 0303807f 820007fc ................ + 800deaf: 0003c003 15e00182 00fe8500 07f80100 ................ + 800debf: c0038200 01820003 820014e0 0003f801 ................ + 800decf: 0007f881 03e00382 c0038200 03820014 ................ + 800dedf: 810003e0 8200077c 0003f803 14c00f82 ....|........... + 800deef: c0078200 3e810003 01870007 0100c0ff .......>........ + 800deff: 001480ff 78800f86 081f0000 15ff0500 .......x........ + 800df0f: 031f8700 0f0000f8 81000780 81ff033f ............?... + 800df1f: 870015fc 00f8071e 07c00700 031f8100 ................ + 800df2f: 15f881ff 0f3c8700 030000f8 850007c0 ......<......... + 800df3f: feff3f1e 87001578 00801f7c 07c00300 .?..x...|....... + 800df4f: 3f1e8500 1578fce7 3e788200 07820003 ...?..x...x>.... + 800df5f: 850007c0 fce33f1e 82001578 00037c78 .....?..x...x|.. + 800df6f: 06800f82 fe038700 7ffcc31f 820014e0 ................ + 800df7f: 000378f0 00071f81 0fff0387 e07ff881 .x.............. + 800df8f: f0820014 81000378 8700071e 0003ff03 ....x........... + 800df9f: 14c0ff40 f0e08200 1e810003 01820007 @............... + 800dfaf: 820003ff 0013c0ff f0e00183 1e810003 ................ + 800dfbf: 01870007 000080ff 0013c0ff f0e00183 ................ + 800dfcf: 0e810003 01870007 0100c0ff 001380ff ................ + 800dfdf: 70e00183 0f810003 ff860008 ff0300c0 ...p............ + 800dfef: 82001380 0004e001 00080f81 00e0ff85 ................ + 800dfff: 0014ff07 04e00182 080f8100 f87f8500 ................ + 800e00f: 14ff0f00 e0018200 0f810004 ff860008 ................ + 800e01f: ff3f00fc 82001380 0004e001 00070f81 ..?............. + 800e02f: ffff0387 c0ffff80 01820013 810004e0 ................ + 800e03f: 8700070f ff3ff007 13e00ffc e0018200 ......?......... + 800e04f: 0f810004 07870007 f8ff1fc0 0013f003 ................ + 800e05f: 04e00182 070e8100 800f8700 00f8ff1f ................ + 800e06f: 810014f8 810004e0 8700071e 3e1f001f ...............> + 800e07f: 14780078 04f08100 071e8100 001e8700 x.x............. + 800e08f: 00f8800f 8100147c 810004f0 8700071e ....|........... + 800e09f: 810f001e 143c00f8 04f08100 073c8100 ......<.......<. + 800e0af: 003c8700 00f8c10f 8100143c 81000478 ..<.....<...x... + 800e0bf: 8700073c c30f003c 141c00f0 047c8100 <...<.........|. + 800e0cf: 07788100 003c8700 00f0c30f 8100141c ..x...<......... + 800e0df: 8100043c 87000778 c30f003c 141e00f0 <...x...<....... + 800e0ef: 043e8100 07f08100 003c8700 00f0c10f ..>.......<..... + 800e0ff: 8100141e 8200031f 0007f001 07003c87 .............<.. + 800e10f: 1e00f0c1 0f860014 03000080 870007e0 ................ + 800e11f: c107003c 141e00f0 c00f8600 c0070000 <............... + 800e12f: 3c870007 f0810700 00141e00 00e00786 ...<............ + 800e13f: 07800f00 003c8700 00e08107 8500141e ......<......... + 800e14f: 0000f003 8700083f 8107003c 141e00e0 ....?...<....... + 800e15f: fc018500 087e0000 003c8700 00e08007 ......~...<..... + 800e16f: 8400151e fc03007f 3c870008 e0800300 ...........<.... + 800e17f: 00151e00 1ff83f84 870008f8 8003003e .....?......>... + 800e18f: 153c00e0 ff0f8400 0008e0ff ff053f81 ..<..........?.. + 800e19f: 0015fc81 ffff0384 81000880 81ff051f ................ + 800e1af: 820016f8 0009fcff ff050f81 0016f081 ................ + 800e1bf: 000a0381 ff050181 297fc081 01820014 ...........).... + 800e1cf: 840006e0 70000004 04810006 01820015 .......p........ + 800e1df: 87000610 88000004 03010000 15048100 ................ + 800e1ef: 08018200 04870006 00040100 00030100 ................ + 800e1ff: 00150481 06040182 00048700 00000401 ................ + 800e20f: 81000301 93001504 173e0401 5c70d001 ..........>...p\ + 800e21f: 00010004 e0870f41 1504f770 04019300 ....A...p....... + 800e22f: 30821801 00046288 10410001 88880041 ...0.b....A.A... + 800e23f: 93001584 10010401 41041144 00010004 ........D..A.... + 800e24f: 01011041 15848804 04019300 1144103f A...........?.D. + 800e25f: 00044004 10410001 88040101 93001584 .@....A......... + 800e26f: 10410401 40fc1144 00010004 01810f41 ..A.D..@....A... + 800e27f: 15848804 04019300 11441041 00004000 ........A.D..@.. + 800e28f: 00410401 88040141 93001580 10410801 ..A.A.........A. + 800e29f: 40003142 04010000 01410041 15808804 B1.@....A.A..... + 800e2af: 10019300 d0411043 00044084 10238800 ....C.A..@....#. + 800e2bf: 80881041 93001584 103de001 40781040 A.........=.@.x@ + 800e2cf: 70000004 e0800f1d 1a848070 26108100 ...p....p......& + 800e2df: 10048200 02820026 82002620 477fc001 ....&... &.....G + 800e2ef: ... + +0800e2f2 : + 800e2f2: 0013247f 26c00382 ffff8200 0c860023 .$.....&....#... + 800e302: ffff0700 860022e0 ff1f001e 0022f8ff ....."........". + 800e312: 7f001e86 22fe7ffe 001e8600 ff0180ff ......."........ + 800e322: 1e870022 0000fc03 0021c03f f0071e87 ".......?.!..... + 800e332: e00f0000 1e870021 0000c00f 0021f003 ....!.........!. + 800e342: 801f1e87 f8010000 1e820021 8100043f ........!...?... + 800e352: 820021fc 00047e1e 00217e81 00fc1e87 .!...~...~!..... + 800e362: 3f00c003 1e870021 c00300f8 00211f00 ...?!.........!. + 800e372: 00f01f88 0f00c003 88002080 0300e01f ......... ...... + 800e382: 800700c0 1f880020 c00300e0 20c00700 .... .......... + 800e392: c01f8800 00c00300 0020c003 fcff1f88 .......... ..... + 800e3a2: 0100c003 880020e0 03feff1f e00100c0 ..... .......... + 800e3b2: 1f880020 c003feff 20f00100 ff1f8800 .......... .... + 800e3c2: 00c003fc 0023f000 00c00385 0023f000 ......#.......#. + 800e3d2: 00c00385 0023f000 00c00385 00237800 ......#......x#. + 800e3e2: 00c00385 00237800 00c00385 00237800 .....x#......x#. + 800e3f2: 00c00385 00237800 00c00385 00237800 .....x#......x#. + 800e402: 00c00385 00237800 00c00385 00237800 .....x#......x#. + 800e412: 00e00385 00237800 00f80385 00247800 .....x#......x$. + 800e422: 0000fc84 84002478 7800007f 3f840024 ....x$.....x$..? + 800e432: 24f00080 c00f8400 0024f000 00e00784 ...$......$..... + 800e442: 840024f0 f001e001 c0830025 0026e001 .$......%.....&. + 800e452: 26e00182 e0038200 07820026 820026c0 ...&....&....&.. + 800e462: 00268007 26800f82 271f8100 273f8100 ..&....&...'..?' + 800e472: 227e8100 04078100 22fc8100 800f8600 ..~".......".... + 800e482: f8010000 0f860022 030000c0 860022f0 ...."........".. + 800e492: 0000f007 0022e00f 00fc0386 23c03f00 ......"......?.# + 800e4a2: 80ff8400 0024ff01 7ffe7f84 840024fe ......$......$.. + 800e4b2: f8ffff1f 07840024 25e0ffff ffff8200 ....$......%.... + 800e4c2: 01820026 212a7f80 08f08100 00088300 &.....*!........ + 800e4d2: 81001c1e 83000888 1c210008 08848100 ..........!..... + 800e4e2: 00088400 001b8000 00088281 00000884 ................ + 800e4f2: 8c001b80 12100e82 072e3ae0 0138e8c0 .........:....8. + 800e502: 828c001c 10131111 21003146 1c024418 ........F1.!.D.. + 800e512: 20828c00 82081291 08228020 001c0482 ... .... ."..... + 800e522: 9120828c 20820812 8208e207 8c001c08 .. .... ........ + 800e532: 12912082 08208208 08fe0822 828b001c . .... ."....... + 800e542: 08a28a20 22082082 001d8008 8a20848b .... ."...... . + 800e552: 204608a2 80082208 888c001d 08a20a11 ..F .".......... + 800e562: 6108203a 1c084218 0ef08c00 02084204 : .a.B.......B.. + 800e572: e8a00720 0021083c 00270281 00278281 ...<.!...'...'. + 800e582: 00274481 477f3881 .D'..8.G... + +0800e58d : + 800e58d: 0013577f 01001c84 850023c0 02002288 .W.......#...".. + 800e59d: 84002320 02002088 88840024 23020020 #... ..$... ..# + 800e5ad: fc038500 23424020 10018500 23424420 .... @B#.... DB# + 800e5bd: 10018600 c04f44fc 01850022 42442010 .....DO.".... DB + 800e5cd: 07850023 424420f8 02850023 822a2020 #.... DB#... *. + 800e5dd: 02850023 822a2020 02850023 822a2020 #... *.#... *. + 800e5ed: 20830025 7d7f0211 %.. ...}... + +0800e5f8 : + 800e5f8: 0002bc7f 0000fe84 82000801 00198007 ................ + 800e608: 18000185 00060100 04001084 85001940 ............@... + 800e618: 000c0001 84000601 20040010 01850019 ........... .... + 800e628: 01000600 10840006 19100400 00019200 ................ + 800e638: 00010003 c141071c 04007e04 075c7010 ......A..~...p\. + 800e648: 01930016 01800100 c2082200 00100421 ........."..!... + 800e658: 62881004 00158008 00000193 410001c0 ...b...........A + 800e668: 04114410 11040010 40104104 01930015 .D.......A.@.... + 800e678: 01e0ff07 44104100 00100411 41041104 .....A.D.......A + 800e688: 00154010 00000193 410001c0 04114410 .@.........A.D.. + 800e698: 11040010 c01f4104 01920015 01800100 .....A.......... + 800e6a8: 44104100 00100411 41041104 92001610 .A.D.......A.... + 800e6b8: 00030001 08410001 100411c4 04210400 ......A.......!. + 800e6c8: 00161041 06000193 22000100 8c204207 A..........".B . + 800e6d8: 40040011 40084188 01930015 01000c00 ...@.A.@........ + 800e6e8: 41001cfc 000e74c0 41708007 00158007 ...A.t....pA.... + 800e6f8: 18000183 40810005 fe810020 10820005 .......@ ....... + 800e708: 82002640 00268008 147f0781 @&....&........ + +0800e717 : + 800e717: 0014577f 00271081 00271081 00272081 .W....'...'.. '. + 800e727: 00272081 00274081 00274081 00258081 . '..@'..@'...%. + 800e737: 81c01f84 810025fc 81002701 81002701 .....%...'...'.. + 800e747: 81002702 81002702 81002704 147c7f04 .'...'...'....|. + ... + +0800e759 : + 800e759: 0003ba7f 0026c081 26c01082 c3308500 ......&....&..0. + 800e769: 06f00100 00078400 001938e0 80c16085 .........8...`.. + 800e779: 00060801 10810884 85001944 0180c040 ........D...@... + 800e789: 84000604 40004110 c0030019 06040182 .....A.@........ + 800e799: 41108400 00194000 40c0808f 201c0401 ...A.@.....@... + 800e7a9: 0070c121 40004110 80850019 040140c0 !.p..A.@.....@.. + 800e7b9: 21872203 41100088 00184000 c0800190 .".!...A.@...... + 800e7c9: 41080160 04112422 e1471000 900018f8 `..A"$....G..... + 800e7d9: 60c08001 2241f001 00001124 40004110 ...`..A"$....A.@ + 800e7e9: 01900018 0160c080 27224100 100000f1 ......`..A"'.... + 800e7f9: 18400041 80019000 00014000 01441541 A.@......@..A.D. + 800e809: 41100000 00194000 4000808f 15410001 ...A.@.....@..A. + 800e819: 00000144 40004110 c08f0019 0001c000 D....A.@........ + 800e829: 11421522 81080000 00194000 8000408f ".B......@...@.. + 800e839: 081c0001 0000e181 40000107 60830019 ...........@...` + 800e849: 00258001 26033082 0c0c8200 07820026 ..%..0.&....&... + 800e859: 24147ff8 ...$.. + +0800e85f : + 800e85f: 000f277f 00052081 ff040181 001c8081 .'... .......... + 800e86f: 00057081 ff040781 001cf081 0005f881 .p.............. + 800e87f: ff040f81 001cf881 0005fc81 ff041f81 ................ + 800e88f: 001cfc81 00057e81 003c1e86 1cfe0700 .....~....<..... + 800e89f: 053f8100 3c1e8600 bf070000 1f82001c ..?....<........ + 800e8af: 87000480 00003c1e 1b809f07 c00f8200 .....<.......... + 800e8bf: 1e870004 0700003c 001bc08f 04e00782 ....<........... + 800e8cf: 3c1e8700 87070000 82001be0 0004f003 ...<............ + 800e8df: 003c1e87 f0830700 0182001b 870004f8 ..<............. + 800e8ef: 00003c1e 1cf88107 04fc8100 3c1e8700 .<.............< + 800e8ff: 80070000 81001cfc 8700047e 00003c1e ........~....<.. + 800e90f: 1c7c8007 043f8100 3c1e8700 80070000 ..|...?....<.... + 800e91f: 82001c3e 0003801f 003c1e87 1e800700 >.........<..... + 800e92f: 0f82001c 820003c0 ff033f1e 1c0e8082 .........?...... + 800e93f: e0078200 1e820003 82ff031f 001c0f00 ................ + 800e94f: 03f00382 1f1e8200 0082ff03 82001c0f ................ + 800e95f: 0003f801 ff0f1e87 0f00feff fc81001d ................ + 800e96f: 1e810003 0f810005 7e81001d 1e810003 ...........~.... + 800e97f: 0f810005 3f81001d 1e810003 0f810005 .......?........ + 800e98f: 1f85001d 1e000080 0f810005 0f85001d ................ + 800e99f: 1e0000c0 0f810005 078b001d 1e0000e0 ................ + 800e9af: 807f0000 00180f00 f08aff06 001e0000 ................ + 800e9bf: 00e0ff01 0600180f 00f88aff 03001e00 ................ + 800e9cf: 0f00f0ff ff060018 0000f08a fb07001e ................ + 800e9df: 180f00f8 057f8100 00e08aff 07001e00 ................ + 800e9ef: 0f007cc0 0f8b001d 1e0000c0 3e800f00 .|.............> + 800e9ff: 001d0f00 00801f8b 0f001e00 0f001e00 ................ + 800ea0f: 3f81001d 1e870003 1e001e00 001d0f00 ...?............ + 800ea1f: 00037e81 1e001e87 0f000e00 fc81001d .~.............. + 800ea2f: 1e870003 0f001e00 001c0f00 03f80182 ................ + 800ea3f: 001e8700 000f001e 82001c0f 0003f003 ................ + 800ea4f: 1e001e87 0f000e00 0782001c 870003e0 ................ + 800ea5f: 001e001e 1c0f001e c00f8200 1e870003 ................ + 800ea6f: 1e000f00 001c0f00 03801f82 001e8700 ................ + 800ea7f: 003e000f 81001c0f 8700043f c007001e ..>.....?....... + 800ea8f: 1c0f007c 047e8100 001e8700 00f8fb07 |.....~......... + 800ea9f: 81001c0f 870004fc ff03001e 1b0f00f0 ................ + 800eaaf: f8018200 1e870004 e0ff0100 001b0f00 ................ + 800eabf: 04f00382 001e8700 00807f00 82001b0f ................ + 800eacf: 0004e007 00051e81 001b0f81 04c00f82 ................ + 800eadf: 051e8100 1b0f8100 801f8200 1e810004 ................ + 800eaef: 0f810005 3f81001b 1e810005 0e810005 .......?........ + 800eaff: 7e81001b 1e810005 1e810005 fc81001b ...~............ + 800eb0f: 1f810005 fe81ff05 f881001b 0f810005 ................ + 800eb1f: fc81ff05 7081001b 07810005 f881ff05 .......p........ + 800eb2f: 2081001b 01810005 e081ff05 00192d7f ... .........-.. + 800eb3f: 07800f82 031c8100 1a048100 05028100 ................ + 800eb4f: 00018400 00032200 001a0481 00050281 .....".......... + 800eb5f: 00000184 81000341 81001a04 84000502 ....A........... + 800eb6f: 41000001 04810003 028e001a 1cf8c005 ...A............ + 800eb7f: 00e00717 c0850f40 8e001a74 04210602 ....@...t.....!. + 800eb8f: 00811822 46004000 001a8c20 1104028e "....@.F ....... + 800eb9f: 41104100 00400000 1a041144 04028e00 .A.A..@.D....... + 800ebaf: 10410011 40000001 0401c40f 028e001a ..A....@........ + 800ebbf: 7ff81004 00000110 01441040 8e001a04 ........@.D..... + 800ebcf: 04100402 00011040 44104100 001a0401 ....@....A.D.... + 800ebdf: 1004028e 01104004 10410000 1a040144 .....@....A.D... + 800ebef: 04028e00 10210411 22001001 8c00c410 ......!....".... + 800ebff: 0f8e001a 1ef81084 00e00010 00440f1c ..............D. + 800ec0f: 0d4b7f74 t.K... + +0800ec15 : + 800ec15: 0010237f 00272081 00087081 f8e31f83 .#... '..p...... + 800ec25: 7181001c 3f830008 001bfeff 80730e83 ...q...?......s. + 800ec35: 7f830007 001bfeff 80770f83 fc830007 ..........w..... + 800ec45: 001b9fff 08ff0782 3ef08400 001a800f ...........>.... + 800ec55: 078e0382 f0018500 23800700 e0018500 ...........#.... + 800ec65: 1bc00300 e0078200 03850006 c00300c0 ................ + 800ec75: 0183001a 0006e087 00c00385 0019e001 ................ + 800ec85: c403f883 07850007 e0010080 01840018 ................ + 800ec95: 07ee07fc 803f8500 17fc0100 fb7f8500 ......?......... + 800eca5: 07cf0ffe 80ff8500 16ff0000 ff038700 ................ + 800ecb5: 871fdfff 82000580 0003ff01 1580ff82 ................ + 800ecc5: ff0f8700 03bf8fff 82000580 0003f803 ................ + 800ecd5: 15c00f82 ff3f8500 07fe07ff e0038200 ......?......... + 800ece5: 03820003 850015e0 0303807f 820007fc ................ + 800ecf5: 0003c003 15e00182 00fe8500 07f80100 ................ + 800ed05: c0038200 01820003 820014e0 0003f801 ................ + 800ed15: 0007f881 03e00382 c0038200 03820014 ................ + 800ed25: 810003e0 8200077c 0003f803 14c00f82 ....|........... + 800ed35: c0078200 3e810003 01870007 0100c0ff .......>........ + 800ed45: 001480ff 78800f86 081f0000 15ff0500 .......x........ + 800ed55: 031f8700 0f0000f8 81000780 81ff033f ............?... + 800ed65: 870015fc 00f8071e 07c00700 031f8100 ................ + 800ed75: 15f881ff 0f3c8700 030000f8 850007c0 ......<......... + 800ed85: feff3f1e 87001578 00801f7c 07c00300 .?..x...|....... + 800ed95: 3f1e8500 1578fce7 3e788200 07820003 ...?..x...x>.... + 800eda5: 850007c0 fce33f1e 82001578 00037c78 .....?..x...x|.. + 800edb5: 06800f82 fe038700 7ffcc31f 820014e0 ................ + 800edc5: 000378f0 00071f81 0fff0387 e07ff881 .x.............. + 800edd5: f0820014 81000378 8700071e 0003ff03 ....x........... + 800ede5: 14c0ff40 f0e08200 1e810003 01820007 @............... + 800edf5: 820003ff 0013c0ff f0e00183 1e810003 ................ + 800ee05: 01870007 000080ff 0013c0ff f0e00183 ................ + 800ee15: 0e810003 01870007 0100c0ff 001380ff ................ + 800ee25: 70e00183 0f810003 ff860008 ff0300c0 ...p............ + 800ee35: 82001380 0004e001 00080f81 00e0ff85 ................ + 800ee45: 0014ff07 04e00182 080f8100 f87f8500 ................ + 800ee55: 14ff0f00 e0018200 0f810004 ff860008 ................ + 800ee65: ff3f00fc 82001380 0004e001 00070f81 ..?............. + 800ee75: ffff0387 c0ffff80 01820013 810004e0 ................ + 800ee85: 8700070f ff3ff007 13e00ffc e0018200 ......?......... + 800ee95: 0f810004 07870007 f8ff1fc0 0013f003 ................ + 800eea5: 04e00182 070e8100 800f8700 00f8ff1f ................ + 800eeb5: 810014f8 810004e0 8700071e 3e1f001f ...............> + 800eec5: 14780078 04f08100 071e8100 001e8700 x.x............. + 800eed5: 00f8800f 8100147c 810004f0 8700071e ....|........... + 800eee5: 810f001e 143c00f8 04f08100 073c8100 ......<.......<. + 800eef5: 003c8700 00f8c10f 8100143c 81000478 ..<.....<...x... + 800ef05: 8700073c c30f003c 141c00f0 047c8100 <...<.........|. + 800ef15: 07788100 003c8700 00f0c30f 8100141c ..x...<......... + 800ef25: 8100043c 87000778 c30f003c 141e00f0 <...x...<....... + 800ef35: 043e8100 07f08100 003c8700 00f0c10f ..>.......<..... + 800ef45: 8100141e 8200031f 0007f001 07003c87 .............<.. + 800ef55: 1e00f0c1 0f860014 03000080 870007e0 ................ + 800ef65: c107003c 141e00f0 c00f8600 c0070000 <............... + 800ef75: 3c870007 f0810700 00141e00 00e00786 ...<............ + 800ef85: 07800f00 003c8700 00e08107 8500141e ......<......... + 800ef95: 0000f003 8700083f 8107003c 141e00e0 ....?...<....... + 800efa5: fc018500 087e0000 003c8700 00e08007 ......~...<..... + 800efb5: 8400151e fc03007f 3c870008 e0800300 ...........<.... + 800efc5: 00151e00 1ff83f84 870008f8 8003003e .....?......>... + 800efd5: 153c00e0 ff0f8400 0008e0ff ff053f81 ..<..........?.. + 800efe5: 0015fc81 ffff0384 81000880 81ff051f ................ + 800eff5: 820016f8 0009fcff ff050f81 0016f081 ................ + 800f005: 000a0381 ff050181 297fc081 3c810014 ...........)...< + 800f015: 80830007 00080e00 00142081 00072281 ......... ...".. + 800f025: 11008083 20820003 81000304 81001420 ....... .... ... + 800f035: 88000721 80200080 04200000 20810003 !..... ... .... + 800f045: 20820014 87000680 80200080 04200000 ... ...... ... . + 800f055: 14208100 87209400 0e3ae0c2 0080800b .. ... ...:..... + 800f065: 08c20720 82031cfc 001420e0 23802094 ........ ... .# + 800f075: 0c114610 20008040 20082200 10430404 .F..@.. .". ..C. + 800f085: 94001420 08228020 20882082 00200080 ... .".. . .. . + 800f095: 04200822 20082208 20940014 8208e287 ". ..". ... .... + 800f0a5: 80008820 e2072000 08042008 14200822 .... ... ..". . + 800f0b5: 88209400 3f820822 00800088 08220820 .. ."..?.... .". + 800f0c5: 22080420 00142008 22882087 08208208 ..". ... .".. . + 800f0d5: 20890003 20082288 08220804 21870015 ... .". .."....! + 800f0e5: 46082208 00030820 22882089 08042008 .".F .... .". .. + 800f0f5: 00150822 62082294 88103a08 11008000 "....".b.:...... + 800f105: 22186108 08420404 94001420 08a2073c .a."..B. ...<... + 800f115: 00080f02 070e0080 041ce8a0 20088203 ............... + 800f125: 02810018 82810027 44810027 38810027 ....'...'..D'..8 + 800f135: 0019477f .G... + +0800f13a : + 800f13a: 0013247f 26f83f82 feff8200 01840025 .$...?.&....%... + 800f14a: 2480ffff f8038400 0024c03f 07e00784 ...$....?.$..... + 800f15a: 840024e0 f001800f 1f840024 24f80000 .$......$......$ + 800f16a: 001e8400 00247800 00003c84 8600227c .....x$..<..|".. + 800f17a: 003c000e 00223c00 78000f88 003c0000 ..<..<"....x..<. + 800f18a: 880020e0 0078800f e0011e00 07880020 . ....x..... ... + 800f19a: 000078c0 20e0031e e0038800 1e000078 .x..... ....x... + 800f1aa: 0020c007 ff060181 00218081 0022ff06 .. .......!...". + 800f1ba: ff047f81 0022fe81 ff047f81 0022fc81 ......".......". + 800f1ca: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f1da: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f1ea: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f1fa: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f20a: 03007886 223c0080 00788600 3c00c003 .x....<"..x....< + 800f21a: 78860022 00c00300 8600223c c0030078 "..x....<"..x... + 800f22a: 00223c00 03007886 213c00c0 f87f8800 .<"..x....: + 800f3d7: 0013247f 26f83f82 feff8200 01840025 .$...?.&....%... + 800f3e7: 2480ffff f8038400 0024c03f 07e00784 ...$....?.$..... + 800f3f7: 840024e0 f001800f 1f840024 24f80000 .$......$......$ + 800f407: 001e8400 00247800 00003c84 8600227c .....x$..<..|".. + 800f417: 003c000e 00223c00 78000f88 003c0000 ..<..<"....x..<. + 800f427: 880020e0 0078800f e0011e00 07880020 . ....x..... ... + 800f437: 000078c0 20e0031e e0038800 1e000078 .x..... ....x... + 800f447: 0020c007 ff060181 00218081 0022ff06 .. .......!...". + 800f457: ff047f81 0022fe81 ff047f81 0022fc81 ......".......". + 800f467: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f477: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f487: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f497: 00047881 00223c81 00047881 00223c81 .x...<"..x...<". + 800f4a7: 03007886 223c0080 00788600 3c00c003 .x....<"..x....< + 800f4b7: 78860022 00c00300 8600223c c0030078 "..x....<"..x... + 800f4c7: 00223c00 03007886 213c00c0 f87f8800 .<"..x....: + 800f671: 0013247f 26c00182 ffff8200 07840025 .$.....&....%... + 800f681: 24e0ffff ff1f8400 0024f8ff 0ff07f84 ...$......$..... + 800f691: 840024fe ff0000ff 03860023 1f0000f8 .$......#....... + 800f6a1: 860022c0 0000e007 0022e00f 00c00f86 ."........"..... + 800f6b1: 22f00300 031f8100 f8018200 3e810022 ..."........"..> + 800f6c1: 7c810004 7c810022 3e810004 f8860022 ...|"..|...>"... + 800f6d1: 00c00300 8600221f c00300f0 00210f00 ....."........!. + 800f6e1: 00f00188 0f00c003 88002080 0300e001 ......... ...... + 800f6f1: 800700c0 03880020 c00300c0 20c00300 .... .......... + 800f701: c0078800 00c00300 0020c003 00800788 .......... ..... + 800f711: 0100c003 880020e0 03008007 e00100c0 ..... .......... + 800f721: 0f880020 c0030000 20f00000 000f8800 .......... .... + 800f731: 00c00300 0020f000 00000f88 0000c003 ...... ......... + 800f741: 880020f0 0300000e 700000c0 1e880020 . .........p ... + 800f751: c0030000 20780000 001e8800 00c00300 ......x ........ + 800f761: 00207800 00001e88 0000c003 88002078 .x .........x .. + 800f771: 0300001e 780000c0 1e880020 c0030000 .......x ....... + 800f781: 20780000 001e8800 00c00300 00207800 ..x .........x . + 800f791: 00001e88 0000e003 88002078 0300001e ........x ...... + 800f7a1: 780000f0 1e880020 f8030000 20780000 ...x .........x + 800f7b1: 001e8800 00fe0100 00207800 00031e81 .........x ..... + 800f7c1: 00007f84 81002078 8400030e 7000803f ....x ......?..p + 800f7d1: 0f810020 1f840003 20f000e0 030f8100 .......... .... + 800f7e1: e0078400 0020f000 00030f81 00e00384 ...... ......... + 800f7f1: 880020f0 00008007 e001c001 07820020 . .......... ... + 800f801: 82000480 0020e001 04c00782 c0038200 ...... ......... + 800f811: 03820020 820004c0 0020c003 04e00182 ......... ..... + 800f821: 80078200 01820020 820004f0 0021800f .... .........!. + 800f831: 0004f081 00220f81 0004f881 00221e81 ......".......". + 800f841: 00047c81 00223e81 00043e81 00227c81 .|...>"..>...|". + 800f851: 00041f81 0022f881 00c00f86 22f00300 ......"........" + 800f861: e0078600 e0070000 03860022 1f0000f8 ........"....... + 800f871: 840023c0 ff0000ff 7f840024 24fe0ff0 .#......$......$ + 800f881: ff1f8400 0024f8ff ffff0784 820025e0 ......$......%.. + 800f891: 0026ffff 7f800182 8500142a 00f88303 ..&.....*....... + 800f8a1: 8200070e 00090101 09104082 01078300 .........@...... + 800f8b1: 880003c0 00004204 80000011 01860004 .....B.......... + 800f8c1: 00010001 86000480 00104040 00068000 ........@@...... + 800f8d1: 20820883 08880003 20000022 04800080 ... ....".. .... + 800f8e1: 01018600 80000100 40860004 00001040 ...........@@... + 800f8f1: 82000680 00040208 00020888 00002000 ............. .. + 800f901: 82000480 00030101 00058081 00104085 .............@.. + 800f911: 00068000 04020882 0208a400 03200000 .............. . + 800f921: 2e82f083 01010003 f003071f 11100000 ................ + 800f931: 001040c0 2e82f003 3800800b 00040208 .@.........8.... + 800f941: 000204a4 40041000 03318280 00110100 .......@..1..... + 800f951: 00800081 40101100 00001040 0c318280 .......@@.....1. + 800f961: 08440040 a5000402 00f08303 8020080e @.D........... . + 800f971: 00802082 81001101 00008000 40401011 . ............@@ + 800f981: 80000010 20882082 0f3f8200 a30004c0 ..... . ..?..... + 800f991: 01000042 82802008 01008020 00811f11 B.... .. ....... + 800f9a1: 11000080 10404010 82800000 00200820 .....@@..... . . + 800f9b1: 05020882 03228100 e08f9f00 80208280 ......"....... . + 800f9c1: 20290100 00800081 40101100 00001040 ..) .......@@... + 800f9d1: 08208280 08820020 81000502 9f000322 .. . ......."... + 800f9e1: 82800088 00008020 008120aa 0a000080 .... .... ...... + 800f9f1: 104040a0 82800000 00200820 04020882 .@@..... . ..... + 800fa01: 22088a00 88200000 31828000 aa970003 ...".. ....1.... + 800fa11: 80008120 a00a0000 00104040 20828000 .......@@..... + 800fa21: 82002008 00040208 004204ce 20041100 . ........B.... + 800fa31: 032e4688 21440000 30880081 40a00a00 .F....D!...0...@ + 800fa41: 00001040 08204688 08440020 00c00002 @....F . .D..... + 800fa51: f8830300 c0030e00 03203a70 1e440000 ........p: ...D. + 800fa61: 30700081 40400400 00000e38 08203a70 ..p0..@@8...p: . + 800fa71: 08380020 0bc00002 08208100 1e608100 .8....... ...`. + 800fa81: 08208100 1ec08100 27208100 7f208100 .. ....... '.. . + 800fa91: 00001d47 G... + +0800fa95 : + 800fa95: 000e237f 00260881 e0ff0783 1f830025 .#....&.....%... + 800faa5: 0025fcff ffff7f83 01850024 c0ff80ff ..%.....$....... + 800fab5: 1f810005 f881ff03 03850019 e01f00f8 ................ + 800fac5: 7f810005 0019ff04 00e00f85 0005f003 ................ + 800fad5: 8081ff05 1f850018 f8010080 01810004 ................ + 800fae5: c081ff05 3f810018 7c810003 01870004 .......?...|.... + 800faf5: 0000c0e3 0018e07f 00037e81 00043e81 .........~...>.. + 800fb05: c0e30187 f07b0000 78810018 1f810003 ......{....x.... + 800fb15: 01870004 0000c0e3 0018f879 0003f881 ........y....... + 800fb25: 03800f82 e3018700 780000c0 820017fc ...........x.... + 800fb35: 0003f001 03800782 e3018700 780000c0 ...............x + 800fb45: 8200177e 0003e001 03c00382 e3018700 ~............... + 800fb55: 780000c0 8200173f 0003e003 03c00382 ...x?........... + 800fb65: e3018800 780000c0 0016801f 03c00382 .......x........ + 800fb75: e0018200 01880003 0000c0e3 16c00f78 ............x... + 800fb85: c0078200 01820003 880003e0 00c0e301 ................ + 800fb95: c0077800 07820016 81000480 880003f0 .x.............. + 800fba5: 00c0e301 e0037800 07820016 81000480 .....x.......... + 800fbb5: 880003f0 00c0e301 e0017800 07810016 .........x...... + 800fbc5: f0810005 01820003 83ff03e3 16e000f8 ................ + 800fbd5: 050f8100 03708100 e1018200 f083ff03 ......p......... + 800fbe5: 0016f000 00050f81 00037881 03e10182 .........x...... + 800fbf5: 00f083ff 810016f0 8100050f 82000378 ............x... + 800fc05: ff03e001 f000e083 0f810016 78810005 ...............x + 800fc15: 01820003 810005e0 810016f0 8100050f ................ + 800fc25: 82000378 0005e001 0016f081 00050f81 x............... + 800fc35: 00037881 05e00182 16f08100 050f8100 .x.............. + 800fc45: 03788100 e0018200 f0810005 0f810016 ..x............. + 800fc55: 78810005 01820003 810005e0 810016f0 ...x............ + 800fc65: 8100050f 88000370 0700e001 f00000f8 ....p........... + 800fc75: 0f810016 f0810005 01880003 fe1f00e0 ................ + 800fc85: 16f00000 05078100 03f08100 e0018800 ................ + 800fc95: 00ff3f00 0016f000 04800782 03f08100 .?.............. + 800fca5: e0018800 80bf7f00 0016f000 04800782 ................ + 800fcb5: 03f08100 e0018800 c0077c00 0016f000 .........|...... + 800fcc5: 03c00382 e0018200 01880003 03f800e0 ................ + 800fcd5: 16f000e0 c0038200 01820003 880003e0 ................ + 800fce5: f000e001 f000e001 03820016 820003e0 ................ + 800fcf5: 0003c003 01e00188 00e001e0 820016f0 ................ + 800fd05: 0003e001 03c00782 e0018800 e000e001 ................ + 800fd15: 0016f000 03f00182 80078200 01880003 ................ + 800fd25: 00e001e0 17f000f0 03f88100 800f8200 ................ + 800fd35: 01880003 00e001e0 17f000f0 037c8100 ..............|. + 800fd45: 041f8100 e0018800 e000e001 0017f000 ................ + 800fd55: 00033e81 00043e81 01e00188 00e001e0 .>...>.......... + 800fd65: 810017f0 8100033f 8800047f f000e001 ....?........... + 800fd75: f000e001 1f860017 ff0100c0 88000380 ................ + 800fd85: f000e001 f000e003 07860017 f70700e0 ................ + 800fd95: 880003c0 7c00e001 f000c007 03860017 .......|........ + 800fda5: e71f00fc 880003e0 7f00e001 f00080bf ................ + 800fdb5: 01810017 8382ff03 880003f0 3f00e001 ...............? + 800fdc5: f00000ff 7f850018 f801ffff 01880003 ................ + 800fdd5: fe1f00e0 18f00000 ff1f8500 03fc00fc ................ + 800fde5: e0018800 00f80700 0018f000 e0ff0385 ................ + 800fdf5: 00037e00 05e00182 1cf08100 033f8100 .~............?. + 800fe05: e0018200 f0810005 1f86001c 01000080 ................ + 800fe15: 810005e0 86001cf0 0000c00f 0005e001 ................ + 800fe25: 001ce081 00e00786 04e00100 e0018200 ................ + 800fe35: 0385001c 010000f0 e081ff06 0182001c ................ + 800fe45: 060003f8 1dc081ff 03fc8100 057f8100 ................ + 800fe55: 1d8081ff 037e8100 041f8100 1efe81ff ......~......... + 800fe65: 273f8100 271f8100 7f0e8100 8100222a ..?'...'....*".. + 800fe75: 810005e0 82002080 00051001 1f048082 ..... .......... + 800fe85: 08028200 80820005 81001f04 81000602 ................ + 800fe95: 8c002080 1f380002 bce0800b e8800b1c . ....8......... + 800fea5: 018c001c 8c004400 04c21041 1d18410c .....D..A....A.. + 800feb5: 82e08b00 08228800 22080482 8b001d08 ......"....".... + 800fec5: 881f8210 04820002 1d082208 fe088b00 ........."...... + 800fed5: 00028820 22080482 8b001d08 88208008 ......"...... . + 800fee5: 04820002 1c082208 08028c00 02882080 ....."....... .. + 800fef5: 08048208 001c1821 4210018f 10018821 ....!......B!... + 800ff05: 20080482 030c30e8 e08e001a 00881e3c ... .0......<... + 800ff15: 080482e0 0c300820 81002403 82002608 .... .0..$...&.. + 800ff25: 00260802 27100182 7fe08100 00001047 ..&....'....G... + +0800ff35 : + 800ff35: 0001bc7f 00030181 05040182 1c018100 ................ + 800ff45: 00018600 0401c00f 01820005 86001b02 ................ + 800ff55: 40080001 00050401 1b020182 00018600 ...@............ + 800ff65: 0401400c 01810005 0190001c 01400600 .@............@. + 800ff75: 45075c04 0e1df8c0 1874c005 01019000 .\.E......t..... + 800ff85: 0401400f 20c60862 06022304 00188c20 .@..b.. .#.. ... + 800ff95: 99030190 410401c0 04104410 11040241 .......A.D..A... + 800ffa5: 90001804 00f00601 10410401 41fc0044 ..........A.D..A + 800ffb5: 04110402 01900018 0100600c 44104104 .........`...A.D + 800ffc5: 02410401 18041104 08019000 04010000 ..A............. + 800ffd5: 01441041 04024104 00180411 00100190 A.D..A.......... + 800ffe5: 62040100 0401c408 10040241 8100188c ...b....A....... + 800fff5: 8b000401 44075c88 02230c01 18741004 .....\.D..#...t. + 8010005: 04018100 40708b00 f4004400 1004021d ......p@.D...... + 8010015: 81001804 83000501 06400040 18048100 ........@.@..... + 8010025: ff018900 0000e0ff 05401040 04018200 ........@.@..... + 8010035: 4083001e 00068008 001e8881 07074082 ...@.........@.. + 8010045: 7f708100 00001714 ..p..... + +0801004d : + 801004d: 0002b97f 26f80782 0c0c8200 30820026 .......&....&..0 + 801005d: 85002603 01800160 81000404 85001e38 .&..`.......8... + 801006d: 0180c040 84000304 02004480 c003001c @........D...... + 801007d: 03040182 40808400 001c0200 40c08085 .......@.......@ + 801008d: 00040401 001e4081 40c0808f 171c0401 .....@.....@.... + 801009d: 41408003 74c0050e 01900018 0160c080 ..@A...t......`. + 80100ad: 80182204 02414080 188c2006 80019000 ."...@A.. ...... + 80100bd: 040160c0 81401041 040241f8 00180411 .`..A.@..A...... + 80100cd: e0800190 41040160 40800010 11040241 ....`..A...@A... + 80100dd: 8f001904 00403080 00107f88 02414080 .....0@......@A. + 80100ed: 19041104 18808f00 40880040 40800010 ........@..@...@ + 80100fd: 11040241 8f001904 00c000c0 00104050 A...........P@.. + 801010d: 02234080 198c1004 00409300 21500080 .@#.......@...P! + 801011d: 40800010 1004021d 01061874 93001580 ...@....t....... + 801012d: 00800160 00101e20 02014080 18041004 `... ....@...... + 801013d: 15800106 03308200 01810008 04810003 ......0......... + 801014d: 0c820019 8500080c 01000001 82001904 ................ + 801015d: 0008f807 00034281 00238881 00033c81 .....B....#..<.. + 801016d: 147f7081 .p..... + +08010174 : + 8010174: 0014237f 00268081 26c00382 c0038200 .#....&....&.... + 8010184: 03820026 820026c0 0026c003 24c00382 &....&....&....$ + 8010194: c0018500 2303c003 e0038600 c007c003 .......#........ + 80101a4: 07860022 07c003e0 860022e0 c003c00f "........"...... + 80101b4: 0022f003 03001f86 22f800c0 003e8600 .."........"..>. + 80101c4: 7c00c003 7c860022 00c00300 8600223e ...|"..|....>".. + 80101d4: c00300f8 00221f00 0300f086 210f00c0 ......"........! + 80101e4: f0018800 00c00300 0020800f 00e00388 .......... ..... + 80101f4: 0700c003 88002080 0300c003 c00300c0 ..... .......... + 8010204: 07880020 c00300c0 20c00300 80078800 .......... .... + 8010214: 00c00300 0020e001 00800788 0100c003 ...... ......... + 8010224: 880020e0 0300000f f00000c0 0f880020 . .......... ... + 8010234: c0030000 20f00000 000f8800 00c00300 ....... ........ + 8010244: 0020f000 00000e88 0000c003 88002070 .. .........p .. + 8010254: 0300001e 780000c0 1e880020 c0030000 .......x ....... + 8010264: 20780000 001e8800 00c00300 00207800 ..x .........x . + 8010274: 00001e88 0000c003 88002078 0300001e ........x ...... + 8010284: 780000c0 1e880020 c0030000 20780000 ...x .........x + 8010294: 001e8800 00c00300 00207800 00001e88 .........x ..... + 80102a4: 0000c003 88002078 0300001e 780000c0 ....x .........x + 80102b4: 1e810020 78810006 1e810020 78810006 ......x ......x + 80102c4: 1e810020 70810006 0f810020 f0810006 ......p ....... + 80102d4: 0f810020 f0810006 0f810020 f0810006 ....... ....... + 80102e4: 07820020 82000480 0020e001 04800782 ......... ..... + 80102f4: e0018200 07820020 820004c0 0020c003 .... ......... . + 8010304: 04c00382 c0038200 03820020 820004e0 ........ ....... + 8010314: 00208007 04f00182 800f8200 f0810021 .. .........!... + 8010324: 0f810004 f8810022 1f810004 7c810022 ...."......."..| + 8010334: 3e810004 3e810022 7c810004 1f810022 ...>"..>...|"... + 8010344: f8810004 0f860022 030000c0 860022f0 ...."........".. + 8010354: 0000e007 0022e007 00f80386 23c01f00 ......"........# + 8010364: 00ff8400 0024ff00 0ff07f84 840024fe ......$......$.. + 8010374: f8ffff1f 07840024 25e0ffff ffff8200 ....$......%.... + 8010384: 01820026 212a7f80 03388100 00088400 &.....*!..8..... + 8010394: 00044040 001b8081 00034481 40000884 @@.......D.....@ + 80103a4: 81000441 81001b80 84000382 41400008 A.............@A + 80103b4: 80810004 8081001b 08840003 04404000 .............@@. + 80103c4: 1b808100 0e808d00 00e88003 e0024740 ............@G.. + 80103d4: 1b800e38 11408d00 00184104 10034144 8.....@..A..DA.. + 80103e4: 1b801144 20388d00 00082288 08024144 D.....8 ."..DA.. + 80103f4: 1b802082 20048d00 00082288 08024144 . ..... ."..DA.. + 8010404: 1b802082 3f028d00 0008e28f 0802414a . .....?....JA.. + 8010414: 1b8020fe 20028d00 00080208 0802812a . ..... ....*... + 8010424: 1b802080 20828d00 00080208 1003812a . ..... ....*... + 8010434: 1b802080 10448d00 00182184 e0020111 . ....D..!...... + 8010444: 1b801142 0f388d00 00e8c003 00020111 B.....8......... + 8010454: 23800e3c 27028100 27028100 27028100 <..#...'...'...' + 8010464: 7f028100 00001147 65737361 64007472 ....G...assert.d + 8010474: 676e776f 65646172 67697300 69616620 owngrade.sig fai + 8010484: 6f6e006c 72696620 7261776d 61460065 l.no firmware.Fa + 8010494: 726f7463 6f622079 5700746f 3a4e5241 ctory boot.WARN: + 80104a4: 64655220 67696c20 57007468 3a4e5241 Red light.WARN: + 80104b4: 736e5520 656e6769 69662064 61776d72 Unsigned firmwa + 80104c4: 47006572 20646f6f 6d726966 65726177 re.Good firmware + 80104d4: 726f6300 74707572 72696620 7261776d .corrupt firmwar + 80104e4: e. + +080104e6 : + 80104e6: 2641cbb4 f36ce1f7 71b4f28f 0123fb1d ..A&..l....q..#. + 80104f6: 66d6760d 6ca38aa7 f6f9539b 0518587b .v.f...l.S..{X.. + 8010506: e93b0b58 b89fc431 113c0444 470f0896 X.;.1...D.<....G + 8010516: 37ed2581 4a9e237a 3818b7af da0438ba .%.7z#.J...8.8.. + 8010526: 1dc8a2d6 df5e811c 6d290ca6 8d8f57b8 ......^...)m.W.. + 8010536: 9269295e c178d1ce 31d7207b b596a17b ^)i...x.{ .1{... + 8010546: 0c1bef3d c31a79aa c8c45845 ffeb2d8a =....y..EX...-.. + 8010556: 01829bfe bc5e5f87 4fe5a596 9ffe68c7 ....._^....O.h.. + 8010566: 0166ef42 95cfc456 38f0b5f4 c5261164 B.f.V......8d.&. + 8010576: 66c13999 14120632 689c254c bad38c35 .9.f2...L%.h5... + 8010586: 8cde7824 6cdfab52 7809bfb8 3a63bb03 $x..R..l...x..c: + 8010596: 0ed90111 8f737aa4 7f3b18bf c87b0af0 .....zs...;...{. + 80105a6: 56546067 c5ec0c82 0882bc1d ef39c116 g`TV..........9. + 80105b6: 32babff5 e35fce7c d7621e74 4cc5fce9 ...2|._.t.b....L + 80105c6: 8d11e88a 13c2adc3 2a4f2992 a4f8d2ea .........)O*.... + 80105d6: fe7cd5c4 3b450512 07598954 88d7d6da ..|...E;T.Y..... + 80105e6: 37cfb143 1f897cd2 f3acfe5b 95fc33ba C..7.|..[....3.. + 80105f6: dde7d981 14ef9525 bb97efdd a7d8f333 ....%.......3... + 8010606: 977a2b34 73aab3ba 32419de7 17a1fcd8 4+z....s..A2.... + 8010616: fe0bb566 89214063 8e7b92c9 590bdf72 f...c@!...{.r..Y + 8010626: 76dc5cd0 30dd3016 56f180c2 61a85c26 .\.v.0.0...V&\.a + 8010636: 69694fd7 3d57b8e5 582ae235 c69acedd .Oii..W=5.*X.... + 8010646: 2b1ca945 8efc010c 13513fbf 137c7e80 E..+.....?Q..~|. + 8010656: 5e4b4fd5 d59b4c9b e0d81d9e 2246c0ad .OK^.L........F" + 8010666: 20314553 666e6f63 66206769 006c6961 SE1 config fail. + 8010676: 72726f63 20747075 72696170 63657320 corrupt pair sec + 8010686: 75636d00 6c756620 7562006c 66206e72 .mcu full.burn f + 8010696: 3a6c6961 76210020 64696c61 6162003f ail: .!valid?.ba + 80106a6: 61762064 66003f6c 20747361 63697262 d val?.fast bric + 80106b6: 2e2e2e6b 64200020 00656e6f 79706f43 k... . done.Copy + 80106c6: 68676972 30322074 202d3831 43207962 right 2018- by C + 80106d6: 6b6e696f 20657469 2e636e49 206f6e00 oinkite Inc..no + 80106e6: 00726573 66206b77 0016006c 01410800 ser.wk fl.....A. + ... + 8010702: 000000ee 006100e1 218f0000 438f808f ......a....!...C + 8010712: 430080af 20834300 43c343c3 43c343c3 ...C.C. .C.C.C.C + 8010722: 43c343c3 0000438f ffffffff 00000000 .C.C.C.......... + 8010732: ffffffff 00000000 00000000 000000f0 ................ + ... + 801074a: 00001502 003c0000 01bc005c 01bc01fc ......<.\....... + 801075a: 01dc01dc 03dc03d1 03dc03dc 03dc03dc ................ + 801076a: 01dc03dc 0001003c 00120000 00000000 ....<........... + 801077a: 00010000 00080000 02000000 00020000 ................ + 801078a: 00000000 00010000 00070000 .............. + +08010798 : + 8010798: 0d0c0b09 .... + +0801079c : + 801079c: 2e312e31 69742030 323d656d 30353230 1.1.0 time=20250 + 80107ac: 2e353134 36333930 67203133 6d3d7469 415.093631 git=m + 80107bc: 65747361 38324072 61363239 0d006463 aster@28926acd.. + 80107cc: .. + +080107ce : + 80107ce: 33323130 37363534 62613938 66656463 0123456789abcdef + 80107de: 41525350 6166204d 50006c69 203a5253 PSRAM fail.PSR: + 80107ee: 6164616e 52535000 6321203a 6b636568 nada.PSR: !check + 80107fe: 52535000 6576203a 6f697372 fc00006e .PSR: version... + 801080e: 00020000 00000000 00030000 000a0000 ................ + 801081e: 00080000 00100000 6f6c0000 7220676e ..........long r + 801082e: 20646165 6c696166 55464400 72617020 ead fail.DFU par + 801083e: 66206573 006c6961 646f6f67 72696620 se fail.good fir + 801084e: 7261776d 72770065 20676e6f 6c726f77 mware.wrong worl + 801085e: 64730064 64726163 6165735f 3a686372 d.sdcard_search: + 801086e: 64730020 64726163 6f72705f 203a6562 .sdcard_probe: + 801087e: 696e6900 61662074 73006c69 64656570 .init fail.speed + 801088e: 64697700 73620065 3f657a69 006b6f00 .wide.bsize?.ok. + 801089e: 6c696166 61657220 66440064 00655375 fail read.DfuSe. + 80108ae: 6e756f66 20402064 63655200 7265766f found @ .Recover + 80108be: 6f6d2079 002e6564 1f000000 00020000 y mode.......... + 80108ce: 00010000 00030000 000c0000 00040000 ................ + 80108de: 00020000 00010000 00030000 000c0000 ................ + ... + +080108f0 : + 80108f0: 01002008 fffffc2f fffffffe ffffffff . ../........... + 8010900: ffffffff ffffffff ffffffff ffffffff ................ + 8010910: ffffffff d0364141 bfd25e8c af48a03b ....AA6..^..;.H. + 8010920: baaedce6 fffffffe ffffffff ffffffff ................ + 8010930: ffffffff 16f81798 59f2815b 2dce28d9 ........[..Y.(.- + 8010940: 029bfcdb ce870b07 55a06295 f9dcbbac .........b.U.... + 8010950: 79be667e fb10d4b8 9c47d08f a6855419 ~f.y......G..T.. + 8010960: fd17b448 0e1108a8 5da4fbfc 26a3c465 H..........]e..& + 8010970: 483ada77 00000007 00000000 00000000 w.:H............ + ... + 8010994: 0800692d 080061b1 08006387 08005f9d -i...a...c..._.. + +080109a4 : + 80109a4: 01002008 ffffffff ffffffff ffffffff . .............. + ... + 80109c0: 00000001 ffffffff fc632551 f3b9cac2 ........Q%c..... + 80109d0: a7179e84 bce6faad ffffffff ffffffff ................ + 80109e0: 00000000 ffffffff d898c296 f4a13945 ............E9.. + 80109f0: 2deb33a0 77037d81 63a440f2 f8bce6e5 .3.-.}.w.@.c.... + 8010a00: e12c4247 6b17d1f2 37bf51f5 cbb64068 GB,....k.Q.7h@.. + 8010a10: 6b315ece 2bce3357 7c0f9e16 8ee7eb4a .^1kW3.+...|J... + 8010a20: fe1a7f9b 4fe342e2 27d2604b 3bce3c3e .....B.OK`.'><.; + 8010a30: cc53b0f6 651d06b0 769886bc b3ebbd55 ..S....e...vU... + 8010a40: aa3a93e7 5ac635d8 08006a75 080061b1 ..:..5.Zuj...a.. + 8010a50: 08006a1b 08006019 .j...`.. + +08010a58 : + ... + 8010a60: 04030201 09080706 ........ + +08010a68 : + 8010a68: 00000000 04030201 ........ + +08010a70 : + 8010a70: 000186a0 00030d40 00061a80 000c3500 ....@........5.. + 8010a80: 000f4240 001e8480 003d0900 007a1200 @B........=...z. + 8010a90: 00f42400 016e3600 01e84800 02dc6c00 .$...6n..H...l.. + 8010aa0: 20727463 3f746573 00702100 00006000 ctr set?.!p..`.. + 8010ab0: 00000012 00000000 00000003 00000004 ................ + +08010ac0 : + 8010ac0: 00008000 .... + +Disassembly of section .relocate: + +2009e000 : +{ +2009e000: b530 push {r4, r5, lr} + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e002: 4920 ldr r1, [pc, #128] ; (2009e084 ) +2009e004: 690c ldr r4, [r1, #16] +2009e006: 03e5 lsls r5, r4, #15 +2009e008: d4fc bmi.n 2009e004 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e00a: 690d ldr r5, [r1, #16] + if(error) { +2009e00c: 4c1e ldr r4, [pc, #120] ; (2009e088 ) +2009e00e: 4225 tst r5, r4 +2009e010: d104 bne.n 2009e01c + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e012: 690c ldr r4, [r1, #16] +2009e014: 07e4 lsls r4, r4, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e016: bf44 itt mi +2009e018: 2401 movmi r4, #1 +2009e01a: 610c strmi r4, [r1, #16] + FLASH->SR = FLASH->SR & FLASH_FLAG_SR_ERRORS; +2009e01c: 4919 ldr r1, [pc, #100] ; (2009e084 ) +2009e01e: 4d1a ldr r5, [pc, #104] ; (2009e088 ) +2009e020: 690c ldr r4, [r1, #16] +2009e022: 402c ands r4, r5 +2009e024: 610c str r4, [r1, #16] + __HAL_FLASH_DATA_CACHE_DISABLE(); +2009e026: 680c ldr r4, [r1, #0] +2009e028: f424 6480 bic.w r4, r4, #1024 ; 0x400 +2009e02c: 600c str r4, [r1, #0] + CLEAR_BIT(FLASH->CR, (FLASH_CR_PG | FLASH_CR_MER1 | FLASH_CR_PER | FLASH_CR_PNB)); // added +2009e02e: 694c ldr r4, [r1, #20] +2009e030: f424 64ff bic.w r4, r4, #2040 ; 0x7f8 +2009e034: f024 0407 bic.w r4, r4, #7 +2009e038: 614c str r4, [r1, #20] + SET_BIT(FLASH->CR, FLASH_CR_PG); +2009e03a: 694c ldr r4, [r1, #20] +2009e03c: f044 0401 orr.w r4, r4, #1 +2009e040: 614c str r4, [r1, #20] + *(__IO uint32_t *)(address) = (uint32_t)val; +2009e042: 6002 str r2, [r0, #0] + __ASM volatile ("isb 0xF":::"memory"); +2009e044: f3bf 8f6f isb sy + *(__IO uint32_t *)(address+4) = (uint32_t)(val >> 32); +2009e048: 6043 str r3, [r0, #4] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e04a: 690b ldr r3, [r1, #16] +2009e04c: 03da lsls r2, r3, #15 +2009e04e: d4fc bmi.n 2009e04a + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e050: 6908 ldr r0, [r1, #16] + if(error) { +2009e052: 4028 ands r0, r5 +2009e054: d104 bne.n 2009e060 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e056: 690b ldr r3, [r1, #16] +2009e058: 07db lsls r3, r3, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e05a: bf44 itt mi +2009e05c: 2301 movmi r3, #1 +2009e05e: 610b strmi r3, [r1, #16] + CLEAR_BIT(FLASH->CR, FLASH_CR_PG); +2009e060: 4b08 ldr r3, [pc, #32] ; (2009e084 ) +2009e062: 695a ldr r2, [r3, #20] +2009e064: f022 0201 bic.w r2, r2, #1 +2009e068: 615a str r2, [r3, #20] + __HAL_FLASH_DATA_CACHE_RESET(); +2009e06a: 681a ldr r2, [r3, #0] +2009e06c: f442 5280 orr.w r2, r2, #4096 ; 0x1000 +2009e070: 601a str r2, [r3, #0] +2009e072: 681a ldr r2, [r3, #0] +2009e074: f422 5280 bic.w r2, r2, #4096 ; 0x1000 +2009e078: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); +2009e07a: 681a ldr r2, [r3, #0] +2009e07c: f442 6280 orr.w r2, r2, #1024 ; 0x400 +2009e080: 601a str r2, [r3, #0] +} +2009e082: bd30 pop {r4, r5, pc} +2009e084: 40022000 .word 0x40022000 +2009e088: 0002c3fa .word 0x0002c3fa + +2009e08c : + if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_ERASE_SIZE)) { +2009e08c: 4b2d ldr r3, [pc, #180] ; (2009e144 ) +2009e08e: 4003 ands r3, r0 +{ +2009e090: b510 push {r4, lr} + if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_ERASE_SIZE)) { +2009e092: 2b00 cmp r3, #0 +2009e094: d054 beq.n 2009e140 + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e096: 4b2c ldr r3, [pc, #176] ; (2009e148 ) + page_num &= 0xff; +2009e098: f3c0 3207 ubfx r2, r0, #12, #8 + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e09c: 6919 ldr r1, [r3, #16] +2009e09e: 03c9 lsls r1, r1, #15 +2009e0a0: d4fc bmi.n 2009e09c + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e0a2: 691c ldr r4, [r3, #16] + if(error) { +2009e0a4: 4929 ldr r1, [pc, #164] ; (2009e14c ) +2009e0a6: 420c tst r4, r1 +2009e0a8: d104 bne.n 2009e0b4 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e0aa: 6919 ldr r1, [r3, #16] +2009e0ac: 07cc lsls r4, r1, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e0ae: bf44 itt mi +2009e0b0: 2101 movmi r1, #1 +2009e0b2: 6119 strmi r1, [r3, #16] + FLASH->SR = FLASH->SR & 0xffff; +2009e0b4: 4b24 ldr r3, [pc, #144] ; (2009e148 ) +2009e0b6: 6919 ldr r1, [r3, #16] +2009e0b8: b289 uxth r1, r1 +2009e0ba: 6119 str r1, [r3, #16] + __HAL_FLASH_DATA_CACHE_DISABLE(); +2009e0bc: 6819 ldr r1, [r3, #0] +2009e0be: f421 6180 bic.w r1, r1, #1024 ; 0x400 +2009e0c2: 6019 str r1, [r3, #0] + SET_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0c4: 6959 ldr r1, [r3, #20] + if(bank2) { +2009e0c6: f010 6ffe tst.w r0, #133169152 ; 0x7f00000 + SET_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0ca: bf14 ite ne +2009e0cc: f441 6100 orrne.w r1, r1, #2048 ; 0x800 + CLEAR_BIT(FLASH->CR, FLASH_CR_BKER); +2009e0d0: f421 6100 biceq.w r1, r1, #2048 ; 0x800 +2009e0d4: 6159 str r1, [r3, #20] + MODIFY_REG(FLASH->CR, FLASH_CR_PNB, (page_num << POSITION_VAL(FLASH_CR_PNB))); +2009e0d6: 6959 ldr r1, [r3, #20] + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +2009e0d8: f44f 63ff mov.w r3, #2040 ; 0x7f8 +2009e0dc: f421 61ff bic.w r1, r1, #2040 ; 0x7f8 +2009e0e0: fa93 f3a3 rbit r3, r3 + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +2009e0e4: fab3 f383 clz r3, r3 +2009e0e8: 409a lsls r2, r3 +2009e0ea: 4b17 ldr r3, [pc, #92] ; (2009e148 ) +2009e0ec: 430a orrs r2, r1 +2009e0ee: 615a str r2, [r3, #20] + SET_BIT(FLASH->CR, FLASH_CR_PER); +2009e0f0: 695a ldr r2, [r3, #20] +2009e0f2: f042 0202 orr.w r2, r2, #2 +2009e0f6: 615a str r2, [r3, #20] + SET_BIT(FLASH->CR, FLASH_CR_STRT); +2009e0f8: 695a ldr r2, [r3, #20] +2009e0fa: f442 3280 orr.w r2, r2, #65536 ; 0x10000 +2009e0fe: 615a str r2, [r3, #20] + while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) { +2009e100: 691a ldr r2, [r3, #16] +2009e102: 03d1 lsls r1, r2, #15 +2009e104: d4fc bmi.n 2009e100 + uint32_t error = (FLASH->SR & FLASH_FLAG_SR_ERRORS); +2009e106: 6918 ldr r0, [r3, #16] +2009e108: 4a10 ldr r2, [pc, #64] ; (2009e14c ) + if(error) { +2009e10a: 4010 ands r0, r2 +2009e10c: d104 bne.n 2009e118 + if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP)) { +2009e10e: 691a ldr r2, [r3, #16] +2009e110: 07d2 lsls r2, r2, #31 + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); +2009e112: bf44 itt mi +2009e114: 2201 movmi r2, #1 +2009e116: 611a strmi r2, [r3, #16] + CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB)); +2009e118: 4b0b ldr r3, [pc, #44] ; (2009e148 ) +2009e11a: 695a ldr r2, [r3, #20] +2009e11c: f422 62ff bic.w r2, r2, #2040 ; 0x7f8 +2009e120: f022 0202 bic.w r2, r2, #2 +2009e124: 615a str r2, [r3, #20] + __HAL_FLASH_DATA_CACHE_RESET(); +2009e126: 681a ldr r2, [r3, #0] +2009e128: f442 5280 orr.w r2, r2, #4096 ; 0x1000 +2009e12c: 601a str r2, [r3, #0] +2009e12e: 681a ldr r2, [r3, #0] +2009e130: f422 5280 bic.w r2, r2, #4096 ; 0x1000 +2009e134: 601a str r2, [r3, #0] + __HAL_FLASH_DATA_CACHE_ENABLE(); +2009e136: 681a ldr r2, [r3, #0] +2009e138: f442 6280 orr.w r2, r2, #1024 ; 0x400 +2009e13c: 601a str r2, [r3, #0] +} +2009e13e: bd10 pop {r4, pc} + return 1; +2009e140: 2001 movs r0, #1 +2009e142: e7fc b.n 2009e13e +2009e144: 07fe0000 .word 0x07fe0000 +2009e148: 40022000 .word 0x40022000 +2009e14c: 0002c3fa .word 0x0002c3fa diff --git a/stm32/q1-bootloader/releases/README.md b/stm32/q1-bootloader/releases/README.md index 54dbd3c7f..31ae9f0e4 100644 --- a/stm32/q1-bootloader/releases/README.md +++ b/stm32/q1-bootloader/releases/README.md @@ -13,3 +13,4 @@ Github is nearly free, so why not capture all the actual bits! - V1.0.2 - bugfix w/ power btn release - V1.0.3 - bugfix to allow (mpy) version numbers < 3.0.0 - V1.0.4 - cleanups and such; final version for first-run of boards. +- V1.1.0 - enable omitted if wrong PIN options & fix "Wipe -> Wallet" trick pin option diff --git a/stm32/q1-bootloader/version.h b/stm32/q1-bootloader/version.h index 6e834330f..b6f098a49 100644 --- a/stm32/q1-bootloader/version.h +++ b/stm32/q1-bootloader/version.h @@ -6,7 +6,7 @@ // Public version number for humans. Lots more version data added by Makefile. // - update ../Q1-Makefile BOOTLOADER_VERSION once this is qualified version -#define RELEASE_VERSION "1.0.4" +#define RELEASE_VERSION "1.1.0" extern const char version_string[]; diff --git a/stm32/shared.mk b/stm32/shared.mk index a568e0562..eb7bfd24d 100644 --- a/stm32/shared.mk +++ b/stm32/shared.mk @@ -136,9 +136,11 @@ release-products: built/production.bin -git commit $(BOARD)/file_time.c -m "For $(NEW_VERSION)" $(SIGNIT) sign -m $(HW_MODEL) $(VERSION_STRING) -r built/production.bin $(PROD_KEYNUM) -o built/production.bin $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin $(RELEASE_FNAME) +ifeq ($(findstring X,$(NEW_VERSION)),) $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \ -b $(BOOTLOADER_BASE):$(BOOTLOADER_DIR)/releases/$(BOOTLOADER_VERSION)/bootloader.bin \ $(RELEASE_FNAME:%.dfu=%-factory.dfu) +endif @echo @echo 'Made release: ' $(RELEASE_FNAME) @echo diff --git a/testing/api.py b/testing/api.py index 2522dbce8..58d7db70f 100644 --- a/testing/api.py +++ b/testing/api.py @@ -11,6 +11,10 @@ def find_bitcoind(): # search for the binary we need # - should be in the path really + env_path = os.environ.get("CC_TEST_BITCOIND", None) + if env_path: + return env_path + easy = shutil.which('bitcoind') if easy: return easy @@ -33,6 +37,7 @@ def __init__(self): self.userpass = None self.supply_wallet = None self.has_bdb = True + self.version = None def start(self): @@ -51,14 +56,17 @@ def get_free_port(): [ self.bitcoind_path, # needed for newest master - # TODO legacy wallet will be deprecated in 26 + # legacy wallet was deprecated in v29 + # and removed completely in v30 "-deprecatedrpc=create_bdb", "-regtest", f"-datadir={self.datadir}", "-noprinttoconsole", "-fallbackfee=0.0002", "-server=1", + "-listen=0", "-keypool=1", + "-listen=0" f"-port={self.p2p_port}", f"-rpcport={self.rpc_port}" ] @@ -68,8 +76,9 @@ def get_free_port(): # Wait for cookie file to be created cookie_path = os.path.join(self.datadir, "regtest", ".cookie") for i in range(20): - if not os.path.exists(cookie_path): - time.sleep(0.5) + if os.path.exists(cookie_path): + break + time.sleep(0.5) else: RuntimeError("'.cookie' not found. Is bitcoind running?") # Read .cookie file to get user and pass @@ -89,12 +98,14 @@ def get_free_port(): pass assert self.rpc.getblockchaininfo()['chain'] == 'regtest' - assert self.rpc.getnetworkinfo()['version'] >= 220000, "we require >= 22.0 of Core" + self.version = self.rpc.getnetworkinfo()['version'] + assert self.version >= 220000, "we require >= 22.0 of Core" # not descriptors so that we can do dumpwallet try: self.supply_wallet = self.create_wallet(wallet_name="supply", descriptors=False) except JSONRPCException as e: - assert "BDB wallet creation is deprecated" in str(e) + assert "BDB wallet creation is deprecated" in str(e) \ + or "no longer possible to create a legacy wallet" in str(e) # before v30.0 vs v30.0+ self.has_bdb = False self.supply_wallet = self.create_wallet(wallet_name="supply", descriptors=True) @@ -170,8 +181,9 @@ def match_key(bitcoind, set_master_key, reset_seed_words): os.unlink(fn) except JSONRPCException as e: - print(str(e)) - assert "Only legacy wallets are supported by this command" in str(e) + assert "Only legacy wallets are supported by this command" in str(e) \ + or "Method not found" in str(e) # v30.0 + prv_descs = bitcoind.supply_wallet.listdescriptors(True) # True --> show private prv = prv_descs["descriptors"][0]["desc"].replace("pkh(", "").split("/")[0] @@ -239,7 +251,6 @@ def bitcoind_d_sim_watch(bitcoind): descriptors = [ { "timestamp": "now", - "label": "Coldcard 0f056943 segwit v0", "active": True, "desc": "wpkh([0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/0/*)#erexmnep", "internal": False @@ -252,7 +263,6 @@ def bitcoind_d_sim_watch(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943 segwit v1", "active": True, "desc": "tr([0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/0/*)#6ghw47ge", "internal": False @@ -265,7 +275,6 @@ def bitcoind_d_sim_watch(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943 p2pkh", "active": True, "desc": "pkh([0f056943/44h/1h/0h]tpubDCiHGUNYdRRBPNYm7CqeeLwPWfeb2ZT2rPsk4aEW3eUoJM93jbBa7hPpB1T9YKtigmjpxHrB1522kSsTxGm9V6cqKqrp1EDaYaeJZqcirYB/0/*)#fxwk08tc", "internal": False @@ -278,7 +287,6 @@ def bitcoind_d_sim_watch(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943 p2sh-p2wpkh", "active": True, "desc": "sh(wpkh([0f056943/49h/1h/0h]tpubDCDqt7XXvhAYY9HSwrCXB7BXqYM4RXB8WFtKgtTXGa6u3U6EV1NJJRFTcuTRyhSY5Vreg1LP8aPdyiAPQGrDJLikkHoc7VQg6DA9NtUxHtj/0/*))#weah3vek", "internal": False @@ -324,7 +332,6 @@ def bitcoind_d_sim_sign(bitcoind): descriptors = [ { "timestamp": "now", - "label": "Coldcard 0f056943", "active": True, "desc": "wpkh([0f056943/84h/1h/0h]tprv8fRh8AYC5iQitbbtzwVaUUyXVZh3Y7HxVYSbqzf45eao9SMfEc3MexJx4y6pU1WjjxcEiYArEjhRTSy5mqfXzBtSncTYhKfxQWywcfeqxFE/0/*)#mzg0pna0", "internal": False @@ -337,7 +344,6 @@ def bitcoind_d_sim_sign(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943 segwit v1", "active": True, "desc": "tr([0f056943/86h/1h/0h]tprv8fxCNe7LnX2rxiS89eqsVD92aJ47ypYd4FgQ9NipWJEHurv95cC2i57yC2mRHnpuHfmgdb17GV9wfSNjswUQXmaY7Qs2Jaa5hEdkxaHy4BK/0/*)#x7dfk9mw", "internal": False @@ -350,7 +356,6 @@ def bitcoind_d_sim_sign(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943", "active": True, "desc": "pkh([0f056943/44h/1h/0h]tprv8g2F84LJV3jWVuWyDZB4EwHGwe8esEG8H6Gxn4CCdNgQTrtH7CMywCmwzuMGZjz13sQ9rcCZucCm6i2zigkYGSPUvCzDQxGW8RCy7FpPdrg/0/*)#kjnlnm3v", "internal": False @@ -363,7 +368,6 @@ def bitcoind_d_sim_sign(bitcoind): }, { "timestamp": "now", - "label": "Coldcard 0f056943", "active": True, "desc": "sh(wpkh([0f056943/49h/1h/0h]tprv8fXojhVHnKUsegFf4CXvmhXRGWq8GBzDvxHYQNRDrJJWCyqTrcYi7vdbSn65CHETVPdw4sxc75v23Ev7o8fCePazRf917CMt1C3mjnKV4Jq/0/*))#0qf5gv2y", "internal": False diff --git a/testing/bip32.py b/testing/bip32.py index fe10bd752..57899a391 100644 --- a/testing/bip32.py +++ b/testing/bip32.py @@ -6,8 +6,9 @@ try: from pysecp256k1 import ( ec_seckey_verify, ec_pubkey_create, ec_pubkey_serialize, ec_pubkey_parse, - ec_seckey_tweak_add, ec_pubkey_tweak_add, + ec_seckey_tweak_add, ec_pubkey_tweak_add, tagged_sha256 ) + from pysecp256k1.extrakeys import xonly_pubkey_from_pubkey, xonly_pubkey_serialize, xonly_pubkey_tweak_add except ImportError: import ecdsa SECP256k1 = ecdsa.curves.SECP256k1 @@ -119,6 +120,10 @@ def tweak_add(self, tweak32: bytes) -> "PrivateKey": tweaked = ec_seckey_tweak_add(self.k, tweak32) return PrivateKey(sec_exp=tweaked) + def address(self, compressed: bool = True, chain: str = "BTC", + addr_fmt: str = "p2wpkh") -> str: + return self.K.address(compressed, chain, addr_fmt) + @classmethod def from_wif(cls, wif_str: str) -> "PrivateKey": """ @@ -193,8 +198,17 @@ def sec(self, compressed: bool = True) -> bytes: return self.K.to_string(encoding="compressed" if compressed else "uncompressed") def tweak_add(self, tweak32: bytes) -> "PublicKey": + assert len(tweak32) == 32 return PublicKey(pub_key=ec_pubkey_tweak_add(self.K, tweak32)) + def taptweak(self, tweak32: bytes = None) -> "bytes": + xonly_key, _ = xonly_pubkey_from_pubkey(self.K) + tweak = tweak32 or xonly_pubkey_serialize(xonly_key) + tweak = tagged_sha256(b"TapTweak", tweak) + tweaked_pubkey = xonly_pubkey_tweak_add(xonly_key, tweak) + tweaked_xonly_pubkey, parity = xonly_pubkey_from_pubkey(tweaked_pubkey) + return xonly_pubkey_serialize(tweaked_xonly_pubkey) + @classmethod def parse(cls, key_bytes: bytes) -> "PublicKey": """ @@ -227,7 +241,7 @@ def h160(self, compressed: bool = True) -> bytes: """ return hash160(self.sec(compressed=compressed)) - def address(self, compressed: bool = True, testnet: bool = False, + def address(self, compressed: bool = True, chain: str = "BTC", addr_fmt: str = "p2wpkh") -> str: """ Generates bitcoin address from public key. @@ -240,18 +254,33 @@ def address(self, compressed: bool = True, testnet: bool = False, 3. p2wpkh (default) :return: bitcoin address """ + if chain == "BTC": + hrp = "bc" + pkh_prefix = b"\x00" + sh_prefix = b"\x05" + else: + pkh_prefix = b"\x6f" + sh_prefix = b"\xc4" + if chain == "XRT": + hrp = "bcrt" + elif chain == "XTN": + hrp = "tb" + else: + assert False + + if addr_fmt == "p2tr": + tweaked_xonly = self.taptweak() + return bech32.encode(hrp=hrp, witver=1, witprog=tweaked_xonly) + h160 = self.h160(compressed=compressed) if addr_fmt == "p2pkh": - prefix = b"\x6f" if testnet else b"\x00" - return encode_base58_checksum(prefix + h160) + return encode_base58_checksum(pkh_prefix + h160) elif addr_fmt == "p2wpkh": - hrp = "tb" if testnet else "bc" return bech32.encode(hrp=hrp, witver=0, witprog=h160) elif addr_fmt == "p2sh-p2wpkh": scr = b"\x00\x14" + h160 # witversion 0 + pubkey hash h160 = hash160(scr) - prefix = b"\xc4" if testnet else b"\x05" - return encode_base58_checksum(prefix + h160) + return encode_base58_checksum(sh_prefix + h160) raise ValueError("Unsupported address type.") @@ -708,6 +737,12 @@ def from_hwif(cls, extended_key): ek = PubKeyNode.parse(extended_key, testnet) return cls(ek, netcode="XTN" if testnet else "BTC") + @classmethod + def from_chaincode_pubkey(cls, chain_code, pubkey, netcode="XTN"): + node = PubKeyNode(pubkey, chain_code, 0, 0, + False if netcode == "BTC" else True) + return cls(node, netcode=netcode) + def subkey_for_path(self, path): path_list = str_to_path(path) node = self.node @@ -730,9 +765,9 @@ def from_wallet_key(cls, extended_key): def hash160(self, compressed=True): return self.node.public_key.h160(compressed) - def address(self, compressed=True, netcode="XTN", addr_fmt="p2pkh"): + def address(self, compressed=True, chain="XTN", addr_fmt="p2pkh"): return self.node.public_key.address(compressed, addr_fmt=addr_fmt, - testnet=False if netcode == "BTC" else True) + chain=chain) def sec(self, compressed=True): return self.node.public_key.sec(compressed) @@ -746,5 +781,9 @@ def netcode(self): def chain_code(self): return self.node.chain_code + def privkey(self): + assert isinstance(self.node, PrvKeyNode) + return bytes(self.node.private_key) + def parent_fingerprint(self): return self.node.parent_fingerprint diff --git a/testing/clone_tests.py b/testing/clone_tests.py index 5f423ec08..01aa0523a 100644 --- a/testing/clone_tests.py +++ b/testing/clone_tests.py @@ -5,7 +5,7 @@ from core_fixtures import _pick_menu_item, _cap_story, _press_select from core_fixtures import _need_keypress, _cap_menu, _sim_exec from run_sim_tests import ColdcardSimulator, clean_sim_data -from ckcc_protocol.client import ColdcardDevice, CKCC_SIMULATOR_PATH +from ckcc_protocol.client import ColdcardDevice def _clone(source, target): @@ -18,7 +18,7 @@ def _clone(source, target): clean_sim_data() # remove all from previous sim_target = ColdcardSimulator(args=[target_sim_arg, "-l"]) sim_target.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, target_is_Q, "Import Existing") _pick_menu_item(device, target_is_Q, "Clone Coldcard") time.sleep(.1) @@ -32,11 +32,11 @@ def _clone(source, target): assert f"Bring that card back and press {'ENTER' if target_is_Q else 'OK'} to complete clone process" in story # SOURCE - # clone with multisig wallet + # clone with miniscript wallet sim_source = ColdcardSimulator(args=[source_sim_arg, "--ms", "--p2wsh", "--set", "nfc=1", "--set", "vidsk=1"]) sim_source.start(start_wait=6) - device_source = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device_source = ColdcardDevice(is_simulator=True) _pick_menu_item(device_source, source_is_Q, "Advanced/Tools") time.sleep(.1) _pick_menu_item(device_source, source_is_Q, "Backup") @@ -89,12 +89,12 @@ def _clone(source, target): # TARGET again. Killed now - restart and verify settings sim_target = ColdcardSimulator(args=[target_sim_arg]) sim_target.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, target_is_Q, "Settings") - _pick_menu_item(device, target_is_Q, "Multisig Wallets") + _pick_menu_item(device, target_is_Q, "Multisig/Miniscript") time.sleep(.1) m = _cap_menu(device) - assert "2/4: P2WSH--2-of-4" in m + assert "P2WSH--2-of-4" in m # check NFC/VDisk after clone - must be disabled # USB enabled as we are on the simulator diff --git a/testing/conftest.py b/testing/conftest.py index 7f71115b7..6982751c1 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1,9 +1,9 @@ # (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -import pytest, time, sys, random, re, ndef, os, glob, hashlib, json, functools, io, math, pdb +import pytest, time, sys, random, re, ndef, os, glob, hashlib, json, functools, io, math, bech32, pdb, base64 from subprocess import check_output from ckcc.protocol import CCProtocolPacker -from helpers import B2A, U2SAT, hash160 +from helpers import B2A, U2SAT, hash160, taptweak, addr_from_display_format from base58 import decode_base58_checksum from bip32 import BIP32Node from msg import verify_message @@ -13,13 +13,15 @@ from binascii import b2a_hex, a2b_hex from constants import * from charcodes import * -from core_fixtures import _need_keypress, _sim_exec, _cap_story, _cap_menu, _cap_screen +from core_fixtures import _need_keypress, _sim_exec, _cap_story, _cap_menu, _cap_screen, _sim_eval from core_fixtures import _press_select, _pick_menu_item, _enter_complex, _dev_hw_label # lock down randomness random.seed(42) +# needs to be run from /testing directory +os.environ["SRC_ROOT"] = os.path.join(os.getcwd().rsplit("/", 1)[0]) if sys.platform == 'darwin': # BUGFIX: my ARM-based MacOS system uses rosetta to run Python in x86 mode # and so I needed this? @@ -31,10 +33,13 @@ def pytest_addoption(parser): default=False, help="run on real dev") parser.addoption("--sim", action="store_true", default=True, help="run on simulator") + parser.addoption("--localhost", action="store_true", + default=False, help="test web stuff against coldcard.com code running on localhost:5070") parser.addoption("--manual", action="store_true", default=False, help="operator must press keys on real CC") parser.addoption("--mk", default=4, help="Assume mark N hardware") + parser.addoption("--sim-socket", "-S", type=str, help="Simulator .socket path", default=None) parser.addoption("--duress", action="store_true", default=False, help="assume logged-in with duress PIN") @@ -77,48 +82,42 @@ def simulator(request): raise pytest.skip('need simulator for this test, have real device') try: - return ColdcardDevice(sn=SIM_PATH) - except: + return ColdcardDevice(sn=request.config.getoption("--sim-socket"), is_simulator=True) + except Exception as e: print("Simulator is required for this test") raise pytest.fail('missing simulator') -@pytest.fixture(scope='module') +@pytest.fixture def sim_exec(dev): - # run code in the simulator's interpretor + # run code in the simulator's interpreter # - can work on real product too, if "debug build" is used. f = functools.partial(_sim_exec, dev) return f -@pytest.fixture(scope='module') +@pytest.fixture def sim_eval(dev): # eval an expression in the simulator's interpretor # - can work on real product too, if "debug build" is used. + f = functools.partial(_sim_eval, dev) + return f - def doit(cmd, timeout=None): - return dev.send_recv(b'EVAL' + cmd.encode('utf-8'), timeout=timeout).decode('utf-8') - - return doit - -@pytest.fixture(scope='module') -def sim_execfile(simulator): +@pytest.fixture +def sim_execfile(simulator, src_root_dir): # run a whole file in the simulator's interpretor # - requires shared filesystem - import os - def doit(fname, timeout=None): - fn = os.path.realpath(fname) - hook = 'execfile("%s")' % fn + hook = 'execfile("%s")' % (src_root_dir + "/testing/" + fname) return simulator.send_recv(b'EXEC' + hook.encode('utf-8'), timeout=timeout).decode('utf-8') return doit -@pytest.fixture(scope='module') +@pytest.fixture def is_simulator(dev): def doit(): return hasattr(dev.dev, 'pipe') return doit -@pytest.fixture(scope='module') +@pytest.fixture def send_ux_abort(simulator): def doit(): @@ -136,7 +135,7 @@ def OK(is_q1): def X(is_q1): return "CANCEL" if is_q1 else "X" -@pytest.fixture(scope='module') +@pytest.fixture def need_keypress(dev, request): def doit(k, timeout=1000): if request.config.getoption("--manual"): @@ -150,7 +149,7 @@ def doit(k, timeout=1000): return doit -@pytest.fixture(scope='module') +@pytest.fixture def enter_number(need_keypress, press_select): def doit(number): number = str(number) if not isinstance(number, str) else number @@ -168,7 +167,7 @@ def enter_complex(dev, is_q1): f = functools.partial(_enter_complex, dev, is_q1) return f -@pytest.fixture(scope='module') +@pytest.fixture def enter_hex(need_keypress, enter_text, is_q1): def doit(hex_str): if is_q1: @@ -183,7 +182,7 @@ def doit(hex_str): return doit -@pytest.fixture(scope='module') +@pytest.fixture def enter_pin(enter_number, press_select, cap_screen, is_q1): def doit(pin): assert '-' in pin @@ -205,7 +204,7 @@ def doit(pin): return doit -@pytest.fixture(scope='module') +@pytest.fixture def do_keypresses(need_keypress): # do a series of keypresses, any kind def doit(value): @@ -215,7 +214,7 @@ def doit(value): return doit -@pytest.fixture(scope='module') +@pytest.fixture def enter_text(need_keypress, is_q1): # enter a text value, might be a number or string ... on Q can be multiline def doit(value, multiline=False): @@ -258,14 +257,14 @@ def master_xpub(dev): return r -@pytest.fixture(scope='module') +@pytest.fixture def unit_test(sim_execfile): def doit(filename): rv = sim_execfile(filename) if rv: pytest.fail(rv) return doit -@pytest.fixture(scope='module') +@pytest.fixture def get_settings(sim_execfile): # get all settings def doit(): @@ -276,7 +275,7 @@ def doit(): return doit -@pytest.fixture(scope='module') +@pytest.fixture def get_setting(sim_execfile, sim_exec): # get an individual setting def doit(name, default=None): @@ -288,31 +287,35 @@ def doit(name, default=None): return doit -@pytest.fixture(scope='module') +@pytest.fixture def addr_vs_path(master_xpub): - from bip32 import BIP32Node - from ckcc_protocol.constants import AF_CLASSIC, AFC_PUBKEY, AF_P2WPKH, AFC_SCRIPT - from ckcc_protocol.constants import AF_P2WPKH_P2SH, AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH - from bech32 import bech32_decode, convertbits, Encoding - from hashlib import sha256 + def doit(given_addr, path=None, addr_fmt=None, script=None, chain="XTN"): + from bip32 import BIP32Node + from ckcc_protocol.constants import AF_CLASSIC, AFC_PUBKEY, AF_P2WPKH, AFC_SCRIPT + from ckcc_protocol.constants import AF_P2WPKH_P2SH, AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH + from bech32 import bech32_decode, convertbits, decode, Encoding + from hashlib import sha256 - def doit(given_addr, path=None, addr_fmt=None, script=None, testnet=True): if not script: try: # prefer using xpub if we can mk = BIP32Node.from_wallet_key(master_xpub) - if not testnet: - mk._netcode = "BTC" - sk = mk.subkey_for_path(path[2:]) + mk._netcode = chain + sk = mk.subkey_for_path(path) except: mk = BIP32Node.from_wallet_key(simulator_fixed_tprv) - if not testnet: - mk._netcode = "BTC" - sk = mk.subkey_for_path(path[2:]) + mk._netcode = chain + sk = mk.subkey_for_path(path) + + if addr_fmt == AF_P2TR: + tweaked_xonly = taptweak(sk.sec()[1:]) + decoded = decode(given_addr[:2], given_addr) + assert not given_addr.startswith("bcrt") # regtest + assert tweaked_xonly == bytes(decoded[1]) - if addr_fmt in {None, AF_CLASSIC}: + elif addr_fmt in {None, AF_CLASSIC}: # easy - assert sk.address(netcode="XTN" if testnet else "BTC") == given_addr + assert sk.address(chain=chain) == given_addr elif addr_fmt & AFC_PUBKEY: @@ -360,20 +363,19 @@ def doit(given_addr, path=None, addr_fmt=None, script=None, testnet=True): return doit - @pytest.fixture(scope='module') def capture_enabled(sim_eval): # need to have sim_display imported early, see unix/frozen-modules/ckcc # - could be xfail or xskip here assert sim_eval("'sim_display' in sys.modules") == 'True' -@pytest.fixture(scope='module') +@pytest.fixture def cap_menu(dev): "Return menu items as a list" f = functools.partial(_cap_menu, dev) return f -@pytest.fixture(scope='module') +@pytest.fixture def is_ftux_screen(sim_exec): "are we presenting a view from ftux.py??" def doit(): @@ -400,12 +402,12 @@ def doit(): return doit -@pytest.fixture(scope='module') +@pytest.fixture def cap_screen(dev): f = functools.partial(_cap_screen, dev) return f -@pytest.fixture(scope='module') +@pytest.fixture def cap_text_box(cap_screen): # provides text inside a lined box on the screen right now - Q1 only def doit(): @@ -421,15 +423,15 @@ def doit(): return doit -@pytest.fixture(scope='module') +@pytest.fixture def cap_story(dev): # returns (title, body) of whatever story is being actively shown f = functools.partial(_cap_story, dev) return f -@pytest.fixture(scope='module') -def cap_image(request, sim_exec, is_q1, is_headless): +@pytest.fixture +def cap_image(request, sim_exec, is_q1, is_headless, sim_root_dir): def flip(raw): reorg = bytearray(128*64) @@ -449,7 +451,7 @@ def doit(): if is_headless: raise pytest.skip("headless mode: QR tests disabled") # trigger simulator to capture a snapshot into a named file, read it. - fn = os.path.realpath(f'./debug/snap-{random.randint(int(1E6), int(9E6))}.png') + fn = os.path.realpath(f'{sim_root_dir}/debug/snap-{random.randint(int(1E6), int(9E6))}.png') try: sim_exec(f"from glob import dis; dis.dis.save_snapshot({fn!r})") for _ in range(20): @@ -478,7 +480,7 @@ def doit(): QR_HISTORY = [] @pytest.fixture(scope='session') -def qr_quality_check(): +def qr_quality_check(sim_root_dir): # Use this with cap_screen_qr print("QR codes will be captured and shown at end of run.") yield None @@ -527,13 +529,13 @@ def qr_quality_check(): #rv = rv.resize(tuple(c*4 for c in rv.size), resample=Image.NEAREST) - rv.save('debug/all-qrs.png') + rv.save(f'{sim_root_dir}/debug/all-qrs.png') rv.show() -@pytest.fixture(scope='module') -def cap_screen_qr(cap_image): +@pytest.fixture +def cap_screen_qr(cap_image, sim_root_dir): def doit(no_history=False): # NOTE: version=4 QR is pixel doubled to be 66x66 with 2 missing lines at bottom # LATER: not doing that anymore; v={1,2,3} doubled, all higher 1:1 pixels (tiny) @@ -557,7 +559,7 @@ def doit(no_history=False): if orig_img.width == 128: # Mk3/4 - pull out just the QR, blow it up 16x - x, w = 2, 64 + x, w = 2, 66 img = orig_img.crop( (x, 0, x+w, w) ) img = ImageOps.expand(img, 16, 0) # add border img = img.resize( (256, 256)) @@ -568,7 +570,7 @@ def doit(no_history=False): w, h = orig_img.size # 320x240 img = orig_img.crop( (0, 0, w, h-5) ).convert('L') - img.save('debug/last-qr.png') + img.save(f'{sim_root_dir}/debug/last-qr.png') #img.show() # Above usually works @ zoom=1, but not always! @@ -592,7 +594,75 @@ def doit(no_history=False): return doit -@pytest.fixture(scope='module') +@pytest.fixture +def verify_qr_address(cap_screen_qr, cap_screen, is_q1): + # check we can read QR and that it has exact value expected + # plus text version of address, if any, is right. + from ckcc_protocol.constants import AFC_BECH32 + + def doit(addr_fmt, expect_addr=None, is_change=None): + qr = cap_screen_qr().decode('ascii') + + if isinstance(addr_fmt, str): + try: + addr_fmt = unmap_addr_fmt[addr_fmt] + except KeyError: + addr_fmt = msg_sign_unmap_addr_fmt[addr_fmt] + + if (addr_fmt & AFC_BECH32) or (addr_fmt & AFC_BECH32M): + qr = qr.lower() + + # check text --if any-- matches QR contents + # - remove spaces and newlines + # - ok if no text, which happens when QR is productively using screen space + # - skips first line, which on Q shows the index number sometimes + # - insists on some spaces + full = cap_screen() + full_split = full.split("\n") + if is_q1: + if is_change: + for i, (c, line) in enumerate(zip("XXXXCHANGE", full_split)): + if i > 3: + assert line.startswith(c) + else: + assert not line.startswith(c) + + for i, (c, line) in enumerate(zip("XXXXXXBACK", full_split)): + if i > 5: + assert line.endswith(c) + else: + assert not line.endswith(c) + + elif is_change is False: + for c, line in zip("XXXXCHANGE", full_split): + assert not line.startswith(c) + + for c, line in zip("XXXXXXBACK", full_split): + assert not line.endswith(c) + + txt = None # most of the time there is no address + else: + if is_change: + assert "CHANGE BACK" in full + elif is_change is False: + assert "CHANGE BACK" not in full + + txt = ''.join(full_split).replace('CHANGE BACK', '') + + if txt: + assert txt == qr + if is_q1: + # addr is not spaced out on Mk4, but check it was on Q + assert (qr[0:4] + ' ' + qr[4:8]) in full, 'was not spaced out' + + if expect_addr is not None: + assert qr == expect_addr + + return qr + + return doit + +@pytest.fixture def get_pp_sofar(sim_exec): # get entry value for bip39 passphrase def doit(): @@ -602,7 +672,7 @@ def doit(): return doit -@pytest.fixture(scope='module') +@pytest.fixture def get_secrets(sim_execfile): # returns big dict based on what we'd normally put into a backup file. def doit(): @@ -622,48 +692,54 @@ def doit(): return doit -@pytest.fixture(scope='module') +@pytest.fixture +def clear_miniscript(unit_test): + def doit(): + unit_test('devtest/wipe_miniscript.py') + return doit + +@pytest.fixture def press_select(dev, has_qwerty): f = functools.partial(_press_select, dev, has_qwerty) return f -@pytest.fixture(scope='module') +@pytest.fixture def press_cancel(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_CANCEL if has_qwerty else 'x', **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_delete(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_DELETE if has_qwerty else 'x', **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_nfc(need_keypress, has_qwerty): def doit(num=3, **kws): need_keypress(KEY_NFC if has_qwerty else str(num), **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_up(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_UP if has_qwerty else "5", **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_down(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_DOWN if has_qwerty else "8", **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_left(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_LEFT if has_qwerty else "7", **kws) return doit -@pytest.fixture(scope='module') +@pytest.fixture def press_right(need_keypress, has_qwerty): def doit(**kws): need_keypress(KEY_RIGHT if has_qwerty else "9", **kws) @@ -705,24 +781,37 @@ def doit(): return doit -@pytest.fixture(scope="module") +@pytest.fixture def pick_menu_item(dev, has_qwerty): f = functools.partial(_pick_menu_item, dev, has_qwerty) return f -@pytest.fixture(scope='module') -def virtdisk_path(request, is_simulator, needs_virtdisk): +@pytest.fixture(scope='session') +def src_root_dir(): + return os.environ.get("SRC_ROOT") + +@pytest.fixture(scope='session') +def sim_root_dir(dev, request, src_root_dir): + if request.config.getoption("--dev"): + return os.path.join(src_root_dir, "unix/work") + + cmd = f"import ckcc; RV.write(ckcc.get_sim_root_dirs()[0])" + rv = _sim_exec(dev, cmd) + return rv + +@pytest.fixture +def virtdisk_path(request, is_simulator, needs_virtdisk, sim_root_dir): # get a path to indicated filename on emulated/shared dir def doit(fn): - # could use: ckcc.get_sim_root_dirs() here if is_simulator(): get_setting = request.getfixturevalue('get_setting') if not get_setting('vidsk', False): raise pytest.xfail('virtdisk disabled') - assert os.path.isdir('../unix/work/VirtDisk') - return '../unix/work/VirtDisk/' + fn + + return sim_root_dir + '/VirtDisk/' + fn elif sys.platform == 'darwin': + # TODO if not request.config.getoption("--manual"): raise pytest.fail('must use --manual CLI option') @@ -733,7 +822,7 @@ def doit(fn): return doit -@pytest.fixture(scope='module') +@pytest.fixture def virtdisk_wipe(dev, needs_virtdisk, virtdisk_path): def doit(): for fn in glob.glob(virtdisk_path('*')): @@ -745,13 +834,12 @@ def doit(): return doit -@pytest.fixture(scope='module') -def microsd_path(simulator): +@pytest.fixture +def microsd_path(simulator, sim_root_dir): # open a file from the simulated microsd def doit(fn): - # could use: ckcc.get_sim_root_dirs() here - return '../unix/work/MicroSD/' + fn + return sim_root_dir + '/MicroSD/' + fn return doit @@ -766,34 +854,34 @@ def doit(): os.remove(dir + fname) return doit -@pytest.fixture(scope='module') +@pytest.fixture def open_microsd(simulator, microsd_path): # open a file from the simulated microsd def doit(fn, mode='rb'): + assert fn, 'empty fname' return open(microsd_path(fn), mode) return doit -@pytest.fixture(scope='module') -def settings_path(simulator): +@pytest.fixture +def settings_path(simulator, sim_root_dir): # open a file from the simulated microsd def doit(fn): - # could use: ckcc.get_sim_root_dirs() here - return '../unix/work/settings/' + fn + return sim_root_dir + '/settings/' + fn return doit @pytest.fixture def settings_slots(settings_path): def doit(): - return [fn + return [settings_path(fn) for fn in os.listdir(settings_path("")) if fn.endswith(".aes")] return doit -@pytest.fixture(scope="function") +@pytest.fixture def set_master_key(sim_exec, sim_execfile, simulator, reset_seed_words): # load simulator w/ a specific bip32 master key @@ -817,7 +905,7 @@ def doit(prv): # - actually need seed words for all tests reset_seed_words() -@pytest.fixture(scope="function") +@pytest.fixture def set_xfp(sim_exec): # set the XFP, without really knowing the private keys # - won't be able to sign, but should accept PSBT for signing @@ -835,7 +923,7 @@ def doit(xfp): sim_exec('from main import settings; settings.set("xfp", 0x%x);' % simulator_fixed_xfp) -@pytest.fixture(scope="function") +@pytest.fixture def set_encoded_secret(sim_exec, sim_execfile, simulator, reset_seed_words): # load simulator w/ a specific secret @@ -861,21 +949,21 @@ def doit(encoded): # - actually need seed words for all tests reset_seed_words() -@pytest.fixture(scope="function") +@pytest.fixture def use_mainnet(settings_set): def doit(): settings_set('chain', 'BTC') yield doit settings_set('chain', 'XTN') -@pytest.fixture(scope="function") +@pytest.fixture def use_testnet(settings_set): def doit(do_testnet=True): settings_set('chain', 'XTN' if do_testnet else 'BTC') yield doit settings_set('chain', 'XTN') -@pytest.fixture(scope="function") +@pytest.fixture def use_regtest(request, settings_set): if request.config.getoption("--manual"): def xrt_warn(): @@ -889,8 +977,19 @@ def doit(): settings_set('chain', 'XTN') -@pytest.fixture(scope="function") -def set_seed_words(sim_exec, sim_execfile, simulator, reset_seed_words): +@pytest.fixture +def set_seed_words(change_seed_words, reset_seed_words): + def doit(w): + return change_seed_words(w) + + yield doit + + # Important cleanup: restore normal key, because other tests assume that + + reset_seed_words() + +@pytest.fixture +def change_seed_words(sim_exec, sim_execfile, simulator): # load simulator w/ a specific bip32 master key def doit(words): @@ -905,66 +1004,64 @@ def doit(words): #print("sim xfp: 0x%08x" % simulator.master_fingerprint) return simulator.master_fingerprint - yield doit - - # Important cleanup: restore normal key, because other tests assume that - - reset_seed_words() + return doit -@pytest.fixture() -def reset_seed_words(sim_exec, sim_execfile, simulator): +@pytest.fixture +def reset_seed_words(change_seed_words): # load simulator w/ a specific bip39 seed phrase def doit(): - words = simulator_fixed_words - cmd = 'import main; main.WORDS = %r;' % words.split() - sim_exec(cmd) - rv = sim_execfile('devtest/set_seed.py') - if rv: pytest.fail(rv) - - simulator.start_encryption() - simulator.check_mitm() + new_xfp = change_seed_words(simulator_fixed_words) #print("sim xfp: 0x%08x (reset)" % simulator.master_fingerprint) - assert simulator.master_fingerprint == simulator_fixed_xfp + assert new_xfp == simulator_fixed_xfp - return words + return simulator_fixed_words return doit -@pytest.fixture() +@pytest.fixture def settings_set(sim_exec): - def doit(key, val): - x = sim_exec("settings.set('%s', %r)" % (key, val)) + def doit(key, val, prelogin=False): + source = "from nvstore import SettingsObject;SettingsObject.prelogin()" if prelogin else "settings" + x = sim_exec("%s.set('%s', %r)" % (source, key, val)) assert x == '' return doit -@pytest.fixture() +@pytest.fixture +def settings_append(sim_exec): + def doit(key, val): + x = sim_exec("x=settings.get('%s',[])\nx.append(%r)\nsettings.set('%s', x)" % (key, val, key)) + assert x == '' + return doit + +@pytest.fixture def settings_get(sim_exec): - def doit(key, def_val=None): - cmd = f"RV.write(repr(settings.get('{key}', {def_val!r})))" + def doit(key, def_val=None, prelogin=False): + source = "from nvstore import SettingsObject;SettingsObject.prelogin()" if prelogin else "settings" + cmd = f"RV.write(repr({source}.get('{key}', {def_val!r})))" resp = sim_exec(cmd) assert 'Traceback' not in resp, resp return eval(resp) return doit -@pytest.fixture() +@pytest.fixture def master_settings_get(sim_exec): def doit(key): - cmd = f"RV.write(repr(settings.master_get('{key}')))" + cmd = f"RV.write(repr(settings.master_get('{key}', False)))" resp = sim_exec(cmd) assert 'Traceback' not in resp, resp return eval(resp) return doit -@pytest.fixture() +@pytest.fixture def settings_remove(sim_exec): def doit(key): @@ -973,19 +1070,14 @@ def doit(key): return doit -@pytest.fixture(scope='module') -def repl(request): - return request.getfixturevalue('mk4_repl') - - -@pytest.fixture(scope='module') -def mk4_repl(sim_eval, sim_exec): +@pytest.fixture(scope='session') +def repl(dev, request): # Provide an interactive connection to the REPL, using the debug build USB commands class Mk4USBRepl: def eval(self, cmd, max_time=3): # send a command, wait for it to finish - resp = sim_eval(cmd) + resp = _sim_eval(dev, cmd) print(f"eval: {cmd} => {resp}") if 'Traceback' in resp: raise RuntimeError(resp) @@ -993,7 +1085,7 @@ def eval(self, cmd, max_time=3): def exec(self, cmd, proc_time=1, raw=False): # send a (one line) command and read the one-line response - resp = sim_exec(cmd) + resp = _sim_exec(dev, cmd) print(f"exec: {cmd} => {resp}") if raw: return resp return eval(resp) if resp else None @@ -1094,7 +1186,7 @@ def exec(self, cmd, proc_time=1): return USBRepl() -@pytest.fixture() +@pytest.fixture def decode_with_bitcoind(bitcoind): def doit(raw_txn): @@ -1108,7 +1200,7 @@ def doit(raw_txn): return doit -@pytest.fixture() +@pytest.fixture def decode_psbt_with_bitcoind(bitcoind): def doit(raw_psbt): @@ -1123,7 +1215,7 @@ def doit(raw_psbt): return doit -@pytest.fixture() +@pytest.fixture def check_against_bitcoind(bitcoind, use_regtest, sim_exec, sim_execfile): def doit(hex_txn, fee, num_warn=0, change_outs=None, dests=[]): @@ -1246,17 +1338,23 @@ def doit(f_or_data, accept=True, finalize=False, accept_ms_import=False, for r in range(10): time.sleep(0.1) title, story = cap_story() - if title == 'PSBT Signed': break + if 'Updated PSBT' in story: break + if 'Finalized transaction' in story: break else: assert False, 'timed out' - txid = None lines = story.split('\n') - if 'Final TXID:' in lines: - txid = lines[-1] - result_fname = lines[-4] - else: - result_fname = lines[-1] + txid = None + if 'TXID:' in lines: + txid = lines[lines.index('TXID:')+1] + + # This is fragile! + # ignore "Press (T) to use Key Teleport to send PSBT to other co-signers" footer + # ignore "Press (0) to save again by..." + # - want the .txn if present, else the .psbt file + t, = [l for l in lines if l.endswith('.txn')] or [None] + p, = [l for l in lines if l.endswith('.psbt')] or [None] + result_fname = t or p result = open_microsd(result_fname, 'rb').read() @@ -1313,16 +1411,18 @@ def doit(f_or_data, accept=True, finalize=False, accept_ms_import=False, @pytest.fixture def try_sign(start_sign, end_sign): - def doit(filename_or_data, accept=True, finalize=False, accept_ms_import=False): - ip = start_sign(filename_or_data, finalize=finalize) - return ip, end_sign(accept, finalize=finalize, accept_ms_import=accept_ms_import) + def doit(filename_or_data, accept=True, finalize=False, accept_ms_import=False, + exit_export_loop=True, miniscript=None): + ip = start_sign(filename_or_data, finalize=finalize, miniscript=miniscript) + return ip, end_sign(accept, finalize=finalize, accept_ms_import=accept_ms_import, + exit_export_loop=exit_export_loop) return doit @pytest.fixture def start_sign(dev): - def doit(filename, finalize=False, stxn_flags=0x0): + def doit(filename, finalize=False, stxn_flags=0x0, miniscript=None): if filename[0:5] == b'psbt\xff': ip = filename filename = 'memory' @@ -1334,36 +1434,38 @@ def doit(filename, finalize=False, stxn_flags=0x0): ll, sha = dev.upload_file(ip) - dev.send_recv(CCProtocolPacker.sign_transaction(ll, sha, finalize, flags=stxn_flags)) + dev.send_recv(CCProtocolPacker.sign_transaction(ll, sha, finalize, flags=stxn_flags, + miniscript_name=miniscript)) return ip return doit @pytest.fixture -def end_sign(dev, need_keypress): +def end_sign(dev, need_keypress, press_cancel): from ckcc_protocol.protocol import CCUserRefused - def doit(accept=True, in_psbt=None, finalize=False, accept_ms_import=False, expect_txn=True): + def doit(accept=True, finalize=False, accept_ms_import=False, expect_txn=True, + exit_export_loop=True): if accept_ms_import: # XXX would be better to do cap_story here, but that would limit test to simulator need_keypress('y', timeout=None) time.sleep(0.050) - if accept != None: + if accept is not None: need_keypress('y' if accept else 'x', timeout=None) - if accept == False: + if accept is False: with pytest.raises(CCUserRefused): done = None - while done == None: + while done is None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) return else: done = None - while done == None: + while done is None: time.sleep(0.00) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) @@ -1399,6 +1501,9 @@ def doit(accept=True, in_psbt=None, finalize=False, accept_ms_import=False, expe for sig in sigs: assert len(sig) <= 71, "overly long signature observed" + if exit_export_loop: + press_cancel() # landed back to export prompt - exit + return psbt_out return doit @@ -1492,7 +1597,7 @@ def has_qwerty(is_q1): return is_q1 @pytest.fixture(scope='module') -def rf_interface(needs_nfc, sim_exec): +def rf_interface(needs_nfc, dev): # provide a read/write connection over NFC # - requires pyscard module and desktop NFC-V reader which doesn't exist raise pytest.xfail('broken NFC-V challenges') @@ -1544,15 +1649,15 @@ def write_nfc(self, ccfile): pass # get the CC into NFC tap mode (but no UX) - sim_exec('glob.NFC.set_rf_disable(0)') + _sim_exec(dev, 'glob.NFC.set_rf_disable(0)') time.sleep(3) yield RFHandler() - sim_exec('glob.NFC.set_rf_disable(1)') + _sim_exec(dev, 'glob.NFC.set_rf_disable(1)') -@pytest.fixture() +@pytest.fixture def nfc_read(request, needs_nfc): # READ data from NFC chip # - perfer to do over NFC reader, but can work over USB too @@ -1570,10 +1675,31 @@ def doit_usb(): except: return doit_usb -@pytest.fixture() +@pytest.fixture +def nfc_read_url(nfc_read, press_cancel): + # gives URL from ndef + + def doit(): + contents = nfc_read() + + press_cancel() # exit NFC animation + + # expect a single record, a URL + got, = ndef.message_decoder(contents) + + assert got.type == 'urn:nfc:wkt:U' + + return got.uri + + return doit + +@pytest.fixture def nfc_write(request, needs_nfc, is_q1): # WRITE data into NFC "chip" def doit_usb(ccfile): + from ckcc.constants import MAX_MSG_LEN + if len(ccfile) >= MAX_MSG_LEN: + pytest.xfail("MAX_MSG_LEN") sim_exec = request.getfixturevalue('sim_exec') press_select = request.getfixturevalue('press_select') rv = sim_exec('list(glob.NFC.big_write(%r))' % ccfile) @@ -1587,20 +1713,26 @@ def doit_usb(ccfile): except: return doit_usb -@pytest.fixture() +@pytest.fixture def enable_nfc(needs_nfc, sim_exec, settings_set): def doit(): settings_set('nfc', 1) sim_exec('import nfc; nfc.NFCHandler.startup()') return doit -@pytest.fixture() -def nfc_disabled(needs_nfc, settings_get): +@pytest.fixture +def nfc_disabled(settings_get): def doit(): return not bool(settings_get('nfc', 0)) return doit -@pytest.fixture() +@pytest.fixture +def vdisk_disabled(settings_get): + def doit(): + return not bool(settings_get('vidsk', 0)) + return doit + +@pytest.fixture def scan_a_qr(sim_exec, is_q1): # simulate a QR being scanned # XXX limitation: our USB protocol can't send a v40 QR, limit is more like 30 or so @@ -1633,14 +1765,14 @@ def ccfile_wrap(recs): return rv -@pytest.fixture() +@pytest.fixture def nfc_write_text(nfc_write): def doit(text): msg = b''.join(ndef.message_encoder([ndef.TextRecord(text), ])) return nfc_write(ccfile_wrap(msg)) return doit -@pytest.fixture() +@pytest.fixture def nfc_read_json(nfc_read): def doit(): import json @@ -1652,7 +1784,7 @@ def doit(): return doit -@pytest.fixture() +@pytest.fixture def nfc_read_text(nfc_read): def doit(): got = list(ndef.message_decoder(nfc_read())) @@ -1662,7 +1794,43 @@ def doit(): return got.text return doit -@pytest.fixture() +@pytest.fixture +def nfc_read_txn(nfc_read, press_select): + def doit(txid=None, contents=None): + if contents is None: + contents = nfc_read() + time.sleep(.5) + press_select() + + got_txid = None + got_txn = None + got_psbt = None + got_hash = None + for got in ndef.message_decoder(contents): + if got.type == 'urn:nfc:wkt:T': + assert 'Transaction' in got.text or 'PSBT' in got.text + if 'Transaction' in got.text and txid: + assert b2a_hex(txid).decode() in got.text + elif got.type == 'urn:nfc:ext:bitcoin.org:txid': + got_txid = b2a_hex(got.data).decode('ascii') + elif got.type == 'urn:nfc:ext:bitcoin.org:txn': + got_txn = got.data + elif got.type == 'urn:nfc:ext:bitcoin.org:psbt': + got_psbt = got.data + elif got.type == 'urn:nfc:ext:bitcoin.org:sha256': + got_hash = got.data + else: + raise ValueError(got.type) + + assert got_psbt or got_txn, 'no data?' + assert got_hash + assert got_hash == hashlib.sha256(got_psbt or got_txn).digest() + + return got_txid, got_psbt, got_txn + return doit + + +@pytest.fixture def nfc_block4rf(sim_eval): # wait until RF is enabled and something to read (doesn't read it tho) def doit(timeout=15): @@ -1675,6 +1843,16 @@ def doit(timeout=15): return doit +@pytest.fixture +def nfc_is_enabled(sim_eval): + # NFC is disabled by default in real product, and simulator w/o args + # - but some tests don't need to fail if it's off + # - or maybe your test can use some other method when it's off + # - use this to see if disabled at present and choose the right path + def doit(): + return eval(sim_eval('bool(glob.NFC)')) + return doit + @pytest.fixture def load_shared_mod(): # load indicated file.py as a module @@ -1689,7 +1867,7 @@ def doit(name, path): return doit @pytest.fixture -def verify_detached_signature_file(microsd_path, virtdisk_path): +def verify_detached_signature_file(microsd_path, virtdisk_path, garbage_collector): def doit(fnames, sig_fname, way, addr_fmt=None): fpaths = [] for fname in fnames: @@ -1698,6 +1876,7 @@ def doit(fnames, sig_fname, way, addr_fmt=None): else: path = virtdisk_path(fname) fpaths.append(path) + garbage_collector.append(path) if way == "sd": sig_path = microsd_path(sig_fname) @@ -1738,9 +1917,7 @@ def doit(fnames, sig_fname, way, addr_fmt=None): assert (hashlib.sha256(contents).digest().hex() + fn_addendum) in msg assert verify_message(address, sig, msg) is True - try: - os.unlink(sig_path) - except: pass + garbage_collector.append(sig_path) return fcontents[0], address return doit @@ -1764,20 +1941,61 @@ def doit(export_story, way, addr_fmt=None, is_json=False, label="wallet", fpatte if is_json: assert fname.endswith(".json") + if addr_fmt == AF_P2TR: + addr_fmt = AF_CLASSIC + contents, address = verify_detached_signature_file([fname], sig_fn, way, addr_fmt) if is_json: - return json.loads(contents), address - return contents, address + return json.loads(contents), address, fname + return contents, address, fname + return doit + +@pytest.fixture +def file_tx_signing_done(virtdisk_path, microsd_path): + def doit(story, encoding="base64", is_vdisk=False): + path_f = virtdisk_path if is_vdisk else microsd_path + enc = "rb" if encoding == "binary" else "r" + _split = story.split("\n\n") + export = None + if 'Updated PSBT is:' == _split[0]: + fname = _split[1] + path = path_f(fname) + with open(path, enc) as f: + export = f.read().strip() + + export_tx = None + if "Finalized transaction (ready for broadcast)" in _split[2]: + fname_tx = _split[3] + path_tx = path_f(fname_tx) + with open(path_tx, enc) as f: + export_tx = f.read().strip() + else: + # just finalized tx + assert "Finalized transaction (ready for broadcast):" == _split[0] + fname_tx = _split[1] + path_tx = path_f(fname_tx) + with open(path_tx, enc) as f: + export_tx = f.read() + + txid = None + for l in _split: + if "TXID" in l: + txid = l.split("\n")[-1].strip() + assert len(txid) == 64, "wrong txid" + break + + return export, export_tx, txid + return doit @pytest.fixture def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_text, nfc_read_json, load_export_and_verify_signature, is_q1, press_cancel, press_select, readback_bbqr, - cap_screen_qr): + cap_screen_qr, nfc_read_txn, file_tx_signing_done, garbage_collector): def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr=False, tail_check=None, sd_key=None, vdisk_key=None, nfc_key=None, ret_fname=False, - fpattern=None, qr_key=None): + fpattern=None, qr_key=None, is_tx=False, encoding="base64", skip_query=False): s_label = None if label == "Address summary": @@ -1789,62 +2007,74 @@ def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr= "nfc": nfc_key or (KEY_NFC if is_q1 else "3"), "qr": qr_key or (KEY_QR if is_q1 else "4"), } - time.sleep(0.2) - title, story = cap_story() - if way == "sd": - if f"({key_map['sd']}) to save {s_label if s_label else label} file to SD Card" in story: - need_keypress(key_map['sd']) + if not skip_query: + time.sleep(0.2) + title, story = cap_story() + if way == "sd": + if (f"({key_map['sd']}) to save {s_label if s_label else label} " + f"{'' if is_tx else 'file '}to SD Card") in story: + need_keypress(key_map['sd']) - elif way == "nfc": - if f"{key_map['nfc'] if is_q1 else '(3)'} to share via NFC" not in story: - pytest.skip("NFC disabled") - else: - need_keypress(key_map['nfc']) - time.sleep(0.2) - if is_json: - nfc_export = nfc_read_json() + elif way == "nfc": + if f"{key_map['nfc'] if is_q1 else '(3)'} to share via NFC" not in story: + pytest.skip("NFC disabled") else: - nfc_export = nfc_read_text() + need_keypress(key_map['nfc']) + time.sleep(0.2) + if is_tx: + nfc_export = nfc_read_txn() + return nfc_export[1:] + + if is_json: + nfc_export = nfc_read_json() + else: + nfc_export = nfc_read_text() + time.sleep(0.3) + press_cancel() # exit NFC animation + return nfc_export + elif way == "qr": + if 'file written' in story: + assert not is_q1 + # mk4 only does QR if fits in normal QR, becaise it can't do BBQr + pytest.skip('no BBQr on Mk4') + + need_keypress(key_map["qr"]) time.sleep(0.3) - press_cancel() # exit NFC animation - return nfc_export - elif way == "qr": - if 'file written' in story: - assert not is_q1 - # mk4 only does QR if fits in normal QR, becaise it can't do BBQr - pytest.skip('no BBQr on Mk4') - - need_keypress(key_map["qr"]) - time.sleep(0.3) - try: - file_type, data = readback_bbqr() - if file_type == "J": - return json.loads(data) - elif file_type == "U": - return data.decode('utf-8') if not isinstance(data, str) else data - else: - raise NotImplementedError - except: - raise - res = cap_screen_qr().decode('ascii') try: - return json.loads(res) + assert is_q1 + file_type, data = readback_bbqr() + if file_type == "J": + return json.loads(data) + elif file_type == "U": + return data.decode('utf-8') if not isinstance(data, str) else data + elif file_type in ("P", "T"): + return data + else: + raise NotImplementedError except: - return res - else: - # virtual disk - if f"({key_map['vdisk']}) to save to Virtual Disk" not in story: - pytest.skip("Vdisk disabled") + res = cap_screen_qr().decode('ascii') + try: + return json.loads(res) + except: + return res else: - need_keypress(key_map['vdisk']) + # virtual disk + if f"({key_map['vdisk']}) to save to Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress(key_map['vdisk']) time.sleep(0.2) title, story = cap_story() + path_f = microsd_path if way == "sd" else virtdisk_path if sig_check: - export, sig_addr = load_export_and_verify_signature(story, way, is_json=is_json, - addr_fmt=addr_fmt, label=label, - tail_check=tail_check, - fpattern=fpattern) + export, sig_addr, fname = load_export_and_verify_signature( + story, way, is_json=is_json, addr_fmt=addr_fmt, + label=label, tail_check=tail_check, fpattern=fpattern + ) + elif is_tx: + export, export_tx, _ = file_tx_signing_done(story, encoding, is_vdisk=(way == "vdisk")) + return export, export_tx else: assert f"{label} file written" in story if tail_check: @@ -1856,15 +2086,15 @@ def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr= assert fpattern in fname if is_json: assert fname.endswith(".json") - if way == "sd": - path = microsd_path(fname) - else: - path = virtdisk_path(fname) + + path = path_f(fname) with open(path, "r") as f: export = f.read() if is_json: export = json.loads(export) + garbage_collector.append(path) + press_select() if ret_sig_addr and sig_addr: @@ -1876,6 +2106,111 @@ def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr= return doit +@pytest.fixture +def signing_artifacts_reexport(cap_story, need_keypress, load_export, press_cancel, is_q1, + settings_get): + + def doit(way, tx_final=False, txid=None, encoding=None, del_after=False, is_usb=False): + label = "Finalized TX ready for broadcast" if tx_final else "Partly Signed PSBT" + def _check_story(the_way): + time.sleep(.2) + title, story = cap_story() + + if the_way in ["qr", "nfc"]: + what = label + " shared via %s." % the_way.upper() + assert what in story + else: + if not del_after: + assert "Updated PSBT is" in story + if tx_final: + assert "Finalized transaction (ready for broadcast)" in story + if txid: + assert txid in story + + to_do = ["sd", "vdisk", "nfc", "qr"] + if not is_usb: + _check_story(way) + to_do.remove(way) # put it as the last item + to_do.append(way) + + if not is_q1: + to_do.remove("qr") + + if not settings_get("nfc", None): + to_do.remove("nfc") + + res = [] + res_tx = [] + for _way in to_do: + try: + rv = load_export(_way, label, is_json=False, sig_check=False, + is_tx=True, encoding=encoding) + if isinstance(rv, tuple): + _psbt, _tx = rv + if _psbt: + res.append(_psbt) + if _tx: + res_tx.append(_tx) + else: + if tx_final: + res_tx.append(rv) + else: + res.append(rv) + if _way in ("qr", "nfc"): + # nfc now needs cancel as it keeps reexporting + # qr needs to go back from qr view + press_cancel() + _check_story(_way) + except BaseException as e: + if _way != "vdisk": + raise + + # check we exported the same - even if in different format + final_res = [] + for x in res: + if x is not None: + x = x.strip() + if isinstance(x, bytearray): + x = bytes(x) + if not isinstance(x, bytes): + try: + # is just a hex string + x = bytes.fromhex(x) + except: + x = base64.b64decode(x) + else: + try: + x = base64.b64decode(x.decode()) + except: pass + + final_res.append(x) + + final_res_tx = [] + for y in res_tx: + if y is not None: + y = y.strip() + try: + y = a2b_hex(y) + except: pass + if isinstance(y, bytearray): + # bytearray is unhashable type + y = bytes(y) + + final_res_tx.append(y) + + if not del_after and final_res: + assert len(set(final_res)) == 1 + + fin_tx = None + if final_res_tx: + assert len(set(final_res_tx)) == 1 + fin_tx = final_res_tx[0] + + return final_res[0] if final_res else None, fin_tx + + return doit + + @pytest.fixture def tapsigner_encrypted_backup(microsd_path, virtdisk_path): def doit(way, testnet=True): @@ -1909,7 +2244,7 @@ def doit(way, testnet=True): return doit @pytest.fixture -def choose_by_word_length(need_keypress): +def choose_by_word_length(need_keypress, press_select): # for use in seed XOR menu system def doit(num_words): if num_words == 12: @@ -1917,7 +2252,7 @@ def doit(num_words): elif num_words == 18: need_keypress("2") else: - need_keypress("y") + press_select() return doit # workaround: need these fixtures to be global so I can call test from a test @@ -1965,44 +2300,23 @@ def doit(fn, passphrase): with open(xfn_path, "r") as f: res = f.read() + os.remove(xfn_path) return res return doit @pytest.fixture -def restore_backup_cs(unit_test, pick_menu_item, cap_story, cap_menu, +def restore_backup_unpacked(unit_test, pick_menu_item, cap_story, cap_menu, press_select, word_menu_entry, get_setting, is_q1, - need_keypress, scan_a_qr, cap_screen): - # restore backup with clear seed as first step - def doit(fn, passphrase, avail_settings=None, pass_way=None): - unit_test('devtest/clear_seed.py') - - m = cap_menu() - assert m[0] == 'New Seed Words' - pick_menu_item('Import Existing') - pick_menu_item('Restore Backup') + need_keypress, scan_a_qr, cap_screen, enter_complex): - time.sleep(.1) - pick_menu_item(fn) - - time.sleep(.1) - if is_q1 and pass_way and pass_way == "qr": - need_keypress(KEY_QR) - time.sleep(.1) - qr = ' '.join(w[:4] for w in passphrase) - scan_a_qr(qr) - for _ in range(20): - scr = cap_screen() - if 'ENTER if all done' in scr: - break - time.sleep(.1) - press_select() - else: - word_menu_entry(passphrase, has_checksum=False) + # check things are right after unpack & install; FTUX shown + def doit(avail_settings=None): time.sleep(.3) title, body = cap_story() + # on simulator Disable USB is always off - so FTUX all the time assert title == 'NO-TITLE' # no Welcome! assert "best security practices" in body @@ -2031,6 +2345,66 @@ def doit(fn, passphrase, avail_settings=None, pass_way=None): return doit +@pytest.fixture +def restore_backup_cs(unit_test, pick_menu_item, cap_story, cap_menu, press_select, word_menu_entry, + get_setting, is_q1, need_keypress, scan_a_qr, cap_screen, enter_complex, + restore_backup_unpacked, press_cancel): + # restore backup with clear seed as first step + def doit(fn, passphrase, avail_settings=None, pass_way=None, custom_bkpw=False, refuse=False): + unit_test('devtest/clear_seed.py') + + m = cap_menu() + assert m[0] == 'New Seed Words' + if custom_bkpw: + pick_menu_item('Advanced/Tools') + pick_menu_item('I Am Developer.') + pick_menu_item('Restore Bkup') + else: + pick_menu_item('Import Existing') + pick_menu_item('Restore Backup') + + time.sleep(.1) + pick_menu_item(fn) + + time.sleep(.1) + if is_q1 and pass_way and (pass_way == "qr"): + need_keypress(KEY_QR) + time.sleep(.1) + qr = ' '.join(w[:4] for w in passphrase) + scan_a_qr(qr) + for _ in range(20): + scr = cap_screen() + if 'ENTER if all done' in scr: + break + time.sleep(.1) + press_select() + elif custom_bkpw: + enter_complex(passphrase, b39pass=False) + else: + # looking at word entry right now + if is_q1: + scr = cap_screen() + assert fn in scr # backup fname shown at the top + assert "Enter Password for:" in scr + + word_menu_entry(passphrase, has_checksum=False) + + time.sleep(.2) + title, story = cap_story() + assert len(title) == 10 + assert title[0] == "[" + assert title[-1] == "]" + assert "Above is the master fingerprint of the seed stored in the backup." in story + assert f"load backup as master seed" in story + if refuse: + press_cancel() + else: + press_select() + + restore_backup_unpacked(avail_settings=avail_settings) + + return doit + @pytest.fixture def seed_story_to_words(): # Q may display words in a number of different ways to get them all onto the screen, @@ -2113,7 +2487,7 @@ def doit(): return doit @pytest.fixture -def txout_explorer(cap_story, press_cancel, need_keypress, is_q1): +def txout_explorer(cap_story, press_cancel, need_keypress, is_q1, verify_qr_address): def doit(data, chain="XTN"): time.sleep(.1) title, story = cap_story() @@ -2131,17 +2505,36 @@ def doit(data, chain="XTN"): assert len(ss) == (len(d) * 2) + 1 assert "Press RIGHT to see next group" in ss[-1] if i: - assert " LEFT to go back." in ss[-1] + assert " LEFT to go back" in ss[-1] else: assert "LEFT" not in ss[-1] - for i, (sa, sb, (af, amount, change)) in enumerate(zip(ss[:-1:2], ss[1::2], d), start=i): + if not is_q1: + assert "(4) to show QR code" in ss[-1] + + # collect QR codes first + need_keypress(KEY_QR if is_q1 else "4") + qr_addr_list = [] + for af, amount, change in d: + qr = verify_qr_address(af, is_change=bool(change)) + qr_addr_list.append(qr) + need_keypress(KEY_RIGHT if is_q1 else "9") + time.sleep(.5) + + press_cancel() # QR code on screen - exit + + start = i + for i, (sa, sb, (af, amount, change)) in enumerate(zip(ss[:-1:2], ss[1::2], d), start=start): if change: assert f"Output {i} (change):" == sa else: assert f"Output {i}:" == sa txt_amount, _, addr = sb.split("\n") + addr = addr_from_display_format(addr) + # verify QR matches what is on screen + assert addr == qr_addr_list[i-start] + assert txt_amount == f'{amount / 100000000:.8f} {chain}' if af == "p2pkh": if chain == "BTC": @@ -2151,6 +2544,9 @@ def doit(data, chain="XTN"): elif af in ("p2wpkh", "p2wsh"): target = "bc1q" if chain == "BTC" else "tb1q" assert addr.startswith(target) + elif af == "p2tr": + target = "bc1p" if chain == "BTC" else "tb1p" + assert addr.startswith(target) elif af in ("p2sh", "p2wpkh-p2sh", "p2wsh-p2sh"): target = "3" if chain == "BTC" else "2" assert addr.startswith(target) @@ -2194,18 +2590,44 @@ def doit(data, chain="XTN"): return doit +@pytest.fixture +def validate_address(): + # Check whether an address is covered by the given subkey + def doit(addr, sk): + if addr[0] in '1mn': + chain = "XTN" if addr[0] != "1" else "BTC" + assert addr == sk.address(addr_fmt="p2pkh", chain=chain) + elif addr[0:4] in {'bc1q', 'tb1q'}: + chain = "XTN" if addr[0:4] != 'bc1q' else "BTC" + assert addr == sk.address(addr_fmt="p2wpkh", chain=chain) + elif addr[0:6] == "bcrt1q": + assert addr == sk.address(addr_fmt="p2wpkh", chain="XRT") + elif addr[0:4] in {'bc1p', 'tb1p'}: + chain = "XTN" if addr[0:4] != 'bc1p' else "BTC" + assert addr == sk.address(addr_fmt="p2tr", chain=chain) + elif addr[0:6] == "bcrt1p": + assert addr == sk.address(addr_fmt="p2tr", chain="XRT") + elif addr[0] in '23': + chain = "XTN" if addr[0] != '3' else "BTC" + assert addr == sk.address(addr_fmt="p2sh-p2wpkh", chain=chain) + else: + raise ValueError(addr) + return doit + @pytest.fixture -def skip_if_useless_way(is_q1, nfc_disabled): +def skip_if_useless_way(is_q1, nfc_disabled, vdisk_disabled): # when NFC is disabled, no point trying to do a PSBT via NFC # - important: run_sim_tests.py will enable NFC for complete testing # - similarly: the Mk4 and earlier had no QR scanner, so cannot use that as input - def doit(way): - if way == "qr" and not is_q1: + def doit(way, allow_mk4_qr=False): + if way == "qr" and (not is_q1 and not allow_mk4_qr): raise pytest.skip("mk4 QR not supported") - if way == 'nfc' and nfc_disabled(): + elif way == 'nfc' and nfc_disabled(): # runner will test these cases, but fail faster otherwise raise pytest.skip("NFC disabled") + elif way == "vdisk" and vdisk_disabled(): + raise pytest.skip("VirtualDisk disabled") return doit @@ -2220,7 +2642,8 @@ def dev_core_import_object(dev): ders = [ ("m/44h/1h/0h", AF_CLASSIC), ("m/49h/1h/0h", AF_P2WPKH_P2SH), - ("m/84h/1h/0h", AF_P2WPKH) + ("m/84h/1h/0h", AF_P2WPKH), + ("m/86h/1h/0h", AF_P2TR), ] descriptors = [] for idx, (path, addr_format) in enumerate(ders): @@ -2239,19 +2662,79 @@ def dev_core_import_object(dev): return descriptors +@pytest.fixture +def garbage_collector(): + to_remove = [] + yield to_remove + for pth in to_remove: + try: + os.remove(pth) + except: pass + + +@pytest.fixture +def build_test_seed_vault(): + def doit(): + from test_ephemeral import SEEDVAULT_TEST_DATA + sv = [] + for item in SEEDVAULT_TEST_DATA: + xfp, entropy, mnemonic = item + + # build stashed encoded secret + entropy_bytes = bytes.fromhex(entropy) + if mnemonic: + vlen = len(entropy_bytes) + assert vlen in [16, 24, 32] + marker = 0x80 | ((vlen // 8) - 2) + stored_secret = bytes([marker]) + entropy_bytes + else: + stored_secret = entropy_bytes + + sv.append((xfp, stored_secret.hex(), f"[{xfp}]", "meta")) + return sv + return doit + +@pytest.fixture +def get_deltamode(sim_exec): + # get current "deltamode" status: T or F + def doit(): + return eval(sim_exec('RV.write(repr(pa.is_deltamode()))')) + return doit + +@pytest.fixture +def set_deltamode(sim_exec): + # control current "deltamode" status: T or F + def doit(val): + # TC_DELTA_MODE = const(0x0400) + if val: + sim_exec('pa.delay_required |= 0x400') + else: + sim_exec('pa.delay_required &= ~0x400') + + yield doit + + doit(False) + + # useful fixtures from test_backup import backup_system -from test_bbqr import readback_bbqr, render_bbqr, readback_bbqr_ll +from test_bbqr import readback_bbqr, render_bbqr, readback_bbqr_ll, try_sign_bbqr, split_scan_bbqr from test_bip39pw import set_bip39_pw +from test_ccc import get_last_violation from test_drv_entro import derive_bip85_secret, activate_bip85_ephemeral from test_ephemeral import generate_ephemeral_words, import_ephemeral_xprv, goto_eph_seed_menu from test_ephemeral import ephemeral_seed_disabled_ui, restore_main_seed, confirm_tmp_seed from test_ephemeral import verify_ephemeral_secret_ui, get_identity_story, get_seed_value_ux, seed_vault_enable -from test_multisig import import_ms_wallet, make_multisig, offer_ms_import, fake_ms_txn -from test_multisig import make_ms_address, clear_ms, make_myself_wallet, import_multisig +from test_msg import verify_msg_sign_story, sign_msg_from_text, msg_sign_export, sign_msg_from_address +from test_multisig import import_ms_wallet, make_multisig, fake_ms_txn +from test_miniscript import offer_minsc_import, get_cc_key, bitcoin_core_signer, import_miniscript, usb_miniscript_get, usb_miniscript_addr, create_core_wallet +from test_multisig import make_ms_address, make_myself_wallet +from test_notes import need_some_notes, need_some_passwords +from test_nfc import try_sign_nfc, ndef_parse_txn_psbt from test_se2 import goto_trick_menu, clear_all_tricks, new_trick_pin, se2_gate, new_pin_confirmed from test_seed_xor import restore_seed_xor -from test_ux import pass_word_quiz, word_menu_entry +from test_sign import txid_from_export_prompt +from test_ux import pass_word_quiz, word_menu_entry, enable_hw_ux from txn import fake_txn # EOF diff --git a/testing/constants.py b/testing/constants.py index 2a6192293..b972cdaf1 100644 --- a/testing/constants.py +++ b/testing/constants.py @@ -1,8 +1,6 @@ # (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -SIM_PATH = '/tmp/ckcc-simulator.sock' - # Simulator normally powers up with this 'wallet' simulator_fixed_tprv = "tprv8ZgxMBicQKsPeXJHL3vPPgTAEqQ5P2FD9qDeCQT4Cp1EMY5QkwMPWFxHdxHrxZhhcVRJ2m7BNWTz9Xre68y7mX5vCdMJ5qXMUfnrZ2si2X4" simulator_fixed_tpub = "tpubD6NzVbkrYhZ4XzL5Dhayo67Gorv1YMS7j8pRUvVMd5odC2LBPLAygka9p7748JtSq82FNGPppFEz5xxZUdasBRCqJqXvUHq6xpnsMcYJzeh" @@ -25,9 +23,11 @@ 'p2wsh': AF_P2WSH, 'p2wsh-p2sh': AF_P2WSH_P2SH, 'p2sh-p2wsh': AF_P2WSH_P2SH, + "p2tr": AF_P2TR, } msg_sign_unmap_addr_fmt = { + 'p2tr': AF_P2TR, # not supported for msg signign tho 'p2pkh': AF_CLASSIC, 'p2wpkh': AF_P2WPKH, 'p2sh-p2wpkh': AF_P2WPKH_P2SH, @@ -35,26 +35,28 @@ } addr_fmt_names = { + AF_P2TR: 'p2tr', AF_CLASSIC: 'p2pkh', AF_P2SH: 'p2sh', AF_P2WPKH: 'p2wpkh', AF_P2WSH: 'p2wsh', AF_P2WPKH_P2SH: 'p2wpkh-p2sh', - AF_P2WSH_P2SH: 'p2wsh-p2sh', + AF_P2WSH_P2SH: 'p2sh-p2wsh', } # all possible addr types, including multisig/scripts -ADDR_STYLES = ['p2wpkh', 'p2wsh', 'p2sh', 'p2pkh', 'p2wsh-p2sh', 'p2wpkh-p2sh'] +ADDR_STYLES = ['p2wpkh', 'p2wsh', 'p2sh', 'p2pkh', 'p2wsh-p2sh', 'p2wpkh-p2sh', 'p2tr'] # single-signer -ADDR_STYLES_SINGLE = ['p2wpkh', 'p2pkh', 'p2wpkh-p2sh'] +ADDR_STYLES_SINGLE = ['p2wpkh', 'p2pkh', 'p2wpkh-p2sh', 'p2tr'] # multi signer ADDR_STYLES_MS = ['p2sh', 'p2wsh', 'p2wsh-p2sh'] # SIGHASH SIGHASH_MAP = { + "DEFAULT": 0, "ALL": 1, "NONE": 2, "SINGLE": 3, @@ -63,5 +65,7 @@ "SINGLE|ANYONECANPAY": 3 | 0x80, } +SIGHASH_MAP_NON_TAPROOT = {k:v for k, v in SIGHASH_MAP.items() if k != "DEFAULT"} + # (2**31) - 1 --> max unhardened, but we handle hardened via h elsewhere MAX_BIP32_IDX = 2147483647 \ No newline at end of file diff --git a/testing/core_fixtures.py b/testing/core_fixtures.py index c7a8689f9..1964bbb0e 100644 --- a/testing/core_fixtures.py +++ b/testing/core_fixtures.py @@ -17,6 +17,11 @@ def _sim_exec(device, cmd, binary=False, timeout=60000): # print(f'sim_exec: {cmd!r} -> {s!r}') return s.decode('utf-8') if not isinstance(s, str) else s +def _sim_eval(device, cmd, binary=False, timeout=None): + s = device.send_recv(b'EVAL' + cmd.encode('utf-8'), timeout=timeout) + if binary: return s + return s.decode('utf-8') + def _cap_story(device): cmd = "RV.write('\0'.join(sim_display.story or []))" rv = _sim_exec(device, cmd) @@ -39,6 +44,10 @@ def _press_select(device, is_Q, timeout=None): btn = KEY_ENTER if is_Q else "y" _need_keypress(device, btn, timeout=timeout) +def _press_cancel(device, is_Q, timeout=None): + btn = KEY_CANCEL if is_Q else "x" + _need_keypress(device, btn, timeout=timeout) + def _dev_hw_label(device): # gets a short string that labels product: mk4 / q1, etc v = device.send_recv(CCProtocolPacker.version()).split() @@ -46,7 +55,7 @@ def _dev_hw_label(device): def _pick_menu_item(device, is_Q, text): print(f"PICK menu item: {text}") - WRAP_IF_OVER = 16 # see ../shared/menu.py .. this is larger of 10 or 16 + WRAP_IF_OVER = 10 # see ../shared/menu.py _need_keypress(device, KEY_HOME if is_Q else "0") m = _cap_menu(device) diff --git a/testing/data/migration_640/big_boy.7z b/testing/data/migration_640/big_boy.7z new file mode 100644 index 000000000..a9df2642f Binary files /dev/null and b/testing/data/migration_640/big_boy.7z differ diff --git a/testing/data/migration_640/big_boy_naked.7z b/testing/data/migration_640/big_boy_naked.7z new file mode 100644 index 000000000..5d1996b41 Binary files /dev/null and b/testing/data/migration_640/big_boy_naked.7z differ diff --git a/testing/data/migration_640/bonus101.txt b/testing/data/migration_640/bonus101.txt new file mode 100644 index 000000000..738810438 --- /dev/null +++ b/testing/data/migration_640/bonus101.txt @@ -0,0 +1,29 @@ +# Coldcard backup file! DO NOT CHANGE. + +# Private key details: Bitcoin Testnet 4 +mnemonic = "common firm bus explain pulp assault meat leave donate fade provide loud" +chain = "XTN" +xprv = "tprv8ZgxMBicQKsPerzTfdAMR1kC2UGuVHAvvLrCAjETakn1Z7y1mkHisXnXsssgPjuVcoeThBzgiAB5CDKfK6FaEqfYsJxTMjWMSCQ9ieEfmgs" +xpub = "tpubD6NzVbkrYhZ4YL2FZGpwpRQJbVnqecMqVeSyTFGm12aQPcDnQ97K42QQ4385PZ4KkfTsvqJEbbiykAxMY7hUjktxxQ2J7YrTQUkt2aUNmpN" +raw_secret = "802e6ae87ba84ad61b2283f7410a3ab3c2" +long_secret = "a5fc3fe6fb64388bcf502ee9382fa9a86d8fad8c9aa01aef509323b4cb17c32c856e790e506003a94819a5bdf21e7a223fb928650380b334d6f35d7c5bcfd97e2461ad5a45c421439da5dfa3cf3d3e11b71d85f86c423cb5e85df8c7d7500dc6457ed432f0272e821f3e19d8839867f41d45f69f68b6a906e880f60424f20ed9ec7e7b205c801f0d40da6958c29f6548c8dc15aa83b839eb35c63b9a2d39c2426fba5422727b412241da8386e0da42b175d281705202666d3700b4189b7bdfef09e12214b14d68d3ee77ef23c967d0c7d52e6ecce242a6545264aa37fddfa33a94564db1a5a3aa147965d9583762b735e75bef9cd9c8f29c8e5e93e524dc80514629c5bff06f0e8e6158a4bdffdcbc31d7174d16d9c0d9ecd6dfa07cd81019a156a2fbd449a246b9d89810623d11151c6155670578eda97af93514ae41c7e2d11bba6690cc35d4f9eca089bfdcea240db1f0884d1b2b872d6d5a856306f08f8722579d4e58a07362ec664d1e2000dc74ae25f471150e3d8c2851a36237de595ca65811209828ecd0710c4402c1ded917f2043ded723e6dadc9404e2c4f78557c" + +# Firmware version (informational) +fw_date = "2025-02-19" +fw_version = "6.3.5QX" +fw_timestamp = "250219194002" + +# Coldcard Hardware +serial = "205033774833" +hardware = "q1" + +# User preferences +setting.chain = "XTN" +setting.axi = "Coldcard and Ledger" +setting.vidsk = 2 +setting.ovc = ["2+vxKLCCkrkmKhFOx0S7o1J+PwWKBhE", "2dStVnHaMkR4o85ZgOeWc1wb0KLowvU", "pyQY2wvJ0ubOpnSMpJ3k55c/9qfEVuU", "gbyyxpKqOoA4viPneFibqhudfFG7PP0", "7rfNL1eo79IbRxwsKsCZHpU3s+VKpJc", "WElDKw79eWHA+JyDWNRueGbqfz4fqqY", "Zq711+wOvTcjdy9z7jMnOK1D2p+ZgDE", "vnlrYUXGcRK1bWPDZXamy2o24ooV/b0", "00i9UxxhkKYvewc//jLM2Lalcl2xPk4", "vmQQIfwuZTEfMukfvQ6yNO2LolSPrnk", "r86d6l+7xcBaBXj1bM7M5kCKo3XIyD0", "HF8cFOhQ8sVq1Gt+V8X0FzluwpDcLco", "7T8Froxu1orv5bEN1ED5Y8vTBVdDvcA", "scA9V/CJuUwYw4yv0S3ETNv6gc0jCA4", "hXr9Na/ksibBEDBF/mWRigD9bM+uiqs", "rojMYqIPX7kSY2a9OAJ8GlDxMHwnz0I", "QAdQ3J647TVuFcBcNCh5XzOMJPk7Yb4", "jn5Dpd32fZGf3ZEz0wC7kb15/usrnic", "Pg310c0VaVKl3PGYS8dDpyXDL60bS4Q"] +setting.accts = [[7, 2147483646], [7, 2147483645]] +setting.batt_to = 1800 +setting.miniscript = [["coldcard_new", "wsh(andor(multi(2,@0/**,@1/**,@2/**),or_i(and_v(v:pkh(@3/**),after(1772755200)),thresh(2,pk(@4/**),s:pk(@5/**),s:pk(@6/**),snl:after(1759363200))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1776643200))))", ["tpubDEA3ba8e1vmGH42xgRBMZKatR649LnNUwSB9wrtrYwdQJ4RbPF5BtxKyVLwWRhBHCNCSPeraKwsjoH613UYZCicmonx4nXerumxgJXNy14J", "tpubDFDtRvv79zVFMkCGC3frybaNtiXryfMQX3hB4U4mZoKdPSSkpJbEohEArV4mb6gkfA9JGTERdExG6Z9rDWwPHYrPp2s8evYCuLk3UN4yoe1", "tpubDFgv4MQCyo8GcEKJbKtPkrURS1uSGQu8pECpPMiKZgsmFZC8krRBoT2EzdkqrjjbYQDEvRLA3jsjfK7BPXS3jkY2GWRy33WDkXpiNdTaa48", "tpubDDvQwemwNy7T6PbQ3Ym2sEMEK8YzpyzSvWqau5hyv8ZJkeY3f6j6pgymd5nPKgLoa7PyQdUqTGzA5sxox9MzsgaBYkDxvu6JieuwNGQy717", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY", "[12ab2a99/48h/1h/0h/2h]tpubDEeWrguYhZWQDvuFKBfhCeMfPiRuW5dFfQeqLLUShoxmDCB2cPBruPXndS2xZVHeuSsAZWiiiSuCncodUiy6pkiJmbK58AUp4im7rLKPKBu", "[dc19da0c/48h/1h/0h/2h]tpubDF3HYCjsteTTEVoy9JaY3GcMCABnqbd1WAH5aLVtssp8gY4mvbUEWYebrdGh29rsnamXs7tTkKfg9ciNxnhdHaoYxwBp2x7ghpMRrkDEmbL"], {"ik_u": false, "af": 14, "ct": "XTN"}], ["bbqr", "wsh(andor(multi(2,@0/**,@1/**,@2/**),or_i(and_v(v:pkh(@3/**),after(1773187200)),thresh(2,pk(@4/**),s:pk(@5/**),s:pk(@6/**),snl:after(1759795200))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1777075200))))", ["tpubDDxm2GDBtg4RZjoYWZoutdtbZ2BFGsTv9ha7i9CrPLKDHE2QVg3fECq1nU2VyDoVozavH4NjpTVKTyoUuLqWZ3bauwcFTvpBwHhiv7EFZjy", "tpubDDz2auawERTfkjFoHm9Dk3m4NnDrw1Xb792tAssf4SWDVVwbHoE59rBKzSU8psFR9u9C7avbRRgS2XCfvk2Cc7WQ9Ndf6JU1qSv3JSSyoUy", "tpubDEgdruGzqmmTUXsuef8YBCWuWa4MfKiLGCtCiipnaHmAynnUhic8o9UgWV9mqrGMktDUrBFbCNdaZdkiRwPBbkTDgFmCL4qoevsVqELSuTm", "tpubDDvQwemwNy7THLKvvHyo2jonUseeGyXBdrWdpvB6nAiQLRV2NGBG7XAC89qf2ge8rnC6UpzjwLGqZncs1D1WqtCW3E8Pn3Yjy9tCKueKZeT", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY", "[12ab2a99/48h/1h/0h/2h]tpubDEeWrguYhZWQDvuFKBfhCeMfPiRuW5dFfQeqLLUShoxmDCB2cPBruPXndS2xZVHeuSsAZWiiiSuCncodUiy6pkiJmbK58AUp4im7rLKPKBu", "[dc19da0c/48h/1h/0h/2h]tpubDF3HYCjsteTTEVoy9JaY3GcMCABnqbd1WAH5aLVtssp8gY4mvbUEWYebrdGh29rsnamXs7tTkKfg9ciNxnhdHaoYxwBp2x7ghpMRrkDEmbL"], {"ik_u": false, "af": 14, "ct": "XTN"}], ["Coldcard and Ledger", "wsh(andor(multi(2,@0/**,@1/**,@2/**),or_i(and_v(v:pkh(@3/**),after(1773273600)),thresh(2,pk(@4/**),s:pk(@5/**),s:pk(@6/**),snl:after(1759881600))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1777161600))))", ["tpubDEqVr77TdusHJb9rTL2CcPUCAVQffsb3QAHaN6vTwZ1SVwAKD3ih7j6if7o5mVgGVdLnas4d4aGWR3nWT38gPEhmyiVKYWEfUh1zT21vVeU", "tpubDFS2NFVDRjfAvstjXFHubA4utbCFyisRVVFTY3GD381XLJmUjZzZa2niFdm2cHz3gjKLm8PYot1F6FUjQeFAHESnd14xGg9wbktS1Bz6LuE", "tpubDFaHA7XNSpnhpCdCgr1Ju9xuCJW7pMTWPK3kr61ZxJZXqUg821dJWzvPkWLHAF7XLZscovkXpZLTsZBhf7riHXAR2mBb7RfKMuq88SZF99C", "tpubDDvQwemwNy7TBUnyxFFWnzh6ZRasucqb4ZyZ6ZkMdsqUESPcNdjtiTRt1d7exQ9dTpDZpSxKsFbfgP38TkVMpRdKsyHGScGNKFxwxAb6pSx", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY", "[dc19da0c/48h/1h/0h/2h]tpubDF3HYCjsteTTEVoy9JaY3GcMCABnqbd1WAH5aLVtssp8gY4mvbUEWYebrdGh29rsnamXs7tTkKfg9ciNxnhdHaoYxwBp2x7ghpMRrkDEmbL", "[eda3d606/48h/1h/0h/2h]tpubDEAmqvQkhqP6SbfbSPu3AeRR9kfHLFXYvNDiWashLy7V2zicg1YLg654AqfomsC6kFwTs4MpcnqwxN2AnYAqi5JZeuVDBn3rfZZLTaAuS8Y"], {"ik_u": false, "af": 14, "ct": "XTN"}], ["cc - oct 30 - v1", "wsh(andor(multi(2,@0/**,@1/**,@2/**),or_i(and_v(v:pkh(@3/**),after(1790726400)),thresh(2,pk(@4/**),s:pk(@5/**),s:pk(@6/**),snl:after(1777334400))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1794614400))))", ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp", "[2b4050d2/48h/1h/7h/2h]tpubDFDhmrJ9Bv2FdN5hJ1WZqXen1JA4nzYJVuKw2L5cPr2XfgmbfT4c7b1RHUs1W9kkuj2nEWxWj5siTFqcYHzQebxRarAZwAgtTsTsbLvBNJ1", "[9d017ded/48h/1h/4h/2h]tpubDFZwAPoqMFCCjfcQ2wzMiAFCqZpCiejxp1UZqjwninwSqhtBsw2YquqqBavkMTgJwd52E7b8ChnJ4N4wUzc6LNQBmKTxH1FVudtTfgTL6w7", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY"], {"af": 14, "ct": "XTN"}], ["minsc-Aug 6th coldcard", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[340e2229/48h/1h/2h/2h]tpubDEQ6R8ei6VZKHkGiYotZythF41bVhudHE4Q9r3sJ5wZM48ohFGFWvSgnmwJCLBjXw3iiLpZcqQsvoCE3JQtBu46XEFYKxuzKUeetgupQhrp/<0;1>/*", "[f2dea0dc/48h/1h/2h/2h]tpubDEnmAnPVRR5wDaSqbxuFBb7zxu92EHz8xa39CznEdNHCvzCuurLfXLLUe3xtG4TtdkvYoi1938dXAAy25JgKjDzhcctYh1AhAd62QRBiQQS/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[340e2229/48h/1h/2h/2h]tpubDEQ6R8ei6VZKHkGiYotZythF41bVhudHE4Q9r3sJ5wZM48ohFGFWvSgnmwJCLBjXw3iiLpZcqQsvoCE3JQtBu46XEFYKxuzKUeetgupQhrp/<2;3>/*", "[f2dea0dc/48h/1h/2h/2h]tpubDEnmAnPVRR5wDaSqbxuFBb7zxu92EHz8xa39CznEdNHCvzCuurLfXLLUe3xtG4TtdkvYoi1938dXAAy25JgKjDzhcctYh1AhAd62QRBiQQS/<2;3>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1783382400)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1769990400))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1787270400)))", false, true, false, false], ["minsc-CC test-2", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[1da9c2e9/48h/1h/0h/2h]tpubDEfdfM5gUbvkmZazpfQ2PCXLAeZQvZGLkCUSutVpChymZ5r2LWHunidEYfSTHjXaDP7BGNRcFuETg3qU252e3AimLqJfwzt4bqWgoquzrUm/<0;1>/*", "[124b5cb9/48h/1h/0h/2h]tpubDEXKTHXZ5UQxYpehSL3vVphnkE7Qca6BxaizFq8MY5UcXTGnAgYJVymftsqtjoRLCWEgFabDfr7xWCSRmjjf1M7WbDU2gRRned89AaZfGJ6/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*", "[1da9c2e9/48h/1h/0h/2h]tpubDEfdfM5gUbvkmZazpfQ2PCXLAeZQvZGLkCUSutVpChymZ5r2LWHunidEYfSTHjXaDP7BGNRcFuETg3qU252e3AimLqJfwzt4bqWgoquzrUm/<2;3>/*", "[124b5cb9/48h/1h/0h/2h]tpubDEXKTHXZ5UQxYpehSL3vVphnkE7Qca6BxaizFq8MY5UcXTGnAgYJVymftsqtjoRLCWEgFabDfr7xWCSRmjjf1M7WbDU2gRRned89AaZfGJ6/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1780876800)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1767484800))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1784764800)))", false, true, false, false], ["minsc-HEREEEEEEEEEEEEEEEEEEEEEEEEE", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[9d5a164b/48h/1h/1h/2h]tpubDEEjQpq11q473oxuSjD7ih1qv4XVWbAkA5imBTT6DyDdbZnqhx41NFRQsvQsfZVWxp9x8cFXQqW8NcHkV6b8sBH5CYPAJE3rGsqgBqKkyZU/<0;1>/*", "[eda3d606/48h/1h/1h/2h]tpubDFFruCeGNRU587SfWR1Y6MRnDULzdbS2pYBr33r7dNJiEcpBEJi7EvXgmjdpzV2wh9V7ZjXZZjmFHcQyp3o3cw2Z2Ce3jteSxzwRxhKZedd/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*", "[9d5a164b/48h/1h/1h/2h]tpubDEEjQpq11q473oxuSjD7ih1qv4XVWbAkA5imBTT6DyDdbZnqhx41NFRQsvQsfZVWxp9x8cFXQqW8NcHkV6b8sBH5CYPAJE3rGsqgBqKkyZU/<2;3>/*", "[eda3d606/48h/1h/1h/2h]tpubDFFruCeGNRU587SfWR1Y6MRnDULzdbS2pYBr33r7dNJiEcpBEJi7EvXgmjdpzV2wh9V7ZjXZZjmFHcQyp3o3cw2Z2Ce3jteSxzwRxhKZedd/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1783382400)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1769990400))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1787270400)))", false, true, false, false], ["minsc-aug 7th cc", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[124b5cb9/48h/1h/1h/2h]tpubDFcmEAfJMqEAuX5MhQTaf18D3rf1syukgACgiyzz95Nrx9h8YqsLBKa4Vu8SQ7QG6HafdrYNJwsbox8EAn2NK2pTN46MYrmQRdX2uCmdGT9/<0;1>/*", "[3526b934/48h/1h/1h/2h]tpubDETAAYNjfXMcZVppE3YzUAYcto7zXH1sZMm2dDw9KT56rQoeih8hA7xFVWFs6PHRhei3M2nKTfaeZvDsxovK2QgLcixPyXGKQTJ8aXXKFar/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*", "[124b5cb9/48h/1h/1h/2h]tpubDFcmEAfJMqEAuX5MhQTaf18D3rf1syukgACgiyzz95Nrx9h8YqsLBKa4Vu8SQ7QG6HafdrYNJwsbox8EAn2NK2pTN46MYrmQRdX2uCmdGT9/<2;3>/*", "[3526b934/48h/1h/1h/2h]tpubDETAAYNjfXMcZVppE3YzUAYcto7zXH1sZMm2dDw9KT56rQoeih8hA7xFVWFs6PHRhei3M2nKTfaeZvDsxovK2QgLcixPyXGKQTJ8aXXKFar/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1783468800)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1770076800))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1787356800)))", false, true, false, false], ["minsc-cc test", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[dc7f959a/48h/1h/0h/2h]tpubDDvasTj1PH4nMxAVbXw3MVd28sy7diMP6JXumzYKZK1tEg5vDRg98VfK9Et21JbJyUR5HTnDN4V4ti9UdMvrDVWBCqxknxxXmePzoRApUr5/<0;1>/*", "[b9f957e9/48h/1h/0h/2h]tpubDFS15gqmwz5MyctfhuLtpHbDnzxJuSYsVDT9PRsEPcudzMRe5xQCGdanzJvdqDpzqbZ5vaBpK7NPBMQicUSmvwnMbT5FfnQDD3xrJxsTAxY/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*", "[dc7f959a/48h/1h/0h/2h]tpubDDvasTj1PH4nMxAVbXw3MVd28sy7diMP6JXumzYKZK1tEg5vDRg98VfK9Et21JbJyUR5HTnDN4V4ti9UdMvrDVWBCqxknxxXmePzoRApUr5/<2;3>/*", "[b9f957e9/48h/1h/0h/2h]tpubDFS15gqmwz5MyctfhuLtpHbDnzxJuSYsVDT9PRsEPcudzMRe5xQCGdanzJvdqDpzqbZ5vaBpK7NPBMQicUSmvwnMbT5FfnQDD3xrJxsTAxY/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1782259200)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1768867200))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1786147200)))", false, true, false, false], ["minsc-july 28th", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[f2dea0dc/48h/1h/1h/2h]tpubDEjCL7gG119Yjo8wHYSzeiFjJJYYRJH42DXewXsAEESM4AGwyGJ3ahZtiXtuTVy7F98v6SZsANKpspBN8H89FWP5P2g1kstKfVDnBya1BG4/<0;1>/*", "[340e2229/48h/1h/1h/2h]tpubDFHUR9kVAJpUkbsRjGjHEGJdpT68dYWgnunzZkmPmFXJSojZP4SmBaNRbV44ikqJH2uy1pRzBCA9DYauAtyQ3fd7Cum9pBfigpvVtDQFTtB/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[f2dea0dc/48h/1h/1h/2h]tpubDEjCL7gG119Yjo8wHYSzeiFjJJYYRJH42DXewXsAEESM4AGwyGJ3ahZtiXtuTVy7F98v6SZsANKpspBN8H89FWP5P2g1kstKfVDnBya1BG4/<2;3>/*", "[340e2229/48h/1h/1h/2h]tpubDFHUR9kVAJpUkbsRjGjHEGJdpT68dYWgnunzZkmPmFXJSojZP4SmBaNRbV44ikqJH2uy1pRzBCA9DYauAtyQ3fd7Cum9pBfigpvVtDQFTtB/<2;3>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1782604800)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1769212800))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1786492800)))", false, true, false, false], ["minsc-real cc test", "XTN", 14, null, ["tpubDEH9VodS749EK6AEKQyAKvXWeAKnmPuZDbBstrGqEPQPd3E91UL7KaJia8JqLvr8gmMiCDQjLwLTwobCx55Choidvi43jGKfyS5uDKcSXf1/<0;1>/*", "tpubDDwMnwg5B5qbbhcjTZPnq21FpBpXKZbNX2xWC7khW44Rt5HnLcvRrGrsF1CXWaCZe4DHLmWCWBmhtawAADvHZZyFy5sJAjo7AcggwstRXpV/<0;1>/*", "tpubDFQH7Ebm14H8pN3LbZnGWki9YPvAHBTjeFoi3GiguMxdVfTTWyQFAsqCk5zRrNWWKzUao3H6H1rHwss8yQBGpxdQNV5x7wjKunQsqFNbRDA/<0;1>/*", "tpubDEmyALkSddGqcKnWqe2LcpwkVPK6o62c27saoH9VFT2x3xo4aSeEhBX8AJDxiAmrG1wKF4dvUKLd86pdQCrYfsi1NDVXdVCtDJbjUWXcSwp/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<0;1>/*", "[3526b934/48h/1h/0h/2h]tpubDESiT13HcKofR5yqwn3LE2LLj9wBRG4d6K3tkowsX7mxnx78WcxBBd6gvcb5N7uqoX7ezsJtAujZ8T66MvumJ25QM2cCiy7Wo9SB2gGf2Ly/<0;1>/*", "[f40f5987/48h/1h/0h/2h]tpubDFHmNY1WuvAk4BmfbyqvU8QaqsXCAx5ezUAhcjXXNRPip5CzRpW4JT6CKySrSb9HLdVTMA3PQUzHBCjztNjy2tkGvBU6XhTQrnwkCHGfzZn/<0;1>/*", "[3d00045d/48h/1h/0h/2h]tpubDEWe62ZwwoJKF2bLf9dy6pw7VRi1GQKNUe2K5tVihD9E6xceLeZXnARkB58cCoEPPFyp3m78ht746LJraSLFMkaquY2A73MFsf853AyjRUY/<2;3>/*", "[3526b934/48h/1h/0h/2h]tpubDESiT13HcKofR5yqwn3LE2LLj9wBRG4d6K3tkowsX7mxnx78WcxBBd6gvcb5N7uqoX7ezsJtAujZ8T66MvumJ25QM2cCiy7Wo9SB2gGf2Ly/<2;3>/*", "[f40f5987/48h/1h/0h/2h]tpubDFHmNY1WuvAk4BmfbyqvU8QaqsXCAx5ezUAhcjXXNRPip5CzRpW4JT6CKySrSb9HLdVTMA3PQUzHBCjztNjy2tkGvBU6XhTQrnwkCHGfzZn/<2;3>/*"], "andor(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*),or_i(and_v(v:pkh(@3/<0;1>/*),after(1780876800)),thresh(2,pk(@4/<0;1>/*),s:pk(@5/<0;1>/*),s:pk(@6/<0;1>/*),snl:after(1767484800))),and_v(v:thresh(2,pkh(@4/<2;3>/*),a:pkh(@5/<2;3>/*),a:pkh(@6/<2;3>/*)),after(1784764800)))", false, true, false, false]] + +# EOF diff --git a/testing/data/taproot/in_internal_key_len.psbt b/testing/data/taproot/in_internal_key_len.psbt new file mode 100644 index 000000000..141da152a Binary files /dev/null and b/testing/data/taproot/in_internal_key_len.psbt differ diff --git a/testing/data/taproot/in_key_pth_sig_len.psbt b/testing/data/taproot/in_key_pth_sig_len.psbt new file mode 100644 index 000000000..1ea3c554b --- /dev/null +++ b/testing/data/taproot/in_key_pth_sig_len.psbt @@ -0,0 +1 @@ +70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701133f173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc3475000000 \ No newline at end of file diff --git a/testing/data/taproot/in_key_pth_sig_len1.psbt b/testing/data/taproot/in_key_pth_sig_len1.psbt new file mode 100644 index 000000000..a9a6bc8aa --- /dev/null +++ b/testing/data/taproot/in_key_pth_sig_len1.psbt @@ -0,0 +1 @@ +70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000 \ No newline at end of file diff --git a/testing/data/taproot/in_leaf_script_cb_len.psbt b/testing/data/taproot/in_leaf_script_cb_len.psbt new file mode 100644 index 000000000..4108d0bad --- /dev/null +++ b/testing/data/taproot/in_leaf_script_cb_len.psbt @@ -0,0 +1 @@ +70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000 \ No newline at end of file diff --git a/testing/data/taproot/in_leaf_script_cb_len1.psbt b/testing/data/taproot/in_leaf_script_cb_len1.psbt new file mode 100644 index 000000000..7de51589a --- /dev/null +++ b/testing/data/taproot/in_leaf_script_cb_len1.psbt @@ -0,0 +1 @@ +70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000 \ No newline at end of file diff --git a/testing/data/taproot/in_script_sig_key_len.psbt b/testing/data/taproot/in_script_sig_key_len.psbt new file mode 100644 index 000000000..0cc688694 --- /dev/null +++ b/testing/data/taproot/in_script_sig_key_len.psbt @@ -0,0 +1 @@ +70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000 \ No newline at end of file diff --git a/testing/data/taproot/in_script_sig_sig_len.psbt b/testing/data/taproot/in_script_sig_sig_len.psbt new file mode 100644 index 000000000..ba6c4daf1 --- /dev/null +++ b/testing/data/taproot/in_script_sig_sig_len.psbt @@ -0,0 +1 @@ +70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000 \ No newline at end of file diff --git a/testing/data/taproot/in_script_sig_sig_len1.psbt b/testing/data/taproot/in_script_sig_sig_len1.psbt new file mode 100644 index 000000000..76c68695f --- /dev/null +++ b/testing/data/taproot/in_script_sig_sig_len1.psbt @@ -0,0 +1 @@ +70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093f89756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd430000 \ No newline at end of file diff --git a/testing/data/taproot/in_tr_deriv_key_len.psbt b/testing/data/taproot/in_tr_deriv_key_len.psbt new file mode 100644 index 000000000..0ad329722 --- /dev/null +++ b/testing/data/taproot/in_tr_deriv_key_len.psbt @@ -0,0 +1 @@ +70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000 \ No newline at end of file diff --git a/testing/debug/.gitignore b/testing/debug/.gitignore deleted file mode 100644 index c81791f8f..000000000 --- a/testing/debug/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.psbt -*.txn -*.txt -*.json -*.png diff --git a/testing/descriptor.py b/testing/descriptor.py new file mode 100644 index 000000000..9dde4c3d4 --- /dev/null +++ b/testing/descriptor.py @@ -0,0 +1,463 @@ +# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# descriptor.py - Bitcoin Core's descriptors and their specialized checksums. +# +import struct +from binascii import unhexlify as a2b_hex +from binascii import hexlify as b2a_hex +from constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH, AF_P2TR + +MULTI_FMT_TO_SCRIPT = { + AF_P2SH: "sh(%s)", + AF_P2WSH_P2SH: "sh(wsh(%s))", + AF_P2WSH: "wsh(%s)", + AF_P2TR: "tr(%s)", + None: "wsh(%s)", + # hack for tests + "p2sh": "sh(%s)", + "p2sh-p2wsh": "sh(wsh(%s))", + "p2wsh-p2sh": "sh(wsh(%s))", + "p2wsh": "wsh(%s)", + "p2tr": "tr(%s)" +} + +SINGLE_FMT_TO_SCRIPT = { + AF_P2WPKH: "wpkh(%s)", + AF_CLASSIC: "pkh(%s)", + AF_P2WPKH_P2SH: "sh(wpkh(%s))", + AF_P2TR: "tr(%s)", + None: "wpkh(%s)", + "p2pkh": "pkh(%s)", + "p2wpkh": "wpkh(%s)", + "p2sh-p2wpkh": "sh(wpkh(%s))", + "p2wpkh-p2sh": "sh(wpkh(%s))", + "p2tr": "tr(%s)", +} + +PROVABLY_UNSPENDABLE = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" +INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ " +CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + +def xfp2str(xfp): + # Standardized way to show an xpub's fingerprint... it's a 4-byte string + # and not really an integer. Used to show as '0x%08x' but that's wrong endian. + return b2a_hex(struct.pack('> 35 + c = ((c & 0x7ffffffff) << 5) ^ val + if (c0 & 1): + c ^= 0xf5dee51989 + if (c0 & 2): + c ^= 0xa9fdca3312 + if (c0 & 4): + c ^= 0x1bab10e32d + if (c0 & 8): + c ^= 0x3706b1677a + if (c0 & 16): + c ^= 0x644d626ffd + + return c + +def descriptor_checksum(desc): + c = 1 + cls = 0 + clscount = 0 + for ch in desc: + pos = INPUT_CHARSET.find(ch) + if pos == -1: + raise ValueError(ch) + + c = polymod(c, pos & 31) + cls = cls * 3 + (pos >> 5) + clscount += 1 + if clscount == 3: + c = polymod(c, cls) + cls = 0 + clscount = 0 + + if clscount > 0: + c = polymod(c, cls) + for j in range(0, 8): + c = polymod(c, 0) + c ^= 1 + + rv = '' + for j in range(0, 8): + rv += CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31] + + return rv + +def append_checksum(desc): + return desc + "#" + descriptor_checksum(desc) + + +def parse_desc_str(string): + """Remove comments, empty lines and strip line. Produce single line string""" + res = "" + for l in string.split("\n"): + strip_l = l.strip() + if not strip_l: + continue + if strip_l.startswith("#"): + continue + res += strip_l + return res + + +class Descriptor: + __slots__ = ( + "keys", + "addr_fmt", + ) + + def __init__(self, keys, addr_fmt): + self.keys = keys + self.addr_fmt = addr_fmt + + @staticmethod + def checksum_check(desc_w_checksum , csum_required=False): + try: + desc, checksum = desc_w_checksum.split("#") + except ValueError: + if csum_required: + raise ValueError("Missing descriptor checksum") + return desc_w_checksum, None + + calc_checksum = descriptor_checksum(desc) + if calc_checksum != checksum: + raise WrongCheckSumError("Wrong checksum %s, expected %s" % (checksum, calc_checksum)) + return desc, checksum + + @staticmethod + def parse_key_orig_info(key: str): + close_index = key.find("]") + if key[0] != "[" or close_index == -1: + raise ValueError("Key origin info is required for %s" % (key)) + key_orig_info = key[1:close_index] # remove brackets + key = key[close_index + 1:] + assert "/" in key_orig_info, "Malformed key derivation info" + return key_orig_info, key + + @staticmethod + def parse_key_derivation_info(key: str): + invalid_subderiv_msg = "Invalid subderivation path - only 0/* or <0;1>/* allowed" + slash_split = key.split("/") + assert len(slash_split) > 1, invalid_subderiv_msg + if all(["h" not in elem and "'" not in elem for elem in slash_split[1:]]): + assert slash_split[-1] == "*", invalid_subderiv_msg + assert slash_split[-2] in ["0", "<0;1>", "<1;0>"], invalid_subderiv_msg + assert len(slash_split[1:]) == 2, invalid_subderiv_msg + return slash_split[0] + else: + raise ValueError("Cannot use hardened sub derivation path") + + def checksum(self): + return descriptor_checksum(self._serialize()) + + def serialize_keys(self, internal=False, int_ext=False, keys=None): + to_do = keys if keys is not None else self.keys + result = [] + for xfp, deriv, xpub in to_do: + if deriv[0] == "m": + # get rid of 'm' + deriv = deriv[1:] + elif deriv[0] != "/": + # input "84'/0'/0'" would lack slash separtor with xfp + deriv = "/" + deriv + if not isinstance(xfp, str): + xfp = xfp2str(xfp) + koi = xfp + deriv + # normalize xpub to use h for hardened instead of ' + key_str = "[%s]%s" % (koi.lower(), xpub) + if int_ext: + key_str = key_str + "/" + "<0;1>" + "/" + "*" + else: + key_str = key_str + "/" + "/".join(["1", "*"] if internal else ["0", "*"]) + result.append(key_str.replace("'", "h")) + return result + + def _serialize(self, internal=False, int_ext=False): + """Serialize without checksum""" + assert len(self.keys) == 1 # "Multiple keys for single signature script" + desc_base = SINGLE_FMT_TO_SCRIPT[self.addr_fmt] + inner = self.serialize_keys(internal=internal, int_ext=int_ext)[0] + return desc_base % (inner) + + def serialize(self, internal=False, int_ext=False): + """Serialize with checksum""" + return append_checksum(self._serialize(internal=internal, int_ext=int_ext)) + + @classmethod + def parse(cls, desc_w_checksum): + # remove garbage + desc_w_checksum = parse_desc_str(desc_w_checksum) + # check correct checksum + desc, checksum = cls.checksum_check(desc_w_checksum) + # legacy + if desc.startswith("pkh("): + addr_fmt = AF_CLASSIC + tmp_desc = desc.replace("pkh(", "") + tmp_desc = tmp_desc.rstrip(")") + + # native segwit + elif desc.startswith("wpkh("): + addr_fmt = AF_P2WPKH + tmp_desc = desc.replace("wpkh(", "") + tmp_desc = tmp_desc.rstrip(")") + + # wrapped segwit + elif desc.startswith("sh(wpkh("): + addr_fmt = AF_P2WPKH_P2SH + tmp_desc = desc.replace("sh(wpkh(", "") + tmp_desc = tmp_desc.rstrip("))") + + # wrapped segwit + elif desc.startswith("tr("): + addr_fmt = AF_P2TR + tmp_desc = desc.replace("tr(", "") + tmp_desc = tmp_desc.rstrip(")") + + else: + raise ValueError("Unsupported descriptor. Supported: pkh(, wpkh(, sh(wpkh(.") + + koi, key = cls.parse_key_orig_info(tmp_desc) + if key[0:4] not in ["tpub", "xpub"]: + raise ValueError("Only extended public keys are supported") + + xpub = cls.parse_key_derivation_info(key) + xfp = str2xfp(koi[:8]) + origin_deriv = "m" + koi[8:] + + return cls(keys=[(xfp, origin_deriv, xpub)], addr_fmt=addr_fmt) + + @classmethod + def is_descriptor(cls, desc_str): + # Quick method to guess whether this is a descriptor + try: + temp = parse_desc_str(desc_str) + except: + return False + + for prefix in ("pk(", "pkh(", "wpkh(", "tr(", "addr(", "raw(", "rawtr(", "combo(", + "sh(", "wsh(", "multi(", "sortedmulti(", "multi_a(", "sortedmulti_a("): + if temp.startswith(prefix): + return True + if prefix in temp: + # weaker case - needed for JSON wrapped imports + # if descriptor is invalid or unsuitable for our purpose + # we fail later (in parsing) + return True + return False + + def bitcoin_core_serialize(self, external_label=None): + # this will become legacy one day + # instead use <0;1> descriptor format + res = [] + for internal in [False, True]: + desc_obj = { + "desc": self.serialize(internal=internal), + "active": True, + "timestamp": "now", + "internal": internal, + "range": [0, 100], + } + if internal is False and external_label: + desc_obj["label"] = external_label + res.append(desc_obj) + + return res + + +class MultisigDescriptor(Descriptor): + # only supprt with key derivation info + # only xpubs + # can be extended when needed + __slots__ = ( + "M", + "N", + "internal_key", + "keys", + "addr_fmt", + "is_sorted" # whether to use sortedmulti() or multi() + ) + + def __init__(self, M, N, keys, addr_fmt, internal_key=None, is_sorted=True): + self.M = M + self.N = N + self.internal_key = is_sorted + self.is_sorted = is_sorted + super().__init__(keys, addr_fmt) + + @classmethod + def parse(cls, desc_w_checksum): + internal_key = None + # remove garbage + desc_w_checksum = parse_desc_str(desc_w_checksum) + # check correct checksum + desc, checksum = cls.checksum_check(desc_w_checksum) + is_sorted = "sortedmulti(" in desc + rplc = "sortedmulti(" if is_sorted else "multi(" + + # wrapped segwit + if desc.startswith("sh(wsh("+rplc): + addr_fmt = AF_P2WSH_P2SH + tmp_desc = desc.replace("sh(wsh("+rplc, "") + tmp_desc = tmp_desc.rstrip(")))") + + # native segwit + elif desc.startswith("wsh("+rplc): + addr_fmt = AF_P2WSH + tmp_desc = desc.replace("wsh("+rplc, "") + tmp_desc = tmp_desc.rstrip("))") + + # legacy + elif desc.startswith("sh("+rplc): + addr_fmt = AF_P2SH + tmp_desc = desc.replace("sh("+rplc, "") + tmp_desc = tmp_desc.rstrip("))") + elif desc.startswith("tr("): + addr_fmt = AF_P2TR + tmp_desc = desc.replace("tr(", "") + tmp_desc = tmp_desc.rstrip(")") + internal_key, tmp_desc = tmp_desc.split(",", 1) + tmp_desc = tmp_desc.replace(rplc + "_a(", "") + tmp_desc = tmp_desc.rstrip(")") + + try: + koi, key = cls.parse_key_orig_info(internal_key) + if key[0:4] not in ["tpub", "xpub"]: + raise ValueError("Only extended public keys are supported") + xpub = cls.parse_key_derivation_info(key) + xfp = str2xfp(koi[:8]) + origin_deriv = "m" + koi[8:] + internal_key = (xfp, origin_deriv, xpub) + except ValueError: + # https://github.com/BlockstreamResearch/secp256k1-zkp/blob/11af7015de624b010424273be3d91f117f172c82/src/modules/rangeproof/main_impl.h#L16 + # H = lift_x(0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0) + # if internal_key == PROVABLY_UNSPENDABLE: + # # unspendable H as defined in BIP-0341 + # pass + # else: + # assert "r=" in internal_key + # _, r = internal_key.split("=") + # if r == "@": + # # pick a fresh integer r in the range 0...n-1 uniformly at random and use H + rG + # kp = ngu.secp256k1.keypair() + # else: + # # H + rG where r is provided from user + # r = a2b_hex(r) + # assert len(r) == 32, "r != 32" + # kp = ngu.secp256k1.keypair(r) + # + # H = a2b_hex(PROVABLY_UNSPENDABLE) + # H_xo = ngu.secp256k1.xonly_pubkey(H) + # internal_key = H_xo.tweak_add(kp.xonly_pubkey().to_bytes()) + # internal_key = b2a_hex(internal_key.to_bytes()).decode() + pass + + else: + raise ValueError("Unsupported descriptor. Supported: sh(), sh(wsh()), wsh().") + + splitted = tmp_desc.split(",") + M, keys = int(splitted[0]), splitted[1:] + N = int(len(keys)) + if M > N: + raise ValueError("M must be <= N: got M=%d and N=%d" % (M, N)) + + res_keys = [] + for key in keys: + koi, key = cls.parse_key_orig_info(key) + if key[0:4] not in ["tpub", "xpub"]: + raise ValueError("Only extended public keys are supported") + + xpub = cls.parse_key_derivation_info(key) + xfp = str2xfp(koi[:8]) + origin_deriv = "m" + koi[8:] + res_keys.append((xfp, origin_deriv, xpub)) + + return cls(M=M, N=N, keys=res_keys, addr_fmt=addr_fmt, + internal_key=internal_key,is_sorted=is_sorted) + + def _serialize(self, internal=False, int_ext=False): + """Serialize without checksum""" + desc_base = MULTI_FMT_TO_SCRIPT[self.addr_fmt] + if self.addr_fmt == AF_P2TR: + if isinstance(self.internal_key, str): + desc_base = desc_base % (self.internal_key + ",sortedmulti_a(%s)") + else: + ik_ser = self.serialize_keys(keys=[self.internal_key])[0] + desc_base = desc_base % (ik_ser + ",sortedmulti_a(%s)") + _type = "sortedmulti" if self.is_sorted else "multi" + _type += "(%s)" + desc_base = desc_base % _type + assert len(self.keys) == self.N + inner = str(self.M) + "," + ",".join( + self.serialize_keys(internal=internal, int_ext=int_ext)) + + return desc_base % (inner) + + def pretty_serialize(self): + """Serialize in pretty and human-readable format""" + _type = "sortedmulti" if self.is_sorted else "multi" + res = "# Coldcard descriptor export\n" + if self.is_sorted: + res += "# order of keys in the descriptor does not matter, will be sorted before creating script (BIP-67)\n" + else: + res += ("# !!! DANGER: order of keys in descriptor MUST be preserved. " + "Correct order of keys is required to compose valid redeem/witness script.\n") + if self.addr_fmt == AF_P2SH: + res += "# bare multisig - p2sh\n" + res += "sh("+_type+"(\n%s\n))" + # native segwit + elif self.addr_fmt == AF_P2WSH: + res += "# native segwit - p2wsh\n" + res += "wsh("+_type+"(\n%s\n))" + + # wrapped segwit + elif self.addr_fmt == AF_P2WSH_P2SH: + res += "# wrapped segwit - p2sh-p2wsh\n" + res += "sh(wsh(" + _type + "(\n%s\n)))" + elif self.addr_fmt == AF_P2TR: + inner_ident = 2 + res += "# taproot multisig - p2tr\n" + res += "tr(\n" + if isinstance(self.internal_key, str): + res += "\t" + "# internal key (provably unspendable)\n" + res += "\t" + self.internal_key + ",\n" + res += "\t" + _type + "_a(\n%s\n))" + else: + ik_ser = self.serialize_keys(keys=[self.internal_key])[0] + res += "\t" + "# internal key\n" + res += "\t" + ik_ser + ",\n" + res += "\t" + _type + "_a(\n%s\n))" + else: + raise ValueError("Malformed descriptor") + + assert len(self.keys) == self.N + inner = "\t" + "# %d of %d (%s)\n" % ( + self.M, self.N, + "requires all participants to sign" if self.M == self.N else "threshold") + inner += "\t" + str(self.M) + ",\n" + ser_keys = self.serialize_keys() + for i, key_str in enumerate(ser_keys, start=1): + if i == self.N: + inner += "\t" + key_str + else: + inner += "\t" + key_str + ",\n" + + checksum = self.serialize().split("#")[1] + + return (res % inner) + "#" + checksum + +# EOF diff --git a/testing/devtest/check_decode.py b/testing/devtest/check_decode.py index 6ef46deba..6d14ba90e 100644 --- a/testing/devtest/check_decode.py +++ b/testing/devtest/check_decode.py @@ -34,8 +34,11 @@ if 'destinations' in expect: for (val, addr), (idx, txo) in zip(expect['destinations'], p.output_iter()): assert val == txo.nValue - txt = active_request.render_output(txo) - assert addr in txt + txt, _ = active_request.render_output(txo) + # normalize from display format + address = txt.split("\n")[-2] + assert address[0] == "\x02" + assert addr == address[1:] assert '%.8f'%(val/1E8) in txt if 'sw_inputs' in expect: diff --git a/testing/devtest/clear_seed.py b/testing/devtest/clear_seed.py index 353efef28..baa506318 100644 --- a/testing/devtest/clear_seed.py +++ b/testing/devtest/clear_seed.py @@ -23,6 +23,7 @@ pa.login() assert pa.is_secret_blank() + settings.blank() SettingsObject.master_sv_data = {} SettingsObject.master_nvram_key = None diff --git a/testing/devtest/menu_dump.py b/testing/devtest/menu_dump.py index a0835fb81..d10be3c77 100644 --- a/testing/devtest/menu_dump.py +++ b/testing/devtest/menu_dump.py @@ -10,12 +10,13 @@ async def doit(): import version async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False): from menu import MenuItem, ToggleMenuItem, MenuSystem, NonDefaultMenuItem - from seed import WordNestMenu, EphemeralSeedMenu, SeedVaultMenu + from seed import WordNestMenu, EphemeralSeedMenu, SeedVaultMenu, not_hobbled_mode from trick_pins import TrickPinMenu from users import UsersMenu from flow import has_secrets, nfc_enabled, vdisk_enabled, word_based_seed from flow import hsm_policy_available, is_not_tmp, has_real_secret - from flow import has_se_secrets, hsm_available + from flow import has_se_secrets, hsm_available, qr_and_has_secrets, has_pushtx_url + from flow import sssp_related_keys, sssp_allow_passphrase, sssp_allow_notes, sssp_allow_vault print("%s%s"% (indent, label), file=fd) @@ -24,7 +25,7 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) m = [] # recursing into functions that do stuff doesn't work well, skip - avoid = {'Clone Coldcard', 'Debug Functions', 'Migrate COLDCARD'} + avoid = {'Clone Coldcard', 'Debug Functions', 'Migrate Coldcard'} if any(label.startswith(a) for a in avoid): return @@ -32,8 +33,9 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) if version.has_qwerty and m.__name__ == "start_seed_import": print('%s[SEED WORD ENTRY]' % indent, file=fd) return - if m.__name__ == "make_custom": + if m.__name__ in ("make_custom", "bkpw_override"): # address explorer custom path menu + # bkpw override = dev thing return print("Calling: %r (%s)" % (m.__name__, label)) @@ -65,11 +67,11 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) # trick pins are not available in EmptyWallet continue - pred = getattr(mi, 'predicate', None) + pred = getattr(mi, '_predicate', None) if pred in (True, False): if here in ("NFC Tools", "Import via NFC", "NFC File Share"): here += ' [IF NFC ENABLED]' - if "QR" in here and "Scan" in here: + if "QR" in here or "Scan" in here or "BBQr" in here: here += ' [IF QR SCANNER]' if "battery" in here: here += ' [IF BATTERIES]' @@ -97,9 +99,26 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) here += ' [IF SECRET AND NOT TMP SEED]' elif pred == hsm_available: here += ' [IF HSM AND SECRET]' + elif pred == qr_and_has_secrets: + here += ' [IF QR AND SECRET]' + # do nothing, only in NormalOps menu, but SSSP has different menu dump + elif pred == not_hobbled_mode: pass + # here += ' [IF SSSP DISABLED]' + elif pred == has_pushtx_url: + here += ' [IF PUSHTX ENABLED]' + elif pred == sssp_related_keys: + here += ' [IF SSSP RELATED KEYS ENABLED]' + elif pred == sssp_allow_passphrase: + here += ' [IF WORD BASED SEED & SSSP RELATED KEYS ENABLED]' + elif pred == sssp_allow_notes: + here += '[IF ENABLED & SSSP ALLOW NOTES]' + elif pred == sssp_allow_vault: + here += '[IF ENABLED & SSSP RELATED KEYS ENABLED]' elif pred: - if here == "Secure Notes & Passwords": + if here in ("Secure Notes & Passwords", "Push Transaction"): here += ' [IF ENBALED]' + if here == "Secure Logout": + here += ' [IF NOT BATTERIES]' else: here += ' [MAYBE]' @@ -131,15 +150,29 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) print('%s%s' % (indent, here), file=fd) - from flow import EmptyWallet, NormalSystem, FactoryMenu, VirginSystem + from flow import EmptyWallet, NormalSystem, FactoryMenu, VirginSystem, HobbledTopMenu from glob import settings # need these to supress warnings and info messages # that need user interaction nad/or show hidden items settings.put("seedvault", 1) + settings.put("seeds", [["7126EB3C", "808ae37a2d3d3d0f9db5ca98c8300e3818", "[7126EB3C]", "TRNG Words"], + ["CCEE13B9", "018d669ed0fddccd7f34ef6dac86864e75fc4036d7dd3992c985ba0e625d8da83ac33b64d371a6d0d1a4a5200f00080ef5e2b341251b30a8b665be42c43fb4c5f3", "[CCEE13B9]", "BIP-39 Passphrase on [0F056943]"], + ["03EE9989", "01a00f4ecbfb55b186bae4486e0e292a34e1afb0c1f64ad4a9a3f378bdeefb7296abce50461838f76979a695d6b4f6ac329661c227f1137400520cbbb1294333a7", "[03EE9989]", "BIP85 Derived from [0F056943], index=543"]]) + settings.put("secnap", 1) + settings.put("notes", [{"misc": "some random notes", "title": "note0"}, + {"password": "AnnounceHalf+~^99891", "site": "abc.org", "misc": "never disclose!!!!!", "user": "satoshi", "title": "secret-PWD"}]) settings.put("axskip", 1) settings.put("b39skip", 1) settings.put("sd2fa", ["a"]) + settings.put("ptxurl", 'https://coldcard.com/pushtx#') + settings.put("multisig", [["CC-2-of-4", [2, 4], [[1130956047, "tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP"], [3503269483, "tpubDFcrvj5n7gyaxWQkoX69k2Zij4vthiAwvN2uhYjDrE6wktKoQaE7gKVZRiTbYdrAYH1UFPGdzdtWJc6WfR2gFMq6XpxA12gCdQmoQNU9mgm"], [2389277556, "tpubDExj5FnaUnPAn7sHGUeBqD3buoNH5dqmjAT6884vbDpH1iDYWigb7kFo2cA97dc8EHb54u13TRcZxC4kgRS9gc3Ey2xc8c5urytEzTcp3ac"], [3190206587, "tpubDFiuHYSJhNbHcbLJoxWdbjtUcbKR6PvLq53qC1Xq6t93CrRx78W3wcng8vJyQnY3giMJZEgNCRVzTojLb8RqPFpW5Ms2dYpjcJYofN1joyu"]], {"pp": "m/48h/1h/0h/2h", "ch": "XTN", "ft": 14}]]) + settings.put("tp", {"11-11": [0, 16384, 0], "333-3334": [1, 4096, 1001], "!p": [2, 33280, 3]}) + + + # saved passphrase on MicroSD + with open("MicroSD/.tmp.tmp", "wb") as f: + f.write(b'\xf0\xc9\xff\x00\xf37c\xdd\x8bz\xfa\x0b\xd9\x16;g8\xf8S0\xa5\x129\x99\xd4\xa2=\n\x01\xf9q$w\xb2sb,\xa7\xf9') with open('menudump.txt', 'wt') as fd: for nm, m in [ @@ -147,8 +180,13 @@ async def dump_menu(fd, m, label, indent, menu_item=None, menu_idx=0, whs=False) ('[IF BLANK WALLET]', EmptyWallet), ('[NORMAL OPERATION]', NormalSystem), ('[FACTORY MODE]', FactoryMenu), + ('[SSSP]', HobbledTopMenu), ]: - await dump_menu(fd, m, nm, '', whs=(m == NormalSystem)) + if "SSSP" in nm: + from pincodes import pa + pa.hobbled_mode = True + + await dump_menu(fd, m, nm, '', whs=(m in (NormalSystem,HobbledTopMenu))) print('---\n', file=fd) print("DONE: check menudump.txt file") diff --git a/testing/devtest/set_encoded_secret.py b/testing/devtest/set_encoded_secret.py index f10080ad1..f9b56c763 100644 --- a/testing/devtest/set_encoded_secret.py +++ b/testing/devtest/set_encoded_secret.py @@ -16,6 +16,6 @@ pa.change(new_secret=raw) pa.new_main_secret(raw) -print("New key in effect: %s" % settings.get('xpub', 'MISSING')) -print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0))) +print("New key in effect (encoded): %s" % settings.get('xpub', 'MISSING')) +print(".. w/ XFP= %s" % xfp2str(settings.get('xfp', 0))) diff --git a/testing/devtest/set_seed.py b/testing/devtest/set_seed.py index 6b2be19b8..acb6255e8 100644 --- a/testing/devtest/set_seed.py +++ b/testing/devtest/set_seed.py @@ -1,6 +1,7 @@ # (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -# load up the simulator w/ indicated list of seed words +# Load up the simulator w/ indicated list of seed words +# from sim_settings import sim_defaults import stash, chains from pincodes import pa @@ -11,7 +12,6 @@ from actions import goto_top_menu from nvstore import SettingsObject - tn = chains.BitcoinTestnet stash.bip39_passphrase = '' @@ -23,14 +23,16 @@ SettingsObject.master_sv_data = {} SettingsObject.master_nvram_key = None set_seed_value(main.WORDS) +stash.SensitiveValues.clear_cache() settings.set('chain', 'XTN') settings.set('words', len(main.WORDS)) settings.set('terms_ok', True) settings.set('idle_to', 0) -print("New key in effect: %s" % settings.get('xpub', 'MISSING')) -print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0))) +print("TESTING: New key in effect [%s]: %s..%s = %s" % ( + xfp2str(settings.get('xfp', 0)), main.WORDS[0], main.WORDS[-1], + settings.get('xpub', 'MISSING'))) # impt: if going from xprv => seed words, main menu needs updating goto_top_menu() diff --git a/testing/devtest/set_tprv.py b/testing/devtest/set_tprv.py index f0d7cbae2..69369fec1 100644 --- a/testing/devtest/set_tprv.py +++ b/testing/devtest/set_tprv.py @@ -1,6 +1,7 @@ # (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # -# load up the simulator w/ indicated test master key +# load up the simulator w/ indicated test master key in TPRV format. +# import main, ngu from sim_settings import sim_defaults import stash, chains @@ -34,8 +35,9 @@ pa.new_main_secret(raw) settings.set('words', False) -print("New key in effect: %s" % settings.get('xpub', 'MISSING')) -print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0))) - assert settings.get('xfp', 0) == swab32(node.my_fp()) +print("TESTING: New tprv in effect [%s]: %s" % ( + settings.get('xpub', 'MISSING'), + xfp2str(settings.get('xfp', 0)))) + diff --git a/testing/devtest/unit_addrs.py b/testing/devtest/unit_addrs.py index 299d7c210..a4db64738 100644 --- a/testing/devtest/unit_addrs.py +++ b/testing/devtest/unit_addrs.py @@ -4,44 +4,51 @@ from h import a2b_hex, b2a_hex from serializations import CTxOut from uio import BytesIO +from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2SH cases = [ # TxOut, type, is_segwit, hash160/pubkey, - ( 'c4f33d0000000000160014ad46a001d55bd55d157e716bf17c02f8964b5a19', - 'p2pkh', True, - 'ad46a001d55bd55d157e716bf17c02f8964b5a19' ), - ( '202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac', - 'p2pkh', False, - '8280b37df378db99f66f85c95a783a76ac7a6d59' ), - + ( + 'c4f33d0000000000160014ad46a001d55bd55d157e716bf17c02f8964b5a19', + AF_P2WPKH, + 'ad46a001d55bd55d157e716bf17c02f8964b5a19' + ), + ( + '202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac', + AF_CLASSIC, + '8280b37df378db99f66f85c95a783a76ac7a6d59' + ), # from legendary txid: 40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8 - ( '301b0f000000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87', - 'p2sh', False, - 'e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a'), - + ( + '301b0f000000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87', + AF_P2SH, + 'e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a' + ), # from testnet: a4c89e0ffb84d06a1e62f0f9f0f5974db250878caa1f71f9992a1f865b8ff2fa # via - ( 'b88201000000000017a914f0ca58dc8e539421a3cb4a9c22c059973075287c87', - 'p2sh', False, - 'f0ca58dc8e539421a3cb4a9c22c059973075287c'), - + ( + 'b88201000000000017a914f0ca58dc8e539421a3cb4a9c22c059973075287c87', + AF_P2SH, + 'f0ca58dc8e539421a3cb4a9c22c059973075287c' + ), # XXX missing: P2SH segwit, 1of1 and N of M - ( 'd0f13d0000000000160014f2369bac6d24ed11313fa65adda1971d10e17bff', - 'p2pkh', True, - 'f2369bac6d24ed11313fa65adda1971d10e17bff') + ( + 'd0f13d0000000000160014f2369bac6d24ed11313fa65adda1971d10e17bff', + AF_P2WPKH, + 'f2369bac6d24ed11313fa65adda1971d10e17bff' + ) ] -for raw_txo, expect_type, expect_sw, expect_hash in cases: +for raw_txo, expect_type, expect_hash in cases: expect_hash = a2b_hex(expect_hash) out = CTxOut() out.deserialize(BytesIO(a2b_hex(raw_txo))) print("Case: %s... " % raw_txo[0:30]) - addr_type, addr_or_pubkey, is_segwit = out.get_address() + addr_type, addr_or_pubkey = out.get_address() - assert is_segwit == expect_sw, 'wrong segwit' assert addr_or_pubkey == expect_hash, 'wrong pubkey/addr' assert addr_type == expect_type, addr_type diff --git a/testing/devtest/unit_bip32.py b/testing/devtest/unit_bip32.py new file mode 100644 index 000000000..7ab29575a --- /dev/null +++ b/testing/devtest/unit_bip32.py @@ -0,0 +1,47 @@ +# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Invalid Extended Keys test +# https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-5 +from desc_utils import Key +from seed import xprv_to_encoded_secret +from glob import settings + +TO_CHECK_PUB = [ + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm", #(pubkey version / prvkey mismatch) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn", #(invalid pubkey prefix 04) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4", #(invalid pubkey prefix 01) + # blinded keys allowed + # "xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ", # (zero depth with non - zero parent fingerprint) + # "xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8", # (zero depth with non - zero index) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY", # (invalid pubkey 020000000000000000000000000000000000000000000000000000000000000007) + "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9", # unknown version +] + +TO_CHECK_PRV = [ + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH", # (prvkey version / pubkey mismatch) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ", # (invalid prvkey prefix 04) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J", # (invalid prvkey prefix 01) + # "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv", # (zero depth with non - zero parent fingerprint) + # "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN", # (zero depth with non - zero index) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx", # (private key 0 not in 1..n - 1) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G", # (private key n not in 1..n - 1) + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL", # (invalid checksum) + "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4", # (unknown version) +] + +settings.set('chain', "BTC") +for i, ek in enumerate(TO_CHECK_PUB): + try: + Key.from_string(ek).validate(None) + raise RuntimeError + except (AssertionError, ValueError) as e: + print("exc", e) + +for i, ek in enumerate(TO_CHECK_PRV): + try: + xprv_to_encoded_secret(ek) + raise AttributeError + except (ValueError, RuntimeError, AssertionError) as e: + print("exc", e) + +# EOF \ No newline at end of file diff --git a/testing/devtest/unit_bip388.py b/testing/devtest/unit_bip388.py new file mode 100644 index 000000000..640146323 --- /dev/null +++ b/testing/devtest/unit_bip388.py @@ -0,0 +1,145 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# BIP-0388 vectors https://github.com/bitcoin/bips/blob/master/bip-0388.mediawiki + +valid = [ + ( + "pkh(@0/**)", + ["[6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb"], + "pkh([6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb/<0;1>/*)", + ), + ( + "sh(wpkh(@0/**))", + ["[6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9"], + "sh(wpkh([6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9/<0;1>/*))", + ), + ( + "wpkh(@0/**)", + ["[6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt"], + "wpkh([6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt/<0;1>/*)", + ), + ( + "tr(@0/**)", + ["[6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL"], + "tr([6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL/<0;1>/*)", + ), + ( + "wsh(sortedmulti(2,@0/**,@1/**))", + ["[6738736c/48'/0'/0'/2']xpub6FC1fXFP1GXLX5TKtcjHGT4q89SDRehkQLtbKJ2PzWcvbBHtyDsJPLtpLtkGqYNYZdVVAjRQ5kug9CsapegmmeRutpP7PW4u4wVF9JfkDhw", + "[b2b1f0cf/48'/0'/0'/2']xpub6EWhjpPa6FqrcaPBuGBZRJVjzGJ1ZsMygRF26RwN932Vfkn1gyCiTbECVitBjRCkexEvetLdiqzTcYimmzYxyR1BZ79KNevgt61PDcukmC7"], + "wsh(sortedmulti(2,[6738736c/48'/0'/0'/2']xpub6FC1fXFP1GXLX5TKtcjHGT4q89SDRehkQLtbKJ2PzWcvbBHtyDsJPLtpLtkGqYNYZdVVAjRQ5kug9CsapegmmeRutpP7PW4u4wVF9JfkDhw/<0;1>/*,[b2b1f0cf/48'/0'/0'/2']xpub6EWhjpPa6FqrcaPBuGBZRJVjzGJ1ZsMygRF26RwN932Vfkn1gyCiTbECVitBjRCkexEvetLdiqzTcYimmzYxyR1BZ79KNevgt61PDcukmC7/<0;1>/*))", + ), + ( + "wsh(thresh(3,pk(@0/**),s:pk(@1/**),s:pk(@2/**),sln:older(12960)))", + ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", + "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", + "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2"], + "wsh(thresh(3,pk([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*),s:pk([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js/<0;1>/*),s:pk([a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2/<0;1>/*),sln:older(12960)))", + ), + ( + "wsh(or_d(pk(@0/**),and_v(v:multi(2,@1/**,@2/**,@3/**),older(65535))))", + ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", + "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", + "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2", + "[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ"], + "wsh(or_d(pk([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*),and_v(v:multi(2,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js/<0;1>/*,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2/<0;1>/*,[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ/<0;1>/*),older(65535))))", + ), + ( + "tr(@0/**,{sortedmulti_a(1,@0/<2;3>/*,@1/**),or_b(pk(@2/**),s:pk(@3/**))})", + ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", + "xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K", + "xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM", + "xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6"], + "tr([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*,{sortedmulti_a(1,[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})", + ), + # ( + # "tr(musig(@0,@1,@2)/**,{and_v(v:pk(musig(@0,@1)/**),older(12960)),{and_v(v:pk(musig(@0,@2)/**),older(12960)),and_v(v:pk(musig(@1,@2)/**),older(12960))}})", + # ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", + # "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", + # "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2"], + # "tr(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*,{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js)/<0;1>/*),older(12960)),{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960)),and_v(v:pk(musig([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960))}})", + # ), +] + +invalid = [ + ( + # Key placeholder with no path following it + "key derivation missing", + "pkh(@0)", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # Key placeholder with an explicit path present + "need multipath", + "pkh(@0/0/**)", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # Key placeholders out of order + "Out of order", + "sh(multi(1,@1/**,@0/**))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg", + "[6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb"], + ), + ( + # Skipped key placeholder @1 + "Out of order", + "sh(multi(1,@0/**,@2/**))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg", + "[6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb"], + ), + ( + # Repeated keys with the same path expression + "Insane", + "sh(multi(1,@0/**,@0/**))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # Non-disjoint multipath expressions (@0/1/* appears twice) + "Non-disjoint multipath", + "sh(multi(1,@0/<0;1>/*,@0/<1;2>/*))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # Taproot non-disjoint multipath expressions (@0/1/* appears twice in tapscript) + "Non-disjoint multipath", + "tr(@0/<5;6>/*,multi_a(1,@0/<0;1>/*,@0/<1;2>/*))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # Taproot non-disjoint multipath expressions (@0/1/* appears twice as internal key and tapscript key) + "Non-disjoint multipath", + "tr(@0/<0;1>/*,multi_a(1,@0/<5;6>/*,@0/<1;2>/*))", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ), + ( + # solved cardinality > 2 + "Solved cardinality > 2", + "pkh(@0/<0;1;2>/*)", + ["[0f056943/99h/0h/0h]xpub6DMjVrmtVxXyn5hBuLScBtHeQ9X3ws6uasj7mWRu7ay7mQrX5suQKwYgNZBJYnWKugRk1KrgmTHtgwGvB7QcgELYCuacE3oA25SGMQZTiRg"], + ) +] + +import glob +from glob import settings +from descriptor import Descriptor +from wallet import MiniScriptWallet + +settings.set('chain', "BTC") + +# valid vectors +for policy, keys_info, desc in valid: + d = Descriptor.from_string(desc) + pol, ki = d.bip388_wallet_policy() + assert pol == policy, "\n" + pol + "\n" + policy + assert keys_info == keys_info + +# invalid vectors +for err, policy, keys_info in invalid: + glob.DESC_CACHE = {} + try: + msc = MiniScriptWallet.from_bip388_wallet_policy("random_name", policy, keys_info) + assert False, "succeeded, but must have failed!" + except BaseException as e: + if err not in str(e): + raise diff --git a/testing/devtest/unit_multisig.py b/testing/devtest/unit_multisig.py deleted file mode 100644 index 8270aa0b5..000000000 --- a/testing/devtest/unit_multisig.py +++ /dev/null @@ -1,61 +0,0 @@ -# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. -# -# unit test for address decoding for multisig -from h import a2b_hex, b2a_hex -from chains import BitcoinMain, BitcoinTestnet, BitcoinRegtest -from multisig import disassemble_multisig -from public_constants import AF_CLASSIC, AF_P2SH, AF_P2WPKH, AF_P2WSH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH -from public_constants import AFC_PUBKEY, AFC_SEGWIT, AFC_BECH32, AFC_SCRIPT, AFC_WRAPPED - -if 1: - - pk = a2b_hex('0202c68b0228cb577123c2f41275dadf8f4958890d3daf3728e38492f4913077dc') - script = a2b_hex('52210202c68b0228cb577123c2f41275dadf8f4958890d3daf3728e38492f4913077dc2102316dfd8d084a2b645061423013b52f513846e80c10816f66330f5609c8f6e7e221025328ece688cdc37d679b3af650f5d51487c1fe2fbd733b38cbfb58a9588a2155210288eb170b0661a6e86d1f1ab53a1099970d1b4d4cdd44d503d926effeec1e20842102fc3285261cccf4e7a44219758ee0383d25133b19a9fa14441ecb6ce9f3a4a52821038d00b6b4752dbba6afe6dcc00ef4b1fb0c212695f28a7908256808c2c201c43521038d5bcc32c89e363d181a08eb1c7613c0ba9aa02643d04cf00ae2cfea4192c9722103a11fd11e66e3d50818e3826a9b157245e6b361e32db9036768b54b4bc09adf092103d357b96bf98bcd5705d0f4745c2557d452d46a7cb9a6b193521de4516790f1182103f5bf5e00104c8956127ff926c0c5dd74690f8e67a21898cecb256dda34428a795aae') - - M, N, pubkeys = disassemble_multisig(script) - - assert M == 2 - assert N == 10 - assert pubkeys[0] == pk - -# assert keys == ['mpsMLTNqBNrsQuYNmZPj7ifqqMTSnZMMWH', 'mjoj9a1cFNPhvFkbrwzNPTBCWxhteAJHE5', -# 'mkjqteuKMDApEzsZbdphtufvVPmCFafLhM', 'mvRSS7xmYBjDUEQsxvNefXLbwQHpwm76wb', -# 'mhGBcrA9xDuBWttQLZFGRBJHcGEZyQpT3b', 'mkYFhxXQY6mMZbKxcuk6j6FD2Ff1gX6zgC', -# 'mmgkFCdHKxCHuTMcJ9CPncRMA2UPainW6j', 'mg5fNCy7TJiZ8L4uxU3XerW2twNYAY3hmU', -# 'myY1Xmhx6CdvFn6uzdUDo5EM2HxmsPXPJB', 'mozpwp3z32g9vBZxbpN6ySxx7A5EWw4Zfi'] - - addr = BitcoinMain.p2sh_address(AF_P2SH, script) - assert addr[0] == '3' - assert addr == '3Kt6KxjirrFS7GexJiXLLhmuaMzSbjp275' - - addr = BitcoinTestnet.p2sh_address(AF_P2SH, script) - assert addr[0] == '2' - assert addr == '2NBSJPhfkUJknK4HVyr9CxemAniCcRfhqp4' - - addr = BitcoinRegtest.p2sh_address(AF_P2SH, script) - assert addr[0] == '2' - assert addr == '2NBSJPhfkUJknK4HVyr9CxemAniCcRfhqp4' - - addr = BitcoinMain.p2sh_address(AF_P2WSH, script) - assert addr[0:4] == 'bc1q', addr - assert len(addr) >= 62 - assert addr == 'bc1qnjw7wy4e9tf4kkqaf43n2cyjwug0ystugum08c5j5hwhfncc4mkqftu4jr' - - addr = BitcoinTestnet.p2sh_address(AF_P2WSH, script) - assert addr[0:4] == 'tb1q', addr - assert len(addr) >= 62 - assert addr == 'tb1qnjw7wy4e9tf4kkqaf43n2cyjwug0ystugum08c5j5hwhfncc4mkq7r26gv' - - addr = BitcoinRegtest.p2sh_address(AF_P2WSH, script) - print(addr) - assert addr[0:6] == 'bcrt1q', addr - assert len(addr) >= 64 - assert addr == 'bcrt1qnjw7wy4e9tf4kkqaf43n2cyjwug0ystugum08c5j5hwhfncc4mkqn6quak' - - -if 1: - from utils import xfp2str, str2xfp - - assert xfp2str(0x10203040) == '40302010' - for i in 0, 1, 0x12345678: - assert str2xfp(xfp2str(i)) == i diff --git a/testing/devtest/unit_script.py b/testing/devtest/unit_script.py new file mode 100644 index 000000000..a5af2512c --- /dev/null +++ b/testing/devtest/unit_script.py @@ -0,0 +1,53 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +from uio import BytesIO +from serializations import ser_push_data, ser_string_vector, deser_string_vector +from serializations import ser_compact_size, deser_compact_size, disassemble + +test_data = [ + # data, result + (55*b"a", b'7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + (75*b"a", b'Kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + (76*b"a", b'LLaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + (77*b"a", b'LMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + (254*b"a", b'L\xfe' + (254 * b"a")), + (255*b"a", b'L\xff' + (255 * b"a")), + (256*b"a", b'M\x00\x01' + (256 * b"a")), + (500*b"a", b'M\xf4\x01' + (500 * b"a")), + (65535*b"a", b'M\xff\xff' + (65535 * b"a")), +] + +for i, (data, result) in enumerate(test_data): + assert ser_push_data(data) == result, i + d, _ = list(disassemble(result))[0] + assert d == data + +try: + # PUSHDATA 4 not implemented + ser_push_data(65536 * b"a") + raise RuntimeError +except AssertionError: pass + +# test serialization/deserialization +# all M/N combinations +V = range(1, 16) +for i, v1 in enumerate(V): + for j in range(i+1, len(V)): + M, N = v1, V[j] + # number of pubkeys times 1 pushdata + 33 pubkey = 34 * N + # +1 M + # +1 N + # +1 OP_CHECKMULTISIG + ms_script_len = (34 * N) + 1 + 1 + 1 + vec = [b"\x00"] + (M * [71*b"s"]) + [ms_script_len*b"w"] + assert vec == deser_string_vector(BytesIO(ser_string_vector(vec))) + + +for i in [253, 0x10000, 0x100000000]: + for j in [-1, 0, 1]: + num = i + j + x = ser_compact_size(num) + + assert num == deser_compact_size(BytesIO(x)) + +# EOF diff --git a/testing/devtest/wipe_miniscript.py b/testing/devtest/wipe_miniscript.py new file mode 100644 index 000000000..4fa8e646b --- /dev/null +++ b/testing/devtest/wipe_miniscript.py @@ -0,0 +1,13 @@ +# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# quickly clear all miniscript wallets installed +from glob import settings +from ux import restore_menu + +if settings.get('miniscript'): + del settings.current['miniscript'] + settings.save() + + print("cleared miniscript") + +restore_menu() \ No newline at end of file diff --git a/testing/devtest/wipe_ms.py b/testing/devtest/wipe_ms.py deleted file mode 100644 index 293b6125e..000000000 --- a/testing/devtest/wipe_ms.py +++ /dev/null @@ -1,13 +0,0 @@ -# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. -# -# quickly clear all multisig wallets installed -from glob import settings -from ux import restore_menu - -if settings.get('multisig'): - del settings.current['multisig'] - settings.save() - - print("cleared multisigs") - -restore_menu() diff --git a/testing/helpers.py b/testing/helpers.py index 2e76e7f25..578fb34b6 100644 --- a/testing/helpers.py +++ b/testing/helpers.py @@ -24,13 +24,15 @@ def prandom(count): return bytes(random.randint(0, 255) for i in range(count)) def taptweak(internal_key, tweak=None): - tweak = internal_key if tweak is None else internal_key + tweak assert len(internal_key) == 32, "not xonly-pubkey (len!=32)" + if tweak is not None: + assert len(tweak) == 32, "tweak (len!=32)" + tweak = internal_key if tweak is None else internal_key + tweak xonly_pubkey = xonly_pubkey_parse(internal_key) tweak = tagged_sha256(b"TapTweak", tweak) tweaked_pubkey = xonly_pubkey_tweak_add(xonly_pubkey, tweak) - tweaked_xonnly_pubkey, parity = xonly_pubkey_from_pubkey(tweaked_pubkey) - return xonly_pubkey_serialize(tweaked_xonnly_pubkey) + tweaked_xonly_pubkey, parity = xonly_pubkey_from_pubkey(tweaked_pubkey) + return xonly_pubkey_serialize(tweaked_xonly_pubkey) def fake_dest_addr(style='p2pkh'): # Make a plausible output address, but it's random garbage. Cant use for change outs @@ -43,7 +45,7 @@ def fake_dest_addr(style='p2pkh'): if style == 'p2wsh': return bytes([0, 32]) + prandom(32) - if style in ['p2sh', 'p2wsh-p2sh', 'p2wpkh-p2sh']: + if style in ['p2sh', 'p2wsh-p2sh', 'p2sh-p2wsh', 'p2wpkh-p2sh', 'p2sh-p2wpkh']: # all equally bogus P2SH outputs return bytes([0xa9, 0x14]) + prandom(20) + bytes([0x87]) @@ -77,7 +79,7 @@ def make_change_addr(wallet, style): is_segwit = False elif style == 'p2wpkh': redeem_scr = bytes([0, 20]) + target - elif style == 'p2wpkh-p2sh': + elif style in ('p2wpkh-p2sh', 'p2sh-p2wpkh'): redeem_scr = bytes([0, 20]) + target actual_scr = bytes([0xa9, 0x14]) + hash160(redeem_scr) + bytes([0x87]) elif style == 'p2tr': @@ -101,8 +103,16 @@ def xfp2str(xfp): from struct import pack return b2a_hex(pack(' 3 @@ -111,8 +121,12 @@ def parse_change_back(story): assert 'address' in lines[s+2] addrs = [] for y in range(s+3, len(lines)): - if not lines[y]: break - addrs.append(lines[y]) + line = lines[y].strip() + if line: + if line[0] == "\x02": + addrs.append(addr_from_display_format(line)) + if line.startswith(("3","2","1","m","n","tb1","bc1","bcrt")): + addrs.append(line) if len(addrs) >= 2: assert 'to addresses' in lines[s+2] @@ -211,4 +225,15 @@ def seconds2human_readable(s): return " ".join(msg) +def bitcoind_addr_fmt(script_type): + if script_type == "p2wsh": + addr_type = "bech32" + elif script_type == "p2sh": + addr_type = "legacy" + else: + assert script_type == "p2sh-p2wsh" + addr_type = "p2sh-segwit" + + return addr_type + # EOF diff --git a/testing/login_settings_tests.py b/testing/login_settings_tests.py index 708edd8fb..1df5e932e 100644 --- a/testing/login_settings_tests.py +++ b/testing/login_settings_tests.py @@ -10,8 +10,8 @@ # import pytest, time, pdb from core_fixtures import _pick_menu_item, _cap_menu, _cap_story, _cap_screen -from core_fixtures import _need_keypress, _enter_complex, _press_select -from ckcc_protocol.client import ColdcardDevice, CKCC_SIMULATOR_PATH +from core_fixtures import _need_keypress, _enter_complex, _press_select, _press_cancel +from ckcc_protocol.client import ColdcardDevice from run_sim_tests import ColdcardSimulator, clean_sim_data @@ -138,7 +138,7 @@ def test_set_nickname(nick, request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"] if is_Q else []) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -146,11 +146,12 @@ def test_set_nickname(nick, request): _set_nickname(device, is_Q, nick) time.sleep(1) sim.stop() # power off + device.close() # new simulator instance - but should get us directly to the last used settings sim = ColdcardSimulator(args= ["--q1" if is_Q else "", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) scr = _cap_screen(device) target = "".join(scr.strip().split("\n")) @@ -160,6 +161,7 @@ def test_set_nickname(nick, request): nick = nick.replace(" " * 4, " " * 2) # max two spaces in sequence (Mk4) assert nick == target sim.stop() + device.close() def test_randomize_pin_keys(request): @@ -167,7 +169,7 @@ def test_randomize_pin_keys(request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"] if is_Q else []) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -175,14 +177,16 @@ def test_randomize_pin_keys(request): time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _login(device, is_Q, "22-22", scrambled=True) time.sleep(3) m = _cap_menu(device) assert "Ready To Sign" in m sim.stop() + device.close() @pytest.mark.parametrize("lcdwn", [" 5 minutes", "15 minutes"]) def test_login_countdown(lcdwn, request): @@ -190,7 +194,7 @@ def test_login_countdown(lcdwn, request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"] if is_Q else []) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -198,9 +202,10 @@ def test_login_countdown(lcdwn, request): time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) secs = int(lcdwn.strip().split()[0]) _login(device, is_Q, "22-22") time.sleep(.15) @@ -214,6 +219,7 @@ def test_login_countdown(lcdwn, request): m = _cap_menu(device) assert "Ready To Sign" in m sim.stop() + device.close() @pytest.mark.parametrize("kbtn", [("A", "1"), ("/", "9")]) @pytest.mark.parametrize("when", [True, False]) @@ -223,7 +229,7 @@ def test_kill_key(kbtn, when, request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"] if is_Q else []) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -231,9 +237,10 @@ def test_kill_key(kbtn, when, request): time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args= ["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) if is_Q: possible_kbtn = [chr(65 + i) for i in range(26)] + [i for i in '\',./'] @@ -271,6 +278,7 @@ def test_kill_key(kbtn, when, request): with pytest.raises(Exception): _press_select(device, is_Q, timeout=1000) sim.stop() + device.close() def test_terms_ok(request): @@ -278,7 +286,7 @@ def test_terms_ok(request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--early-usb", "-w", "--q1" if is_Q else ""]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) time.sleep(.1) _, story = _cap_story(device) @@ -315,14 +323,16 @@ def test_terms_ok(request): _login(device, is_Q, "22-22") time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args=["-l", "--q1" if is_Q else "", "--early-usb", "--pin", "22-22"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _login(device, is_Q, "22-22") time.sleep(3) m = _cap_menu(device) assert "New Seed Words" in m sim.stop() + device.close() @pytest.mark.parametrize("brick", [True, False]) @@ -331,7 +341,7 @@ def test_wrong_pin_input(request, brick): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--early-usb", "--q1" if is_Q else "", "--pin", "22-22"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) time.sleep(.1) num_attmeptss = 13 for ii, i in enumerate(range(31, 43), start=1): @@ -368,7 +378,10 @@ def test_wrong_pin_input(request, brick): assert "After 13 failed PIN attempts this Coldcard is locked forever" in story assert "no way to reset or recover the secure element" in story assert "forever inaccessible" in story - assert "Restore your seed words onto a new Coldcard" in story + if is_Q: + assert "Calculator mode starts now." in story + else: + assert "Restore your seed words onto a new Coldcard" in story else: _login(device, is_Q, "22-22", num_failed=12) time.sleep(.5) @@ -380,6 +393,7 @@ def test_wrong_pin_input(request, brick): assert "Ready To Sign" in m sim.stop() + device.close() @pytest.mark.parametrize("nick", [None, "In trust we trust NOT"]) @@ -394,7 +408,7 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"] if is_Q else []) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -414,9 +428,10 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill # at this point all is set - reboot and test time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) if nick: scr = _cap_screen(device) @@ -429,6 +444,7 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill with pytest.raises(Exception): _press_select(device, is_Q, timeout=1000) sim.stop() + device.close() return # done here else: # move on, nick there, continue to login @@ -441,12 +457,14 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill with pytest.raises(Exception): _press_select(device, is_Q, timeout=1000) sim.stop() + device.close() return # done here was_killed = _login(device, is_Q, "22-22", scrambled=randomize, mk4_kbtn=kill_btn if kill_when else None) if was_killed: sim.stop() + device.close() return if login_ctdwn: @@ -463,6 +481,7 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill with pytest.raises(Exception): _press_select(device, is_Q, timeout=1000) sim.stop() + device.close() return # done here # second login after countdown is done @@ -470,12 +489,14 @@ def test_login_integration(request, nick, randomize, login_ctdwn, kill_btn, kill mk4_kbtn=None if kill_when else kill_btn) if was_killed: sim.stop() + device.close() return time.sleep(3) m = _cap_menu(device) assert "Ready To Sign" in m sim.stop() + device.close() def test_calc_login(request): is_Q = request.config.getoption('--Q') @@ -484,7 +505,7 @@ def test_calc_login(request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, is_Q, "Settings") _pick_menu_item(device, is_Q, "Login Settings") @@ -492,9 +513,10 @@ def test_calc_login(request): time.sleep(1) sim.stop() # power off + device.close() sim = ColdcardSimulator(args=["--q1", "--pin", "22-22", "--early-usb"]) sim.start(start_wait=6) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) scr = _cap_screen(device) assert 'ECC Calculator' in scr @@ -529,6 +551,302 @@ def entry(cmd, delay=.5): assert "Ready To Sign" in m sim.stop() + device.close() + +@pytest.mark.parametrize("word_check", [True, False]) +@pytest.mark.parametrize("randomize", [True, False]) +def test_sssp_bypass_pin(request, word_check, randomize): + main_pin = "22-22" + bypass_pin = "111-111" + is_Q = request.config.getoption('--Q') + clean_sim_data() # remove all from previous + sim = ColdcardSimulator(args=["--q1"] if is_Q else []) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + + if randomize: + _pick_menu_item(device, is_Q, "Settings") + _pick_menu_item(device, is_Q, "Login Settings") + _set_scramble_pin_entry(device, is_Q) + time.sleep(1) + + for _ in range(2): + _press_cancel(device, is_Q) + + time.sleep(.1) + + _pick_menu_item(device, is_Q, "Advanced/Tools") + _pick_menu_item(device, is_Q, "Spending Policy") + _pick_menu_item(device, is_Q, "Single-Signer") + _press_select(device, is_Q) # confirm story + # now create bypass PIN + # 1st entry + _login(device, is_Q, bypass_pin) + # 2nd confirmation entry + _login(device, is_Q, bypass_pin) + + if word_check: + _pick_menu_item(device, is_Q, "Word Check") + title, story = _cap_story(device) + assert "Enable?" in story + assert "must provide the first and last seed words" in story + _press_select(device, is_Q) + + time.sleep(2) # needed here to actually save to settings + sim.stop() + device.close() + + sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", main_pin, "--early-usb"]) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + + # first login, but with main PIN, ends up in SSSP + _login(device, is_Q, main_pin, scrambled=randomize) + time.sleep(.1) + menu = _cap_menu(device) + assert "Settings" not in menu + + sim.stop() + device.close() + + sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", main_pin, "--early-usb"]) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + + # now bypass PIN, normal operation + time.sleep(.1) + _login(device, is_Q, bypass_pin, scrambled=randomize) + time.sleep(.1) + title, story = _cap_story(device) + assert "Spending Policy Unlock" in story + _press_select(device, is_Q) + time.sleep(.1) + _login(device, is_Q, main_pin, scrambled=randomize) + time.sleep(.1) + + if word_check: + # first do incorrect words + if is_Q: + assert "First and Last Seed Word" in _cap_screen(device) + # just because of auto-fil feature + _enter_complex(device, is_Q, "wif") + _enter_complex(device, is_Q, "kic") + else: + # this is not a text input field - but word nest menu + # wife -> 3xUP, 3xDOWN, DOWN + for _ in range(3): + _need_keypress(device, "5") + _press_select(device, is_Q) + time.sleep(.1) + + for _ in range(3): + _need_keypress(device, "8") + _press_select(device, is_Q) + time.sleep(.1) + + _need_keypress(device, "8") + _press_select(device, is_Q) + + time.sleep(.1) + # abandon -> 3xOK + for _ in range(3): + _press_select(device, is_Q) + + time.sleep(.1) + title, story = _cap_story(device) + assert "Sorry, those words are incorrect" in story + _press_select(device, is_Q) + time.sleep(.1) + # now insert correct words + if is_Q: + # just because of auto-fil feature + _enter_complex(device, is_Q, "wif") + _enter_complex(device, is_Q, "clar") + else: + # wife -> 3xUP, 3xDOWN, DOWN + for _ in range(3): + _need_keypress(device, "5") + _press_select(device, is_Q) + time.sleep(.1) + + for _ in range(3): + _need_keypress(device, "8") + _press_select(device, is_Q) + time.sleep(.1) + + _need_keypress(device, "8") + _press_select(device, is_Q) + + # clarify 2xDOWN, 4xDOWN, 2xDOWN + for _ in range(2): + _need_keypress(device, "8") + _press_select(device, is_Q) + time.sleep(.1) + + for _ in range(4): + _need_keypress(device, "8") + _press_select(device, is_Q) + time.sleep(.1) + + for _ in range(2): + _need_keypress(device, "8") + _press_select(device, is_Q) + time.sleep(.1) + + menu = _cap_menu(device) + assert "Settings" in menu # not in SSSP + + sim.stop() + device.close() + + +def test_sssp_login_countdown(request): + bypass_pin = "236-156" + is_Q = request.config.getoption('--Q') + clean_sim_data() # remove all from previous + sim = ColdcardSimulator(args=["--q1"] if is_Q else []) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + + _pick_menu_item(device, is_Q, "Settings") + _pick_menu_item(device, is_Q, "Login Settings") + _set_login_countdown(device, is_Q, " 5 minutes") + + time.sleep(.2) + for _ in range(2): # go back + _press_cancel(device, is_Q) + + time.sleep(.1) + _pick_menu_item(device, is_Q, "Advanced/Tools") + _pick_menu_item(device, is_Q, "Spending Policy") + _pick_menu_item(device, is_Q, "Single-Signer") + _press_select(device, is_Q) # confirm story + # now create bypass PIN + # 1st entry + time.sleep(.1) + _login(device, is_Q, bypass_pin) + # 2nd confirmation entry + _login(device, is_Q, bypass_pin) + + time.sleep(2) + sim.stop() # power off + device.close() + + sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + secs = 5 + + _login(device, is_Q, bypass_pin) + time.sleep(.1) + title, story = _cap_story(device) + assert "Spending Policy Unlock" in story + _press_select(device, is_Q) + time.sleep(.1) + _login(device, is_Q, "22-22") + + time.sleep(.15) + scr = " ".join(_cap_screen(device).split("\n")) + assert "Login countdown in effect" in scr + assert "Must wait:" in scr + assert f"{secs}s" in scr + time.sleep(secs + 1) + _login(device, is_Q, "22-22") + time.sleep(3) + m = _cap_menu(device) + assert "Ready To Sign" in m + sim.stop() + device.close() + + +def test_sssp_trick_pins(request): + # only testing countdown TP + ct_pin = "89-89" + bypass_pin = "15-16" + is_Q = request.config.getoption('--Q') + clean_sim_data() # remove all from previous + sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + _login(device, is_Q, "22-22") + + _pick_menu_item(device, is_Q, "Settings") + _pick_menu_item(device, is_Q, "Login Settings") + _pick_menu_item(device, is_Q, "Trick PINs") + + # now countdown TP + _pick_menu_item(device, is_Q, "Add New Trick") + time.sleep(.1) + for ch in ct_pin[:2]: + _need_keypress(device, ch) + time.sleep(.1) + _press_select(device, is_Q) + + if not is_Q: + # anti-phishing words + _press_select(device, is_Q) + + for ch in ct_pin[-2:]: + _need_keypress(device, ch) + time.sleep(.1) + _press_select(device, is_Q) + + _pick_menu_item(device, is_Q, "Login Countdown") + _press_select(device, is_Q) + time.sleep(.1) + + _pick_menu_item(device, is_Q, "Just Countdown") + for _ in range(2): + _press_select(device, is_Q) + time.sleep(.1) + + # adjust countdown to lowest possible value + _pick_menu_item(device, is_Q, f'↳{ct_pin}') + _pick_menu_item(device, is_Q, '↳Countdown') + _need_keypress(device, "4") + _pick_menu_item(device, is_Q, " 5 minutes") + + for _ in range(10): + _press_cancel(device, is_Q) + + time.sleep(.1) + _pick_menu_item(device, is_Q, "Advanced/Tools") + _pick_menu_item(device, is_Q, "Spending Policy") + _pick_menu_item(device, is_Q, "Single-Signer") + _press_select(device, is_Q) # confirm story + # now create bypass PIN + # 1st entry + time.sleep(.1) + _login(device, is_Q, bypass_pin) + # 2nd confirmation entry + _login(device, is_Q, bypass_pin) + + time.sleep(2) + sim.stop() + device.close() + + sim = ColdcardSimulator(args=["--q1" if is_Q else "", "--pin", "22-22", "--early-usb"]) + sim.start(start_wait=6) + device = ColdcardDevice(is_simulator=True) + + _login(device, is_Q, bypass_pin) + time.sleep(.1) + title, story = _cap_story(device) + assert "Spending Policy Unlock" in story + _press_select(device, is_Q) + time.sleep(.1) + # try to log in with countdown TP instead of main + # send you directly into countdown + _login(device, is_Q, ct_pin) + time.sleep(.15) + scr = " ".join(_cap_screen(device).split("\n")) + assert "Login countdown in effect" in scr + assert "Must wait:" in scr + assert "5s" in scr + time.sleep(6) + + sim.stop() + device.close() # EOF diff --git a/testing/msg.py b/testing/msg.py index 442052e7a..e95945022 100644 --- a/testing/msg.py +++ b/testing/msg.py @@ -22,11 +22,12 @@ def parse_signed_message(msg): - msplit = msg.strip().split("\n") - assert msplit[0] == "-----BEGIN BITCOIN SIGNED MESSAGE-----" - assert msplit[2] == "-----BEGIN BITCOIN SIGNATURE-----" - assert msplit[5] == "-----END BITCOIN SIGNATURE-----" - return msplit[1], msplit[3], msplit[4] + msplit = msg.strip().rsplit("\n", 4) + assert msplit[0].startswith("-----BEGIN BITCOIN SIGNED MESSAGE-----\n") + msg = msplit[0].replace("-----BEGIN BITCOIN SIGNED MESSAGE-----\n", "") + assert msplit[1] == "-----BEGIN BITCOIN SIGNATURE-----" + assert msplit[4] == "-----END BITCOIN SIGNATURE-----" + return msg, msplit[2], msplit[3] def sig_hdr_base(addr_fmt): diff --git a/testing/psbt.py b/testing/psbt.py index 130356581..3c70e0666 100644 --- a/testing/psbt.py +++ b/testing/psbt.py @@ -123,6 +123,9 @@ def defaults(self): self.taproot_bip32_paths = {} self.taproot_internal_key = None self.taproot_key_sig = None + self.taproot_merkle_root = None + self.taproot_scripts = {} + self.taproot_script_sigs = {} self.redeem_script = None self.witness_script = None self.previous_txid = None # v2 @@ -135,7 +138,8 @@ def defaults(self): def __eq__(a, b): if a.sighash != b.sighash: - if a.sighash is not None and b.sighash is not None: + # no sighash == SIGHASH_ALL + if {a.sighash, b.sighash} != {None, 1}: return False rv = a.utxo == b.utxo and \ @@ -147,6 +151,9 @@ def __eq__(a, b): a.taproot_key_sig == b.taproot_key_sig and \ a.taproot_bip32_paths == b.taproot_bip32_paths and \ a.taproot_internal_key == b.taproot_internal_key and \ + a.taproot_merkle_root == b.taproot_merkle_root and \ + a.taproot_scripts == b.taproot_scripts and \ + a.taproot_script_sigs == b.taproot_script_sigs and \ sorted(a.part_sigs.keys()) == sorted(b.part_sigs.keys()) and \ a.previous_txid == b.previous_txid and \ a.prevout_idx == b.prevout_idx and \ @@ -189,7 +196,7 @@ def parse_kv(self, kt, key, val): self.others[kt] = val elif kt == PSBT_IN_TAP_BIP32_DERIVATION: self.taproot_bip32_paths[key] = val - elif kt == PSBT_OUT_TAP_INTERNAL_KEY: + elif kt == PSBT_IN_TAP_INTERNAL_KEY: self.taproot_internal_key = val elif kt == PSBT_IN_TAP_KEY_SIG: self.taproot_key_sig = val @@ -203,6 +210,21 @@ def parse_kv(self, kt, key, val): self.req_time_locktime = struct.unpack(" 32, "PSBT_IN_TAP_LEAF_SCRIPT control block is too short" + assert (len(key) - 1) % 32 == 0, "PSBT_IN_TAP_LEAF_SCRIPT control block is not valid" + assert len(val) != 0, "PSBT_IN_TAP_LEAF_SCRIPT cannot be empty" + leaf_script = (val[:-1], int(val[-1])) + if leaf_script not in self.taproot_scripts: + self.taproot_scripts[leaf_script] = set() + self.taproot_scripts[leaf_script].add(key) + elif kt == PSBT_IN_TAP_MERKLE_ROOT: + self.taproot_merkle_root = val else: self.unknown[bytes([kt]) + key] = val @@ -236,6 +258,16 @@ def serialize_kvs(self, wr, v2): if self.taproot_key_sig: wr(PSBT_IN_TAP_KEY_SIG, self.taproot_key_sig) + if self.taproot_merkle_root: + wr(PSBT_IN_TAP_MERKLE_ROOT, self.taproot_merkle_root) + if self.taproot_scripts: + for (script, leaf_ver), control_blocks in self.taproot_scripts.items(): + for control_block in control_blocks: + wr(PSBT_IN_TAP_LEAF_SCRIPT, script + struct.pack("B", leaf_ver), control_block) + if self.taproot_script_sigs: + for (xonly, leaf_hash), sig in self.taproot_script_sigs.items(): + wr(PSBT_IN_TAP_SCRIPT_SIG, sig, xonly + leaf_hash) + if v2: if self.previous_txid is not None: wr(PSBT_IN_PREVIOUS_TXID, self.previous_txid) @@ -267,6 +299,7 @@ def defaults(self): self.bip32_paths = {} self.taproot_bip32_paths = {} self.taproot_internal_key = None + self.taproot_tree = None self.script = None # v2 self.amount = None # v2 self.proprietary = {} @@ -282,6 +315,7 @@ def __eq__(a, b): a.taproot_bip32_paths == b.taproot_bip32_paths and \ a.taproot_internal_key == b.taproot_internal_key and \ a.proprietary == b.proprietary and \ + a.taproot_tree == b.taproot_tree and \ a.unknown == b.unknown def parse_kv(self, kt, key, val): @@ -297,6 +331,18 @@ def parse_kv(self, kt, key, val): self.taproot_bip32_paths[key] = val elif kt == PSBT_OUT_TAP_INTERNAL_KEY: self.taproot_internal_key = val + elif kt == PSBT_OUT_TAP_TREE: + res = [] + reader = io.BytesIO(val) + while True: + depth = reader.read(1) + if not depth: + break + leaf_version = reader.read(1)[0] + script_len = deser_compact_size(reader) + script = reader.read(script_len) + res.append((depth[0], leaf_version, script)) + self.taproot_tree = res elif kt == PSBT_OUT_SCRIPT: self.script = val elif kt == PSBT_OUT_AMOUNT: @@ -319,6 +365,11 @@ def serialize_kvs(self, wr, v2): wr(PSBT_OUT_TAP_BIP32_DERIVATION, self.taproot_bip32_paths[k], k) if self.taproot_internal_key: wr(PSBT_OUT_TAP_INTERNAL_KEY, self.taproot_internal_key) + if self.taproot_tree: + res = b'' + for depth, leaf_version, script in self.taproot_tree: + res += bytes([depth, leaf_version]) + ser_compact_size(len(script)) + script + wr(PSBT_OUT_TAP_TREE, res) if v2 and self.script is not None: wr(PSBT_OUT_SCRIPT, self.script) if v2 and self.amount is not None: diff --git a/testing/pytest.ini b/testing/pytest.ini index 5c80ecdd6..a56b54e9d 100644 --- a/testing/pytest.ini +++ b/testing/pytest.ini @@ -1,7 +1,7 @@ [pytest] -addopts = -vvx --disable-warnings +;addopts = -vvx --disable-warnings # you need to comment above and uncomment below to use run_sim_tests.py -#addopts = -vv --disable-warnings +addopts = -vv --disable-warnings markers = bitcoind: indicates local bitcoind (testnet) will be needed onetime: test cant be combined with any others, likely needs board reset diff --git a/testing/requirements.txt b/testing/requirements.txt index e0e1a6ff4..920c4b4b0 100644 --- a/testing/requirements.txt +++ b/testing/requirements.txt @@ -23,3 +23,5 @@ git+https://github.com/coinkite/bsms-bitcoin-secure-multisig-setup.git@master#eg # BBQr library git+https://github.com/coinkite/BBQr.git@master#egg=bbqr&subdirectory=python +# for backend testing +requests==2.32.4 diff --git a/testing/run_sim_tests.py b/testing/run_sim_tests.py index d5fb9797e..0cf8d5a57 100644 --- a/testing/run_sim_tests.py +++ b/testing/run_sim_tests.py @@ -30,16 +30,82 @@ python run_sim_tests.py --collect manual # just print all manual tests to stdout Make sure to run manual test if you want to state that your changes passed all the tests. + +Testing on multiple simulators in parallel + +python run_sim_tests.py --q1 --multiproc # to run all Q tests in parallel (default num-proc=14 simulators) +python run_sim_tests.py --multiproc --num-proc 6 # to run all Mk4 tests in parallel max 6 simulators at once +python run_sim_tests.py -m test_addr.py -m test_bbqr.py --multiproc # just desired test +python run_sim_tests.py --q1 -m test_sign.py --multiproc # just desired test +python run_sim_tests --multiproc --turbo # turbo causes both Mk4 & Q tests to run simultaneously (turbo doubles num-procs) +python run_sim_tests --multiproc --turbo # all Mk4 & Q tests run in 60 minutes total!! +python run_sim_tests --multiproc --turbo -m test_addr.py -m test_ux.py # will spawn 4 simulators: one Q and one Mk4 for address tests & one Q and one Mk4 for ux tests + +Console output has some useful info: +* when job is started it will print its PID +* when job is done you'll get elapsed time from start (test duration) +* when all is done - complete test session duration + +``` +$ python run_sim_tests.py -m test_addr.py -m test_drv_entro.py -m test_usb.py --multiproc --turbo +started: Mk4 test_addr.py 38824 +started: Q test_addr.py 38935 +started: Mk4 test_drv_entro.py 39042 +started: Q test_drv_entro.py 39150 +started: Mk4 test_usb.py 39257 +started: Q test_usb.py 39364 +done: Mk4 test_usb.py 0:00:06.043072 +done: Q test_usb.py 0:00:06.081147 +done: Mk4 test_addr.py 0:00:51.141250 +done: Q test_addr.py 0:01:03.185571 +done: Mk4 test_drv_entro.py 0:03:24.234521 +done: Q test_drv_entro.py 0:03:30.278795 + + +elapsed: 0:03:50.308146 +``` + +After jobs are finished, or even during execution you can inspect `/tmp/cc-simulators` directory: +* contains simulator work directories named as of specific simulator +* log directories where pytest output is piped + * mk4_logs + * q1_logs + +``` +$ pwd +/tmp/cc-simulators +$ ls +38824 38935 39042 39150 39257 39364 mk4_logs q1_logs +$ ls 39042/* +39042/debug: +last-qr.png + +39042/MicroSD: +drv-hex-idx0-2.txt drv-pw-idx0.txt drv-words-idx0-2.txt drv-words-idx0.txt +drv-hex-idx0.txt drv-wif-idx0.txt drv-words-idx0-3.txt drv-xprv-idx0.txt + +39042/settings: + +39042/VirtDisk: +README.md +$ ls mk4_logs/ +test_addr.py.log test_drv_entro.py.log test_usb.py.log +``` + +To parse only failures use below cmd in {mk4,q1}_logs directory: +``` +for f in $(ls); do x=`grep -n "short test summary info" $f | grep -Eo '^[^:]+'`; if [ -n "$x" ];then tail -n +"$x" $f | grep -E '^FAILED|^ERROR';fi ;done +``` """ -import os, time, glob, json, pytest, atexit, signal, argparse, subprocess, contextlib +import os, time, glob, json, pytest, atexit, signal, argparse, subprocess, contextlib, shutil +from datetime import timedelta from typing import List - from pytest import ExitCode SIM_INIT_WAIT = 2 # 2 seconds, can be tweaked via cmdline arguments ( -w 6 ) - +DEFAULT_PYTEST_MARKS = "not onetime and not veryslow and not manual" @contextlib.contextmanager def pushd(new_dir): @@ -50,13 +116,17 @@ def pushd(new_dir): finally: os.chdir(previous_dir) +def clean_directory(pth): + for root, dirs, files in os.walk(pth): + for f in files: + os.unlink(os.path.join(root, f)) + for d in dirs: + shutil.rmtree(os.path.join(root, d)) -def remove_client_sockets(): +def remove_all_client_sockets(): with pushd("/tmp"): for fn in glob.glob("ckcc-client*.sock"): os.remove(fn) - print("Removed all client sockets") - def remove_cautious(fpath: str) -> None: if os.path.basename(fpath) in ["README.md", ".gitignore"]: @@ -99,7 +169,7 @@ def is_ok(ec: ExitCode) -> bool: def _run_pytest_tests(test_module: str, pytest_marks: str, pytest_k: str, pdb: bool, - failed_first: bool, psbt2=False, is_Q=False, headless=False) -> ExitCode: + failed_first: bool, psbt2=False, is_Q=False, headless=False, sim_socket=None) -> ExitCode: cmd_list = [ "--cache-clear", "-m", pytest_marks, "--sim", test_module if test_module is not None else "" @@ -116,42 +186,50 @@ def _run_pytest_tests(test_module: str, pytest_marks: str, pytest_k: str, pdb: b cmd_list.insert(0, "--Q") # only changes behavior in login_settings_test if headless: cmd_list.append("--headless") + if sim_socket: + cmd_list.append("--sim-socket") + cmd_list.append(sim_socket) return pytest.main(cmd_list) -def _run_coldcard_tests(test_module: str, simulator_args: List[str], pytest_marks: str, +def _run_coldcard_tests(test_module: str, simulator_args: List[str], pytest_k: str, pdb: bool, failed_first: bool, psbt2=False, - is_Q=False, headless=False) -> ExitCode: + is_Q=False, headless=False, pytest_marks: str = DEFAULT_PYTEST_MARKS, + sim_segregate=False) -> ExitCode: + sock_path = None if simulator_args is not None: - sim = ColdcardSimulator(args=simulator_args, headless=headless) + sim = ColdcardSimulator(args=simulator_args, headless=headless, segregate=sim_segregate) sim.start() time.sleep(1) + sock_path = sim.socket exit_code = _run_pytest_tests(test_module, pytest_marks, pytest_k, pdb, - failed_first, psbt2, is_Q, headless) + failed_first, psbt2, is_Q, headless, sock_path) if simulator_args is not None: sim.stop() time.sleep(1) clean_sim_data() + remove_all_client_sockets() + return exit_code def run_coldcard_tests(test_module=None, simulator_args=None, pytest_k=None, pdb=False, failed_first=False, psbt2=False, is_Q=False, headless=False, - pytest_marks="not onetime and not veryslow and not manual"): + pytest_marks=DEFAULT_PYTEST_MARKS): failed = [] - exit_code = _run_coldcard_tests(test_module, simulator_args, pytest_marks, pytest_k, - pdb, failed_first, psbt2, is_Q, headless) + exit_code = _run_coldcard_tests(test_module, simulator_args, pytest_k, + pdb, failed_first, psbt2, is_Q, headless, pytest_marks) if not is_ok(exit_code): # no success, no nothing - give failed another try, each alone with its own simulator last_failed = get_last_failed() print("Running failed from last run", last_failed) exit_codes = [] for failed_test in last_failed: - exit_code_2 = _run_coldcard_tests(failed_test, simulator_args, pytest_marks, + exit_code_2 = _run_coldcard_tests(failed_test, simulator_args, pytest_k, pdb, failed_first, psbt2, is_Q, - headless) + headless, pytest_marks) exit_codes.append(exit_code_2) if not is_ok(exit_code_2): failed.append(failed_test) @@ -173,11 +251,12 @@ def pytest_collection_modifyitems(self, items): class ColdcardSimulator: - def __init__(self, path=None, args=None, headless=False): + def __init__(self,args=None, headless=False, segregate=False): self.proc = None self.args = args - self.path = "/tmp/ckcc-simulator.sock" if path is None else path self.headless = headless + self.segregate = segregate + self.socket = "/tmp/ckcc-simulator.sock" def start(self, start_wait=None): # here we are in testing directory @@ -188,14 +267,20 @@ def start(self, start_wait=None): cmd_list.extend(self.args) if self.headless: cmd_list.append("--headless") + if self.segregate: + cmd_list.append("--segregate") self.proc = subprocess.Popen( cmd_list, # this needs to be in firmware/unix - expected to be run from firmware/testing cwd="../unix", - preexec_fn=os.setsid + preexec_fn=os.setsid, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, ) time.sleep(start_wait or SIM_INIT_WAIT) + if self.segregate: + self.socket = "/tmp/ckcc-simulator-%d.sock" % self.proc.pid atexit.register(self.stop) def stop(self): @@ -205,7 +290,6 @@ def stop(self): os.waitpid(os.getpgid(self.proc.pid), 0) atexit.unregister(self.stop) - remove_client_sockets() def main(): @@ -233,6 +317,12 @@ def main(): help="only run tests which match the given substring expression") parser.add_argument("--headless", action="store_true", default=False, help="run simulator instance in headless mode") + parser.add_argument("--multiproc", action="store_true", default=False, + help="Run tests & simulators in parallel") + parser.add_argument("--num-proc", type=int, default=16, + help="How many executors/simulators to run in parallel in --multiproc mode") + parser.add_argument("--turbo", action="store_true", default=False, + help="Both Mk4 and Q at the same time") args = parser.parse_args() if args.sim_init_wait: @@ -258,46 +348,171 @@ def main(): if args.module is None: test_modules = [] elif len(args.module) == 1 and args.module[0].lower() == "all": - test_modules = sorted(glob.glob("test_*.py")) + test_modules = glob.glob("test_*.py") assert test_modules, "please run in ../testing subdir" else: for fn in args.module: if not os.path.exists(fn): raise RuntimeError(f"{fn} does not exist") - test_modules = sorted(args.module) - - result = [] - for test_module in test_modules: - test_args = DEFAULT_SIMULATOR_ARGS - if test_module in ["test_rng.py", "test_pincodes.py", "test_rolls.py"]: - # test_pincodes.py can only be run against real device - # test_rng.py not needed when using simulator - # test_rolls.py should be run alone as it does not need simulator - print("Skipped", test_module) - continue - - print("Started", test_module) + test_modules = args.module + + # test_pincodes.py can only be run against real device + # test_rng.py not needed when using simulator + # test_rolls.py should be run alone as it does not need simulator + # set diff + test_modules = set(test_modules) - {"test_rng.py", "test_pincodes.py", "test_rolls.py", + "test_640_der_pth_migration.py", + "test_640_migration_name_clash.py", + "test_640_miniscript_migration.py", + "test_640_multisig_migration.py"} + + module_args = [] + for test_module in sorted(list(test_modules)): + sim_args = DEFAULT_SIMULATOR_ARGS if test_module in ["test_bsms.py", "test_address_explorer.py", "test_export.py", "test_multisig.py", "test_ux.py"]: - test_args = DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] + sim_args = DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] if test_module == "test_vdisk.py": - test_args = ["--eject"] + DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] + sim_args = ["--eject"] + DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] if test_module == "test_bip39pw.py": - test_args = [] - if test_module in ["test_unit.py", "test_se2.py", "test_backup.py"]: + sim_args = [] + if test_module in ["test_unit.py", "test_se2.py", "test_backup.py", "test_teleport.py", + "test_hobble.py", "test_sssp.py"]: # test_nvram_mk4 needs to run without --eff # se2 duress wallet activated as ephemeral seed requires proper `settings.load` - test_args = ["--set", "nfc=1"] - if test_module in ["test_ephemeral.py", "test_notes.py"]: - test_args = ["--set", "nfc=1", "--set", "vidsk=1"] - - if args.q1 and '--q1' not in test_args: - test_args.append('--q1') + sim_args = ["--set", "nfc=1"] + if test_module in ["test_ephemeral.py", "test_notes.py", "test_ccc.py"]: + # proper `settings.load` _ virtual disk + sim_args = ["--set", "nfc=1", "--set", "vidsk=1"] + + if args.q1 and '--q1' not in sim_args: + sim_args.append('--q1') + + module_args.append((test_module, sim_args, args.pytest_k, args.pdb, + args.ff, args.psbt2, args.q1, args.headless)) + + if args.multiproc: + start_time = time.time() + def add_to_queue(module_name, simulator_args, queue): + if module_name == "test_miniscript.py": + queue.append((2, [module_name, simulator_args, "not liana_miniscripts_simple and not test_tapscript and not test_bitcoind_tapscript_address and not test_minitapscript", ""])) + queue.append((0, [module_name, simulator_args, "liana_miniscripts_simple", "-sep1"])) + queue.append((2, [module_name, simulator_args, "test_tapscript", "-sep2"])) + queue.append((0, [module_name, simulator_args, "test_bitcoind_tapscript_address", "-sep3"])) + queue.append((0, [module_name, simulator_args, "test_minitapscript", "-sep4"])) + + elif module_name == "test_multisig.py": + # split takes too much time + queue.append((0, [module_name, simulator_args, "not tutorial and not airgapped and not ms_address and not descriptor_export", ""])) + queue.append((0, [module_name, simulator_args, "airgapped", "-sep1"])) + queue.append((0, [module_name, simulator_args, "tutorial", "-sep2"])) + queue.append((0, [module_name, simulator_args, "ms_address", "-sep3"])) + queue.append((0, [module_name, simulator_args, "descriptor_export", "-sep4"])) + + elif module_name == "test_seed_xor.py": + # split takes too much time + queue.append((0, [module_name, simulator_args, "test_import_xor", "-sep1"])) + queue.append((0, [module_name, simulator_args, "not test_import_xor", ""])) + + elif module_name in ["test_export.py", "test_ephemeral.py", "test_sign.py", "test_msg.py", + "test_backup.py", "test_bsms.py"]: + # higher priority + queue.append((1, [module_name, simulator_args, None, ""])) + + else: + # standard priority + queue.append((2, [module_name, simulator_args, None, ""])) + + # will clear everything there from previous runs + tmp_dir = "/tmp/cc-simulators" + clean_directory(tmp_dir) # clean it + mk4_log_dir = f"{tmp_dir}/mk4_logs" + q1_log_dir = f"{tmp_dir}/q1_logs" + os.makedirs(mk4_log_dir, exist_ok=True) + os.makedirs(q1_log_dir, exist_ok=True) + + q = [] # build priority queue + for mod_name, sim_args, *_ in module_args: + if args.turbo: + if "--q1" in sim_args: + add_to_queue(mod_name, sim_args, q) + add_to_queue(mod_name, [i for i in sim_args if i == "--q1"], q) + else: + add_to_queue(mod_name, sim_args, q) + add_to_queue(mod_name, sim_args + ["--q1"], q) + + else: + add_to_queue(mod_name, sim_args, q) + + # sort queue by priority, highest priority elements at the end + q = [i[1] for i in sorted(q, reverse=True)] + + num_proc = args.num_proc + if args.turbo: + # double num-proc + num_proc *= 2 + + procs = [] + while True: + # create as many processes as allowed by --num-proc (default=14) + if q and (len(procs) < num_proc): + # start simulators first + q_chunks = [] + for _ in range (num_proc - len(procs)): + try: + mn, sim_args, k, mod_add = q.pop() # remove element + except IndexError: + # priority queue is empty + break + sim = ColdcardSimulator(sim_args, segregate=True) + sim.start(start_wait=0) + ld = q1_log_dir if "--q1" in sim_args else mk4_log_dir + q_chunks.append((sim, mn, mod_add, k, ld)) + + time.sleep(5) + for sim, mn, mod_add, k, log_dir in q_chunks: + assert sim.socket + out_log_path = f"{log_dir}/%s.log" % (mn + mod_add) + out_fd = open(out_log_path, "w") + cmd_list = ["pytest", "--cache-clear", "-m", DEFAULT_PYTEST_MARKS, "--sim", + mn, "--sim-socket", sim.socket] + if k: + cmd_list.extend(["-k", k]) + p = subprocess.Popen(cmd_list, preexec_fn=os.setsid, stdout=out_fd, stderr=out_fd) + mark = "Q" if "q1" in log_dir else "Mk4" + procs.append((mn+mod_add, p, out_fd, sim, mark, time.time())) + print(f'started: {mark:<6}{mn+mod_add:<30}{sim.socket.split("-")[-1].split(".")[0]:<10}') + + if not procs and not q: + # done + break + + i = 0 + while i < len(procs): + mn, p, out_fd, sim, mark, st = procs[i] + if p.poll() is None: + # still running + i += 1 + continue + else: + # done + p.communicate() + out_fd.close() + sim.stop() + del procs[i] + print(f"done: {mark:<6}{mn:<30}{str(timedelta(seconds=time.time()-st)):<15}") + + time.sleep(3) + + # multiprocess done + print(f"\n\nelapsed: {str(timedelta(seconds=time.time()-start_time))}") + return - ec, failed_tests = run_coldcard_tests(test_module, simulator_args=test_args, - pytest_k=args.pytest_k, pdb=args.pdb, - failed_first=args.ff, psbt2=args.psbt2, - headless=args.headless) + result = [] + for arguments in module_args: + test_module = arguments[0] + print("Started", test_module) + ec, failed_tests = run_coldcard_tests(*arguments) result.append((test_module, ec, failed_tests)) print("Done", test_module) print(80 * "=") @@ -362,5 +577,8 @@ def main(): if __name__ == "__main__": main() - + # sim = ColdcardSimulator(args=["--eff", "--segregate"]) + # sim.start() + # import pdb;pdb.set_trace() + # x = 5 # EOF diff --git a/testing/seedless_tests.py b/testing/seedless_tests.py index 05555e5d5..4920bb848 100644 --- a/testing/seedless_tests.py +++ b/testing/seedless_tests.py @@ -3,9 +3,9 @@ import pytest, pdb, time, random, os from charcodes import KEY_CANCEL from core_fixtures import _pick_menu_item, _press_select -from core_fixtures import _need_keypress, _cap_screen, _sim_exec +from core_fixtures import _need_keypress, _sim_exec from run_sim_tests import ColdcardSimulator, clean_sim_data -from ckcc_protocol.client import ColdcardDevice, CKCC_SIMULATOR_PATH +from ckcc_protocol.client import ColdcardDevice def test_status_bar_rewrite_after_restore_master(request): @@ -13,7 +13,7 @@ def test_status_bar_rewrite_after_restore_master(request): clean_sim_data() # remove all from previous sim = ColdcardSimulator(args=["--q1", "-l"]) sim.start(start_wait=3) - device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH) + device = ColdcardDevice(is_simulator=True) _pick_menu_item(device, True, "Advanced/Tools") _pick_menu_item(device, True, "Temporary Seed") diff --git a/testing/teleport_cli.py b/testing/teleport_cli.py new file mode 100644 index 000000000..636310ce3 --- /dev/null +++ b/testing/teleport_cli.py @@ -0,0 +1,138 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Key Teleport protocol re-implementation: CLI for humans (testing purposes only). +# +import click, pyqrcode, json +from bbqr import split_qrs +from pysecp256k1.extrakeys import keypair_create, keypair_sec +from teleport_protocol import (receiver_step1, sender_step1, txt_grouper, stash_encode_secret, + stash_decode_secret, receiver_step2) + + +def show_payload(payload, type_code, title, msg): + vers, parts = split_qrs(payload, type_code, max_version=5) + qs = [pyqrcode.create(part, error='L', version=vers, mode='alphanumeric') + for part in parts] + + for q in qs: + click.echo(q.terminal()) + + click.echo("\nBBQr payload:") + for p in parts: + click.echo(p) + + click.echo() + click.echo(title) + click.echo(msg) + +def show_received(dtype, data): + if dtype == 's': + # words / bip 32 master / xprv, etc + noun, decoded = stash_decode_secret(data) + print(f"Received {noun} via teleport:\n", decoded) + + elif dtype == 'x': + # TODO seems can be removed + # it's an XPRV, but in binary.. some extra data we throw away here; sigh + # XXX no way to send this .. but was thinking of address explorer + raise NotImplementedError + + elif dtype == 'p': + # raw PSBT -- much bigger more complex + raise NotImplementedError + + elif dtype == 'b': + # full system backup, including master: text lines + print("Received backup via Teleport:\n") + for ln in data.decode().split('\n'): + if not ln: continue + print(ln) + + elif dtype == 'v': + # one key export from a seed vault + # - watch for incompatibility here if we ever change VaultEntry + print("Received Seed Vault entry via Teleport:\n", json.loads(data)) + elif dtype == 'n': + # import secure note(s) + print("Received secure note(s) via Teleport:\n", json.loads(data)) + else: + raise ValueError("Unknown type", dtype) + +@click.group() +def main(): + pass + +@main.command('recv_init') +@click.option('--secret', '-k', type=str, default=None, + help='Ephemeral private key used to create shared ECDH key') +def recv_init(secret): + number_pass, enc_pubkey, kp_receiver = receiver_step1(secret=secret) + msg = (f'To receive sensitive data from another COLDCARD,' + f'share this Receiver Password with sender:\n\t{number_pass}' + f' = {txt_grouper(number_pass)}') + + show_payload(enc_pubkey, "R", 'Key Teleport: Receive', msg) + if secret is None: + # if user haven't specified secret for ECDH keypair dump it to stdout + # it is needed as second arguemnt to "recv" cmd + click.echo("Picked ephemeral ECDH key: " + keypair_sec(kp_receiver).hex()) + + click.echo() + # encrypted pubkey payload is first argument to "send" cmd + click.echo("Encrypted pubkey (payload): " + enc_pubkey.hex()) + + +@main.command('send') +@click.argument('payload', type=str) +@click.option('--secret', '-k', type=str, default=None, + help='Ephemeral private key used to create shared ECDH key') +@click.option('--password', prompt=True, required=True) +@click.option('--mnemonic', type=str, default=None) +@click.option('--xprv', type=str, default=None) +@click.option('--text', type=str, default=None) +@click.option('--backup', type=click.Path(exists=True), default=None) +def send(payload, secret, password, mnemonic, xprv, text, backup): + if mnemonic: + cleartext = b"s" + stash_encode_secret(words=mnemonic) + elif xprv: + cleartext = b"s" + stash_encode_secret(xprv=xprv) + elif text: + cleartext = b"n" + json.dumps([{"title": "Quick Note", "misc":text}]).encode() + else: + assert backup + out = [] + with open(backup, "r") as f: # this needs to be cleartext backup + for ln in f.readlines(): + if not ln: continue + if ln[0] == '#': continue + out.append(ln.encode()) + + cleartext = b"b" + b'\n'.join(ln for ln in out) + + noid_txt, encrypted_payload, kp_sender, pk_rec = sender_step1( + password, bytes.fromhex(payload), cleartext, secret=secret + ) + msg = ("Share this password with the receiver, via some different channel:" + "\n\n\t%s = %s" % (noid_txt, txt_grouper(noid_txt))) + show_payload(encrypted_payload, "S", 'Teleport Password', msg) + + click.echo() + # encrypted payload is first arguemnt to "recv" cmd + click.echo("Encrypted payload: " + encrypted_payload.hex()) + + +@main.command('recv') +@click.argument('payload', type=str) +@click.argument('secret', type=str) +@click.option('--password', prompt=True, required=True) +def recv(payload, secret, password): + dtype, received = receiver_step2(password.upper(), bytes.fromhex(payload), + keypair_create(bytes.fromhex(secret))) + click.echo() + show_received(dtype, received) + + +if __name__ == "__main__": + main() + +# EOF diff --git a/testing/teleport_protocol.py b/testing/teleport_protocol.py new file mode 100644 index 000000000..0bfebe52c --- /dev/null +++ b/testing/teleport_protocol.py @@ -0,0 +1,256 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Key Teleport protocol re-implementation. +# +import os, pyaes, hashlib, base64 +from bip32 import BIP32Node, PrvKeyNode +from mnemonic import Mnemonic +from pysecp256k1 import ec_seckey_verify, ec_pubkey_serialize, ec_pubkey_parse +from pysecp256k1.extrakeys import keypair_create, keypair_sec, keypair_pub +from pysecp256k1.ecdh import ecdh, ECDH_HASHFP_CLS + + +wordlist = Mnemonic('english').wordlist + +def py_ckcc_hashfp(output, x, y, data=None): + try: + m = hashlib.sha256() + m.update(x.contents.raw) + m.update(y.contents.raw) + output.contents.raw = m.digest() + return 1 + except: + return 0 + +ckcc_hashfp = ECDH_HASHFP_CLS(py_ckcc_hashfp) + + +def txt_grouper(txt): + # split into 2-char groups and add spaces -- to make it easier to read/remember + return ' '.join(txt[n:n+2] for n in range(0, len(txt), 2)) + +def stash_encode_secret(words=None, xprv=None): + nv = bytearray(72) + if words: + wlen = len(words.split(" ")) + assert wlen in [12, 18, 24] + entropy = Mnemonic('english').to_entropy(words) + nv[0] = (0x80 | ((len(entropy) // 8) - 2)) + nv[1:1 + wlen] = entropy + + elif xprv: + node = BIP32Node.from_wallet_key(xprv) + nv[0] = 0x01 + nv[1:33] = node.chain_code() + nv[33:65] = node.privkey() + + # trim zeros + while nv[-1] == 0: + nv = nv[0:-1] + + return nv + +def stash_decode_secret(secret_bytes): + marker = secret_bytes[0] + + if marker == 0x01: + ch, pk = secret_bytes[1:33], secret_bytes[33:65] + n = PrvKeyNode(pk, ch) + node = BIP32Node(netcode='BTC', node=n) + return "xprv", node.hwif(as_private=True) + + elif marker & 0x80: + # seed phrase + ll = ((marker & 0x3) + 2) * 8 + assert ll in [16, 24, 32] + + # make master secret, using the memonic words, and passphrase (or empty string) + seed_bits = secret_bytes[1:1 + ll] + + return "words", Mnemonic('english').to_mnemonic(seed_bits) + + +def generate_rx_code(kp): + # Receiver-side password: given a pubkey (33 bytes, compressed format) + # - construct an 8-digit decimal "password" + # - it's an AES key, but only 26 bits worth + pubkey = bytearray(ec_pubkey_serialize(keypair_pub(kp), compressed=True)) + + # - want the code to be deterministic, but I also don't want to save it + # - double sha256 TODO why ? single sha is imo enough and twice as fast + nk = hashlib.sha256(hashlib.sha256(keypair_sec(kp) + b'COLCARD4EVER').digest()).digest() + + # first byte will be 0x02 or 0x03 (Y coord) -- remove those known 7 bits + pubkey[0] ^= nk[20] & 0xfe + + num = '%08d' % (int.from_bytes(nk[4:8], 'big') % 1_0000_0000) + + # encryption after baby key stretch + kk = hashlib.sha256(num.encode()).digest() + + enc = pyaes.AESModeOfOperationCTR(kk, pyaes.Counter(0)).encrypt + ciphertext = enc(bytes(pubkey)) + + return num, ciphertext + + +def decrypt_rx_pubkey(code, payload): + # given an 8-digit numeric code, make the key and then decrypt/checksum check + # - every value works, there is no fail. + kk = hashlib.sha256(code.encode()).digest() + dec = pyaes.AESModeOfOperationCTR(kk, pyaes.Counter(0)).decrypt + rx_pubkey = bytearray(dec(payload)) + + # first byte will be 0x02 or 0x03 but other 7 bits are noise + rx_pubkey[0] &= 0x01 + rx_pubkey[0] |= 0x02 + + pubkey = bytes(rx_pubkey) + + # validate that it's on the curve... otherwise the code is wrong + try: + ec_pubkey_parse(pubkey) + return pubkey + except: + return None + + +def pick_noid_key(): + # pick an 40 bit password, shown as base32 + # - on rx, libngu base32 decoder will convert '018' into 'OLB' + # - but a little tempted to removed vowels here? + # TODO what about base64.b32encode + k = os.urandom(5) + txt = base64.b32encode(k).decode() + return k, txt + + +def noid_stretch(session_key, noid_key): + return hashlib.pbkdf2_hmac('sha512', session_key, noid_key, 5000)[0:32] + + +def encode_payload(my_keypair, his_pubkey, noid_key, body, for_psbt=False): + assert len(his_pubkey) == 33 + assert len(noid_key) == 5 + + session_key = ecdh(keypair_sec(my_keypair), ec_pubkey_parse(his_pubkey), hashfp=ckcc_hashfp) + + # stretch noid key out -- will be slow + pk = noid_stretch(session_key, noid_key) + + enc = pyaes.AESModeOfOperationCTR(pk, pyaes.Counter(0)).encrypt + b1 = enc(body) + b1 += hashlib.sha256(body).digest()[-2:] + + enc = pyaes.AESModeOfOperationCTR(session_key, pyaes.Counter(0)).encrypt + b2 = enc(b1) + b2 += hashlib.sha256(b1).digest()[-2:] + + if for_psbt: + # no need to share pubkey for PSBT files + return b2 + + return ec_pubkey_serialize(keypair_pub(my_keypair)) + b2 + +def decode_step1(my_keypair, his_pubkey, body): + # Do ECDH and remove top layer of encryption + try: + session_key = ecdh(keypair_sec(my_keypair), ec_pubkey_parse(his_pubkey), hashfp=ckcc_hashfp) + dec = pyaes.AESModeOfOperationCTR(session_key, pyaes.Counter(0)).decrypt + rv = dec(body[:-2]) + chk = hashlib.sha256(rv).digest()[-2:] + assert chk == body[-2:] # likely means wrong rx key, or truncation + except: + return None, None + + return session_key, rv + +def decode_step2(session_key, noid_key, body): + assert len(noid_key) == 5 + pk = noid_stretch(session_key, noid_key) + dec = pyaes.AESModeOfOperationCTR(pk, pyaes.Counter(0)).decrypt + msg = dec(body[:-2]) + chk = hashlib.sha256(msg).digest()[-2:] + return msg if chk == body[-2:] else None + +def receiver_step1(secret=None): + if secret is None: + secret = os.urandom(32) + ec_seckey_verify(secret) + kpr = keypair_create(secret) + num, payload = generate_rx_code(kpr) + return num, payload, kpr + +def sender_step1(num_pwd, encrypted_pubkey, to_send, secret=None): + pkr = decrypt_rx_pubkey(num_pwd, encrypted_pubkey) + + # Pick and show noid key to sender + noid_key, noid_txt = pick_noid_key() + + if secret is None: + secret = os.urandom(32) + + ec_seckey_verify(secret) + kps = keypair_create(secret) + + # "to_send" has to be properly encoded (dtype + what) + payload = encode_payload(kps, pkr, noid_key, to_send) + return noid_txt, payload, kps, pkr + +def receiver_step2(teleport_pwd, payload, keypair): + assert len(teleport_pwd) == 8 + noid_key = base64.b32decode(teleport_pwd) + his_pubkey = payload[0:33] + body = payload[33:] + + session_key, body = decode_step1(keypair, his_pubkey, body) + final = decode_step2(session_key, noid_key, body) + if final: + return chr(final[0]), final[1:] + else: + return None, None + + +def selftest(): + # WORDS + # RECEIVER INIT + number_pass, enc_pubkey, kp_receiver = receiver_step1() + + # SENDER + # what are we sending ? + words = "talk retire wisdom poet actress hood goose case amateur zebra analyst radar" + cleartext = b"s" + stash_encode_secret(words=words) + noid_txt, encrypted_payload, kp_sender, pk_rec = sender_step1(number_pass, enc_pubkey, cleartext) + + # check we properly decrypted receiver pubkey + assert pk_rec == ec_pubkey_serialize(keypair_pub(kp_receiver)) + + # RECEIVER STEP2 + _, received = receiver_step2(noid_txt, encrypted_payload, kp_receiver) + assert words == stash_decode_secret(received)[1] + # === + + # XPRV + # RECEIVER INIT + number_pass, enc_pubkey, kp_receiver = receiver_step1() + + # SENDER + # what are we sending ? + xprv = "xprv9s21ZrQH143K4BwRCYKSEPwcAMYweWkfKLURabnnv2GLNhJN1LSCgDQyGWyNcat72najQKwyshCBXWfHHVbcdxPAZPqByMyWDbWp5SjCfEa" + cleartext = b"s" + stash_encode_secret(xprv=xprv) + noid_txt, encrypted_payload, kp_sender, pk_rec = sender_step1(number_pass, enc_pubkey, cleartext) + + # check we properly decrypted receiver pubkey + assert pk_rec == ec_pubkey_serialize(keypair_pub(kp_receiver)) + + # RECEIVER STEP2 + _, received = receiver_step2(noid_txt, encrypted_payload, kp_receiver) + assert xprv == stash_decode_secret(received)[1] + # === + + print("Selftest passed.") + +if __name__ == "__main__": + selftest() + +# EOF diff --git a/testing/test_640_der_pth_migration.py b/testing/test_640_der_pth_migration.py new file mode 100644 index 000000000..a7630ffc3 --- /dev/null +++ b/testing/test_640_der_pth_migration.py @@ -0,0 +1,42 @@ +# needs to run against simulator with '--der-pth-mig' flag as multisigs need to be there before login sequence executes +import time, base64 + +def test_multisig_derivation_path_migration(start_sign, end_sign, settings_set, goto_home, clear_miniscript, + pick_menu_item, cap_story, cap_menu, press_cancel, settings_get): + + # psbt from nunchuk, with global xpubs belonging to above ms wallet + b64_psbt = "cHNidP8BAF4CAAAAAfkDjXlS32gzOjVhSRArKxvkAecMTnp1g8wwMJTtq74/AAAAAAD9////AekaAAAAAAAAIgAgzs2e4h4vctbFvvauK+QVFAPzCFnMi1H9hTacH7498P8AAAAATwEENYfPBC7g3O2AAAACLvzTgnL7V0DNOnISJdvOgq/6Pw6DAtkPflmZ+Hc04qwC5CShG0rDIlh8gu7gH2NMBLfrIzYSzoSomnVHeMxtxVQUDwVpQzAAAIAAAACAIQAAgAIAAIBPAQQ1h88EkEB8moAAAALv/1L+Cfeg2EPc01pS00f18DIdU5BOeExlGsXyEFOKGwL71tcAiRuL4Bs+uT1JJjU6AbR3j3X60/rI+rTMJmnOgRRiIUGIMAAAgAAAAIBjAACAAgAAgAABAIkCAAAAAZ5Im3CxbYDyByyrr4luss5vr+s0r7Vt8pK+OvicPLO7AAAAAAD9////AnM2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnTmvqUXAAAAACJRIJF/VcIeZ3E4f+ZEjwiUl5AUUxBJgoaEaPaHHJecq18lq+4qAAEBK3M2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnQiAgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3kcwRAIgHNmbk4J9wu4ljq6UouY132eX1i/2jWvJjuuWWyLRFScCIBPyPCuZ/Hmd06h9KtVkSropBonIuqIc/BK8JZ50YKp/AQEDBAEAAAABBUdSIQMBr34TVHrqSk8K6505//5YTOkHmHqF83J8iUURtL/ptCEDUXZhsRMLD/PMbva5f7RgF6u5D5v+99Mk8kOlx0C/w95SriIGAwGvfhNUeupKTwrrnTn//lhM6QeYeoXzcnyJRRG0v+m0HA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAAAAAAiBgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3hxiIUGIMAAAgAAAAIBjAACAAgAAgAAAAAAAAAAAAAEBR1IhAscIZVvBcy3Q0GKO4UqR3gDB3pm/tWas8siH3Ej8MmuCIQN8lTj0MMTpT+Dlk2MbMdAaL93hezzNP3WDsRn/gwlVQlKuIgICxwhlW8FzLdDQYo7hSpHeAMHemb+1ZqzyyIfcSPwya4IcYiFBiDAAAIAAAACAYwAAgAIAAIAAAAAAAQAAACICA3yVOPQwxOlP4OWTYxsx0Bov3eF7PM0/dYOxGf+DCVVCHA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAEAAAAA" + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") # migration happens here + time.sleep(.1) + + names = ["ms", "ms1", "ms2"] + menu = cap_menu() + for n in names: + assert n in menu + + assert len(settings_get("multisig")) == 3 + msc = settings_get("miniscript") + assert len(msc) == 3 + for w in msc: + assert len(w) == 4 # new format (name, policy, keys, opts) + + # in time of creation of PSBT, lopp was making testnet3 unusable... + settings_set("fee_limit", -1) + start_sign(base64.b64decode(b64_psbt)) + title, story = cap_story() + assert title == "OK TO SEND?" + end_sign() + settings_set("fee_limit", 10) # rollback + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + for msi in names: # three wallets imported + pick_menu_item(msi) + pick_menu_item("View Details") + time.sleep(.1) + _, story = cap_story() + assert "'" not in story + press_cancel() + press_cancel() diff --git a/testing/test_640_migration_name_clash.py b/testing/test_640_migration_name_clash.py new file mode 100644 index 000000000..8a1b1705b --- /dev/null +++ b/testing/test_640_migration_name_clash.py @@ -0,0 +1,40 @@ +# needs to run against simulator with '--name-clash-mig' flag as multisigs need to be there before login sequence executes + + +def test_name_clash(settings_append, clear_miniscript, settings_remove, goto_home, pick_menu_item, + settings_get): + # names need to be unique per miniscript/multisig + # but now we are merging them together - check and resolve + # miniscript names are preserved, multisig names can be altered if needed + # clear_miniscript() + # settings_remove("multisig") + + # new_msc6 = list(msc6) + # new_msc6[0] = "ms0" # same name as ms0 + + # length issue, name cannot be longer than 30 chars + # but adding '1' would cause length failure - need some replacing + # now handled in sim_settings + # new_ms2 = list(ms2) + # new_ms2[0] = 35*"a" + + # new_msc16 = list(msc16) + # new_msc16[0] = 31*"a" + # + # new_msc11 = list(msc11) + # new_msc11[0] = 32*"a" + # + # for w in [new_msc6, new_msc11, new_msc16]: + # settings_append("miniscript", w) + # settings_set("multisig", [ms0, ms1, new_ms2, ms3]) + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + + # multisig key preserved in settings + assert len(settings_get("multisig")) == 4 + miniscripts = settings_get("miniscript") + + assert all([len(m[0]) <= 30 for m in miniscripts]) + assert len(set([m[0] for m in miniscripts])) == 7 \ No newline at end of file diff --git a/testing/test_640_miniscript_migration.py b/testing/test_640_miniscript_migration.py new file mode 100644 index 000000000..f11934c19 --- /dev/null +++ b/testing/test_640_miniscript_migration.py @@ -0,0 +1,378 @@ +import pytest, time, base64, shutil + + +msc0 = ('msc0', 'XTN', 14, None, + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*'], + 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@0/<2;3>/*),older(5)))', + False, True, False, False) +psbt0 = 'cHNidP8BAIkCAAAAAZUEvRkqYPrqNKdvyfg7NQopNrWECJgMNqjca1IcEIVTAQAAAAD9////ApQuGh4BAAAAIgAgGjlJEPeLTybA3gHmfxuqEd7X1+PQZN31PiCG5GIfo5QA4fUFAAAAACIAIBO/Xj1D55iB+2Vdhp0jRls7TMrYi3kDWrSGbF07PwfHAAAAAAABAH0CAAAAAXB5YpRHvulXzMx3Gb+uCMYBCG3m4huSm9AAc8OoWQ70AAAAAAD9////AgzV9QUAAAAAFgAUOG3K9ZXBYY3cvxmqFWXyHewk/K0AERAkAQAAACIAIGdWKDqhxwBgKRV7QF+tYv516km5iOKZw8CktnFKO6VmZQAAAAEBKwARECQBAAAAIgAgZ1YoOqHHAGApFXtAX61i/nXqSbmI4pnDwKS2cUo7pWYBBUEhAv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyrHNkdqkUM+9pq+yldgqOn/1x/0rqPEMVp86IrVWyaCIGAv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyGA8FaUNUAACAAQAAgAAAAIAAAAAAAAAAACIGA2e65FIqxSTkQqvwsotxrrWTyaY1q47lIMO19ZaZ8D9zGA8FaUNUAACAAQAAgAAAAIACAAAAAAAAAAABAUEhAv4IiIHn01IKyw4lEaIxhArVyFqGABpomkhcTjULKKv7rHNkdqkUqOoqpzlXZR4XRJ8ozXFV3QTy8bCIrVWyaCICAt97v3uk5Jw1GcvpwazScC2ZRQPNOI7QqwAO7GYhQFJ7GA8FaUNUAACAAQAAgAAAAIADAAAAAAAAACICAv4IiIHn01IKyw4lEaIxhArVyFqGABpomkhcTjULKKv7GA8FaUNUAACAAQAAgAAAAIABAAAAAAAAAAABAUEhA4/rg1i66LDEfY+O55hN2NoI/I/DlwP21VPYbrGIoWiMrHNkdqkUxeG8e0VP5VXSyTnAGNyZJdSljFSIrVWyaCICAxtQTXbpjMrxg7OlwJvsVHMO0LcUtasy4ofc4KsG7lMaGA8FaUNUAACAAQAAgAAAAIACAAAAAQAAACICA4/rg1i66LDEfY+O55hN2NoI/I/DlwP21VPYbrGIoWiMGA8FaUNUAACAAQAAgAAAAIAAAAAAAQAAAAA=' + +msc1 = ('msc1', 'XTN', 14, None, + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*'], + 'or_i(and_v(v:pkh(@0/<2147483646;2147483647>/*),older(10)),or_d(multi(3,@0/<100;101>/*,@0/<26;27>/*,@0/<4;5>/*),and_v(v:thresh(2,pkh(@0/<20;21>/*),a:pkh(@0/<104;105>/*),a:pkh(@0/<22;23>/*)),older(5))))', + False, True, False, False) +psbt1 = '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' + +msc2 = ('msc2', 'XTN', 35, + 'tpubD6NzVbkrYhZ4WhUnV3cPSoRWGf9AUdG2dvNpsXPiYzuTnxzAxemnbajrATDBWhaAVreZSzoGSe3YbbkY2K267tK3TrRmNiLH2pRBpo8yaWm/<2;3>/*', + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*'], + '{or_d(pk(@0/<0;1>/*),and_v(v:pkh(@0/<2;3>/*),older(5))),or_i(and_v(v:pkh(@0/<2147483646;2147483647>/*),older(10)),or_d(multi_a(3,@0/<100;101>/*,@0/<26;27>/*,@0/<4;5>/*),and_v(v:thresh(2,pkh(@0/<20;21>/*),a:pkh(@0/<104;105>/*),a:pkh(@0/<22;23>/*)),older(5))))}', + False, False, False, True) +psbt2 = 'cHNidP8BAIkCAAAAAV7Et4+7k7tC7AqwBMQZmSJ7tpa/XzsvXkT+V3ujnm9lAAAAAAD9////AgDh9QUAAAAAIlEg11R7uhA9lMLC/t4g2DVlDL4N4kVCzBO6vcekfw9Qg6LKLhoeAQAAACJRIP/gLo6zpT3T6cVLUhWVjDRE4ijXDZVOSjT9XPb3vBecAAAAAAABASsAERAkAQAAACJRINpVnJNSCNIet3cwseEUC9DzXpphMQKkdrpOBujCy36RQhXAn8zZA9X83/xd/KP0Ie1ACFL5cgelvW1lgLbNN5zoyibk1CTwH+P+58+GoaN+tOcVh0brhsZd5KrszDmFlTAlrkEg/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnKsc2R2qRQ1vCcfCm7Zx3t3gZNQLt1eb0CGSIitVbJowEIVwJ/M2QPV/N/8Xfyj9CHtQAhS+XIHpb1tZYC2zTec6MombONQ/V0viaDrQsMz/4XFvB3YV1WOxT6DfoPiUDRVovzfY3apFENO6vzRBFvTNjoafqK3FTxcOV6CiK1asmcgERCpO7QMW+l4FCLMgHTb5wOHmLOxAZrV/0A6XXpUnmSsIFk0/OsC7qE5qA8YSS0cDErLSNR2Zya6xHLJUtDpKKhYuiAwwtjUcfxJSfPEktCPn8eBnyhi9zQ4QPI8doPqnPdhibpTnHNkdqkU+oLFcK8j8JpOSA5noLyA8paKOimIrGt2qRSfA98x9eDpR2X4HSMTgwcfWZ5FpIisbJNrdqkUCv0+Wljp82Uh4/k81vTfZQyk4tuIrGyTUohVsmhowCEWERCpO7QMW+l4FCLMgHTb5wOHmLOxAZrV/0A6XXpUnmQ5AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgGQAAAAAAAAAIRYwwtjUcfxJSfPEktCPn8eBnyhi9zQ4QPI8doPqnPdhiTkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACABAAAAAAAAAAhFjdvV0jPAVs9pVSitGty7HlX7MEwqmYy/ktUn2GdwpVXOQHk1CTwH+P+58+GoaN+tOcVh0brhsZd5KrszDmFlTAlrg8FaUNUAACAAQAAgAAAAIAWAAAAAAAAACEWOfmzFNDKh546Iy6Gq5riybHhsQNhnkTLDXo1B/80NM05AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgBQAAAAAAAAAIRZZNPzrAu6hOagPGEktHAxKy0jUdmcmusRyyVLQ6SioWDkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACAGgAAAAAAAAAhFme65FIqxSTkQqvwsotxrrWTyaY1q47lIMO19ZaZ8D9zOQFs41D9XS+JoOtCwzP/hcW8HdhXVY7FPoN+g+JQNFWi/A8FaUNUAACAAQAAgAAAAIACAAAAAAAAACEWn8zZA9X83/xd/KP0Ie1ACFL5cgelvW1lgLbNN5zoyiYNAHxGHl0CAAAAAAAAACEWpq9GSXm6sn5yv1LcSc20LeZSE7ZdLpZ+x6ZrESpmS+A5AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgP7//38AAAAAIRbjVoChmWGIGp3O8x0Fe4meQI50YtgS+SsBEUXO19F0JzkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACAaAAAAAAAAAAhFv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyOQFs41D9XS+JoOtCwzP/hcW8HdhXVY7FPoN+g+JQNFWi/A8FaUNUAACAAQAAgAAAAIAAAAAAAAAAAAEXIJ/M2QPV/N/8Xfyj9CHtQAhS+XIHpb1tZYC2zTec6MomARggaOyso/jhzXHbC0zm0hjyYcds9nupkUHMXOmamgUV40sAAQUgMstVfbBphQ0q3IdVi9tuKxmPDPEpWwxuX+0Po/kGYKIBBv0kAQHA3mN2qRScbAWen68W18pugXbnZuplXkkUz4itWrJnIPiF7CoWlyWJiCYe/KymfcmZiKN8LRVzwXIx8I1Hs6xBrCBPNRKR7w2WTJU1EUAYT/OdEVxZOWoHw6W16c7lzEHqsrogb95ZJCK1pol3ekXNcQF+I2rIRx6L+gfp76cyY5BGili6U5xzZHapFNKjXE9nRBWcnQhN9lBNqLuJb9oCiKxrdqkUPWJO/V6RTQjEbwybQHpEMwL/G7+IrGyTa3apFCTrpJcjact1XWnYCMnisiwBcS/xiKxsk1KIVbJoaAHAQCCP64NYuuiwxH2PjueYTdjaCPyPw5cD9tVT2G6xiKFojKxzZHapFEQGKuvvuYp5IaqUQoS5Y6sr1LhriK1VsmghBxJ+f2chx8OdfiY953A43ru/YuQ5PyQLb5v4yoF4nGosOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAID+//9/AQAAACEHG1BNdumMyvGDs6XAm+xUcw7QtxS1qzLih9zgqwbuUxo5AVKe3uCrde03Xd/oOQHFz4B5hu0KcVxbM+v/r8xWju1uDwVpQ1QAAIABAACAAAAAgAIAAAABAAAAIQcyy1V9sGmFDSrch1WL224rGY8M8SlbDG5f7Q+j+QZgog0AfEYeXQIAAAABAAAAIQdAULk9F74C45pSReaWqp9Zgk8F7q8yh2jWsmAEgURViDkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAaAAAAAEAAAAhB081EpHvDZZMlTURQBhP850RXFk5agfDpbXpzuXMQeqyOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAIAaAAAAAQAAACEHUiVS9UvPk/V4XwoerSkiaz8SJXYNr0ZyV0Vo7Z27JKA5AYPUW3IIf/t1wDcRIuoKB5HkvFvNZwcnTcEYBAQ9PWlMDwVpQ1QAAIABAACAAAAAgBQAAAABAAAAIQddqP5VmH037m9snOOCb+G6CgUhPnJGNjziKJE7GVOh4zkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAFgAAAAEAAAAhB2/eWSQitaaJd3pFzXEBfiNqyEcei/oH6e+nMmOQRopYOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAIAEAAAAAQAAACEHj+uDWLrosMR9j47nmE3Y2gj8j8OXA/bVU9husYihaIw5AVKe3uCrde03Xd/oOQHFz4B5hu0KcVxbM+v/r8xWju1uDwVpQ1QAAIABAACAAAAAgAAAAAABAAAAIQf4hewqFpcliYgmHvyspn3JmYijfC0Vc8FyMfCNR7OsQTkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAZAAAAAEAAAAAAQUgXLz4I1frXoLRXldWxNTTqkBfpF3ykt44qWtBWnVaHjIBBv0kAQHA3mN2qRTPaTJAj950gLkD5Qn+sg3/RaJl54itWrJnINajs2p/yEJ5Ue5tstanM0rTxjX9OlzHExYSl7RA4rFjrCCUEA9I72t1Ef2fvpZa9XcjoRVbjEe6V6SREXoHPUknPLogX32YQ7P6VUA14uPe4GmpPE13a6UAcKAKjPs9yvf/WXO6U5xzZHapFPx3i5yTd9Lu3mTKCkh86ImFYtTjiKxrdqkUZ+3H2/esTKUV+hccSyIu2G3rV1GIrGyTa3apFEXvLLYFbGcSyzPBut2177ka1HsJiKxsk1KIVbJoaAHAQCD+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+6xzZHapFAnIBcoj2qqfCExWezaqbxU5wv4FiK1VsmghB1y8+CNX616C0V5XVsTU06pAX6Rd8pLeOKlrQVp1Wh4yDQB8Rh5dAwAAAAAAAAAhB199mEOz+lVANeLj3uBpqTxNd2ulAHCgCoz7Pcr3/1lzOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIAFAAAAAAAAACEHlBAPSO9rdRH9n76WWvV3I6EVW4xHulekkRF6Bz1JJzw5AeR/tCAX8gCIS4r1qNEZv5Fc6MWrXgtZyNjamGTjz5oHDwVpQ1QAAIABAACAAAAAgBsAAAAAAAAAIQeU4neuiEsTo5gDs/K/0xutesAgqfFVh/5C0eTD54rgVTkB5H+0IBfyAIhLivWo0Rm/kVzoxateC1nI2NqYZOPPmgcPBWlDVAAAgAEAAIAAAACA////fwAAAAAhB8ArxXCIRBeI8enaGerQN9IdNiWaLaKDC3TsROWhOCwDOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIAVAAAAAAAAACEHwIGSH8B4botWc5BZJCzox145gLSh7cZAjFfEMloSPIo5AeR/tCAX8gCIS4r1qNEZv5Fc6MWrXgtZyNjamGTjz5oHDwVpQ1QAAIABAACAAAAAgGkAAAAAAAAAIQfNc0HuBhbJfXibsM7wrWabsAN4AKZlvnWqZORy94jXZjkB5H+0IBfyAIhLivWo0Rm/kVzoxateC1nI2NqYZOPPmgcPBWlDVAAAgAEAAIAAAACAFwAAAAAAAAAhB9ajs2p/yEJ5Ue5tstanM0rTxjX9OlzHExYSl7RA4rFjOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIBlAAAAAAAAACEH33u/e6TknDUZy+nBrNJwLZlFA804jtCrAA7sZiFAUns5AQ8NMS5pIVAZ/QQMjngspmMdsT+e3o1arz8qesCAGMzGDwVpQ1QAAIABAACAAAAAgAMAAAAAAAAAIQf+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+zkBDw0xLmkhUBn9BAyOeCymYx2xP57ejVqvPyp6wIAYzMYPBWlDVAAAgAEAAIAAAACAAQAAAAAAAAAA' + +msc3 = ('msc3', 'XTN', 35, + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<66;67>/*', + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*'], + '{or_d(pk(@0/<0;1>/*),and_v(v:pkh(@0/<2;3>/*),older(5))),or_i(and_v(v:pkh(@0/<2147483646;2147483647>/*),older(10)),or_d(multi_a(3,@0/<100;101>/*,@0/<26;27>/*,@0/<4;5>/*),and_v(v:thresh(2,pkh(@0/<20;21>/*),a:pkh(@0/<104;105>/*),a:pkh(@0/<22;23>/*)),older(5))))}', + False, False, False, True) +psbt3 = 'cHNidP8BAIkCAAAAAS6rRydkXeqXzKb4uqlCSIY4I7CQkytW1qWvX/l0ueshAQAAAAD9////AgDh9QUAAAAAIlEg1Ta5m13y/0b1mWg/tWjG7C5t2znOx6+lJGUJ4b/xHYXKLhoeAQAAACJRIH50GHI355WHxfECbE7stz7e9nDbhpCTCFsKfdif1CPvAAAAAAABASsAERAkAQAAACJRIG8970+J0KJIlB3IQAHoEC7DTEu8q+8gJ06HnCi8009bQhXAeJnnG0fDwiARgspbamyEY2FrGU+WXIpmLKfcvzSef8Dk1CTwH+P+58+GoaN+tOcVh0brhsZd5KrszDmFlTAlrkEg/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnKsc2R2qRQ1vCcfCm7Zx3t3gZNQLt1eb0CGSIitVbJowEIVwHiZ5xtHw8IgEYLKW2pshGNhaxlPllyKZiyn3L80nn/AbONQ/V0viaDrQsMz/4XFvB3YV1WOxT6DfoPiUDRVovzfY3apFENO6vzRBFvTNjoafqK3FTxcOV6CiK1asmcgERCpO7QMW+l4FCLMgHTb5wOHmLOxAZrV/0A6XXpUnmSsIFk0/OsC7qE5qA8YSS0cDErLSNR2Zya6xHLJUtDpKKhYuiAwwtjUcfxJSfPEktCPn8eBnyhi9zQ4QPI8doPqnPdhibpTnHNkdqkU+oLFcK8j8JpOSA5noLyA8paKOimIrGt2qRSfA98x9eDpR2X4HSMTgwcfWZ5FpIisbJNrdqkUCv0+Wljp82Uh4/k81vTfZQyk4tuIrGyTUohVsmhowCEWERCpO7QMW+l4FCLMgHTb5wOHmLOxAZrV/0A6XXpUnmQ5AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgGQAAAAAAAAAIRYwwtjUcfxJSfPEktCPn8eBnyhi9zQ4QPI8doPqnPdhiTkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACABAAAAAAAAAAhFjdvV0jPAVs9pVSitGty7HlX7MEwqmYy/ktUn2GdwpVXOQHk1CTwH+P+58+GoaN+tOcVh0brhsZd5KrszDmFlTAlrg8FaUNUAACAAQAAgAAAAIAWAAAAAAAAACEWOfmzFNDKh546Iy6Gq5riybHhsQNhnkTLDXo1B/80NM05AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgBQAAAAAAAAAIRZZNPzrAu6hOagPGEktHAxKy0jUdmcmusRyyVLQ6SioWDkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACAGgAAAAAAAAAhFme65FIqxSTkQqvwsotxrrWTyaY1q47lIMO19ZaZ8D9zOQFs41D9XS+JoOtCwzP/hcW8HdhXVY7FPoN+g+JQNFWi/A8FaUNUAACAAQAAgAAAAIACAAAAAAAAACEWeJnnG0fDwiARgspbamyEY2FrGU+WXIpmLKfcvzSef8AZAA8FaUNUAACAAQAAgAAAAIBCAAAAAAAAACEWpq9GSXm6sn5yv1LcSc20LeZSE7ZdLpZ+x6ZrESpmS+A5AeTUJPAf4/7nz4aho3605xWHRuuGxl3kquzMOYWVMCWuDwVpQ1QAAIABAACAAAAAgP7//38AAAAAIRbjVoChmWGIGp3O8x0Fe4meQI50YtgS+SsBEUXO19F0JzkB5NQk8B/j/ufPhqGjfrTnFYdG64bGXeSq7Mw5hZUwJa4PBWlDVAAAgAEAAIAAAACAaAAAAAAAAAAhFv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyOQFs41D9XS+JoOtCwzP/hcW8HdhXVY7FPoN+g+JQNFWi/A8FaUNUAACAAQAAgAAAAIAAAAAAAAAAAAEXIHiZ5xtHw8IgEYLKW2pshGNhaxlPllyKZiyn3L80nn/AARggaOyso/jhzXHbC0zm0hjyYcds9nupkUHMXOmamgUV40sAAQUgk4NpwA1wFqT0qPZ2ct+X/2KDWYQrdstXgxq9B9mJHnUBBv0kAQHA3mN2qRScbAWen68W18pugXbnZuplXkkUz4itWrJnIPiF7CoWlyWJiCYe/KymfcmZiKN8LRVzwXIx8I1Hs6xBrCBPNRKR7w2WTJU1EUAYT/OdEVxZOWoHw6W16c7lzEHqsrogb95ZJCK1pol3ekXNcQF+I2rIRx6L+gfp76cyY5BGili6U5xzZHapFNKjXE9nRBWcnQhN9lBNqLuJb9oCiKxrdqkUPWJO/V6RTQjEbwybQHpEMwL/G7+IrGyTa3apFCTrpJcjact1XWnYCMnisiwBcS/xiKxsk1KIVbJoaAHAQCCP64NYuuiwxH2PjueYTdjaCPyPw5cD9tVT2G6xiKFojKxzZHapFEQGKuvvuYp5IaqUQoS5Y6sr1LhriK1VsmghBxJ+f2chx8OdfiY953A43ru/YuQ5PyQLb5v4yoF4nGosOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAID+//9/AQAAACEHG1BNdumMyvGDs6XAm+xUcw7QtxS1qzLih9zgqwbuUxo5AVKe3uCrde03Xd/oOQHFz4B5hu0KcVxbM+v/r8xWju1uDwVpQ1QAAIABAACAAAAAgAIAAAABAAAAIQdAULk9F74C45pSReaWqp9Zgk8F7q8yh2jWsmAEgURViDkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAaAAAAAEAAAAhB081EpHvDZZMlTURQBhP850RXFk5agfDpbXpzuXMQeqyOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAIAaAAAAAQAAACEHUiVS9UvPk/V4XwoerSkiaz8SJXYNr0ZyV0Vo7Z27JKA5AYPUW3IIf/t1wDcRIuoKB5HkvFvNZwcnTcEYBAQ9PWlMDwVpQ1QAAIABAACAAAAAgBQAAAABAAAAIQddqP5VmH037m9snOOCb+G6CgUhPnJGNjziKJE7GVOh4zkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAFgAAAAEAAAAhB2/eWSQitaaJd3pFzXEBfiNqyEcei/oH6e+nMmOQRopYOQGD1FtyCH/7dcA3ESLqCgeR5LxbzWcHJ03BGAQEPT1pTA8FaUNUAACAAQAAgAAAAIAEAAAAAQAAACEHj+uDWLrosMR9j47nmE3Y2gj8j8OXA/bVU9husYihaIw5AVKe3uCrde03Xd/oOQHFz4B5hu0KcVxbM+v/r8xWju1uDwVpQ1QAAIABAACAAAAAgAAAAAABAAAAIQeTg2nADXAWpPSo9nZy35f/YoNZhCt2y1eDGr0H2YkedRkADwVpQ1QAAIABAACAAAAAgEIAAAABAAAAIQf4hewqFpcliYgmHvyspn3JmYijfC0Vc8FyMfCNR7OsQTkBg9Rbcgh/+3XANxEi6goHkeS8W81nBydNwRgEBD09aUwPBWlDVAAAgAEAAIAAAACAZAAAAAEAAAAAAQUgXQPABPvx0lCxlxzfyDQLc4oFEb02tIR9P73xljv0zvMBBv0kAQHA3mN2qRTPaTJAj950gLkD5Qn+sg3/RaJl54itWrJnINajs2p/yEJ5Ue5tstanM0rTxjX9OlzHExYSl7RA4rFjrCCUEA9I72t1Ef2fvpZa9XcjoRVbjEe6V6SREXoHPUknPLogX32YQ7P6VUA14uPe4GmpPE13a6UAcKAKjPs9yvf/WXO6U5xzZHapFPx3i5yTd9Lu3mTKCkh86ImFYtTjiKxrdqkUZ+3H2/esTKUV+hccSyIu2G3rV1GIrGyTa3apFEXvLLYFbGcSyzPBut2177ka1HsJiKxsk1KIVbJoaAHAQCD+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+6xzZHapFAnIBcoj2qqfCExWezaqbxU5wv4FiK1VsmghB10DwAT78dJQsZcc38g0C3OKBRG9NrSEfT+98ZY79M7zGQAPBWlDVAAAgAEAAIAAAACAQwAAAAAAAAAhB199mEOz+lVANeLj3uBpqTxNd2ulAHCgCoz7Pcr3/1lzOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIAFAAAAAAAAACEHlBAPSO9rdRH9n76WWvV3I6EVW4xHulekkRF6Bz1JJzw5AeR/tCAX8gCIS4r1qNEZv5Fc6MWrXgtZyNjamGTjz5oHDwVpQ1QAAIABAACAAAAAgBsAAAAAAAAAIQeU4neuiEsTo5gDs/K/0xutesAgqfFVh/5C0eTD54rgVTkB5H+0IBfyAIhLivWo0Rm/kVzoxateC1nI2NqYZOPPmgcPBWlDVAAAgAEAAIAAAACA////fwAAAAAhB8ArxXCIRBeI8enaGerQN9IdNiWaLaKDC3TsROWhOCwDOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIAVAAAAAAAAACEHwIGSH8B4botWc5BZJCzox145gLSh7cZAjFfEMloSPIo5AeR/tCAX8gCIS4r1qNEZv5Fc6MWrXgtZyNjamGTjz5oHDwVpQ1QAAIABAACAAAAAgGkAAAAAAAAAIQfNc0HuBhbJfXibsM7wrWabsAN4AKZlvnWqZORy94jXZjkB5H+0IBfyAIhLivWo0Rm/kVzoxateC1nI2NqYZOPPmgcPBWlDVAAAgAEAAIAAAACAFwAAAAAAAAAhB9ajs2p/yEJ5Ue5tstanM0rTxjX9OlzHExYSl7RA4rFjOQHkf7QgF/IAiEuK9ajRGb+RXOjFq14LWcjY2phk48+aBw8FaUNUAACAAQAAgAAAAIBlAAAAAAAAACEH33u/e6TknDUZy+nBrNJwLZlFA804jtCrAA7sZiFAUns5AQ8NMS5pIVAZ/QQMjngspmMdsT+e3o1arz8qesCAGMzGDwVpQ1QAAIABAACAAAAAgAMAAAAAAAAAIQf+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+zkBDw0xLmkhUBn9BAyOeCymYx2xP57ejVqvPyp6wIAYzMYPBWlDVAAAgAEAAIAAAACAAQAAAAAAAAAA' + +msc4 = ('msc4', 'XTN', 35, + 'unspend(eaa76f06a330102de2653adda09522b704868d1e5ec63b6eadf35906d5ef47a8)/<2;3>/*', + ['[5baa3998/44h/1h/0h]tpubDC8cZY4a6nmoa9DDumkEayC7Sn6sMXjwTPYbYb91KH5GoT8G9nHFpLSaWe7xcHXARepk3q5h1vLsJgpeSzcggFVdhGpKkqK1Ujv8KFBbv9K/<0;1>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<4;5>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<6;7>/*'], + '{or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<4;5>/*),older(5))),pk(@1/<6;7>/*)}', + False, False, False, True) +psbt4 = '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' + +msc5 = ('msc5', 'XTN', 35, + 'tpubD6NzVbkrYhZ4WeQCEoPi88B5FpWsnGfBmm76EUtexSg17KaNVAPeGwYWJ4aD1YDrFZ9P6pNAZEpWZHtBczPryBDKXQnhn1VPWAiSE5AzgSc/<0;1>/*', + ['[c65b017a/44h/1h/0h]tpubDD1b2M82BJ2PWbC2KyUaTEgFiUsYrd9QU8dMTbcXTRZA9rBMnbMQLB1ZU1icGSBZwd8KNm7oD3GceMMW7y1t27xmVLUaSChGxtyM3NoxLi2/<0;1>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + '[ff12824b/44h/1h/0h]tpubDDcx2UWrF6LRNdGGYoLFkfZZuahG63yBN4BuNiuSEfFfhCAGJU81QKw1zbQVM8sn1NomMa1LSZLh7mzK8gxfpuRHbXhykg4wXK6dg56pnti/<0;1>/*', + '[b69b3715/44h/1h/0h]tpubDDRVW8LtH1wB2JrSEGf8eixM1HrjZVJCx6NNbxNpyZ775CGFMVLQ3dppmK7b75Ze3uZeAKiV1RDWotMXU2ns2mcUEmdZosaSfCg6QwJsRnH/<0;1>/*'], + '{pk(@0/<0;1>/*),or_d(multi_a(2,@1/<0;1>/*,@2/<0;1>/*),and_v(v:pkh(@3/<0;1>/*),older(5)))}', + False, False, False, True) +psbt5 = 'cHNidP8BAIkCAAAAASVwoGngdxBqa9fz4Qd+GIrYjdWZwg9kGXUjaAI1VUvHAQAAAAD9////AuQjGh4BAAAAIlEgUoaPhtWF8tZQ7NustBoMcSh1Z6ddflry4gNYs+ZQ4zwA4fUFAAAAACJRIG1HwYFqkl4OkNX7ZVk6aX6Sfw15Hm1k2ubXcQSPB0oUAAAAAAABASsAERAkAQAAACJRIEO1lvA8EY06Sp08CGFcg91o6ZjUvmt5Wt1qUeRjO+aTQRQ1PteSF4DA99XsRncxOeBwTlG9JMCkfTSO67469u7DtwnXMVPrthv0Ts1GirsiKs0Dr0nM6oajq6skLnOO8FR4QRW7riPGOz1G7Q3j7t/RZqUOHXbAtuLXTSxIcAF1tk8+E/nS+il+rlB4ZI3zDp2c2kFdbPZxVxACbzvY7PgtRmwBQhXA6tGlm2j8fDOyx/PqqKD1y7DHFBQSlfAdDm20wZKTr41nUk819M2yS1Ymexl1Wz4qMpYuW7WCJe4k44EzgR8YE2UgLLi5dAlxJ/+ae055SCdDbEztz+A4pseGXnJHZzj+m0msIDU+15IXgMD31exGdzE54HBOUb0kwKR9NI7rvjr27sO3ulKcc2R2qRSLCeUv/BGI46N+Q8HzBg8DWZqbbIitVbJowEIVwOrRpZto/Hwzssfz6qig9cuwxxQUEpXwHQ5ttMGSk6+NCdcxU+u2G/ROzUaKuyIqzQOvSczqhqOrqyQuc47wVHgjIFXznJDaY/zcXngh+OSVvfLEWONWKuK4z8pe2ZVaPslerMAhFiy4uXQJcSf/mntOeUgnQ2xM7c/gOKbHhl5yR2c4/ptJOQEJ1zFT67Yb9E7NRoq7IirNA69JzOqGo6urJC5zjvBUeA8FaUNWAACAAQAAgAAAAIAAAAAAAAAAACEWNT7XkheAwPfV7EZ3MTngcE5RvSTApH00juu+Ovbuw7c5AQnXMVPrthv0Ts1GirsiKs0Dr0nM6oajq6skLnOO8FR4/xKCSywAAIABAACAAAAAgAAAAAAAAAAAIRZV85yQ2mP83F54Ifjklb3yxFjjViriuM/KXtmVWj7JXjkBZ1JPNfTNsktWJnsZdVs+KjKWLlu1giXuJOOBM4EfGBPGWwF6LAAAgAEAAIAAAACAAAAAAAAAAAAhFsi6BnyXl8vuCn6KlT96YFh4dsCENmYcagnUxotHhgryOQEJ1zFT67Yb9E7NRoq7IirNA69JzOqGo6urJC5zjvBUeLabNxUsAACAAQAAgAAAAIAAAAAAAAAAACEW6tGlm2j8fDOyx/PqqKD1y7DHFBQSlfAdDm20wZKTr40NAHxGHl0AAAAAAAAAAAEXIOrRpZto/Hwzssfz6qig9cuwxxQUEpXwHQ5ttMGSk6+NARgg2aYLKxkBp15WmCPrS59kgNA+1LgXWQNMY8Qj9GWFsToAAQUgcDw4MJiBHmVy6kZ6FpwhZGH0mJFSiL/5RseVEeNUsikBBowBwGQg8QbYTJvFy8t3G5pjfKAzKzCDL8QM6b/WBENQmSrr0JOsINBM9lJsp1zbNud/wtFrDtu9MYUwOc9HH/kKA+Bkq/FfulKcc2R2qRRwJhQ6bH36R8ZWotv2ZS7XZz2KzIitVbJoAcAiIOPJ16aAMauP/I4IsM1RRa/qIxIhcmj9SmUw2sgbC6XfrCEHZOG9cQ3xMLNAaEd9oc4vTGcOQGpdupAdqoE9iLw0yFw5AfuO8M1qcxj8u5L5luzRM06QQFYXgC1h7eY1QFm0wet0tps3FSwAAIABAACAAAAAgAAAAAABAAAAIQdwPDgwmIEeZXLqRnoWnCFkYfSYkVKIv/lGx5UR41SyKQ0AfEYeXQAAAAABAAAAIQfQTPZSbKdc2zbnf8LRaw7bvTGFMDnPRx/5CgPgZKvxXzkB+47wzWpzGPy7kvmW7NEzTpBAVheALWHt5jVAWbTB63T/EoJLLAAAgAEAAIAAAACAAAAAAAEAAAAhB+PJ16aAMauP/I4IsM1RRa/qIxIhcmj9SmUw2sgbC6XfOQGNJmWzgVBI0anL7RMt4ZpTsaDtyvSaqXjCsjNcOsvrs8ZbAXosAACAAQAAgAAAAIAAAAAAAQAAACEH8QbYTJvFy8t3G5pjfKAzKzCDL8QM6b/WBENQmSrr0JM5AfuO8M1qcxj8u5L5luzRM06QQFYXgC1h7eY1QFm0wet0DwVpQ1YAAIABAACAAAAAgAAAAAABAAAAAAEFIFzL7zvQF0T6CLYHodhwKfkBX1B6Dm8jlzVZ6x1t/E6nAQaMAcBkIBcVBtJwhlIUuu3ssEx4RJRGCFVg5GnIk7EXLZvHRU8UrCAubjTQGonbNv7DBhU0hrbmDO4USRpqXo8GsH6W1XnOYbpSnHNkdqkUVDR4LD0U6Wiid0/UPzGdqqB/yJyIrVWyaAHAIiBkXN6wrxHzZNpC49iG+pGMOLGxSidkygYbPg/xQXdfa6whBxcVBtJwhlIUuu3ssEx4RJRGCFVg5GnIk7EXLZvHRU8UOQFOI1HIBTSIzHDmW6wTAPSTofrPJGL/LIQzgJ/LAHadVg8FaUNWAACAAQAAgAAAAIABAAAAAAAAACEHLm400BqJ2zb+wwYVNIa25gzuFEkaal6PBrB+ltV5zmE5AU4jUcgFNIjMcOZbrBMA9JOh+s8kYv8shDOAn8sAdp1W/xKCSywAAIABAACAAAAAgAEAAAAAAAAAIQdSUujPYgZ8x9w+isIHh75SOY4BatMUS+UZ1wy4FSowODkBTiNRyAU0iMxw5lusEwD0k6H6zyRi/yyEM4CfywB2nVa2mzcVLAAAgAEAAIAAAACAAQAAAAAAAAAhB1zL7zvQF0T6CLYHodhwKfkBX1B6Dm8jlzVZ6x1t/E6nDQB8Rh5dAQAAAAAAAAAhB2Rc3rCvEfNk2kLj2Ib6kYw4sbFKJ2TKBhs+D/FBd19rOQGnDDhYkZb4Vw8+8vOcCJkvBHbh83yG+dHySz8SYqdKY8ZbAXosAACAAQAAgAAAAIABAAAAAAAAAAA=' + +msc6 = ('msc6', 'XTN', 14, None, + ['[22da0343/44h/1h/0h]tpubDDTfrYcgqkLoq79TNkVThZ9nqW4VWJRY3zCWkQnNkXzoraF5GMENKPJM1dMQascfTBNJstMLkmJLJ3k4b1k9rAjf3dgNMhYMfHJSUcM4hgL/<0;1>/*', + '[0f056943/84h/0h/0h]tpubDCx8y86cKonoPyTtj3f9NZLpBYoBNkbAzUdafMHhggjxkhF8Dny2aekWfDafywEMZEQaQjkK9Gxn7aN7usLRUQdYbvDgcnmYRf72khPEouL/<0;1>/*'], + 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<0;1>/*),older(5)))', + False, True, False, False) +psbt6 = 'cHNidP8BAIkCAAAAAZ+qJdb0lqGAEMPMICKWzLnUyZliwuofNPuaoUXOdEeyAQAAAAAFAAAAAgDh9QUAAAAAIgAgeZUP3ag3Z9LPoDX/lgAjY4rVzLqCGWvH80nX61Ri3S3IIRoeAQAAACIAIAVYxCfKPiVyBGJlO00OLCd2WF3l/2GIKn1QmzW57W9BAAAAAAABAH0CAAAAAZ2QKy6wW7VGwLcuoMi8VF1lwZasFgPRiFjsgYvBBcU6AAAAAAD9////AgzV9QUAAAAAFgAU5QE7LsYrtnlIArQ3lTiyZt3RpdMAERAkAQAAACIAICxBuCUbPghtGArbOkNpOP+fezN7K2cnLIZl13CsXHeNZQAAAAEBKwARECQBAAAAIgAgLEG4JRs+CG0YCts6Q2k4/597M3srZycshmXXcKxcd40BBUEhA9u0i9ZASI19YAJbZ/7VTcyi+Xp/P1y2kGWIK7KwZhY0rHNkdqkUwlbbAR/7nTxMoBnCUFuf0H4/YzqIrVWyaCIGAm6bjASKZuCeW7cfyV8nBfPu8dy/m21Jul2/3o7pZPSqGA8FaUNUAACAAAAAgAAAAIAAAAAAAAAAACIGA9u0i9ZASI19YAJbZ/7VTcyi+Xp/P1y2kGWIK7KwZhY0GCLaA0MsAACAAQAAgAAAAIAAAAAAAAAAAAABAUEhA0fM+aMLp/bIHqWfZNEJQQSSgVjBM7BitpHTmnAVojhKrHNkdqkUD5Z+eTYp3lim2aGwAcgbWLMQ/FSIrVWyaCICAzJ8UTRtnTziGrKZsdRtCDYI2jQ0Vk3XCCHGKWdC/8g5GA8FaUNUAACAAAAAgAAAAIABAAAAAAAAACICA0fM+aMLp/bIHqWfZNEJQQSSgVjBM7BitpHTmnAVojhKGCLaA0MsAACAAQAAgAAAAIABAAAAAAAAAAABAUEhA1JLXehw3jTSBY949/rYqUbK0RH0CzOEDBRnJ3G0X7bRrHNkdqkUSehqo5gEBUUX2Bqe5BRy3GENV46IrVWyaCICAvakK3+5GsKevk86P0vPv5oJAQKOrNs4wjrwfBciK/GiGA8FaUNUAACAAAAAgAAAAIAAAAAAAQAAACICA1JLXehw3jTSBY949/rYqUbK0RH0CzOEDBRnJ3G0X7bRGCLaA0MsAACAAQAAgAAAAIAAAAAAAQAAAAA=' + +msc7 = ('msc7', + 'XTN', + 26, + None, + ['[0f056943/84h/0h/0h]tpubDCx8y86cKonoPyTtj3f9NZLpBYoBNkbAzUdafMHhggjxkhF8Dny2aekWfDafywEMZEQaQjkK9Gxn7aN7usLRUQdYbvDgcnmYRf72khPEouL/<0;1>/*', + '[15ace8ea/44h/1h/0h]tpubDDu2UQkKM2qC4PbeeF6QLjpa2zLhzqNTEMCvR4tcvsNLDSFU71LhE2iuu4n4r6EpcMiRqWefkPFvqXbpEPrLbu845kLzvomc9TLcU6MFRnz/<0;1>/*', + '[f6d171f3/44h/1h/0h]tpubDCHEhsb2A6wPTSMfehSyAz1A2e2KLTRsxP6esPFcWaDHMB34KypYx9ghUfjP4yvEHwpQVhq3rMPk5NNpF7f9TXLBMYjHsbi97oY2MKwpdqr/<0;1>/*'], + 'or_d(pk(@0/<0;1>/*),and_v(v:multi(2,@1/<0;1>/*,@2/<0;1>/*),after(105)))', + True, + True, + False, + False) +psbt7 = 'cHNidP8BAHMCAAAAAee9+U9VlOKIxxnS8lU1B2UAxG4rBr7b19E1x3+rdFyuAQAAAAD9////AgDh9QUAAAAAF6kUuKH4qKblAE+rGF/umyLErOfKj82HIB8aHgEAAAAXqRQMj2DqZNf+8oPXrt6ESCCkV7vUn4cAAAAAAAEAcwIAAAABrDsI989CKpOYTdwsKuoLwj2TWtZCKP8O7cBkGsZDMMUAAAAAAP3///8C1NX1BQAAAAAXqRQ/2fGSLgsGlZ8IOlwomAfdAFNiIYcAERAkAQAAABepFDqmHpfbOn965Rg2xIIyaA2+KHITh2UAAAABASAAERAkAQAAABepFDqmHpfbOn965Rg2xIIyaA2+KHIThwEEIgAgKGcMdKaBlKaEzA74l8+lK0tNJTNbGTOCxCjnU8Wt/fUBBXAhAm6bjASKZuCeW7cfyV8nBfPu8dy/m21Jul2/3o7pZPSqrHNkUiEC50cc+7+DxrUum7kBSYahPUCXtgXNXy6jdM9pbJutnschAq6mTi6CHG7QC9b9Gi8ndfdf6pN3vblmyZG1GIRNJKpXUq8BabFoIgYCbpuMBIpm4J5btx/JXycF8+7x3L+bbUm6Xb/ejulk9KoYDwVpQ1QAAIAAAACAAAAAgAAAAAAAAAAAIgYCrqZOLoIcbtAL1v0aLyd191/qk3e9uWbJkbUYhE0kqlcY9tFx8ywAAIABAACAAAAAgAAAAAAAAAAAIgYC50cc+7+DxrUum7kBSYahPUCXtgXNXy6jdM9pbJutnscYFazo6iwAAIABAACAAAAAgAAAAAAAAAAAAAEAIgAgvXK/OfnmRlAu7Y1ppZ57P+DZc1Bwr1tFX/ZPLrKfDi8BAXAhAzJ8UTRtnTziGrKZsdRtCDYI2jQ0Vk3XCCHGKWdC/8g5rHNkUiEDozCTNehsgXKb9Zwv4lETze2cKVWBM9dwVPc2fjsxyrshA2CZXftGOd/e7wQjorqvhpk8BbqKBf7oez4H7l1auk4rUq8BabFoIgIDMnxRNG2dPOIaspmx1G0INgjaNDRWTdcIIcYpZ0L/yDkYDwVpQ1QAAIAAAACAAAAAgAEAAAAAAAAAIgIDYJld+0Y5397vBCOiuq+GmTwFuooF/uh7PgfuXVq6TisY9tFx8ywAAIABAACAAAAAgAEAAAAAAAAAIgIDozCTNehsgXKb9Zwv4lETze2cKVWBM9dwVPc2fjsxyrsYFazo6iwAAIABAACAAAAAgAEAAAAAAAAAAAEAIgAgmGa1T5b553Ej/m9XtZFGytZlEX7JE/ByRkJb3Hr3E5ABAXAhAvakK3+5GsKevk86P0vPv5oJAQKOrNs4wjrwfBciK/GirHNkUiEDDQt6gVVSrSzNggc3rBNVSWs0kj01zqAgRJeYzzGWZm8hAjOaWesJtTs7s635NUHEmtenezqS63t3fQSufcp4BNoOUq8BabFoIgICM5pZ6wm1Ozuzrfk1QcSa16d7OpLre3d9BK59yngE2g4Y9tFx8ywAAIABAACAAAAAgAAAAAABAAAAIgIC9qQrf7kawp6+Tzo/S8+/mgkBAo6s2zjCOvB8FyIr8aIYDwVpQ1QAAIAAAACAAAAAgAAAAAABAAAAIgIDDQt6gVVSrSzNggc3rBNVSWs0kj01zqAgRJeYzzGWZm8YFazo6iwAAIABAACAAAAAgAAAAAABAAAAAA==' + +msc8 = ('msc8', + 'XTN', + 14, + None, + ['[01d03393/44h/1h/0h]tpubDDAF9jGCaBw7dJ6DLnPH2mzq1RJcDuf11hQYv68yB9h54mJ3SH9HqcPPewekxeYRr5dYCUVyjwyBCCuDQjooFjJAJkcmwBPKJT5GB9dYhX4/<0;1>/*', + '[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + '[36007fa0/44h/1h/0h]tpubDDH4Qq4JEWcqGZ6quCxMsq5qJrbQkSEx4bRoTn7XUt3kgnwuSrvCA1gehroD6T2HWHBcV7XEDDYxVDkpV342htHxRJ4PQc7RcHxdwkrbCy8/<0;1>/*', + '[2ef62e5b/44h/1h/0h]tpubDCRSQVsy1ueB34k4G3GyWgFGKfRLNksBeGgEwUZiZYGhvnVUsP4CWHPeyy8ryGkjtV1YjLBMWTbRnGaEqEETnJXj7XP8Y2pYa2WY2mFT9bn/<0;1>/*', + '[f8dc2d63/44h/1h/0h]tpubDDrqVehtKQ53gHAwXhxo3M1ZwwEXhraA5xdPR5JWDviFLnuPECUttAWYpFKF9UTGqg6rzXKfujyrUyUuVXVdk6gxt2tZaz8qoMVTHA26e4o/<0;1>/*', + '[185ec28a/44h/1h/0h]tpubDCGy13DqjMd4gTWhmobGtpP3TKzHySci2CQBBU2ypDKLuGUyNLqctYeypXWS56EVizni2FqP9YD8tsXDWzs76YW7epABV9ad3dKU52GAAbz/<0;1>/*', + '[d8a8170c/44h/1h/0h]tpubDDcSLWkXynTZ2h4dGgybHmE9pPHkURy1QbDQBES6v5vjRRgrNU5vD9DzpjeiQewpPsAz6ZptPBpa44qw5Aczx1CA8zyGrHVCbnXeyHeqbvJ/<0;1>/*'], + 'or_i(and_v(v:pkh(@0/<0;1>/*),older(10)),or_d(multi(3,@1/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*),and_v(v:thresh(2,pkh(@4/<0;1>/*),a:pkh(@5/<0;1>/*),a:pkh(@6/<0;1>/*)),older(5))))', + False, + True, + False, + False) +psbt8 = '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' + +msc9 = ('msc9', + 'XTN', + 35, + 'tpubD6NzVbkrYhZ4WMCcYXEgDrM1AFATSGmrFyMnA2Dgx1Y551VueaE3iemHhQBxhKWjepG76Cy5QmXv8z4KwN6ARTnZ5hVuShfxL129NVGXZuE/<0;1>/*', + ['[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*', + '[73c99def/44h/1h/0h]tpubDDU2nuesHbwsZdZcjSXaLSYLpMmkRq6DSxVsGyyM3Zftojb8xEr7RGcjUMLL84YYzkvrMD6SiRBJgo2BXfXsYVAve667Hu1S3noWNDjA342/<0;1>/*', + '[843a20d6/44h/1h/0h]tpubDDgVgV5xoaEr2Qn4SACCtG8wjwHU5mdMkAkr5xhqRkn51cNmmQiDSDCP9wzXm2cKKJ9enoqQ3wrZ2WEoS1Lcs9MVW3kRh8MvBG7WXe2pY2U/<0;1>/*', + '[327c93bb/44h/1h/0h]tpubDD61Z1UQmq5uzfDY6rZHHatCsVGjL5fEmgfq6weAeNSt1o1nz5FguJM8RgfWRRSiTQVknLJqTqpCcYg918bPtF9HcntvUtPz1h1xd9azxZs/<0;1>/*', + '[110d8beb/44h/1h/0h]tpubDDgM8tY3G7wByVp3Gjk6KCnF9qD5mFRhUyzRXLaKyuhAGFm1961dHG5NKNGDZuPwJyxkbE8TrbVXDcArx6UwHr61LC95u1RXjXckBD3a8h4/<0;1>/*', + '[f34034e3/44h/1h/0h]tpubDC9vBBKf193c5NGz2JgW15NkaahrdAbhfKdTKse4K1SRGpsoWCr4BVDVmTq7aZBzcLLye5EbSgq8FbVVKjCjkoTC1Yj3x9fmmtvXQ5xfFqZ/<0;1>/*', + '[0f056943/86h/1h/1000h]tpubDCeEX49avtjHpPSsvyzvtLw3XdPLZD1FKwXa6oRDfWzV3tQFgA8qAzS1SLRFZtyEjkedCHbEx82ABzoqUH97hkFFN4UY9ob38X9XoAz5MD5/<0;1>/*'], + '{{sortedmulti_a(5,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*,@4/<0;1>/*),{sortedmulti_a(5,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*,@5/<0;1>/*),sortedmulti_a(5,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*,@4/<0;1>/*,@5/<0;1>/*)}},{sortedmulti_a(5,@0/<0;1>/*,@1/<0;1>/*,@3/<0;1>/*,@4/<0;1>/*,@5/<0;1>/*),{sortedmulti_a(5,@0/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*,@4/<0;1>/*,@5/<0;1>/*),{sortedmulti_a(5,@1/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*,@4/<0;1>/*,@5/<0;1>/*),pk(@6/<0;1>/*)}}}}', + False, + False, + False, + True) +psbt9 = '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' + +msc10 = ('msc10', + 'XTN', + 35, + '[8eca7947/44h/1h/0h]tpubDDMb4yomGjaFLfw7H5Vf5hg9NX6gYPbufimrPPJiujBRkkTAM6KLjVapsgg4jiZWFpUESFfnum5uqHW1LoHrwvvhVKGDJyZAxmdWfkLdZzg/<0;1>/*', + ['[0f056943/86h/0h/100h]tpubDCrr3F1M4TwdzHNLh6bKhxKAxY56MSdF65ziwC5uni3ozDUfsYVA2GDGSSwrAKdmgZeZBvBFYsb3pwC7baN7DY7y6QSNka93itxZqSUfED9/<0;1>/*', + '[9ad7aaf5/44h/1h/0h]tpubDCbQqRCKRkhmsVuNNFtkMW2k5w8MoWfWcZdSNEgyrSLFf3X9KL3F5N2DkVU7YPEaZ7mHUngHFkmJTEhz7BS4ixXsav68FMXWfXq4btqHqVo/<0;1>/*', + '[7d6611a0/44h/1h/0h]tpubDCRFv9sR4cmGD59CgjYWgqXZwCwH8ce7UmpTc5cihMyb5NGHhGQta5EPAcuQjbsBH4qmkSicTL1qRzzycrdEbEP2bMwCPcwviGaPE1AdPgN/<0;1>/*', + '[948dc473/44h/1h/0h]tpubDCLRovrtFZ99FW6PqAsHpYThgUFVhDohRgq4dKciFDTTkVjaDttDUfjDKEFt9n8vWoDYgoTqaZJhyLsFiSf2SH7Ns4uEY3SoLjXE56AP9me/<0;1>/*', + '[24e307f7/44h/1h/0h]tpubDCv3JZrbaza97d68DsGD864qwfosuLNm9amJ3jNKqjEcfgKN7Xytnfkgmm9JDJuVwVyPyKMyM853h2M1E8QyY8FPuGzbgHwGZKFocHxqxwZ/<0;1>/*', + '[fd503bd6/44h/1h/0h]tpubDDLCDAXYMkfBM9dK3SyF5rVCBnxpxS3uJj22Qk29JYVRxcywEdLyADp6gtjYFC93pCXUS76D1pKHutDmEktm8TXGD3A8q4XsebnMukXJnWw/<0;1>/*', + '[b7076d9e/44h/1h/0h]tpubDCvBpiqp4FjBtG5BPSyJzDRf44QDuGzPVrbpjH7yp2TnW5r1noong1pDv826hvD5nZitq7PXn5mjkiwgBUPuynNvZqkf28QY1ZL8F9ms8tj/<0;1>/*', + '[2366936b/44h/1h/0h]tpubDCEZPU51TzpzMjEYm41qDZLwGCtmhtEbamgAvLyUHK99vpxExCF1up8c8739HYJXHZnybcvsjT5XNLH47pfAgbscDq21zUjxPUwS7BbZgm1/<0;1>/*'], + '{{{pk(@0/<0;1>/*),pk(@1/<0;1>/*)},{pk(@2/<0;1>/*),pk(@3/<0;1>/*)}},{{pk(@4/<0;1>/*),pk(@5/<0;1>/*)},{pk(@6/<0;1>/*),pk(@7/<0;1>/*)}}}', + False, + False, + False, + True) +psbt10 = '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' + +msc11 = ('msc11', + 'XTN', + 14, + None, + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + '[0f056943/84h/1h/9h]tpubDC7jGaaSE66QBAcX8TUD3JKWari1zmGH4gNyKZcrfq6NwCofKujNF2kyeVXgKshotxw5Yib8UxLrmmCmWd8NVPVTAL8rGfMdc7TsAKqsy6y/<0;1>/*'], + 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<0;1>/*),older(5)))', + False, + True, + False, + False) +psbt11 = 'cHNidP8BAIkCAAAAAeajJP7NnniNTc0ijFoXLSLtb7PO2fnQ8jbl8C+DVxdsAQAAAAD9////ApQuGh4BAAAAIgAgYQyLpJkO9kBVeWXDYdxaXSvS7QQnOoi4FD+PamMH/X4A4fUFAAAAACIAIBWoIlvyWbWTvKnqtnPqHQPvHp0zOuzSxZykLf6o39JAAAAAAAABAH0CAAAAAQ8q09oG5iqNoaLh+d+wnJx8ZmSV6stHDXD168XsKpa1AAAAAAD9////AgzV9QUAAAAAFgAUK/ucYCV98CDUd+ad9FhOyNOVF3QAERAkAQAAACIAILDk63Iobkx0I4JV4PBNdXfwdQYWXsogF9IUgV6VAbSlYgAAAAEBKwARECQBAAAAIgAgsOTrcihuTHQjglXg8E11d/B1BhZeyiAX0hSBXpUBtKUBBUEhAv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyrHNkdqkUHjL11qZ7EjPWsWmzAEK9NtBwWT6IrVWyaCIGAtH+84GMHyKHtjE4maikoSEkG/eMCQak6oo4yNI+EbPUGA8FaUNUAACAAQAAgAkAAIAAAAAAAAAAACIGAv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyGA8FaUNUAACAAQAAgAAAAIAAAAAAAAAAAAABAUEhAv4IiIHn01IKyw4lEaIxhArVyFqGABpomkhcTjULKKv7rHNkdqkUd5gurNTUtMrZ7ZiTL4iKDnmlpeyIrVWyaCICAkSD3BVCWHBK+5zkCAFC8SHxd5FDK8kqVUabscA1eUONGA8FaUNUAACAAQAAgAkAAIABAAAAAAAAACICAv4IiIHn01IKyw4lEaIxhArVyFqGABpomkhcTjULKKv7GA8FaUNUAACAAQAAgAAAAIABAAAAAAAAAAABAUEhA4/rg1i66LDEfY+O55hN2NoI/I/DlwP21VPYbrGIoWiMrHNkdqkUSB+Xtanv3jBJzKRXZWA12VyS99KIrVWyaCICA0w19Yw7qJXx92jmtN1wbu7fMoSAhE84tUWxTQnpFIGlGA8FaUNUAACAAQAAgAkAAIAAAAAAAQAAACICA4/rg1i66LDEfY+O55hN2NoI/I/DlwP21VPYbrGIoWiMGA8FaUNUAACAAQAAgAAAAIAAAAAAAQAAAAA=' + +msc12 = ('msc12', + 'XTN', + 35, + 'tpubD6NzVbkrYhZ4WMCcYXEgDrM1AFATSGmrFyMnA2Dgx1Y551VueaE3iemHhQBxhKWjepG76Cy5QmXv8z4KwN6ARTnZ5hVuShfxL129NVGXZuE/<0;1>/*', + ['[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + '[c6cf0772/44h/1h/0h]tpubDDpU5jGokiDTkDwh9buZtwHZtzFdt1ieJUo9wTZJbQjKPoDqwkbRbgLAjcudTFckERECg62Po3TpYaJEDo2rKGGHHHNLNATx1rVoXkjp8Af/<0;1>/*', + '[4632a5b7/44h/1h/0h]tpubDCd5ZhLsPEtgobKPyu6UCi4fZTN1GW9Cv8vPyYV7D9PB7EQpUG72yF42uYPE2gYKs4Y7GLSCcsibf8h4TLEyg6MLoKLgH6yNVEWAHMZ8Guh/<0;1>/*'], + 'thresh(3,c:pk_k(@0/<0;1>/*),sc:pk_k(@1/<0;1>/*),sc:pk_k(@2/<0;1>/*),sdv:older(5))', + False, + False, + False, + True) +psbt12 = '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' + +msc14 = ('msc14', + 'XTN', + 14, + None, + ['[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + '[868f806d/44h/1h/0h]tpubDDg3avkJTE6FpMMMcZW4diGGvPv4UdnkRCsHiyuwkuQQx59CRErNoju5veGZJtj4n78cFRQsCexWNQg1xdxapsfGUU58uHDo9b1Mk81PNEq/<0;1>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<2;3>/*', + '[868f806d/44h/1h/0h]tpubDDg3avkJTE6FpMMMcZW4diGGvPv4UdnkRCsHiyuwkuQQx59CRErNoju5veGZJtj4n78cFRQsCexWNQg1xdxapsfGUU58uHDo9b1Mk81PNEq/<2;3>/*', + '[8ee3999f/44h/1h/0h]tpubDCyWeEF2pHfsQQPMXp6dVKAVBHpmDKEpMRGUZDKocwE7Ts8HvvrxmEJwdRd9ctDc8ikL7KHRCywbLbkQmkpTS6xZNj5SKSmTE9SyNWSx7sA/<0;1>/*'], + 'or_d(multi(2,@0/<0;1>/*,@1/<0;1>/*),and_v(v:thresh(2,pkh(@0/<2;3>/*),a:pkh(@1/<2;3>/*),a:pkh(@2/<0;1>/*)),older(10)))', + False, + True, + False, + False) +psbt14 = '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' + +msc15 = ('msc15', + 'XTN', + 35, + 'tpubD6NzVbkrYhZ4WbzhCs1gLUM8s8LAwTh68xVh1a3nRQyA3tbAJFSE2FEaH2CEGJTKmzcBagpyG35Kjv3UGpTEWbc7qSCX6mswrLQVVPgXECd/<0;1>/*', + ['[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<2;3>/*', + '[607d04ba/44h/1h/0h]tpubDCpjTp9e7txGnMp2jmNGDjtyvBgznTFnD51TxNW1uvpZQS7HDKQKhkYAHcZA9jBRcXY6S4pD6xkJmD7JbBfdzZptRmgwfyj8vKBrkf8F4bA/<2;3>/*', + '[aa29a974/44h/1h/0h]tpubDDHGzw6SUa25Wx9SJohiSA319YRVakSqaXngLbUnnmVUxses7T9fiLp2bpcAT3gJMFs7aYvbfyREYv21oiXXNMNVnNjw9ZkohPgBXe1NRC2/<0;1>/*', + '[eb64e281/44h/1h/0h]tpubDCpbWyRPLE4oZSvng2vA8LXGKZFxuuSTgDrxFbvxZRtgMFjhJM2F6H7gjQrykHmqXShWXmXJwrpjtQhEgWNvprf9k8QYRjxmL2JeumUEK97/<0;1>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + '[607d04ba/44h/1h/0h]tpubDCpjTp9e7txGnMp2jmNGDjtyvBgznTFnD51TxNW1uvpZQS7HDKQKhkYAHcZA9jBRcXY6S4pD6xkJmD7JbBfdzZptRmgwfyj8vKBrkf8F4bA/<0;1>/*'], + '{and_v(v:multi_a(2,@0/<2;3>/*,@1/<2;3>/*,@2/<0;1>/*,@3/<0;1>/*),older(10)),multi_a(2,@0/<0;1>/*,@1/<0;1>/*)}', + False, + False, + False, + True) +psbt15 = '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' + +msc16 = ('msc16', + 'XTN', + 14, + None, + ['[0f056943/99h/0h/0h]tpubDDjN26baDEVS3st3MXRhPod1jGchwFby8WKR84V3TVj1WhXEA6kVUPDWcbG65HTZhaxecuNZJZ7wP7mXZyFrZfnqGWKuuaTPc32g7Nuhf65/<0;1>/*'], + 'and_v(v:pk(@0/<0;1>/*),older(10))', + False, + True, + False, + False) +psbt16 = 'cHNidP8BAP0rAgIAAAABbrgOjAJDURGqMEcT+ZvA3JuqvmgIi2g+qDG6dILyxeIAAAAAAAoAAAAMKH3XFwAAAAAiACAXvREURi+bEGPEzyYwFkx40dNukTHQD7bnjKcOLEdRHACE1xcAAAAAIgAg82YwkDKmGWXF9eDgLWs799QUDU93UonnwtTelzhTwt8AhNcXAAAAACIAIIaY3fNpoRCDUokvE29CYa7GitHC+0ioTnZ0CpNNKLhaAITXFwAAAAAiACCBmi2gwuNY1gNbXGDE/AGHHAGankLwupjw8C1IiEwsxgCE1xcAAAAAIgAgaPCvuQu7QRzRyCLP12XjjQI7NkIOspATfhEMlmOH5lAAhNcXAAAAACIAIAW/NkijUIaPzVNcWm3LGVbi7/lGuV06nXgqSouWfEVkAITXFwAAAAAiACATYiODOibx/Nq31y51uVZa9OW4DUdnomgaY+cMdO3b5gCE1xcAAAAAIgAgAAo9da8XV1uysv92eYSjj+yCgOc24la2HXoi0oeOqdwAhNcXAAAAACIAIImKoP9eMyZLI4gCJ44v1RQ45dusiWpFOzvb8rK+iF0HAITXFwAAAAAiACDScnrC65oAi19Ty2UJH3UyK+ikhc8KdAlnAbvTMGn+ZgCE1xcAAAAAIgAgDUVW1cy51y6Ich2zZ+7M3ACJfQKLdQH1iBFOrRABUJsAZc0dAAAAABYAFNrW6NpeV3s7hVY60a5N5J6FGj9JAAAAAAABAH0CAAAAAVOMFtHmmW/gCS1ZvO4Ar92c/zfDiRc4Zoup882SzMCcAAAAAAD9////AgARECQBAAAAIgAgoh4qToikK9p5O5/5N/UTB6ASGc5/2RR4FbZrXLLkcGMM1fUFAAAAABYAFLJpIaWT6GxbJCK8FK3F4mVP8TNKZQAAAAEBKwARECQBAAAAIgAgoh4qToikK9p5O5/5N/UTB6ASGc5/2RR4FbZrXLLkcGMBBSUhAv8l7Z0VQpZJwpc4MnFkYZmoCE6fQ6c/MDgwauQGt5YArVqyIgYC/yXtnRVClknClzgycWRhmagITp9Dpz8wODBq5Aa3lgAYDwVpQ2MAAIAAAACAAAAAgAAAAAAAAAAAAAEBJSEC+rhXxdTAlHAE2zmwS6mVAiQHrtR/j90gJnUHcf4g6P2tWrIiAgL6uFfF1MCUcATbObBLqZUCJAeu1H+P3SAmdQdx/iDo/RgPBWlDYwAAgAAAAIAAAACAAAAAAAEAAAAAAQElIQK39zVSTLs0W7XvavOn8RYVy188gQ7H/dmkPJilY14ORa1asiICArf3NVJMuzRbte9q86fxFhXLXzyBDsf92aQ8mKVjXg5FGA8FaUNjAACAAAAAgAAAAIAAAAAAAgAAAAABASUhAwbSJM09dE7lyUZ0J3ycJfTTdoJWs02Min7SwuxYsvGjrVqyIgIDBtIkzT10TuXJRnQnfJwl9NN2glazTYyKftLC7Fiy8aMYDwVpQ2MAAIAAAACAAAAAgAEAAAAAAAAAAAEBJSED75ivgCohRLVbD9mDQE7bd+gNEH9Bvb+Sn7JI/LAqWRCtWrIiAgPvmK+AKiFEtVsP2YNATtt36A0Qf0G9v5Kfskj8sCpZEBgPBWlDYwAAgAAAAIAAAACAAAAAAAMAAAAAAQElIQLEvGIemna9B6Jmc0W0VWD699UE0XuR6gKZyOnbvT9UCq1asiICAsS8Yh6adr0HomZzRbRVYPr31QTRe5HqApnI6du9P1QKGA8FaUNjAACAAAAAgAAAAIAAAAAABAAAAAABASUhA4C3jRvgH2io2HpS5+jx+A7AtwydxUSxNqxhLwUQQpUprVqyIgIDgLeNG+AfaKjYelLn6PH4DsC3DJ3FRLE2rGEvBRBClSkYDwVpQ2MAAIAAAACAAAAAgAAAAAAFAAAAAAEBJSECyQKZVcBSooF53La7C0TWPYP2ZnbbLL/4Uz/v0mS0fiqtWrIiAgLJAplVwFKigXnctrsLRNY9g/Zmdtssv/hTP+/SZLR+KhgPBWlDYwAAgAAAAIAAAACAAAAAAAYAAAAAAQElIQMB00zk5jbEGXF9vBsGdDdxgHsA5yqX4SGrKv707usPIq1asiICAwHTTOTmNsQZcX28GwZ0N3GAewDnKpfhIasq/vTu6w8iGA8FaUNjAACAAAAAgAAAAIAAAAAABwAAAAABASUhAx0uSVi691k9jlLnRdfxXikw/N2Yu45gRKR6r7qBZ2nirVqyIgIDHS5JWLr3WT2OUudF1/FeKTD83Zi7jmBEpHqvuoFnaeIYDwVpQ2MAAIAAAACAAAAAgAAAAAAIAAAAAAEBJSEDSlS2wv2KBnk9pc2gDmm8uXcnfUFUX/YFOxkqAEVbqOGtWrIiAgNKVLbC/YoGeT2lzaAOaby5dyd9QVRf9gU7GSoARVuo4RgPBWlDYwAAgAAAAIAAAACAAAAAAAkAAAAAAQElIQOc+KncAYX5xPuiWs31EXgN7XBJCFkhekjFD5eeo8UcQa1asiICA5z4qdwBhfnE+6JazfUReA3tcEkIWSF6SMUPl56jxRxBGA8FaUNjAACAAAAAgAAAAIAAAAAACgAAAAAA' + +msc17 = ('msc17', + 'XTN', + 35, + 'tpubD6NzVbkrYhZ4XgXS51CV3bhoP5dJeQqPhEyhKPDXBgEs64VdSyAfku99gtDXQzY6HEXY5Dqdw8Qud1fYiyewDmYjKe9gGJeDx7x936ur4Ju/<0;1>/*', + ['[0f056943/99h/0h/0h]tpubDDjN26baDEVS3st3MXRhPod1jGchwFby8WKR84V3TVj1WhXEA6kVUPDWcbG65HTZhaxecuNZJZ7wP7mXZyFrZfnqGWKuuaTPc32g7Nuhf65/<0;1>/*'], + 'and_v(v:pk(@0/<0;1>/*),older(10))', + False, + False, + False, + True) +psbt17 = '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' + +msc18 = ('msc18', + 'XTN', + 14, + None, + ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', + 'tpubDCxa9ZSg31K2um9qcMogpVDK2YbAbMn7XqaDyBeKtFSBzHEFAJYtnXDUwBitg39d8TtucYxyKsbWJoKzxmsJbdXruxxdh6zG2LXQuBiRSCp/<0;1>/*'], + 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<0;1>/*),older(100)))', + False, + True, + False, + False) +psbt18 = 'cHNidP8BAP0rAgIAAAABed/+LxRRugb+ZXBpXtwKtlfXUyTE7gTRYPjVMQ7cPbgBAAAAAP3///8M+HzXFwAAAAAiACCnAJGr1/YEXbFzRbpa9B8w251yGFycZAlnKj/s4plmIQCE1xcAAAAAIgAgzbu9x3VG6axlll4FeBaqpYAMlbdTIyWDmin+mVI/U/sAhNcXAAAAACIAIMv+6HXNzpnGwtBlQq25eQrIjdLpMXiUapQEjH9u0XEFAITXFwAAAAAiACAiXhU8VJfboKH5/r7YP3d3REUmGKoSDtoIus4velK2oQCE1xcAAAAAIgAg6XpRg2hGb/euVQCUPRvmA9FP/58hP5eoJVE5oPYsGSsAhNcXAAAAACIAIBaeY2jTqJ2GzOTDeNiue1w/C5WpE96cmEYMVg3PAjPSAITXFwAAAAAiACBJ/RF8WokIp8y1RYmwBwRubZdWPT5xwjIEOXKfiOvahgCE1xcAAAAAIgAgRvJgwL+ekPcRTVwaDLDhlbTR09/RrGAipITRAd6CSd0AhNcXAAAAACIAIKNjT2loCo2ZaHIRLs7Ai00g2wX/2ga+LpmuNlPnK8n+AITXFwAAAAAiACA+aI8gXacap/tELoqpuILLbUSCSjxr5QhlrfoEDxCzsACE1xcAAAAAIgAgGnyLaHkOpdTxgFqW8PYus1L/8lP4mdZ9KWdYFHQ/gusAZc0dAAAAABYAFFzV9puMgfhApqGrWWe4ZK/YUfB3AAAAAAABAH0CAAAAAWDo/mQ9kerhbWjmA47ZRStuEoREMJG+O1srwjEuObK0AAAAAAD9////AgzV9QUAAAAAFgAUJVH6QN7eSa6yyZqjhw6yI/nlrBIAERAkAQAAACIAIBZKCOOMgeSl51TicvU28M01vl8KS3+YxNQQMqX40jv0ZQAAAAEBKwARECQBAAAAIgAgFkoI44yB5KXnVOJy9TbwzTW+XwpLf5jE1BAypfjSO/QBBUIhAv4uHbVQEQkVrUThtBx6BnGlZJcyVHMKOYaJoBpEjQJyrHNkdqkUKtdgW1bC6JPDScW9xsWij62Dam2IrQFksmgiBgKWTmIUf4gTHzsX5TzKVhL8iAEh5mag2Igife+2COpaAwzQ7eKgAAAAAAAAAAAiBgL+Lh21UBEJFa1E4bQcegZxpWSXMlRzCjmGiaAaRI0CchgPBWlDVAAAgAEAAIAAAACAAAAAAAAAAAAAAQFCIQOP64NYuuiwxH2PjueYTdjaCPyPw5cD9tVT2G6xiKFojKxzZHapFAYGrgLYIpGAaPLEJ2s/09cgkZP5iK0BZLJoIgIDj+uDWLrosMR9j47nmE3Y2gj8j8OXA/bVU9husYihaIwYDwVpQ1QAAIABAACAAAAAgAAAAAABAAAAIgIDvilIhOxOv1XPm/yNdy7IC5DX3ryra//Wvtf9dsLVAl8M0O3ioAAAAAABAAAAAAEBQiEDRvg+STRArYr4KT0Il+jVZovQSb7k0ewlSfphDZYNWcysc2R2qRSxrf1gTOaqNOQI6Jnt5mgID7bfyIitAWSyaCICA0b4Pkk0QK2K+Ck9CJfo1WaL0Em+5NHsJUn6YQ2WDVnMGA8FaUNUAACAAQAAgAAAAIAAAAAAAgAAACICA2+rzoSXT+LYs3f2MpQjnKx8mHEKOJ9T7/EleG7piaFQDNDt4qAAAAAAAgAAAAABAUIhAhrU9HhDSwXVwUYMMMfrJVmscAtvtIBzmfdk6Errf0X0rHNkdqkUdJLmyPpMD3s5Irngkgb/5pkPIWiIrQFksmgiAgIa1PR4Q0sF1cFGDDDH6yVZrHALb7SAc5n3ZOhK639F9BgPBWlDVAAAgAEAAIAAAACAAAAAAAMAAAAiAgOzB3EzhcROfT1EFTOVwSHo5UnAI6Qpl8kSRbwJGd8+sgzQ7eKgAAAAAAMAAAAAAQFCIQM4chGJnXg783SSa71bZcic/aOmnhKdif6zJOQKF7yrS6xzZHapFLCwyb5eulpfzeAYiqMslPujMVixiK0BZLJoIgIDOHIRiZ14O/N0kmu9W2XInP2jpp4SnYn+syTkChe8q0sYDwVpQ1QAAIABAACAAAAAgAAAAAAEAAAAIgIDmVrbJzph8A3OOhoSXRHkTK2HNk5Eheyv8McULAk3oIwM0O3ioAAAAAAEAAAAAAEBQiEC0LE+iEVA2DEGb0j64UfLNjg6tA92J9M7uJ2DFIMe7fGsc2R2qRRWZyjtxk3Mo2135GGw8xL5hR85h4itAWSyaCICAkFRyzvLqzLKFJfyy+NiNTF9ElIimBbRmdfMUhahJeGxDNDt4qAAAAAABQAAACICAtCxPohFQNgxBm9I+uFHyzY4OrQPdifTO7idgxSDHu3xGA8FaUNUAACAAQAAgAAAAIAAAAAABQAAAAABAUIhA2ZeZAVrerAgRsJnQLBADmMW9HU73NhP2yEm646zAf/krHNkdqkUIlsztJIyxZge4H4nvAluWnLSY1qIrQFksmgiAgMt8s/1ZMMANFDmI+W7DpmhLY/TeHpdg5jg/hxvIn3aJAzQ7eKgAAAAAAYAAAAiAgNmXmQFa3qwIEbCZ0CwQA5jFvR1O9zYT9shJuuOswH/5BgPBWlDVAAAgAEAAIAAAACAAAAAAAYAAAAAAQFCIQL+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+6xzZHapFIDRt84B4O5rS97QtZnZ0QckaqJbiK0BZLJoIgICXOfM9IsSLh9AFRXYb7LZkYuVYodfRA84z2RoNfGftaYM0O3ioAEAAAAAAAAAIgIC/giIgefTUgrLDiURojGECtXIWoYAGmiaSFxONQsoq/sYDwVpQ1QAAIABAACAAAAAgAEAAAAAAAAAAAEBQiEDvX0JkYUNdYHS0YFClEK3not13QVIftqElMmbXivc/fesc2R2qRTGWtVTQ5QQnIs1eyrj13N63RFv3oitAWSyaCICAjQcJSM7KklPu1md5ci/KD1NEbqfyQGHJm0X0MoYSPXcDNDt4qAAAAAABwAAACICA719CZGFDXWB0tGBQpRCt56Ldd0FSH7ahJTJm14r3P33GA8FaUNUAACAAQAAgAAAAIAAAAAABwAAAAABAUIhAgkK5ypWHfxPrzJHIHuA87Uj9n2kIuSoKvWEMTHN5h3ErHNkdqkUUDfVvZpzl0RndhxC10jCAAVbViiIrQFksmgiAgIJCucqVh38T68yRyB7gPO1I/Z9pCLkqCr1hDExzeYdxBgPBWlDVAAAgAEAAIAAAACAAAAAAAgAAAAiAgOrn4WqT/HCgHZiUMcK+jkfl3urCRxAzO9USfF0RxpQKAzQ7eKgAAAAAAgAAAAAAQFCIQIlOebM4u8iz9IE3lv9ECT0E62y+jmMb2b72eAtX6runqxzZHapFHYg8YNrnm6CEbltUEtdsa4NLgE7iK0BZLJoIgICJTnmzOLvIs/SBN5b/RAk9BOtsvo5jG9m+9ngLV+q7p4YDwVpQ1QAAIABAACAAAAAgAAAAAAJAAAAIgIDi/eg9JiA8pbJGbh3r7qsA8/B1FZcASexJRhAzduRS4kM0O3ioAAAAAAJAAAAAAEBQiEDcpmkt2D6oIreqDnNoDI1vNW47HRkyK2xIEPFFzhGX1+sc2R2qRQLEVoMisxnbzFXuMCqvXhKgXWdc4itAWSyaCICA3KZpLdg+qCK3qg5zaAyNbzVuOx0ZMitsSBDxRc4Rl9fGA8FaUNUAACAAQAAgAAAAIAAAAAACgAAACICA5bW4LfVkXOEZXMOGSK21rwqeddFM15nHml4zbXYLp4gDNDt4qAAAAAACgAAAAAA' + +msc19 = ('msc19', + 'XTN', + 14, + None, + ['[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + '[e0fa0d67/44h/1h/0h]tpubDCgP2uAUWT1mNJrSmjDuDcWoKz5A1irJLZ8NYLEmNoEKfWhr7v7EwNRFjTgYbyU12HnD2DwQBGMUimB4M5N1Rb1qJCbgUhk6ZyB2pFzz1ye/<0;1>/*', + '[e0fa0d67/44h/1h/0h]tpubDCgP2uAUWT1mNJrSmjDuDcWoKz5A1irJLZ8NYLEmNoEKfWhr7v7EwNRFjTgYbyU12HnD2DwQBGMUimB4M5N1Rb1qJCbgUhk6ZyB2pFzz1ye/<2;3>/*', + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<2;3>/*', + '[136d22cf/44h/1h/0h]tpubDDfNoDCpdfQNuBt1VaT9VEgdMMGgdJ8xKycXedazyHwAAvBU7Fr7M5HE1RtuWuAwzs79XKxXaMz87cCQjv1zVghcDsZmo96cihzt1Zkwc6t/<0;1>/*'], + 'or_d(multi(2,@0/<0;1>/*,@1/<0;1>/*),and_v(v:thresh(2,pkh(@1/<2;3>/*),a:pkh(@0/<2;3>/*),a:pkh(@2/<0;1>/*)),older(10)))', + False, + True, + False, + False) +psbt19 = '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' + +msc20 = ('msc20', + 'XTN', + 35, + '[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<0;1>/*', + ['[0f056943/86h/1h/0h]tpubDCeEX49avtiXrBTv3JWTtco99Ka499jXdZHBRtm7va2gkMAui11ctZjqNAT9dLVNaEozt2C1kfTM88cnvZCXsWLJN2p4viGvsyGjtKVV7A1/<2;3>/*', + '[758ce887/44h/1h/0h]tpubDCmTGaAGyukMjeL1cv5CMByjuaNULHrF5iemnVAEdQAGeNdGqqxcsyDY5Tef3HfRC6DRR8nA82Tsw51aeNdNeAieLg5vUgAWyNbKDCNTg39/<2;3>/*', + '[80f987dd/44h/1h/0h]tpubDDCEzMY1oAUdp3MHNgrEA2Y67AsLdeaS1gJ529kbkto9NAVB76KpgxyiUQV4uiMLUK79rj67N3oSmKAwhHrxUEYFQEATEcas6jARA5Do2t6/<2;3>/*', + '[cff263dc/44h/1h/0h]tpubDCcXPLhk177GnGXuAV5rYcxbGQHH58ddBZWk5yqyT5Syp6UX875WoXuZn3XxTkpmBp2PCUdixHuDznTom2YtcqKLtX8ksvqwvFCNKr4cgJi/<0;1>/*', + '[758ce887/44h/1h/0h]tpubDCmTGaAGyukMjeL1cv5CMByjuaNULHrF5iemnVAEdQAGeNdGqqxcsyDY5Tef3HfRC6DRR8nA82Tsw51aeNdNeAieLg5vUgAWyNbKDCNTg39/<0;1>/*', + '[80f987dd/44h/1h/0h]tpubDDCEzMY1oAUdp3MHNgrEA2Y67AsLdeaS1gJ529kbkto9NAVB76KpgxyiUQV4uiMLUK79rj67N3oSmKAwhHrxUEYFQEATEcas6jARA5Do2t6/<0;1>/*'], + '{and_v(v:multi_a(2,@0/<2;3>/*,@1/<2;3>/*,@2/<2;3>/*,@3/<0;1>/*),older(10)),multi_a(2,@1/<0;1>/*,@2/<0;1>/*)}', + False, + False, + False, + True) +psbt20 = 'cHNidP8BAP1UAQIAAAABeH0FGizrmRQzdP4JpyYBLxUw7VvqoQ2tmiwRo7PY1FwBAAAAAP3///8HCJc/cQAAAAAiUSAj7HrvnG8YkpovCdxqOKAaGjCz7aVa5M0dbIueUrW9GABlzR0AAAAAIlEgR9KS65JjC4zjEqjASeez/NUNRYU/9nMZYU4rsQeWsj0AZc0dAAAAACJRINDHnNVNeek31mG9iEEMjFgjZhGK+wSibtFFWB+jB4q+AGXNHQAAAAAiUSAVAYfngcb9bn+ZfENNoAayPfy9moCQlmLBTkQfAYHbJQBlzR0AAAAAIlEgW3TXMsbChoz/1j6yqaG4NxlvR7UP5RNQh4eG0DAxOr0AZc0dAAAAACJRIC8s2zd/ZJ/mXNRuvp4OqCB/6fuOU06y4rFHR+CE+wj3AGXNHQAAAAAWABRNXpz2lE03M9Bgjs/TNqu1ZnKxUAAAAAAAAQErABEQJAEAAAAiUSCvC/bGDmunFryGvMhDDcfhhix2bJ/A+HC5QssGTs87kEEUrWLDI/BAp9td942CZthJDXUznv27Z10u9hCmBZFJ2YFkpQ8vzkWHq3H5Lu6mPxqeZns94PaTA73U/Uq9fy9xGUAsl+tbucdT4N/HjjNrw8aJm6VPSkFB8R7pDjEAG3pVMHBinDFRRdTHbgj9L6OyPYtOpOJE/AmAXAUtRf7ADXEJQhXBLLi5dAlxJ/+ae055SCdDbEztz+A4pseGXnJHZzj+m0l4K8dFo2FSkB94hZEU7wWs3p8CH2ABhe9ILh6rjQ5WnUcgrWLDI/BAp9td942CZthJDXUznv27Z10u9hCmBZFJ2YGsIOiedtuLauwrVzwN2xMUHSLLsJNcQU7Vquw2F0evf1dmulKcwEIVwSy4uXQJcSf/mntOeUgnQ2xM7c/gOKbHhl5yR2c4/ptJZKUPL85Fh6tx+S7upj8anmZ7PeD2kwO91P1KvX8vcRmNIOKhM3WHy4totA22m6aysw0TXW0qZtVv7u8fJO767q+vrCAy+XULFPG86GZbEqFa1tbZ6H8jTDQQ5C7aYH/MdevkFbogFxtbPHuN66ucF8/tyeVzJAyT/Dcxj80Go/Y50O22Ofi6INoOqLmkEUK+Q2tKEUnuf9dnWCKs5TRniXmn1anJeeGAulKdWrLAIRYXG1s8e43rq5wXz+3J5XMkDJP8NzGPzQaj9jnQ7bY5+DkBeCvHRaNhUpAfeIWRFO8FrN6fAh9gAYXvSC4eq40OVp2A+YfdLAAAgAEAAIAAAACAAgAAAAAAAAAhFiy4uXQJcSf/mntOeUgnQ2xM7c/gOKbHhl5yR2c4/ptJGQAPBWlDVgAAgAEAAIAAAACAAAAAAAAAAAAhFjL5dQsU8bzoZlsSoVrW1tnofyNMNBDkLtpgf8x16+QVOQF4K8dFo2FSkB94hZEU7wWs3p8CH2ABhe9ILh6rjQ5WnXWM6IcsAACAAQAAgAAAAIACAAAAAAAAACEWrWLDI/BAp9td942CZthJDXUznv27Z10u9hCmBZFJ2YE5AWSlDy/ORYercfku7qY/Gp5mez3g9pMDvdT9Sr1/L3EZdYzohywAAIABAACAAAAAgAAAAAAAAAAAIRbaDqi5pBFCvkNrShFJ7n/XZ1girOU0Z4l5p9WpyXnhgDkBeCvHRaNhUpAfeIWRFO8FrN6fAh9gAYXvSC4eq40OVp3P8mPcLAAAgAEAAIAAAACAAAAAAAAAAAAhFuKhM3WHy4totA22m6aysw0TXW0qZtVv7u8fJO767q+vOQF4K8dFo2FSkB94hZEU7wWs3p8CH2ABhe9ILh6rjQ5WnQ8FaUNWAACAAQAAgAAAAIACAAAAAAAAACEW6J5224tq7CtXPA3bExQdIsuwk1xBTtWq7DYXR69/V2Y5AWSlDy/ORYercfku7qY/Gp5mez3g9pMDvdT9Sr1/L3EZgPmH3SwAAIABAACAAAAAgAAAAAAAAAAAARcgLLi5dAlxJ/+ae055SCdDbEztz+A4pseGXnJHZzj+m0kBGCBK7isQ/3b7mi8sYfmG2+ATfzej+OpH7oQq5bP1WUfnCQABBSAXFQbScIZSFLrt7LBMeESURghVYORpyJOxFy2bx0VPFAEG2AHARiB3qQ0LmHpYI30gNUOdUO+jG2tV2xcsrPlInwKKS2bOt6wgy2QPatPWN1KVu/kfdaxWqXvUj+XYgNqs/9gID+fLhNK6UpwBwIwg2FUMxmZwrxxtpcGsYQ9yoLyL/N8g6LBWs/Pc9QAv2GqsIBW9mZjmw71M7lk8iAJvo7icnpdD044xOR1VH0praHG2uiDWRjJWqOlZcQ72zO2L+apGvzYas6Vyh8NbxhA5AeXya7ogqa7BlNCfMiSM9QadYJOeIqe7oErMV2sPbJmBwlFKBk+6Up1asiEHFb2ZmObDvUzuWTyIAm+juJyel0PTjjE5HVUfSmtocbY5AQjSTuSmDXI3czZS3ydXESVLh4747B4MAa7DNVIunk3vdYzohywAAIABAACAAAAAgAMAAAAAAAAAIQcXFQbScIZSFLrt7LBMeESURghVYORpyJOxFy2bx0VPFBkADwVpQ1YAAIABAACAAAAAgAEAAAAAAAAAIQd3qQ0LmHpYI30gNUOdUO+jG2tV2xcsrPlInwKKS2bOtzkBnaGxK5LBzgJ6wTA6LQ/1/iBwxicF8smv6n927ELW7JZ1jOiHLAAAgAEAAIAAAACAAQAAAAAAAAAhB6muwZTQnzIkjPUGnWCTniKnu6BKzFdrD2yZgcJRSgZPOQEI0k7kpg1yN3M2Ut8nVxElS4eO+OweDAGuwzVSLp5N78/yY9wsAACAAQAAgAAAAIABAAAAAAAAACEHy2QPatPWN1KVu/kfdaxWqXvUj+XYgNqs/9gID+fLhNI5AZ2hsSuSwc4CesEwOi0P9f4gcMYnBfLJr+p/duxC1uyWgPmH3SwAAIABAACAAAAAgAEAAAAAAAAAIQfWRjJWqOlZcQ72zO2L+apGvzYas6Vyh8NbxhA5AeXyazkBCNJO5KYNcjdzNlLfJ1cRJUuHjvjsHgwBrsM1Ui6eTe+A+YfdLAAAgAEAAIAAAACAAwAAAAAAAAAhB9hVDMZmcK8cbaXBrGEPcqC8i/zfIOiwVrPz3PUAL9hqOQEI0k7kpg1yN3M2Ut8nVxElS4eO+OweDAGuwzVSLp5N7w8FaUNWAACAAQAAgAAAAIADAAAAAAAAAAABBSDxBthMm8XLy3cbmmN8oDMrMIMvxAzpv9YEQ1CZKuvQkwEG2AHARiDqEvX0iIaKy0Ki0ha468pazR0SqwBPEpB/5RMspd8lPqwgfh807L/tKieupZize61CcTNha9PJvJzXTrBQHc5aAs66UpwBwIwgUQjXYdEjgOvuSOaVl01h3ju0VG+27GASWscaa0hb7W2sIBcyEHIk6l+OMa+VyXd/ZgVlSdtM8BYWr2qHbcnWhVgZuiCq0LP8d/DjMcLb/1NBcX80IokzT9LlPL6QN+lo8tuM5LogaXYjAN5K+44wtCOEWfRbQgvVC7FuU5ZNglVF0me7g7S6Up1asiEHFzIQciTqX44xr5XJd39mBWVJ20zwFhavaodtydaFWBk5Af74KC0TbQtNs0TRZlwRjOHrnWfzhfxnGCldzjRCev5jdYzohywAAIABAACAAAAAgAIAAAABAAAAIQdRCNdh0SOA6+5I5pWXTWHeO7RUb7bsYBJaxxprSFvtbTkB/vgoLRNtC02zRNFmXBGM4eudZ/OF/GcYKV3ONEJ6/mMPBWlDVgAAgAEAAIAAAACAAgAAAAEAAAAhB2l2IwDeSvuOMLQjhFn0W0IL1QuxblOWTYJVRdJnu4O0OQH++CgtE20LTbNE0WZcEYzh651n84X8ZxgpXc40Qnr+Y8/yY9wsAACAAQAAgAAAAIAAAAAAAQAAACEHfh807L/tKieupZize61CcTNha9PJvJzXTrBQHc5aAs45Aabze1T0K5HrrxJIXVxGysb4d0kFpIbw8M+eaSDtQRQkgPmH3SwAAIABAACAAAAAgAAAAAABAAAAIQeq0LP8d/DjMcLb/1NBcX80IokzT9LlPL6QN+lo8tuM5DkB/vgoLRNtC02zRNFmXBGM4eudZ/OF/GcYKV3ONEJ6/mOA+YfdLAAAgAEAAIAAAACAAgAAAAEAAAAhB+oS9fSIhorLQqLSFrjrylrNHRKrAE8SkH/lEyyl3yU+OQGm83tU9CuR668SSF1cRsrG+HdJBaSG8PDPnmkg7UEUJHWM6IcsAACAAQAAgAAAAIAAAAAAAQAAACEH8QbYTJvFy8t3G5pjfKAzKzCDL8QM6b/WBENQmSrr0JMZAA8FaUNWAACAAQAAgAAAAIAAAAAAAQAAAAABBSAu5rQkEJWkY8srfBaWoLFi3q3BmFoLyor1KTrqGYWqbQEG2AHARiBz3Ya9kCTLgFi57QYWsZF9xl4BcIoDovBXOzPVAEF45awgFEuHzLHab+qyWU3GRPoLaZlHiv0u6983lfPPIckkn5i6UpwBwIwgDuJ1FIEu0O24MyW0YjOuMUpTDtZG1s2dI99FW34y4TCsIP+11gqy9J+pUJffrOw4amPabF6PpVjNYSmIT3aceJtduiDM0rSv7FDxKW7KWKjhwPlBw+yeiHT/BKmXmVyu2QY2cLog2sMT/ezQLX+BZ71SOb8g78Z7cG7jSTYS0RkCMbFcs1C6Up1asiEHDuJ1FIEu0O24MyW0YjOuMUpTDtZG1s2dI99FW34y4TA5ATooT5TIAQc9tUEb+uJH8568Jt+io9PxQnUYLfg/mRf1DwVpQ1YAAIABAACAAAAAgAIAAAACAAAAIQcUS4fMsdpv6rJZTcZE+gtpmUeK/S7r3zeV888hySSfmDkBb1KmP0rfBuosbZ2XlIHeKTtYn5KkUGwjU13XGP6f4ZOA+YfdLAAAgAEAAIAAAACAAAAAAAIAAAAhBy7mtCQQlaRjyyt8FpagsWLercGYWgvKivUpOuoZhaptGQAPBWlDVgAAgAEAAIAAAACAAAAAAAIAAAAhB3Pdhr2QJMuAWLntBhaxkX3GXgFwigOi8Fc7M9UAQXjlOQFvUqY/St8G6ixtnZeUgd4pO1ifkqRQbCNTXdcY/p/hk3WM6IcsAACAAQAAgAAAAIAAAAAAAgAAACEHzNK0r+xQ8Sluylio4cD5QcPsnoh0/wSpl5lcrtkGNnA5ATooT5TIAQc9tUEb+uJH8568Jt+io9PxQnUYLfg/mRf1gPmH3SwAAIABAACAAAAAgAIAAAACAAAAIQfawxP97NAtf4FnvVI5vyDvxntwbuNJNhLRGQIxsVyzUDkBOihPlMgBBz21QRv64kfznrwm36Kj0/FCdRgt+D+ZF/XP8mPcLAAAgAEAAIAAAACAAAAAAAIAAAAhB/+11gqy9J+pUJffrOw4amPabF6PpVjNYSmIT3aceJtdOQE6KE+UyAEHPbVBG/riR/OevCbfoqPT8UJ1GC34P5kX9XWM6IcsAACAAQAAgAAAAIACAAAAAgAAAAABBSDa2GsnyhtZOAXKxkdfvdIKmfJ7PfyyulGmRwBHfZhReAEG2AHARiCc3X3b43RG/HpZiLK4vdB0VAk6EHHzz1FlqD3jsibgIawgmneQ41rAR/BamDMpEvYh1vQa1PUHgj06aH7/oJoJ8gO6UpwBwIwgjtwsOmNol1EUv1z4aClBpF/wmWWKfrJJoABrOAWBLKisIA9ZVyLaAxtH92F9iNlLwQJ/Amyso4lwpzynfDY6nBGzuiCNLCwGG/u3xAvSF7LVBEMAgOH/LlOdqaNJc9lXVM7WwrogQKs4VYrZCxAdgAi+iUj6Zf6au+8JnmwQFNwtvPUBW7C6Up1asiEHD1lXItoDG0f3YX2I2UvBAn8CbKyjiXCnPKd8NjqcEbM5AY3SqzediQ5VopMyAVO25mSJTgFiC+wyNfLfC5JN1/8OdYzohywAAIABAACAAAAAgAIAAAADAAAAIQdAqzhVitkLEB2ACL6JSPpl/pq77wmebBAU3C289QFbsDkBjdKrN52JDlWikzIBU7bmZIlOAWIL7DI18t8Lkk3X/w7P8mPcLAAAgAEAAIAAAACAAAAAAAMAAAAhB40sLAYb+7fEC9IXstUEQwCA4f8uU52po0lz2VdUztbCOQGN0qs3nYkOVaKTMgFTtuZkiU4BYgvsMjXy3wuSTdf/DoD5h90sAACAAQAAgAAAAIACAAAAAwAAACEHjtwsOmNol1EUv1z4aClBpF/wmWWKfrJJoABrOAWBLKg5AY3SqzediQ5VopMyAVO25mSJTgFiC+wyNfLfC5JN1/8ODwVpQ1YAAIABAACAAAAAgAIAAAADAAAAIQead5DjWsBH8FqYMykS9iHW9BrU9QeCPTpofv+gmgnyAzkBUCpdsx7NYL3GbGDbKdpzEkL7Tmu/oUu4HwqUqvtsfqGA+YfdLAAAgAEAAIAAAACAAAAAAAMAAAAhB5zdfdvjdEb8elmIsri90HRUCToQcfPPUWWoPeOyJuAhOQFQKl2zHs1gvcZsYNsp2nMSQvtOa7+hS7gfCpSq+2x+oXWM6IcsAACAAQAAgAAAAIAAAAAAAwAAACEH2thrJ8obWTgFysZHX73SCpnyez38srpRpkcAR32YUXgZAA8FaUNWAACAAQAAgAAAAIAAAAAAAwAAAAABBSA+0zSl8VmSWHQwT7kAtYQqc/IULDtik1BzCi9NUZtQQgEG2AHARiCfN9R8fBf0EcX3yVkYQj+16KJm9AP0Aj2w8Brlz7Gd66wgmR5JYvKnpkBKhsvYr8UxhCpaAHtor0xlGEPLLtbC2Qu6UpwBwIwgIgfrh+o5g/e3bmtvZ4Bzzajv3rfl271Oe4WRonVuHX6sIDZ7R4mis+xKMoG22ljT7NrrwpeD+iu4AoRibsjRgQ96uiAGnqi0k5oLElCRvP2ZJ5g52EBsZoGCgQRMumjHHjhOhrogS/j7aYTw4O5XrKVljUY4gxTQFf1RjLSX3XXRhlOiQ7K6Up1asiEHBp6otJOaCxJQkbz9mSeYOdhAbGaBgoEETLpoxx44ToY5AfexiG0dIhZ0POKf/xwEywf4XkCSS+5weBDKVyVzb7u5gPmH3SwAAIABAACAAAAAgAIAAAAEAAAAIQciB+uH6jmD97dua29ngHPNqO/et+XbvU57hZGidW4dfjkB97GIbR0iFnQ84p//HATLB/heQJJL7nB4EMpXJXNvu7kPBWlDVgAAgAEAAIAAAACAAgAAAAQAAAAhBzZ7R4mis+xKMoG22ljT7NrrwpeD+iu4AoRibsjRgQ96OQH3sYhtHSIWdDzin/8cBMsH+F5AkkvucHgQylclc2+7uXWM6IcsAACAAQAAgAAAAIACAAAABAAAACEHPtM0pfFZklh0ME+5ALWEKnPyFCw7YpNQcwovTVGbUEIZAA8FaUNWAACAAQAAgAAAAIAAAAAABAAAACEHS/j7aYTw4O5XrKVljUY4gxTQFf1RjLSX3XXRhlOiQ7I5AfexiG0dIhZ0POKf/xwEywf4XkCSS+5weBDKVyVzb7u5z/Jj3CwAAIABAACAAAAAgAAAAAAEAAAAIQeZHkli8qemQEqGy9ivxTGEKloAe2ivTGUYQ8su1sLZCzkBaiJpgoMudkqyB+CLsMdLYMXWhcgq9m2wWEsGYeKo7tGA+YfdLAAAgAEAAIAAAACAAAAAAAQAAAAhB5831Hx8F/QRxffJWRhCP7Xoomb0A/QCPbDwGuXPsZ3rOQFqImmCgy52SrIH4Iuwx0tgxdaFyCr2bbBYSwZh4qju0XWM6IcsAACAAQAAgAAAAIAAAAAABAAAAAABBSAr1evudQ9sepn6aXlfQloya94b2pFbF+Bmv77T+qqGXgEG2AHARiBvdxT0/WVqIR3b8iMcRpLVLT2pPTLCUA8a/RP9z/WR/Kwgo6PpAhO7GET8tLM+Q6oMq5tf+Ca1Zf89F5qx3eqVWG+6UpwBwIwgV0MIbeZX4bYAWx95M1hJ0aK9AbTCsb4ol6uOFV+uMd+sIK5QK5Nkt39a3CNt09pCwXT245oGsWhmEXfJUZhbZcjAuiAZh5IWQKwgCP9dmZ80g5bay6GViv992Ibd9g5lY9M2cLogtN1X60dQ2P8w/6bcEFO3cOZRms2WGNbm4xrM+Eaw4gq6Up1asiEHGYeSFkCsIAj/XZmfNIOW2suhlYr/fdiG3fYOZWPTNnA5AZDLRAK5f0cKEJibEFKj4Cnb+uEeOJsRRejN2Ndjr3y3gPmH3SwAAIABAACAAAAAgAIAAAAFAAAAIQcr1evudQ9sepn6aXlfQloya94b2pFbF+Bmv77T+qqGXhkADwVpQ1YAAIABAACAAAAAgAAAAAAFAAAAIQdXQwht5lfhtgBbH3kzWEnRor0BtMKxviiXq44VX64x3zkBkMtEArl/RwoQmJsQUqPgKdv64R44mxFF6M3Y12OvfLcPBWlDVgAAgAEAAIAAAACAAgAAAAUAAAAhB293FPT9ZWohHdvyIxxGktUtPak9MsJQDxr9E/3P9ZH8OQF75zQ911VL6aliQfsl/4zOQolAkf8M+ILE0c0akXafrHWM6IcsAACAAQAAgAAAAIAAAAAABQAAACEHo6PpAhO7GET8tLM+Q6oMq5tf+Ca1Zf89F5qx3eqVWG85AXvnND3XVUvpqWJB+yX/jM5CiUCR/wz4gsTRzRqRdp+sgPmH3SwAAIABAACAAAAAgAAAAAAFAAAAIQeuUCuTZLd/WtwjbdPaQsF09uOaBrFoZhF3yVGYW2XIwDkBkMtEArl/RwoQmJsQUqPgKdv64R44mxFF6M3Y12OvfLd1jOiHLAAAgAEAAIAAAACAAgAAAAUAAAAhB7TdV+tHUNj/MP+m3BBTt3DmUZrNlhjW5uMazPhGsOIKOQGQy0QCuX9HChCYmxBSo+Ap2/rhHjibEUXozdjXY698t8/yY9wsAACAAQAAgAAAAIAAAAAABQAAAAAA' + + +def test_miniscript(settings_get, settings_set, clear_miniscript, goto_home, pick_menu_item, + cap_menu, try_sign): + + # try one by one + for msc, psbt in [(msc0, psbt0), (msc1, psbt1), (msc2, psbt2), (msc3, psbt3), (msc4, psbt4), + (msc5, psbt5), (msc6, psbt6), (msc7, psbt7), (msc8, psbt8),(msc9, psbt9), + (msc10, psbt10), (msc11, psbt11), (msc12, psbt12), (msc14, psbt14), + (msc15, psbt15), (msc16, psbt16), (msc17, psbt17), (msc18, psbt18), + (msc19, psbt19), (msc20, psbt20)]: + + clear_miniscript() + name = msc[0] + print(name) # debug in case of failure + settings_set("miniscript", [msc]) + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") # migration happens here as we start deserializing the wallets + time.sleep(.1) + assert name in cap_menu() + res = settings_get("miniscript") + assert len(res) == 1 + assert len(res[0]) == 4 # new format (name, policy, keys, opts) + assert res[0][0] == name + try_sign(base64.b64decode(psbt)) + + +def test_big_guys(microsd_path, src_root_dir, goto_home, pick_menu_item, need_keypress, + set_seed_words, microsd_wipe, enter_complex, press_select, cap_story, + settings_get, press_cancel): + # unable to import via settings_set (USB max size) + # so good place to test migration via backup + # + fname0 = "big_boy_naked.7z" + psbt0 = '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' + + fname1 = "big_boy.7z" + psbt1 = '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' + for fname, psbt in [(fname0, psbt0), (fname1, psbt1)]: + print(fname) # debug + microsd_wipe() + shutil.copy(f"{src_root_dir}/testing/data/migration_640/{fname}", microsd_path("")) + # need some other seed as main secret as we are gonna use simulator words as tmp seed + set_seed_words("type goat number acoustic embark allow onion express monitor pudding mutual thank") + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("I Am Developer.") + pick_menu_item("Restore Bkup") + pick_menu_item(fname) + time.sleep(.1) + enter_complex(32*"a", apply=False, b39pass=False) + time.sleep(.5) + press_select() + time.sleep(.1) + press_select() + + with open(microsd_path(fname.split(".")[0]+".psbt"), "w") as f: + f.write(psbt) + + pick_menu_item("Ready To Sign") + time.sleep(1) + title, story = cap_story() + assert "OK TO SEND" in title + press_select() + time.sleep(1) + title, stoty = cap_story() + assert title == "PSBT Signed" + + msc = settings_get("miniscript") + assert len(msc) == 1 + assert len(msc[0]) == 4 # new format + press_cancel() + + +def test_anchor_bug(goto_home, pick_menu_item, microsd_path, src_root_dir, press_select, + unit_test, cap_menu): + fname = "bonus101.txt" + shutil.copy(f"{src_root_dir}/testing/data/migration_640/{fname}", microsd_path("")) + # unit_test('devtest/clear_seed.py') + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("I Am Developer.") + pick_menu_item("Restore Bkup") + pick_menu_item(fname) + time.sleep(.1) + press_select() + + time.sleep(.1) + press_select() + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(2) + m = cap_menu() + assert len(m) == 17 + assert all(len(name) <= 30 for name in m) + +# EOF \ No newline at end of file diff --git a/testing/test_640_multisig_migration.py b/testing/test_640_multisig_migration.py new file mode 100644 index 000000000..3736a1321 --- /dev/null +++ b/testing/test_640_multisig_migration.py @@ -0,0 +1,82 @@ +# needs to run against simulator with "--multi-mig" flag as multisigs need to be there before login sequence executes + +import base64, pytest +from test_640_miniscript_migration import (msc0, msc1, msc2, msc3, msc4, msc5, msc6, msc7, msc8, msc9, msc10, + msc11, msc12, msc14, msc15, msc16, msc17, msc18, msc19, msc20) + +# MULTISIGS +ms0 = ['ms0', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP'), (4118082990, 0, 'tpubDDX85PzueTZjod816TDBdJPk8vWhqyZkSAXJ5xUjvSd1PyuEKnjt5UxiinKJSZzTTFVGSsSEm57LtpxQGdmSjQJtBmz1KUKtA9H63EzZmbA')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/2h'], 'ch': 'XTN', 'ft': 14}, 0] +ms_psbt0 = 'cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEBAfsEAgAAAAABAJACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAIgAgmpn9BiIVcQF8SNxOBdxHZnr4zV50wqEfgao3H2nXwQYAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAABASsA8gUqAQAAACIAIJqZ/QYiFXEBfEjcTgXcR2Z6+M1edMKhH4GqNx9p18EGAQVHUiEDpu0LHSZwTffTfIc4jmXAz2wEHnpdj8wqeEmXnjhsLmQhA2TIP6eApQSJp8iL+LUKERNbAllqpwd359L99FBGOPdtUq4iAgNkyD+ngKUEiafIi/i1ChETWwJZaqcHd+fS/fRQRjj3bUcwRAIgWd1qBvLJ3w7BCnKnf/lqC2NWx+k2ZckyogR7jvIa6Z0CIEGD4eI8QB34FHub2MqS6+V4pKbAVPW9Yb2f3e+3u6uhAQEDBAEAAAAiBgNkyD+ngKUEiafIi/i1ChETWwJZaqcHd+fS/fRQRjj3bRiu9XT1LAAAgAEAAIAAAACAAAAAAAAAAAAiBgOm7QsdJnBN99N8hziOZcDPbAQeel2PzCp4SZeeOGwuZBwPBWlDMAAAgAEAAIAAAACAAgAAgAAAAAAAAAAAAQ4g4ufuvyFOaMtZDSxF3z96nMBVdUModjxZLZnWC+AJdMwBDwQAAAAAARAE/f///wABAUdSIQOjp2xvvj06HYuo4Nu5+DDkWJK2g6Nw3I4z0U645qLW+iEDHguZsfImfX5ke8u+ZPYHqIQ2OchLKxSdQ9O+uL1qfSZSriICAx4LmbHyJn1+ZHvLvmT2B6iENjnISysUnUPTvri9an0mGK71dPUsAACAAQAAgAAAAIAAAAAAAQAAACICA6OnbG++PTodi6jg27n4MORYkraDo3DcjjPRTrjmotb6HA8FaUMwAACAAQAAgAAAAIACAACAAAAAAAEAAAABBCIAIA6r03dk/6wErujg+YtRD4AykJKKhjg6az38chGPiG2OAQMISOYFKgEAAAAA' + +ms1 = ['ms1', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP'), (642592534, 0, 'tpubDCRchFK4N5fkmpD19kfdVBTPcRbcG321XpZc9EF5y9uH2d6DZdiYsVWvuZ6mTQpfqNuTVjqgb4ye33bFGHdhdS1eNwqrdbVQAwSwsftTCGZ')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/2h'], 'ch': 'XTN', 'ft': 14}] +ms_psbt1 = 'cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEBAfsEAgAAAAABAJACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAIgAgO7Et1Pc0tJ02+BhzSyIaAyUnI0+XDeJSG1+9yVYYKhQAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAABASsA8gUqAQAAACIAIDuxLdT3NLSdNvgYc0siGgMlJyNPlw3iUhtfvclWGCoUAQVHUiEDgwkO0ZNeLYTc0jARk0ubSvJSVLUeWXbOJI5gspAixFghA6btCx0mcE3303yHOI5lwM9sBB56XY/MKnhJl544bC5kUq4iAgODCQ7Rk14thNzSMBGTS5tK8lJUtR5Zds4kjmCykCLEWEcwRAIgI2cs69L4CIcU83erd/vww+0gfITnEDGSVTfCl55d33ICIDujRu9l8AUSkHUaz7syn5mJwnP81D3pxUYIBvoVmX30AQEDBAEAAAAiBgODCQ7Rk14thNzSMBGTS5tK8lJUtR5Zds4kjmCykCLEWBgWL00mLAAAgAEAAIAAAACAAAAAAAAAAAAiBgOm7QsdJnBN99N8hziOZcDPbAQeel2PzCp4SZeeOGwuZBwPBWlDMAAAgAEAAIAAAACAAgAAgAAAAAAAAAAAAQ4g1dxbBflVNuLZ2Ul1HWuYv3YvB+1WVb8try3mMtNWnpEBDwQAAAAAARAE/f///wABAUdSIQI1DhBwGxC7cQhnJ80CPFsg5dA/8ZVi447B1hj12FYq8yEDo6dsb749Oh2LqODbufgw5FiStoOjcNyOM9FOuOai1vpSriICAjUOEHAbELtxCGcnzQI8WyDl0D/xlWLjjsHWGPXYVirzGBYvTSYsAACAAQAAgAAAAIAAAAAAAQAAACICA6OnbG++PTodi6jg27n4MORYkraDo3DcjjPRTrjmotb6HA8FaUMwAACAAQAAgAAAAIACAACAAAAAAAEAAAABBCIAIBzbGbUkOtQUlU758Be6etJ319rIzQhJ2CMnsdGC4PFQAQMISOYFKgEAAAAA' + +ms2 = ['ms2', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP'), (2783214288, 0, 'tpubDCqWSUR4xtNPhMrVjQ2h5rdN2BACCHfviVnUrAynei9WaqvuykcjGyvGcbY9hJfpeovM4xVy5E3jMPw1tUc19PeqpVT9LxiTvgS9bZT5ceE')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/1h'], 'ch': 'XTN', 'ft': 26}] +ms_psbt2 = 'cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEBAfsEAgAAAAABAIUCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAF6kUb91OzriEjIgsWsSckPDC0Q+Jg6SHAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEgAPIFKgEAAAAXqRRv3U7OuISMiCxaxJyQ8MLRD4mDpIcBBCIAIAtFYt0+m5ogPeAsF0wFYNUBTIr8zh9b3qyzA/GacXm0AQVHUiECOW8/hX0o1kO8nwuxBxbuW4vBfkcUSC7HQfJzbz2kUU0hA0AM5GQodaOUjCy0igI+AveDhgmkPzUR7Uq5tMXXqdTRUq4iAgI5bz+FfSjWQ7yfC7EHFu5bi8F+RxRILsdB8nNvPaRRTUcwRAIgYAx0HVnw1ptPsDxwA8LO/btP44LaPvKneUUYHY7hyG8CIEk1IDl6R5zJDHqGYkXoBwmLamUuHQ0XR814wPo1JYjUAQEDBAEAAAAiBgI5bz+FfSjWQ7yfC7EHFu5bi8F+RxRILsdB8nNvPaRRTRjQeuSlLAAAgAEAAIAAAACAAAAAAAAAAAAiBgNADORkKHWjlIwstIoCPgL3g4YJpD81Ee1KubTF16nU0RwPBWlDMAAAgAEAAIAAAACAAQAAgAAAAAAAAAAAAQ4gui/D81PS/KT/SauPldOYn71xRmcYZ0Kj2dxDPpRW0ZABDwQAAAAAARAE/f///wABACIAIEB0TJfmwXDzSb/VIr0lxfWIilZ4/9NxxZIw1ckjnQuxAQFHUiEDSK842mj16CcwA8Oxafwy+HR4T9vgB3S6eLdqeeAcetchA4mgQByfozPkgmIIhTWOPBs3dPU0X6FoXoJSvAM0VIHJUq4iAgNIrzjaaPXoJzADw7Fp/DL4dHhP2+AHdLp4t2p54Bx61xjQeuSlLAAAgAEAAIAAAACAAAAAAAEAAAAiAgOJoEAcn6Mz5IJiCIU1jjwbN3T1NF+haF6CUrwDNFSByRwPBWlDMAAAgAEAAIAAAACAAQAAgAAAAAABAAAAAQQXqRSPvHXlyB6V3gbZGQNf3pn0ajFr2IcBAwho5AUqAQAAAAA=' + +# originless key +ms3 = ['ms3', (2, 2), [(1130956047, 1, 'tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r'), (2267113793, 0, 'tpubDCGx6bNmE4zRFgfeV2PbGfcuhg6aeqtLYgNEGZ2pghgFiarh8j2yVruetVWUd6ykfkxaGgB8GhEkaGva1jXvqJrLXC3LboxsQTHqqCZD5Jj')], {'ch': 'XTN', 'd': ['m', 'm/84h/1h/0h'], 'ft': 14}] +ms_psbt3 = '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' + + +def test_multisig(settings_set, settings_get, try_sign, goto_home, pick_menu_item, cap_menu, + clear_miniscript): + # # try one by one + # for ms, psbt in [(ms0, ms_psbt0), (ms1, ms_psbt1), (ms2, ms_psbt2), (ms3, ms_psbt3)]: + # clear_miniscript() + # name = ms[0] + # settings_set("multisig", [ms]) + # goto_home() + # pick_menu_item("Settings") + # pick_menu_item("Multisig/Miniscript") # migration happens here + # time.sleep(.1) + # assert name in cap_menu() + # assert settings_get("multisig", None) is None + # msc = settings_get("miniscript") + # assert len(msc) == 1 + # assert len(msc[0]) == 4 # new format (name, policy, keys, opts) + # assert msc[0][0] == name + # try_sign(base64.b64decode(psbt)) + + # now try bulk migration + # clear_miniscript() + # settings_set("multisig", [ms0, ms1, ms2, ms3]) + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") # migration happens here + menu = cap_menu() + for name in ["ms0", "ms1", "ms2", "ms3"]: + assert name in menu + + assert len(settings_get("multisig")) == 4 # preserved + msc = settings_get("miniscript") + assert len(msc) == 4 + for x in msc: + assert len(x) == 4 # new format (name, policy, keys, opts) + + for i, psbt in enumerate([ms_psbt0, ms_psbt1, ms_psbt2, ms_psbt3]): + try_sign(base64.b64decode(psbt), miniscript="ms%d" % i) + + +def test_multisig_miniscript_migration(settings_append, clear_miniscript, settings_get, + settings_remove, settings_set, goto_home, pick_menu_item): + + # clear_miniscript() + # settings_remove("multisig") + + for msc in [msc0, msc1, msc2, msc3, msc4, msc5, msc6, msc7, msc8, msc9, msc10, + msc11, msc12, msc14, msc15, msc16, msc17, msc18, msc19, msc20]: + settings_append("miniscript", msc) + + # settings_set("multisig", [ms0, ms1, ms2, ms3]) + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") # migration happened here + + miniscripts = settings_get("miniscript") + assert len(miniscripts) == 24 # 20 miniscript wallets & 4 multisigs + for m in miniscripts: + assert len(m) == 4 + + assert len(settings_get("multisig")) == 4 diff --git a/testing/test_addr.py b/testing/test_addr.py index a874bb593..a8566537d 100644 --- a/testing/test_addr.py +++ b/testing/test_addr.py @@ -10,9 +10,10 @@ from ckcc_protocol.constants import * from charcodes import KEY_QR from constants import msg_sign_unmap_addr_fmt +from helpers import addr_from_display_format @pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'"]) -@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ]) +@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR ]) def test_show_addr_usb(dev, press_select, addr_vs_path, path, addr_fmt, is_simulator): addr = dev.send_recv(CCProtocolPacker.show_address(path, addr_fmt), timeout=None) @@ -27,9 +28,9 @@ def test_show_addr_usb(dev, press_select, addr_vs_path, path, addr_fmt, is_simul @pytest.mark.qrcode @pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'", "m/0h/500h"]) -@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ]) +@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR ]) def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, - cap_story, cap_screen_qr, qr_quality_check, + cap_story, verify_qr_address, qr_quality_check, press_cancel, is_q1): time.sleep(0.1) @@ -45,8 +46,7 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, else: assert path in story - assert addr in story - assert addr in story.split('\n') + assert addr in addr_from_display_format(story.split("\n\n")[0]) # check expected addr was used addr_vs_path(addr, path, addr_fmt) @@ -55,34 +55,51 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, need_keypress(KEY_QR if is_q1 else '4') time.sleep(0.1) - qr = cap_screen_qr().decode('ascii') - assert qr == addr or qr == addr.upper() + verify_qr_address(addr_fmt, addr) @pytest.mark.bitcoind -def test_addr_vs_bitcoind(use_regtest, press_select, dev, bitcoind_d_sim_sign): +@pytest.mark.parametrize("addr_fmt", [ + (AF_CLASSIC, "legacy"), + (AF_P2WPKH_P2SH, "p2sh-segwit"), + (AF_P2WPKH, "bech32"), + (AF_P2TR, "bech32m") +]) +def test_addr_vs_bitcoind(addr_fmt, use_regtest, press_select, dev, bitcoind_d_sim_sign): # check our p2wpkh wrapped in p2sh is right use_regtest() + addr_fmt, addr_fmt_bitcoind = addr_fmt for i in range(5): - core_addr = bitcoind_d_sim_sign.getnewaddress(f"{i}-addr", "p2sh-segwit") - assert core_addr[0] == '2' + core_addr = bitcoind_d_sim_sign.getnewaddress(f"{i}-addr", addr_fmt_bitcoind) resp = bitcoind_d_sim_sign.getaddressinfo(core_addr) - assert resp['embedded']['iswitness'] == True - assert resp['isscript'] == True + assert resp["ismine"] is True + if addr_fmt in (AF_P2TR, AF_P2WPKH): + wit_ver = resp["witness_version"] + if addr_fmt == AF_P2TR: + assert wit_ver == 1 + else: + assert wit_ver == 0 + assert resp["iswitness"] is True + if addr_fmt == AF_P2WPKH_P2SH: + assert resp['embedded']['iswitness'] is True + assert resp['isscript'] is True + assert resp['embedded']['witness_version'] == 0 path = resp['hdkeypath'] - addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2WPKH_P2SH), timeout=None) + addr = dev.send_recv(CCProtocolPacker.show_address(path, addr_fmt), timeout=None) press_select() assert addr == core_addr @pytest.mark.parametrize("body_err", [ - ("m\np2wsh", "Invalid address format: 'p2wsh'"), - ("m\np2sh-p2wsh", "Invalid address format: 'p2sh-p2wsh'"), - ("m\np2tr", "Invalid address format: 'p2tr'"), + ("m\np2wsh", "Unsupported address format: 'p2wsh'"), + ("m\np2sh-p2wsh", "Unsupported address format: 'p2sh-p2wsh'"), ("m/0/0/0/0/0/0/0/0/0/0/0/0/0\np2pkh", "too deep"), ("m/0/0/0/0/0/q/0/0/0\np2pkh", "invalid characters"), ]) -def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_text, cap_story): +def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_text, cap_story, + skip_if_useless_way): + skip_if_useless_way("nfc") + body, err = body_err goto_home() pick_menu_item('Advanced/Tools') @@ -94,10 +111,12 @@ def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_te assert err in story @pytest.mark.parametrize("path", ["m/84'/0'/0'/300/0", "m/800h/0h", "m/0/0/0/0/1/1/1"]) -@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"]) +@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh", "p2tr"]) def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item, goto_home, cap_story, press_nfc, addr_vs_path, press_select, is_q1, - cap_screen): + cap_screen, skip_if_useless_way): + + skip_if_useless_way("nfc") # import pdb;pdb.set_trace() for _ in range(5): # need to wait for ApproveMessageSign to be popped from ux stack @@ -122,7 +141,7 @@ def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_m _, story = cap_story() split_story = story.split("\n\n") - story_addr = split_story[0] + story_addr = addr_from_display_format(split_story[0]) story_path = split_story[1][2:] # remove "= " if not is_q1: assert "Press (3) to share via NFC" in story @@ -142,4 +161,59 @@ def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_m assert story_addr == addr addr_vs_path(addr, path, addr_fmt) -# EOF +def test_bip86(dev, set_seed_words, use_mainnet, need_keypress): + # https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki + mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + set_seed_words(mnemonic) + use_mainnet() + + path = "m/86'/0'/0'" + xp = dev.send_recv(CCProtocolPacker.get_xpub(path), timeout=None) + # xprv = "xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk" + xpub = "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ" + assert xp == xpub + + # Account 0, first receiving + path = "m/86'/0'/0'/0/0" + addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2TR), timeout=None) + need_keypress('y') + xp = dev.send_recv(CCProtocolPacker.get_xpub(path), timeout=None) + + # xprv = "xprvA449goEeU9okwCzzZaxiy475EQGQzBkc65su82nXEvcwzfSskb2hAt2WymrjyRL6kpbVTGL3cKtp9herYXSjjQ1j4stsXXiRF7kXkCacK3T" + xpub = "xpub6H3W6JmYJXN49h5TfcVjLC3onS6uPeUTTJoVvRC8oG9vsTn2J8LwigLzq5tHbrwAzH9DGo6ThGUdWsqce8dGfwHVBxSbixjDADGGdzF7t2B" + # internal_key = "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115" + # output_key = "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c" + # scriptPubKey = "5120a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c" + address = "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr" + assert xp == xpub + assert addr == address + + # Account 0, second receiving + path = "m/86'/0'/0'/0/1" + addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2TR), timeout=None) + need_keypress('y') + xp = dev.send_recv(CCProtocolPacker.get_xpub(path), timeout=None) + # xprv = "xxprvA449goEeU9okyiF1LmKiDaTgeXvmh87DVyRd35VPbsSop8n8uALpbtrUhUXByPFKK7C2yuqrB1FrhiDkEMC4RGmA5KTwsE1aB5jRu9zHsuQ" + xpub = "xpub6H3W6JmYJXN4CCKUSnriaiQRCZmG6aq4sCMDqTu1ACyngw7HShf59hAxYjXgKDuuHThVEUzdHrc3aXCr9kfvQvZPit5dnD3K9xVRBzjK3rX" + # internal_key = "83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145" + # output_key = "a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb" + # scriptPubKey = "5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb" + address = "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh" + assert xp == xpub + assert addr == address + + # Account 0, first change + path = "m/86'/0'/0'/1/0" + addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2TR), timeout=None) + need_keypress('y') + xp = dev.send_recv(CCProtocolPacker.get_xpub(path), timeout=None) + # xprv = "xprvA3Ln3Gt3aphvUgzgEDT8vE2cYqb4PjFfpmbiFKphxLg1FjXQpkAk5M1ZKDY15bmCAHA35jTiawbFuwGtbDZogKF1WfjwxML4gK7WfYW5JRP" + xpub = "xpub6GL8SnQwRCGDhB59LEz9HMyM6sRYoByXBzXK3iEKWgCz8XrZNHUzd9L3AUBELW5NzA7dEFvMas1F84TuPH3xqdUA5tumaGWFgihJzWytXe3" + # internal_key = "399f1b2f4393f29a18c937859c5dd8a77350103157eb880f02e8c08214277cef" + # output_key = "882d74e5d0572d5a816cef0041a96b6c1de832f6f9676d9605c44d5e9a97d3dc" + # scriptPubKey = "5120882d74e5d0572d5a816cef0041a96b6c1de832f6f9676d9605c44d5e9a97d3dc" + address = "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7" + assert xp == xpub + assert addr == address + +# EOF \ No newline at end of file diff --git a/testing/test_address_explorer.py b/testing/test_address_explorer.py index 608f24b21..4321b32b3 100644 --- a/testing/test_address_explorer.py +++ b/testing/test_address_explorer.py @@ -2,13 +2,13 @@ # # Test the address explorer. # -# Only single-sig here. Multisig cases are elsewhere. +# Only single-sig here. Multisig cases are in test_multisig.py. # import pytest, time, io, csv, bech32 from ckcc_protocol.constants import * from bip32 import BIP32Node from base58 import decode_base58_checksum -from helpers import detruncate_address, hash160 +from helpers import detruncate_address, hash160, addr_from_display_format from charcodes import KEY_QR, KEY_LEFT, KEY_RIGHT from constants import MAX_BIP32_IDX @@ -24,9 +24,10 @@ def doit(netcode): # Removed in v4.1.3: ( "m/{change}/{idx}", AF_CLASSIC ), #( "m/{account}'/{change}'/{idx}'", AF_CLASSIC ), #( "m/{account}'/{change}'/{idx}'", AF_P2WPKH ), - ( "m/44h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_CLASSIC ), - ( "m/49h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_P2WPKH_P2SH ), - ( "m/84h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_P2WPKH ) + ("m/44h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_CLASSIC), + ("m/49h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_P2WPKH_P2SH), + ("m/84h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_P2WPKH), + ("m/86h/{coin_type}h/{account}h/{change}/{idx}".replace('{coin_type}', coin_type), AF_P2TR), ] return doit @@ -52,37 +53,19 @@ def doit(start, n): d = dict() for path_raw, addr, empty in zip(*[iter(raw_addrs)]*3): path = path_raw.split(" =>")[0] - d[path] = addr + d[path] = addr_from_display_format(addr) assert len(d) == n return d return doit -@pytest.fixture -def validate_address(): - # Check whether an address is covered by the given subkey - def doit(addr, sk): - if addr[0] in '1mn': - assert addr == sk.address() - elif addr[0:3] in { 'bc1', 'tb1' }: - h20 = sk.hash160() - assert addr == bech32.encode(addr[0:2], 0, h20) - elif addr[0:5] == "bcrt1": - h20 = sk.hash160() - assert addr == bech32.encode(addr[0:4], 0, h20) - elif addr[0] in '23': - h20 = hash160(b'\x00\x14' + sk.hash160()) - assert h20 == decode_base58_checksum(addr)[1:] - else: - raise ValueError(addr) - return doit @pytest.fixture def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_text, load_export_and_verify_signature, - press_select, press_nfc): + press_select, press_nfc, load_export): # Generates the address file through the simulator, reads the file and # returns a list of tuples of the form (subpath, address) - def doit(start_idx=0, way="sd", change=False, is_custom_single=False): + def doit(start_idx=0, way="sd", change=False, is_custom_single=False, is_p2tr=False): expected_qty = 250 if way != "nfc" else 10 if (start_idx + expected_qty) > MAX_BIP32_IDX: expected_qty = (MAX_BIP32_IDX - start_idx) + 1 @@ -92,7 +75,8 @@ def doit(start_idx=0, way="sd", change=False, is_custom_single=False): if change and not is_custom_single: need_keypress("0") if way == "sd": - need_keypress('1') + if "Press (1)" in story: + need_keypress('1') elif way == "vdisk": if "save to Virtual Disk" not in story: raise pytest.skip("Vdisk disabled") @@ -110,9 +94,11 @@ def doit(start_idx=0, way="sd", change=False, is_custom_single=False): assert len(addresses.split("\n")) == expected_qty raise pytest.xfail("PASSED - different export format for NFC") + time.sleep(.5) # always long enough to write the file? title, body = cap_story() - contents, sig_addr = load_export_and_verify_signature(body, way, label="Address summary") + contents, sig_addr, _ = load_export_and_verify_signature(body, way, label="Address summary") + addr_dump = io.StringIO(contents) cc = csv.reader(addr_dump) hdr = next(cc) @@ -120,7 +106,11 @@ def doit(start_idx=0, way="sd", change=False, is_custom_single=False): for n, (idx, addr, deriv) in enumerate(cc, start=start_idx): assert int(idx) == n if n == start_idx: - assert sig_addr == addr + if sig_addr: + if is_p2tr: + pass + else: + assert sig_addr == addr if not is_custom_single: assert ('/%s' % idx) in deriv @@ -272,7 +262,7 @@ def test_address_display(goto_address_explorer, parse_display_screen, mk_common_ press_cancel() # back -@pytest.mark.parametrize('click_idx', ["Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH"]) +@pytest.mark.parametrize('click_idx', ["Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH", 'Taproot P2TR']) @pytest.mark.parametrize("change", [True, False]) @pytest.mark.parametrize("start_idx", [MAX_BIP32_IDX, 80965, 0]) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) @@ -289,7 +279,8 @@ def test_dump_addresses(way, change, generate_addresses_file, mk_common_derivati set_addr_exp_start_idx(start_idx) pick_menu_item(click_idx) # Generate the addresses file and get each line in a list - for subpath, addr in generate_addresses_file(way=way, start_idx=start_idx, change=change): + is_p2tr = click_idx == 'Taproot P2TR' + for subpath, addr in generate_addresses_file(way=way, start_idx=start_idx, change=change, is_p2tr=is_p2tr): # derive the subkey and validate the corresponding address assert subpath.split("/")[-2] == "1" if change else "0" sk = node_prv.subkey_for_path(subpath) @@ -336,7 +327,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, # derive index=0 address assert '{account}' in path - subpath = path.format(account=account_num, change=0, idx=start_idx) # e.g. "m/44'/1'/X'/0/0" + subpath = path.format(account=account_num, change=0, idx=start_idx, is_p2tr=addr_format==AF_P2TR) # e.g. "m/44'/1'/X'/0/0" sk = node_prv.subkey_for_path(subpath) # capture full index=0 address from display screen & validate it @@ -357,7 +348,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, assert expected_addr.startswith(start) assert expected_addr.endswith(end) - for subpath, addr in generate_addresses_file(way=way, start_idx=start_idx): + for subpath, addr in generate_addresses_file(way=way, start_idx=start_idx,is_p2tr=addr_format==AF_P2TR): assert subpath.split('/')[-3] == str(account_num)+"h" sk = node_prv.subkey_for_path(subpath) validate_address(addr, sk) @@ -378,12 +369,13 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, ("m/1/2/3/4/5", MAX_BIP32_IDX), ("m/1h/2h/3h/4h/5h", 0), ]) -@pytest.mark.parametrize('which_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ]) +@pytest.mark.parametrize('which_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR]) def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_address_explorer, need_keypress, cap_menu, parse_display_screen, validate_address, - cap_screen_qr, qr_quality_check, nfc_read_text, get_setting, + verify_qr_address, qr_quality_check, nfc_read_text, get_setting, press_select, press_cancel, is_q1, press_nfc, cap_story, - generate_addresses_file, settings_set, set_addr_exp_start_idx): + generate_addresses_file, settings_set, set_addr_exp_start_idx, + sign_msg_from_address): path, start_idx = path_sidx settings_set('aei', True if start_idx else False) @@ -443,14 +435,16 @@ def ss(x): time.sleep(.5) # .2 not enuf m = cap_menu() - assert m[0] == 'Classic P2PKH' - assert m[1] == 'Segwit P2WPKH' - assert m[2] == 'P2SH-Segwit' - + assert m[1] == 'Classic P2PKH' + assert m[0] == 'Segwit P2WPKH' + assert m[2] == 'Taproot P2TR' + assert m[3] == 'P2SH-Segwit' + fmts = { AF_CLASSIC: 'Classic P2PKH', AF_P2WPKH: 'Segwit P2WPKH', AF_P2WPKH_P2SH: 'P2SH-Segwit', + AF_P2TR: 'Taproot P2TR', } pick_menu_item(fmts[which_fmt]) @@ -473,16 +467,12 @@ def ss(x): assert 'Showing single addr' in body assert path in body - addr = body.split("\n")[3] + addr = addr_from_display_format(body.split("\n")[3]) addr_vs_path(addr, path, addr_fmt=which_fmt) need_keypress(KEY_QR if is_q1 else '4') - qr = cap_screen_qr().decode('ascii') - if which_fmt == AF_P2WPKH: - assert qr == addr.upper() - else: - assert qr == addr + verify_qr_address(which_fmt, addr) if get_setting('nfc', 0): # this is actually testing NFC export in qr code menu @@ -501,10 +491,23 @@ def ss(x): # remove QR from screen press_cancel() - addr_gen = generate_addresses_file(change=False, is_custom_single=True) + addr_gen = generate_addresses_file(change=False, is_custom_single=True, is_p2tr=which_fmt == AF_P2TR) f_path, f_addr = next(addr_gen) assert f_path == path assert f_addr == addr + press_select() # file written + + # msg sign + time.sleep(.1) + title, body = cap_story() + if which_fmt == AF_P2TR: + assert "Press (0) to sign message with this key" not in body + else: + assert "Press (0) to sign message with this key" in body + need_keypress('0') + msg = "COLDCARD the rock solid HWW" + sign_msg_from_address(msg, addr, path, which_fmt, "sd", "XTN") + press_cancel() else: n = 10 if (start_idx + n) > MAX_BIP32_IDX: @@ -528,9 +531,7 @@ def ss(x): qr_addr_list = [] need_keypress(KEY_QR if is_q1 else '4') for i in range(n): - qr = cap_screen_qr().decode('ascii') - if which_fmt == AF_P2WPKH: - qr = qr.lower() + qr = verify_qr_address(which_fmt) qr_addr_list.append(qr) need_keypress(KEY_RIGHT if is_q1 else "9") time.sleep(.5) @@ -542,11 +543,92 @@ def ss(x): assert sorted(qr_addr_list) == sorted(addr_dict.values()) - addr_gen = generate_addresses_file(start_idx=start_idx, change=False) + addr_gen = generate_addresses_file(start_idx=start_idx, change=False, is_p2tr=which_fmt==AF_P2TR) assert addr_dict == {p: a for i,(p, a) in enumerate(addr_gen) if i < n} # check the rest of file export for p, a in addr_gen: addr_vs_path(a, p, addr_fmt=which_fmt) + +@pytest.mark.bitcoind +@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2WPKH_P2SH, AF_CLASSIC, AF_P2TR]) +@pytest.mark.parametrize("acct_num", [None, "999"]) +def test_bitcoind_descriptor_address(addr_fmt, acct_num, bitcoind, goto_home, pick_menu_item, cap_story, + use_regtest, need_keypress, microsd_path, generate_addresses_file, + bitcoind_d_wallet_w_sk, load_export, settings_set, cap_menu, + goto_address_explorer, press_cancel, press_select, enter_number): + # export single sig descriptors (external, internal) + # export addressses from address explorer + # derive addresses from descriptor with bitcoind + # compare bitcoind derived addressses with those exported from address explorer + bitcoind = bitcoind_d_wallet_w_sk + use_regtest() + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Export Wallet") + pick_menu_item("Descriptor") + time.sleep(.1) + _, story = cap_story() + assert "This saves a ranged xpub descriptor" in story + assert "Press (1) to enter a non-zero account number" in story + assert "sensitive--in terms of privacy" in story + assert "not compromise your funds directly" in story + + if isinstance(acct_num, str): + need_keypress("1") # chosse account number + for ch in acct_num: + need_keypress(ch) # input num + press_select() # confirm selection + else: + press_select() # confirm story + + time.sleep(.1) + _, story = cap_story() + assert "press (1) to export receiving and change descriptors separately" in story + need_keypress("1") + + sig_check = True + if addr_fmt == AF_P2WPKH: + menu_item = "Segwit P2WPKH" + desc_prefix = "wpkh(" + elif addr_fmt == AF_P2WPKH_P2SH: + menu_item = "P2SH-Segwit" + desc_prefix = "sh(wpkh(" + elif addr_fmt == AF_P2TR: + menu_item = "Taproot P2TR" + desc_prefix = "tr(" + sig_check = False + else: + # addr_fmt == AF_CLASSIC: + menu_item = "Classic P2PKH" + desc_prefix = "pkh(" + + pick_menu_item(menu_item) + contents = load_export("sd", label="Descriptor", is_json=False, addr_fmt=addr_fmt, + sig_check=sig_check) + descriptors = contents.strip() + ext_desc, int_desc = descriptors.split("\n") + assert ext_desc.startswith(desc_prefix) + assert int_desc.startswith(desc_prefix) + + # check both external and internal + for chng in [False, True]: + goto_address_explorer() + if acct_num: + menu = cap_menu() + # can be "Account number" or "Account: N" + mi = [m for m in menu if "Account" in m] + assert len(mi) == 1 + pick_menu_item(mi[0]) + enter_number(acct_num) + + desc = int_desc if chng else ext_desc + settings_set("axi", 0) + pick_menu_item(menu_item) + cc_addrs_gen = generate_addresses_file(change=chng, is_p2tr=addr_fmt == AF_P2TR) + cc_addrs = [addr for deriv, addr in cc_addrs_gen] + bitcoind_addrs = bitcoind.deriveaddresses(desc, [0, 249]) + assert cc_addrs == bitcoind_addrs + # EOF diff --git a/testing/test_backup.py b/testing/test_backup.py index cf22468ab..d6768388f 100644 --- a/testing/test_backup.py +++ b/testing/test_backup.py @@ -2,23 +2,102 @@ # # Testing backups. # -import pytest, time, json, os, shutil +import pytest, time, json, os, shutil, re from constants import simulator_fixed_words, simulator_fixed_tprv from charcodes import KEY_QR from bip32 import BIP32Node from mnemonic import Mnemonic +from ckcc_protocol.protocol import CCProtocolPacker +@pytest.fixture +def override_bkpw(goto_home, pick_menu_item, cap_story, need_keypress, seed_story_to_words, + cap_menu, press_select, press_cancel, enter_complex, is_q1): + + def purge_current(exit=False): + time.sleep(.1) + title, story = cap_story() + if "(1) to forget current" in story: + need_keypress("1") + time.sleep(.1) + title, story = cap_story() + assert "Delete current stored password?" in story + press_select() + time.sleep(.1) + title, story = cap_story() + assert "(1) to forget current" not in story + if exit: + press_cancel() + + def doit(password=None, old_password=None): + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("I Am Developer.") + pick_menu_item("BKPW Override") + time.sleep(.1) + title, story = cap_story() + current_bkpw = None + if "(2) to show current active backup password" in story: + need_keypress("2") + time.sleep(.1) + title, story = cap_story() + assert 'Anyone with knowledge of the password will be able to decrypt your backups.' in story + press_select() + time.sleep(.1) + title, current_bkpw = cap_story() + current_bkpw = current_bkpw.strip() + press_select() + + if old_password: + assert current_bkpw == old_password, "old_password mismatch" + + if password is None: + # purge current bkpw + purge_current(exit=True) + return + + # purge what was there from before + purge_current() + + need_keypress("0") + enter_complex(password, apply=False, b39pass=False) + + time.sleep(.1) + title, story = cap_story() + assert "(2) to show current active backup password" in story + need_keypress("2") + press_select() # are you sure? + time.sleep(.1) + title, story = cap_story() + new_current_bkpw = story.strip() + press_select() + + time.sleep(.1) + title, story = cap_story() + if ((3*" ") in password) and not is_q1: + assert password.replace(" ", " ") == new_current_bkpw + else: + assert new_current_bkpw == password + + assert "(1) to forget current password" in story + assert "(0) to change" in story + + return doit + @pytest.fixture def backup_system(settings_set, settings_remove, goto_home, pick_menu_item, cap_story, need_keypress, cap_screen_qr, pass_word_quiz, get_setting, seed_story_to_words, press_cancel, is_q1, press_select, is_headless): - def doit(reuse_pw=False, save_pw=False, st=None, ct=False): + def doit(reuse_pw=None, save_pw=False, st=None, ct=False): # st -> seed type # ct -> cleartext backup if reuse_pw: - settings_set('bkpw', ' '.join('zoo' for _ in range(12))) + if reuse_pw is True: + reuse_pw = ['zoo' for _ in range(12)] + + settings_set('bkpw', ' '.join(reuse_pw)) else: settings_remove('bkpw') @@ -48,23 +127,24 @@ def doit(reuse_pw=False, save_pw=False, st=None, ct=False): need_keypress("6") time.sleep(.1) - _, story = cap_story() - assert "Are you SURE ?!?" in story + title, story = cap_story() + assert "Are you SURE ?!?" in (title if is_q1 else story) assert "**NOT** be encrypted" in story press_select() return # nothing more to be done if reuse_pw: - assert ' 1: zoo' in body - assert '12: zoo' in body + if len(reuse_pw) == 1: + reuse_pw = reuse_pw[0] + assert f"{reuse_pw[0]}...{reuse_pw[-1]}" in body + else: + assert (' 1: %s' % reuse_pw[0]) in body + assert ('12: %s' % reuse_pw[-1]) in body press_select() words = ['zoo'] * 12 - - time.sleep(0.1) - title, body = cap_story() else: assert title == 'NO-TITLE' - assert 'Record this' in body + assert 'Record this (12 word)' in body assert 'password:' in body words = seed_story_to_words(body) @@ -98,11 +178,36 @@ def doit(reuse_pw=False, save_pw=False, st=None, ct=False): return doit +@pytest.fixture +def make_big_notes(settings_set, sim_exec): + def doit(count=9): + print(">>> Making huge backup file") + + # - to bypass USB msg limit, append as we go + notes = [] + settings_set('notes', []) + for n in range(count): + v = { fld:('a'*30) if fld != 'misc' else 'b'*1800 + for fld in ['user', 'password', 'site', 'misc'] } + v['title'] = f'Note {n+1}' + notes.append(v) + rv = sim_exec(cmd := f'settings.current["notes"].append({v!r})') + assert 'error' not in rv.lower() + + rv = sim_exec('settings.changed()') + assert 'error' not in rv.lower() + + assert len(notes) == count + + return notes + + return doit + @pytest.mark.qrcode @pytest.mark.parametrize('multisig', [False, 'multisig']) @pytest.mark.parametrize('st', ["b39pass", "eph", None]) -@pytest.mark.parametrize('reuse_pw', [False, True]) +@pytest.mark.parametrize('reuse_pw', [True, False]) @pytest.mark.parametrize('save_pw', [False, True]) @pytest.mark.parametrize('seedvault', [False, True]) @pytest.mark.parametrize('pass_way', ["qr", None]) @@ -111,11 +216,11 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre pass_word_quiz, reset_seed_words, import_ms_wallet, get_setting, reuse_pw, save_pw, settings_set, settings_remove, press_select, generate_ephemeral_words, set_bip39_pw, verify_backup_file, - check_and_decrypt_backup, restore_backup_cs, clear_ms, seedvault, + check_and_decrypt_backup, restore_backup_cs, clear_miniscript, seedvault, restore_main_seed, import_ephemeral_xprv, backup_system, - press_cancel, sim_exec, pass_way): + press_cancel, sim_exec, pass_way, garbage_collector, make_big_notes): # Make an encrypted 7z backup, verify it, and even restore it! - clear_ms() + clear_miniscript() reset_seed_words() settings_set("seedvault", int(seedvault)) settings_set("seeds", [] if seedvault else None) @@ -123,20 +228,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre # test larger backup files > 10,000 bytes if multisig == False and st == None and not reuse_pw and not save_pw and not seedvault: # pick just one test case. - # - to bypass USB msg limit, append as we go - print(">>> Making huge backup file") - notes = [] - settings_set('notes', []) - for n in range(9): - v = { fld:('a'*30) if fld != 'misc' else 'b'*1800 - for fld in ['user', 'password', 'site', 'misc'] } - v['title'] = f'Note {n+1}' - notes.append(v) - rv = sim_exec(cmd := f'settings.current["notes"].append({v!r})') - print(rv) - assert 'error' not in rv.lower() - rv = sim_exec(cmd := f'settings.changed()') - assert 'error' not in rv.lower() + notes = make_big_notes() else: notes = None @@ -145,11 +237,15 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre import_ms_wallet(15, 15) press_select() time.sleep(.1) - assert len(get_setting('multisig')) == 1 + assert len(get_setting('miniscript')) == 1 + + if not reuse_pw: + # drop saved bkpw before we get to ephemeral settings + settings_remove("bkpw") if st == "b39pass": xfp_pass = set_bip39_pw("coinkite", reset=False, seed_vault=seedvault) - assert not get_setting('multisig', None) + assert not get_setting('miniscript', None) elif st == "eph": eph_seed = generate_ephemeral_words(num_words=24, dice=False, from_main=True, seed_vault=seedvault) @@ -158,7 +254,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre import_ms_wallet(15, 15, dev_key=True, common="605'/0'/0'") press_select() time.sleep(.1) - assert len(get_setting('multisig')) == 1 + assert len(get_setting('miniscript')) == 1 else: # create ephemeral seed - add to seed vault if necessary # and restore master (just so we have something in setting.seeds) @@ -175,7 +271,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre # multisig is only in main wallet # must not be copied from main to b39pass # must not be available after backup done - assert not get_setting('multisig', None) + assert not get_setting('miniscript', None) if notes: # verify large notes survived @@ -193,9 +289,12 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre print("filename %d: %s" % (copy, fn)) files.append(fn) + garbage_collector.append(microsd_path(fn)) # write extra copy. - need_keypress('2') + if not copy: + need_keypress('2') + time.sleep(.01) bk_a = open_microsd(files[0]).read() @@ -224,7 +323,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre # test verify on device (CRC check) if multisig: - avail_settings.append("multisig") + avail_settings.append("miniscript") restore_backup_cs(files[0], words, avail_settings=avail_settings, pass_way=pass_way) @@ -438,7 +537,6 @@ def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_wo assert "Press (1) to save" in body press_cancel() time.sleep(.01) - assert get_setting('bkpw', 'xxx') == 'xxx' title, story = cap_story() assert "Backup file written:" in story fn = story.split("\n\n")[1] @@ -463,28 +561,10 @@ def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_wo assert xfp_ui in sv_xfp_menu -def test_seed_vault_backup_frozen(reset_seed_words, settings_set, repl): - from test_ephemeral import SEEDVAULT_TEST_DATA - +def test_seed_vault_backup_frozen(reset_seed_words, settings_set, repl, build_test_seed_vault): reset_seed_words() settings_set("seedvault", 1) - - sv = [] - for item in SEEDVAULT_TEST_DATA: - xfp, entropy, mnemonic = item - - # build stashed encoded secret - entropy_bytes = bytes.fromhex(entropy) - if mnemonic: - vlen = len(entropy_bytes) - assert vlen in [16, 24, 32] - marker = 0x80 | ((vlen // 8) - 2) - stored_secret = bytes([marker]) + entropy_bytes - else: - stored_secret = entropy_bytes - - sv.append((xfp, stored_secret.hex(), f"[{xfp}]", "meta")) - + sv = build_test_seed_vault() settings_set("seeds", sv) bk = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1) assert 'Coldcard backup file' in bk @@ -492,13 +572,14 @@ def test_seed_vault_backup_frozen(reset_seed_words, settings_set, repl): assert target in bk -def test_clone_start(reset_seed_words, pick_menu_item, cap_story, goto_home): - sd_dir = "../unix/work/MicroSD" +def test_clone_start(reset_seed_words, pick_menu_item, cap_story, goto_home, src_root_dir, + sim_root_dir): + sd_dir = f"{sim_root_dir}/MicroSD" num_7z = len([i for i in os.listdir(sd_dir) if i.endswith(".7z")]) fname = "ccbk-start.json" reset_seed_words() goto_home() - shutil.copy(f"data/{fname}", sd_dir) + shutil.copy(f"{src_root_dir}/testing/data/{fname}", sd_dir) pick_menu_item("Advanced/Tools") pick_menu_item("Backup") pick_menu_item("Clone Coldcard") @@ -513,4 +594,252 @@ def test_clone_start(reset_seed_words, pick_menu_item, cap_story, goto_home): # TODO check file made is a good backup, with correct password +def test_bkpw_override(reset_seed_words, override_bkpw, goto_home, pick_menu_item, + cap_story, press_select, garbage_collector, microsd_path, + restore_backup_cs, is_q1): + reset_seed_words() # clean slate + old_pw = None + test_cases = [ + 32 * "a", + (26 * "?") + "!@#$%^&*()", + ] + if is_q1: + # not needed on mk4 - even tho works (takes too much time) + # Mk4 display is not suitable for these type of passwords anyways + test_cases += [ + "arm prob slot merc hub fiel wing aver tale undo diar boos army cabl mous teac drif risk frow achi poet ecol boss grit", + " ".join(12 * ["elevator"]), + " ".join(12 * ["fever"]), + 64 * "Q", + ] + + fnames = [] + for pw in test_cases: + override_bkpw(pw, old_pw) + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Backup") + pick_menu_item("Backup System") + time.sleep(1) + title, story = cap_story() + split_pw = pw.split(" ") + if len(split_pw) == 12: + assert (' 1: %s' % split_pw[0]) in story + assert ('12: %s' % split_pw[-1]) in story + else: + # not words of len 12 + assert ("%s...%s" % (pw[0], pw[-1])) in story + + press_select() + time.sleep(1) + title, story = cap_story() + assert "Backup file written" in story + fname = story.split("\n\n")[1] + garbage_collector.append(microsd_path(fname)) + fnames.append(fname) + press_select() + + for pw, fn in zip(test_cases, fnames): + restore_backup_cs(fn, pw, custom_bkpw=True) + + +@pytest.mark.parametrize('btype', ["classic", "custom_bkpw", "plaintext"]) +@pytest.mark.parametrize('force_tmp', [True, False]) +def test_restore_usb_backup(backup_system, set_seed_words, cap_story, verify_ephemeral_secret_ui, + settings_slots, reset_seed_words, word_menu_entry, confirm_tmp_seed, + dev, microsd_path, press_select, btype, force_tmp, + unit_test, restore_main_seed, cap_menu, is_q1, enter_complex): + + from test_ephemeral import SEEDVAULT_TEST_DATA + xfp_str, encoded_str, mnemonic = SEEDVAULT_TEST_DATA[2] + set_seed_words(mnemonic) + bkpw = 34*"Z" + plaintext = (btype == "plaintext") + password = False + + # ACTUAL BACKUP + if plaintext: + bk_pw = backup_system(ct=True) + elif btype == "custom_bkpw": + # encrypted but with custom pwd + password = True + bk_pw = backup_system(reuse_pw=[bkpw]) + else: + # classic word-based encrypted backup + bk_pw = backup_system() + + time.sleep(.1) + title, story = cap_story() + fname = story.split("\n\n")[1] + + # remove all saved slots, one of them will be the one where we just created backup + # slot where backup was created needs to be removed - otherwise we will load back to it + # and see multisig wallet there without the need for backup to actually copy it + for s in settings_slots(): + try: + os.remove(s) + except: pass + + # clear seed + unit_test('devtest/clear_seed.py') + + with open(microsd_path(fname), "rb") as f: + file_len, sha = dev.upload_file(f.read()) + + dev.send_recv(CCProtocolPacker.restore_backup(file_len, sha, password, plaintext, force_tmp), + timeout=None) + time.sleep(.2) + _, story = cap_story() + assert f"Restore uploaded backup as a {'temporary' if force_tmp else 'master'} seed" in story + press_select() + + time.sleep(.1) + if btype == "classic": + word_menu_entry(bk_pw, has_checksum=False) + elif password: + enter_complex(bkpw, apply=False, b39pass=False) + + time.sleep(.2) + mnemonic = mnemonic.split(" ") + + title, story = cap_story() + assert f"[{xfp_str}]" == title + assert "Above is the master fingerprint of the seed stored in the backup." in story + assert f"load backup as {'temporary' if force_tmp else 'master'} seed" in story + press_select() + time.sleep(.1) + + if force_tmp: + confirm_tmp_seed(seedvault=False) + verify_ephemeral_secret_ui(mnemonic=mnemonic, xpub=None, seed_vault=False) + restore_main_seed() + time.sleep(.1) + assert "New Seed Words" in cap_menu() + else: + _, story = cap_story() + assert "configured for best security practices" in story + press_select() + time.sleep(.1) + _, story = cap_story() + assert "now reboot" in story + + +@pytest.mark.parametrize('way', ["sd", "usb"]) +@pytest.mark.parametrize('tmp', [True, False]) +def test_refuse_backup(way, tmp, set_seed_words, backup_system, cap_story, unit_test, microsd_path, + dev, press_select, word_menu_entry, X, press_cancel, cap_menu, pick_menu_item, + reset_seed_words, need_keypress, get_secrets, goto_home): + + from test_ephemeral import SEEDVAULT_TEST_DATA + xfp_str, encoded_str, mnemonic = SEEDVAULT_TEST_DATA[0] + set_seed_words(mnemonic) + bk_pw = backup_system() + + time.sleep(.1) + title, story = cap_story() + fname = story.split("\n\n")[1] + press_select() + + if tmp: + reset_seed_words() + press_cancel() + else: + unit_test('devtest/clear_seed.py') + + if way == "usb": + with open(microsd_path(fname), "rb") as f: + file_len, sha = dev.upload_file(f.read()) + + dev.send_recv(CCProtocolPacker.restore_backup(file_len, sha), timeout=None) + time.sleep(.2) + press_select() + else: + if tmp: + pick_menu_item("Advanced/Tools") + pick_menu_item("Temporary Seed") + need_keypress("4") + pick_menu_item("Coldcard Backup") + else: + pick_menu_item("Import Existing") + pick_menu_item("Restore Backup") + + pick_menu_item(fname) + + time.sleep(.2) + word_menu_entry(bk_pw, has_checksum=False) + time.sleep(.2) + title, story = cap_story() + assert f"[{xfp_str}]" == title + assert "Above is the master fingerprint of the seed stored in the backup." in story + assert f"load backup as {'temporary' if tmp else 'master'} seed" in story + assert f"Press {X} to abort" in story + press_cancel() # refuse backup + time.sleep(.1) + if tmp: + cur_mnemonic = get_secrets()["mnemonic"] + assert mnemonic != cur_mnemonic # nothing was loaded + else: + goto_home() + assert "New Seed Words" in cap_menu() # nothing was loaded + + +@pytest.mark.parametrize('tmp', [True, False]) +def test_exit_dev_backup(tmp, unit_test, goto_home, pick_menu_item, need_keypress, src_root_dir, + microsd_path, press_cancel, cap_menu, cap_story): + fname = 'backup.7z' + fn = microsd_path(fname) + shutil.copy(f'{src_root_dir}/docs/backup.7z', fn) + + if not tmp: + unit_test('devtest/clear_seed.py') + + goto_home() + pick_menu_item('Advanced/Tools') + if tmp: + pick_menu_item("Danger Zone") + pick_menu_item('I Am Developer.') + pick_menu_item('Restore Bkup') + + time.sleep(.1) + pick_menu_item(fname) + + # do not write anything just exit + # yikes + press_cancel() + time.sleep(.2) + pick_menu_item("Restore Bkup") + press_cancel() + + +@pytest.mark.parametrize("fname", [ + '03edd162a5f57eece68d8eea3891e2a150383a225187179ecb1599efe00d16dd70-ccbk.7z', + ('W'*31) + ".7z", +]) +def test_backup_long_name_display(fname, goto_home, pick_menu_item, need_keypress, src_root_dir, + microsd_path, press_cancel, cap_screen, is_q1): + if not is_q1: + raise pytest.skip("Only Q") + + fn = microsd_path(fname) + shutil.copy(f'{src_root_dir}/docs/backup.7z', fn) + + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('Temporary Seed') + need_keypress("4") + pick_menu_item('Coldcard Backup') + + time.sleep(.1) + pick_menu_item(fname) + time.sleep(.1) + scr = cap_screen() + if len(fname) > 34: # CHARS_W + assert fname[:16] in scr + assert fname[-16:] in scr + else: + assert fname in scr + + press_cancel() + # EOF diff --git a/testing/test_bbqr.py b/testing/test_bbqr.py index a4010b494..f71d27f00 100644 --- a/testing/test_bbqr.py +++ b/testing/test_bbqr.py @@ -7,6 +7,7 @@ from binascii import a2b_hex from bbqr import split_qrs, join_qrs from charcodes import KEY_QR +from base64 import b32decode, b32encode # All tests in this file are exclusively meant for Q # @@ -149,6 +150,60 @@ def doit(data=None, str_expr=None, file_type='B', msg=None, setup=''): return doit + +@pytest.fixture +def split_scan_bbqr(scan_a_qr, goto_home, need_keypress): + + # take big data and send it via series of BBQr thru emulated scanner + def doit(raw_data, type_code, **kws): + goto_home() + need_keypress(KEY_QR) + + # def split_qrs(raw, type_code, encoding=None, + # min_split=1, max_split=1295, min_version=5, max_version=40 + actual_vers, parts = split_qrs(raw_data, type_code, **kws) + random.shuffle(parts) + + for p in parts: + scan_a_qr(p) + time.sleep(2.0 / len(parts)) # just so we can watch + + return doit + +@pytest.fixture +def try_sign_bbqr(cap_story, scan_a_qr, press_select, press_cancel, need_keypress, + readback_bbqr, split_scan_bbqr): + def doit(psbt, type_code="P", approve=True, nfc_push_tx=False, **kws): + + split_scan_bbqr(psbt, type_code, **kws) + + for r in range(20): + title, story = cap_story() + if 'OK TO SEND' in title: + break + time.sleep(.1) + else: + raise pytest.fail('never saw it?') + + if not approve: + press_cancel() + return + + # approve it + press_select() + + if nfc_push_tx: + return psbt, None, None + + time.sleep(.2) + + # expect signed txn back + file_type, rb = readback_bbqr() + assert file_type in 'TP' + return psbt, file_type, rb + + return doit + @pytest.mark.parametrize('size', [ 1, 20, 990, 2060*2, 5000, 65537] ) def test_show_bbqr_sizes(size, cap_screen_qr, sim_exec, render_bbqr): # test lengths @@ -164,14 +219,14 @@ def test_show_bbqr_sizes(size, cap_screen_qr, sim_exec, render_bbqr): assert ft == 'U' @pytest.mark.parametrize('src', [ 'rng', 'gpu', 'bigger'] ) -def test_show_bbqr_contents(src, cap_screen_qr, sim_exec, render_bbqr, load_shared_mod): +def test_show_bbqr_contents(src, cap_screen_qr, sim_exec, render_bbqr, load_shared_mod, src_root_dir): args = dict(msg=f'Test {src}', file_type='B') if src == 'rng': args['data'] = expect = prandom(500) # limited by simulated USB path elif src in { 'gpu', 'bigger' }: args['setup'] = 'from gpu_binary import BINARY' - cc_gpu_bin = load_shared_mod('cc_gpu_bin', '../shared/gpu_binary.py') + cc_gpu_bin = load_shared_mod('cc_gpu_bin', f'{src_root_dir}/shared/gpu_binary.py') if src == 'gpu': args['str_expr'] = 'BINARY' expect = cc_gpu_bin.BINARY @@ -188,67 +243,36 @@ def test_show_bbqr_contents(src, cap_screen_qr, sim_exec, render_bbqr, load_shar assert ft == 'B' @pytest.mark.bitcoind +@pytest.mark.reexport @pytest.mark.parametrize('size', [ 2, 10 ] ) @pytest.mark.parametrize('max_ver', [ 20 ] ) # 20 max due to 4k USB buffer limit @pytest.mark.parametrize('encoding', '2HZ' ) @pytest.mark.parametrize('partial', [False, True]) @pytest.mark.parametrize('base64str', [False, True]) -@pytest.mark.parametrize('segwit', [True, False]) -def test_bbqr_psbt(size, encoding, max_ver, partial, segwit, scan_a_qr, readback_bbqr, +@pytest.mark.parametrize('addr_fmt', ["p2wpkh", "p2tr"]) +def test_bbqr_psbt(size, encoding, max_ver, partial, addr_fmt, scan_a_qr, readback_bbqr, cap_screen_qr, render_bbqr, goto_home, use_regtest, cap_story, decode_psbt_with_bitcoind, decode_with_bitcoind, fake_txn, dev, start_sign, end_sign, press_cancel, press_select, need_keypress, - base64str): + base64str, try_sign_bbqr, signing_artifacts_reexport, sim_root_dir): num_in = size num_out = size*10 - def hack(psbt): - if partial: - # change first input to not be ours - pk = list(psbt.inputs[0].bip32_paths.keys())[0] - pp = psbt.inputs[0].bip32_paths[pk] - psbt.inputs[0].bip32_paths[pk] = b'what' + pp[4:] + inputs = [[]] * (num_in - int(partial)) + if partial: + inputs += [[addr_fmt, None, None, False]] # foreign input + + psbt = fake_txn(inputs, num_out, dev.master_xpub, addr_fmt=addr_fmt) - if not segwit: - psbt = fake_txn(num_in, num_out, dev.master_xpub, psbt_hacker=hack) - else: - psbt = fake_txn(num_in, num_out, dev.master_xpub, psbt_hacker=hack, - segwit_in=True, outstyles=['p2wpkh']) if base64str: psbt = base64.b64encode(psbt).decode() - open('debug/last.psbt', 'w' if base64str else 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/last.psbt', 'w' if base64str else 'wb') as f: + f.write(psbt) - goto_home() - need_keypress(KEY_QR) - - # def split_qrs(raw, type_code, encoding=None, - # min_split=1, max_split=1295, min_version=5, max_version=40 - actual_vers, parts = split_qrs(psbt, 'U' if base64str else 'P', - max_version=max_ver, encoding=encoding) - random.shuffle(parts) - - for p in parts: - scan_a_qr(p) - time.sleep(4.0 / len(parts)) # just so we can watch - - for r in range(20): - title, story = cap_story() - if 'OK TO SEND' in title: - break - time.sleep(.1) - else: - raise pytest.fail('never saw it?') - - # approve it - press_select() - - time.sleep(.2) - - # expect signed txn back - file_type, rb = readback_bbqr() - assert file_type in 'TP' + _, file_type, rb = try_sign_bbqr(psbt, type_code="U" if base64str else "P", + max_version=max_ver, encoding=encoding) if file_type == 'T': assert not partial @@ -274,6 +298,12 @@ def hack(psbt): assert oc == num_out press_cancel() # back to menu + _psbt, _txn = signing_artifacts_reexport("qr", tx_final=not partial, + encoding="binary") + if partial: + assert _psbt == rb + else: + assert _txn == rb @pytest.mark.parametrize('test_size', [7854, 4592, 758, 375, 465, # v15 capacity @@ -330,38 +360,13 @@ def test_split_unit(test_size, encoding, sim_exec, sim_eval): ]) def test_psbt_static(file, goto_home, cap_story, scan_a_qr, press_select, readback_bbqr, need_keypress, press_cancel, start_sign, - end_sign, bitcoind): - - goto_home() - need_keypress(KEY_QR) + end_sign, bitcoind, try_sign_bbqr): + # final tx qrs are versions 23,24,25 with open(file, "rb") as f: psbt = f.read() - # def split_qrs(raw, type_code, encoding=None, - # min_split=1, max_split=1295, min_version=5, max_version=40 - actual_vers, parts = split_qrs(psbt, 'P', max_version=20, encoding="2") - random.shuffle(parts) - - for p in parts: - scan_a_qr(p) - time.sleep(4.0 / len(parts)) # just so we can watch - - for r in range(20): - title, story = cap_story() - if 'OK TO SEND' in title: - break - time.sleep(.1) - else: - raise pytest.fail('never saw it?') - - # approve it - press_select() - time.sleep(.3) - - # expect signed txn back - file_type, rb = readback_bbqr() - assert file_type in 'TP' + _, file_type, rb = try_sign_bbqr(psbt, type_code="P", max_version=20, encoding="2") press_cancel() # back to menu @@ -373,4 +378,38 @@ def test_psbt_static(file, goto_home, cap_story, scan_a_qr, press_select, assert res["complete"] is True assert rb.hex() == res["hex"] + +def test_verify_signed_msg(goto_home, need_keypress, scan_a_qr, cap_story): + goto_home() + need_keypress(KEY_QR) + + data = """\n\n\n \t \n-----BEGIN BITCOIN SIGNED MESSAGE----- +5b9e372262952ed399dcdd4f5f08458a6d2811f120cddcb4267099f68f60207c addresses.csv +-----BEGIN BITCOIN SIGNATURE----- +tb1qupyd58ndsh7lut0et0vtrq432jvu9jtdyws9n9 +KDOloGMDU3fv+Y3NRSe17SoO4uSKo9IUU2+baJ/pqaHZBuvmW6j5nnv/N4M5BCVawiUig/qzExZpFsA7ZKzlUmU= +-----END BITCOIN SIGNATURE-----\n\n\n\n""" + + actual_vers, parts = split_qrs(data, 'U', max_version=20) + + for p in parts: + scan_a_qr(p) + time.sleep(4.0 / len(parts)) # just so we can watch + + title, story = cap_story() + assert "Good signature by address" in story + +@pytest.mark.qrcode +@pytest.mark.manual +@pytest.mark.parametrize("i", range(1,25)) +def test_qr_sizes(i, dev, fake_txn, press_cancel, cap_screen_qr, try_sign_bbqr): + + # QRs from version 10 to version 25, everything from v26(included) and above is BBQR + # only v17 contains 2 lines of txid + psbt = fake_txn(1, i, dev.master_xpub, addr_fmt='p2wpkh') + + try_sign_bbqr(psbt, type_code="P") + cap_screen_qr() + press_cancel() + # EOF diff --git a/testing/test_bip39pw.py b/testing/test_bip39pw.py index decd2b470..4dde91aeb 100644 --- a/testing/test_bip39pw.py +++ b/testing/test_bip39pw.py @@ -50,7 +50,7 @@ def test_b9p_basic(pw, set_bip39_pw): set_bip39_pw(pw) -@pytest.fixture() +@pytest.fixture def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story, sim_execfile, press_select): @@ -274,10 +274,9 @@ def test_cancel_on_empty_added_numbers(pick_menu_item, is_q1, cap_menu, @pytest.mark.parametrize('stype', ["bip39pw", "words", "xprv", None]) -def test_lockdown_ux(stype, pick_menu_item, set_bip39_pw, goto_home, - press_cancel, get_setting, reset_seed_words, - generate_ephemeral_words, import_ephemeral_xprv, - press_select, is_q1, cap_story, cap_menu): +def test_lockdown_ux(stype, pick_menu_item, set_bip39_pw, goto_home, is_q1, + get_setting, reset_seed_words, import_ephemeral_xprv, + generate_ephemeral_words, cap_story, cap_menu, press_select): # test UX and operation of the 'seed lockdown' option if stype: @@ -313,7 +312,10 @@ def test_lockdown_ux(stype, pick_menu_item, set_bip39_pw, goto_home, assert "Convert currently used BIP-39 passphrase to master seed" in story assert "but the passphrase itself is erased" in story - press_cancel() + assert "Press (4) to prove you read to the end of this message and accept all consequences" in story + press_select() # enter does not active, sends you back to menu + time.sleep(.1) + assert "Lock Down Seed" in cap_menu() reset_seed_words() # real code does reboot, which is poorly simulated; avoid that # this needs to be tested with real HW !!! @@ -484,7 +486,6 @@ def test_tmp_on_xprv_master(generate_ephemeral_words, cap_menu, go_to_passphrase time.sleep(.1) title, story = cap_story() - assert parent_fp in title # no choice story assert "current active temporary seed" in story press_select() diff --git a/testing/test_bsms.py b/testing/test_bsms.py new file mode 100644 index 000000000..2face156f --- /dev/null +++ b/testing/test_bsms.py @@ -0,0 +1,1661 @@ +import sys +sys.path.append("../shared") +import pytest, time, pdb, os, random, hashlib, base64 +from constants import simulator_fixed_tprv +from charcodes import KEY_NFC +from bsms import CoordinatorSession, Signer +from bsms.encryption import key_derivation_function, decrypt, encrypt +from bsms.util import bitcoin_msg, str2path +from bsms.bip32 import PrvKeyNode, PubKeyNode +from bsms.ecdsa import ecdsa_verify, ecdsa_recover +from bsms.address import p2wsh_address, p2sh_p2wsh_address +from descriptor import MultisigDescriptor, append_checksum +from msg import sign_message +from bip32 import BIP32Node + + +BSMS_VERSION = "BSMS 1.0" +ALLOWED_PATH_RESTRICTIONS = "/0/*,/1/*" + + +# keys in settings object +BSMS_SETTINGS = "bsms" +BSMS_SIGNER_SETTINGS = "s" +BSMS_COORD_SETTINGS = "c" + + +et_map = { + "1": "STANDARD", + "2": "EXTENDED", + "3": "NO_ENCRYPTION" +} + +af_map = { + "p2wsh": 14, + "p2sh-p2wsh": 26 +} + + +def coordinator_label(M, N, addr_fmt, et, index=None): + fmt_str = "%dof%d_%s_%s" % (M, N, "native" if addr_fmt == "p2wsh" else "nested", et) + if index: + fmt_str = "%d %s" % (index, fmt_str) + return fmt_str + + +def assert_coord_summary(title, story, M, N, addr_fmt, et): + assert title == "SUMMARY" + assert f"{M} of {N}" in story + assert f"Address format:\n{addr_fmt}" in story + assert f"Encryption type:\n{et_map[et].replace('_', ' ')}" in story + tokens = story.split("\n\n")[3:-1] + if et == "1": + assert len(tokens) == 1 + elif et == "2": + assert len(tokens) == N + else: + assert len(tokens) == 0 + return tokens + +@pytest.fixture +def make_coordinator_round1(settings_remove, settings_get, settings_set, microsd_path, virtdisk_path): + def doit(M, N, addr_fmt, et, way, purge_bsms=True, tokens_only=False): + if purge_bsms: + settings_remove(BSMS_SETTINGS) # clear bsms + bsms = settings_get(BSMS_SETTINGS) or {} + tokens = [] + if et == "1": + tokens = [os.urandom(8).hex()] + elif et == "2": + tokens = [os.urandom(16).hex() for _ in range(N)] + coord_tuple = (M, N, af_map[addr_fmt], et, tokens) + if BSMS_COORD_SETTINGS in bsms: + bsms[BSMS_COORD_SETTINGS].append(coord_tuple) + else: + bsms[BSMS_COORD_SETTINGS] = [coord_tuple] + settings_set(BSMS_SETTINGS, bsms) + if tokens_only: + return tokens + if way == "sd": + path_fn = microsd_path + elif way == "vdisk": + path_fn = virtdisk_path + else: + return tokens + for token_hex in tokens: + basename = "bsms_%s.token" % token_hex[:4] + with open(path_fn(basename), "w") as f: + f.write(token_hex) + return tokens + return doit + + +def bsms_sr1_fname(token, is_extended, suffix, index=None): + fname = "bsms_sr1" + if is_extended: + fname += "_" + token[:4] + else: + if index: # ignores index = 0 + fname += "-" + str(index) + return fname + suffix + + +@pytest.fixture +def make_signer_round1(settings_get, settings_set, settings_remove, microsd_path, virtdisk_path): + def doit(token, way, root_xprv=None, bsms_version=BSMS_VERSION, description=None, purge_bsms=True, + add_to_settings=False, data_only=False, index=None, wrong_sig=False, wrong_encryption=False): + is_extended = len(token) == 32 + if purge_bsms: + settings_remove(BSMS_SETTINGS) # clear bsms + if add_to_settings: + bsms = settings_get(BSMS_SETTINGS) or {} + if BSMS_SIGNER_SETTINGS in bsms: + bsms[BSMS_COORD_SETTINGS].append(token) + else: + bsms[BSMS_SIGNER_SETTINGS] = [token] + + if root_xprv: + wk = BIP32Node.from_wallet_key(root_xprv) + else: + wk = BIP32Node.from_master_secret(os.urandom(32), netcode="XTN") + root_xfp = wk.fingerprint().hex() + paths = ["48'/1'/0'/2'", "48'/1'/0'/1'", "0'/1'/0'/0'", "0'", "100'/0'"] + path = random.choice(paths) + sk = wk.subkey_for_path(path) + xpub = sk.hwif(as_private=False) + key_expr = "[%s/%s]%s" % (root_xfp, path, xpub) + data = "%s\n" % bsms_version + data += "%s\n" % token + data += "%s\n" % key_expr + if description is None: + description = "Coldcard Signer %s" % root_xfp + data += "%s" % description + sig = sign_message(bytes(sk.node.private_key), + data.encode()+b"ff" if wrong_sig else data.encode(), + b64=True) + data += "\n%s" % sig + suffix = ".txt" + mode = "wt" + if token != "00": + suffix = ".dat" + mode = "wb" + dkey = key_derivation_function(token) + if wrong_encryption: + wrong = "ffff" + token[4:] + dkey = key_derivation_function(wrong) + data = encrypt(dkey, token, data) + data = bytes.fromhex(data) + if data_only: + return data + if way != "nfc": + if way == "sd": + path_fn = microsd_path + else: + # vdisk + path_fn = virtdisk_path + basename = bsms_sr1_fname(token, is_extended, suffix, index) + with open(path_fn(basename), mode) as f: + f.write(data) + return data + + return doit + + +def ms_address_from_descriptor_bsms(desc_obj: MultisigDescriptor, subpath="0/0", network="XTN"): + testnet = True if network == "XTN" else False + nodes = [ + PubKeyNode.parse(ek).derive_path(str2path(subpath)) + for _, _, ek in desc_obj.keys + ] + secs = [node.sec() for node in nodes] + secs.sort() + if desc_obj.addr_fmt == af_map["p2wsh"]: + address = p2wsh_address(secs, desc_obj.M, sortedmulti=True, testnet=testnet) + else: + address = p2sh_p2wsh_address(secs, desc_obj.M, sortedmulti=True, testnet=testnet) + return address + + +def bsms_cr2_fname(token, is_extended, suffix): + fname = "bsms_cr2" + if is_extended: + fname += "_" + token[:4] + return fname + suffix + + +@pytest.fixture +def make_coordinator_round2(make_coordinator_round1, settings_get, settings_set, microsd_path, virtdisk_path): + def doit(M, N, addr_fmt, et, way, has_ours=True, ours_no=1, path_restrictions=ALLOWED_PATH_RESTRICTIONS, + bsms_version=BSMS_VERSION, sortedmulti=True, wrong_address=False, wrong_encryption=False, + wrong_chain=False, add_checksum=False, wrong_checksum=False): + tokens = make_coordinator_round1(M, N, addr_fmt, et, way=way, purge_bsms=True, tokens_only=True) + range_num = N if has_ours is False else N - ours_no + keys = [] + for _ in range(range_num): + wk = BIP32Node.from_master_secret(os.urandom(32), netcode="BTC" if wrong_chain else "XTN") + root_xfp = wk.fingerprint().hex() + paths = ["48'/1'/0'/2'", "48'/1'/0'/1'", "0'/1'/0'/0'", "0'", "100'/0'"] + path = random.choice(paths) + sk = wk.subkey_for_path(path) + xpub = sk.hwif(as_private=False) + keys.append((root_xfp, "m/" + path, xpub)) + if has_ours: + for _ in range(ours_no): + wk = BIP32Node.from_wallet_key(simulator_fixed_tprv) + root_xfp = wk.fingerprint().hex() + paths = ["48'/1'/0'/2'", "48'/1'/0'/1'", "0'/1'/0'/0'", "0'", "100'/0'"] + path = random.choice(paths) + sk = wk.subkey_for_path(path) + xpub = sk.hwif(as_private=False) + keys.append((root_xfp, "m/" + path, xpub)) + + desc_obj = MultisigDescriptor(M=M, N=N, addr_fmt=af_map[addr_fmt], keys=keys) + desc = desc_obj._serialize(int_ext=True) + wcs = append_checksum(desc).split("#")[-1] + desc = desc.replace("/<0;1>/*", "/**") + if add_checksum: + desc = append_checksum(desc) + elif wrong_checksum: + desc = desc + "#" + wcs + if not sortedmulti: + desc = desc.replace("sortedmulti", "multi") + desc_template = "%s\n" % bsms_version + desc_template += "%s\n" % desc + desc_template += "%s\n" % path_restrictions + if wrong_address: + addr = ms_address_from_descriptor_bsms(desc_obj, subpath="1000/100") + else: + addr = ms_address_from_descriptor_bsms(desc_obj) + desc_template += "%s" % addr + + # create signer artificialy and produce correct descriptor template file + bsms = settings_get(BSMS_SETTINGS) or {} + bsms[BSMS_SIGNER_SETTINGS] = [] # purge + if not tokens: + token = "00" + bsms[BSMS_SIGNER_SETTINGS].append(token) + res = desc_template + else: + token = tokens[0] + # same for STANDARD and EXTENDED --> encrypt + bsms[BSMS_SIGNER_SETTINGS].append(token) + if wrong_encryption: + res = encrypt(key_derivation_function(os.urandom(16).hex()), token, desc_template) + else: + res = encrypt(key_derivation_function(token), token, desc_template) + res = bytes.fromhex(res) + + settings_set(BSMS_SETTINGS, bsms) + if way != "nfc": + if way == "sd": + path_fn = microsd_path + else: + # vdisk + path_fn = virtdisk_path + mode = "wb" if et in ["1", "2"] else "wt" + suffix = ".dat" if et in ["1", "2"] else ".txt" + basename = bsms_cr2_fname(token, et == "2", suffix) + with open(path_fn(basename), mode) as f: + f.write(res) + + return res, token + + return doit + + +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk"]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +def test_coordinator_round1(way, encryption_type, M_N, addr_fmt, clear_miniscript, goto_home, need_keypress, + pick_menu_item, cap_menu, cap_story, microsd_path, settings_remove, + nfc_read_text, request, settings_get, microsd_wipe, press_select, + is_q1, press_cancel): + if way == "vdisk": + virtdisk_wipe = request.getfixturevalue("virtdisk_wipe") + virtdisk_path = request.getfixturevalue("virtdisk_path") + virtdisk_wipe() + + M, N = M_N + + microsd_wipe() + settings_remove(BSMS_SETTINGS) # clear bsms + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 1 # nothing should be in menu at this point but round 1 + pick_menu_item('Create BSMS') + # choose number of signers N + for num in str(N): + need_keypress(num) + press_select() + # choose threshold M + for num in str(M): + need_keypress(num) + press_select() + if addr_fmt == "p2wsh": + press_select() + else: + need_keypress("2") + time.sleep(0.1) + title, story = cap_story() + assert story == "Choose encryption type. Press (1) for STANDARD encryption, (2) for EXTENDED, and (3) for no encryption" + need_keypress(encryption_type) + time.sleep(0.1) + title, story = cap_story() + tokens = assert_coord_summary(title, story, M, N, addr_fmt, encryption_type) + press_select() # confirm summary + time.sleep(0.1) + title, story = cap_story() + assert "Press (1) to participate as co-signer in this BSMS" in story + press_select() # continue normally + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "3": + assert story == "Success. Coordinator round 1 saved." + else: + if way == "sd": + if "Press (1) to save BSMS token file(s) to SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "nfc": + + if f"press {KEY_NFC if is_q1 else '(3)'} to share via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "3") + time.sleep(0.2) + bsms_tokens = nfc_read_text() + time.sleep(0.2) + press_cancel() # exit NFC UI simulation + time.sleep(0.5) + else: + # virtual disk + if "press (2) to save to Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("2") + + read_tokens = [] + if way == "nfc" and encryption_type != "3": + read_tokens = bsms_tokens.split("\n\n") + else: + time.sleep(0.2) + _, story = cap_story() + assert 'BSMS token file(s) written' in story + fnames = story.split('\n\n')[2:] + # check token files contains first 4 chars of token + try: + token_start = set([tok.split(" ")[1][:4] for tok in tokens]) + except IndexError: + # only one token - special case without numbering + assert len(tokens) == 1 + token_start = set([tokens[0].split("\n")[1][:4]]) + token_fnames_start = set([fn.replace(".token", "").split("_")[-1].split("-")[0] for fn in fnames]) + assert token_start == token_fnames_start + read_tokens = [] + for fname in fnames: + if way == "vdisk": + path = virtdisk_path(fname) + else: + path = microsd_path(fname) + with open(path, 'rt') as f: + token = f.read().strip() + read_tokens.append(token) + + if encryption_type == "1": + assert len(read_tokens) == 1 + elif encryption_type == "2": + assert len(read_tokens) == N + else: + assert len(tokens) == 0 + + press_select() # confirm success or files written story + time.sleep(0.1) + menu = cap_menu() + assert len(menu) == 2 + current_coord_menu_item = coordinator_label(M, N, addr_fmt, encryption_type, index=1) + assert menu[0] == current_coord_menu_item + assert menu[1] == "Create BSMS" + # check correct summary in detail + pick_menu_item(menu[0]) + time.sleep(0.1) + menu = cap_menu() + assert len(menu) == 3 + assert menu[0] == "Round 2" + assert menu[1] == "Detail" + assert menu[2] == "Delete" + pick_menu_item("Detail") + time.sleep(0.1) + title, story = cap_story() + assert_coord_summary(title, story, M, N, addr_fmt, encryption_type) + press_select() + # check correct coord tuple saved + bsms_settings = settings_get(BSMS_SETTINGS) + if BSMS_SIGNER_SETTINGS in bsms_settings: + assert bsms_settings[BSMS_SIGNER_SETTINGS] == [] + coord_settings = bsms_settings[BSMS_COORD_SETTINGS] + assert len(coord_settings) == 1 + assert coord_settings[0] == ( + M, N, af_map[addr_fmt], encryption_type, + [tok.split(" ")[-1].replace("Tokens:\n", "") for tok in tokens] if tokens else [] + ) + # delete coordinator settings + pick_menu_item("Delete") + time.sleep(0.1) + menu = cap_menu() + assert len(menu) == 1 + assert menu[0] == "Create BSMS" + bsms_settings = settings_get(BSMS_SETTINGS) + coord_settings = bsms_settings[BSMS_COORD_SETTINGS] + assert coord_settings == [] + + +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk"]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +def test_signer_round1(way, encryption_type, M_N, addr_fmt, clear_miniscript, goto_home, need_keypress, + cap_story, microsd_path, settings_remove, nfc_read_text, request, settings_get, + make_coordinator_round1, nfc_write_text, microsd_wipe, press_select, + is_q1, pick_menu_item, cap_menu, press_cancel): + if way == "vdisk": + virtdisk_wipe = request.getfixturevalue("virtdisk_wipe") + virtdisk_path = request.getfixturevalue("virtdisk_path") + virtdisk_wipe() + + M, N = M_N + microsd_wipe() + tokens = make_coordinator_round1(M, N, addr_fmt, encryption_type, way) + if encryption_type != "3": + assert tokens + else: + assert tokens == [] + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + menu = cap_menu() + assert len(menu) == 1 # nothing should be in menu at this point but round 1 + pick_menu_item('Round 1') + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "3": + token = "00" + need_keypress("3") # no token (unencrypted BSMS) + else: + token = random.choice(tokens) + if way == "sd": + if "Press (1) to import token file from SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "nfc": + if f"{KEY_NFC if is_q1 else '(4)'} to import via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "4") + time.sleep(0.1) + nfc_write_text(token) + time.sleep(0.4) + else: + # virtual disk + if "(6) to import from Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("6") + + if way != "nfc": + time.sleep(0.2) + fname = "bsms_%s.token" % token[:4] + pick_menu_item(fname) + + time.sleep(0.1) + title, story = cap_story() + assert "You have entered token:\n%s" % token in story + press_select() + time.sleep(0.1) + _, story = cap_story() + # address format a.k.a. SLIP derivation path - ignore and use SLIP agnostic + assert "Choose co-signer address format for correct SLIP derivation path" in story + press_select() # default + # account number prompt + press_select() + time.sleep(0.1) + _, story = cap_story() + # textual key description + assert "Choose key description" in story + press_select() # default + time.sleep(0.1) + title, story = cap_story() + suffix = ".txt" if encryption_type == "3" else ".dat" + mode = "rt" if encryption_type == "3" else "rb" + if way == "sd": + if "Press (1) to save BSMS signer round 1 file to SD Card" in story: + need_keypress("1") + elif way == "nfc": + if f"press {KEY_NFC if is_q1 else '(3)'} to share via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "3") + time.sleep(0.2) + signer_r1 = nfc_read_text() + time.sleep(0.2) + press_cancel() # exit NFC UI simulation + time.sleep(0.5) + else: + # virtual disk + if "press (2) to save to Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("2") + + if way != "nfc": + time.sleep(0.2) + _, story = cap_story() + assert 'BSMS signer round 1 file written' in story + fname = story.split('\n\n')[-1] + assert suffix in fname + if encryption_type == "2": + # check token files contains first 4 chars of token or just 00 + assert token[:4] == fname.split(".")[0][-4:] + if way == "vdisk": + path = virtdisk_path(fname) + else: + path = microsd_path(fname) + with open(path, mode) as f: + signer_r1 = f.read() + + bsms = settings_get(BSMS_SETTINGS) + assert len(bsms[BSMS_SIGNER_SETTINGS]) == 1 + assert bsms[BSMS_SIGNER_SETTINGS][0] == token + + if encryption_type in ["1", "2"]: + # decrypt + if isinstance(signer_r1, bytes): + signer_r1 = signer_r1.hex() + signer_r1 = decrypt(key_derivation_function(token), signer_r1) + + version, tok, key_exp, description, sig = signer_r1.strip().split("\n") + assert version == BSMS_VERSION + assert tok == token + close_index = key_exp.find("]") + assert key_exp[0] == "[" and close_index != -1 + key_orig_info = key_exp[1:close_index] # remove brackets + xpub = key_exp[close_index + 1:] + assert xpub[:4] in ["xpub", "tpub"] + xfp, path = key_orig_info.split("/", 1) + # pycoin xpub check + mk = BIP32Node.from_wallet_key(simulator_fixed_tprv) + sk = mk.subkey_for_path(path) + pycoin_xpub = sk.hwif(as_private=False) + assert xpub == pycoin_xpub + # bsms lib xpub check + mk0 = PrvKeyNode.parse(simulator_fixed_tprv, testnet=True) + sk0 = mk0.derive_path(str2path(path)) + bsms_xpub = sk0.extended_public_key() + assert xpub == bsms_xpub + signed_data = "\n".join([version, tok, key_exp, description]) + # verify msg bsms lib (pure python ecdsa) + signed_digest = bitcoin_msg(signed_data) + decoded_sig = base64.b64decode(sig) + recovered_sec = ecdsa_recover(signed_digest, decoded_sig) + assert ecdsa_verify(signed_digest, decoded_sig, recovered_sec), "Signature invalid" + + +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk"]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +@pytest.mark.parametrize("auto_collect", [True, False]) +def test_coordinator_round2(way, encryption_type, M_N, addr_fmt, auto_collect, clear_miniscript, goto_home, + cap_menu, cap_story, microsd_path, settings_remove, nfc_read_text, request, + settings_get, make_coordinator_round1, make_signer_round1, nfc_write_text, + microsd_wipe, pick_menu_item, press_select, is_q1, need_keypress, press_cancel): + def get_token(index): + if len(tokens) == 1 and encryption_type == "1": + token = tokens[0] + elif len(tokens) == N and encryption_type == "2": + token = tokens[index] + else: + token = "00" + return token + + if way == "vdisk": + virtdisk_wipe = request.getfixturevalue("virtdisk_wipe") + virtdisk_path = request.getfixturevalue("virtdisk_path") + virtdisk_wipe() + + M, N = M_N + microsd_wipe() + tokens = make_coordinator_round1(M, N, addr_fmt, encryption_type, way=way, tokens_only=True) + all_data = [] + for i in range(N): + token = get_token(i) + index = None + if encryption_type != "2": + index = i + 1 + + all_data.append(make_signer_round1(token, way, purge_bsms=False, index=index)) + + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 2 + coord_menu_item = coordinator_label(M, N, addr_fmt, encryption_type, index=1) + assert coord_menu_item in menu + pick_menu_item(coord_menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if way == "sd": + if "Press (1) to import co-signer round 1 files from SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "vdisk": + if "(2) to import from Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("2") + else: + # NFC + if f"{KEY_NFC if is_q1 else '(3)'} to import via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "3") + + if way == "nfc": + if auto_collect is True: + pytest.skip("No auto-collection for NFC") + for i, data in enumerate(all_data): + time.sleep(0.1) + title, story = cap_story() + token = get_token(i) + if encryption_type == "2": + expect = "Share co-signer #%d round-1 data for token starting with %s" % (i + 1, token[:4]) + else: + expect = "Share co-signer #%d round-1 data" % (i + 1) + assert expect in story + press_select() + time.sleep(.2) + nfc_write_text(data.hex() if isinstance(data, bytes) else data) + time.sleep(0.3) + else: + suffix = ".txt" if encryption_type == "3" else ".dat" + time.sleep(0.1) + title, story = cap_story() + assert "Press OK to pick co-signer round 1 files manually, or press (1) to attempt auto-collection." in story + assert "For auto-collection to succeed all filenames have to start with 'bsms_sr1'" in story + suffix_target = "and end with extension '%s'" % suffix + assert suffix_target in story + if encryption_type == "2": + assert "In addition for EXTENDED encryption all files must contain first four characters of respective token." in story + elif encryption_type == "3": + assert ("In addition for NO ENCRYPTION cases, number of files with above mentioned" + " pattern and suffix must equal number of signers (N).") in story + assert "If above is not respected auto-collection fails and defaults to manual selection of files." in story + if auto_collect: + need_keypress("1") + else: + press_select() # continue with manual selection + for i, _ in enumerate(all_data, start=1): + token = get_token(i - 1) + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "2": + expect = 'Select co-signer #%d file containing round 1 data for token starting with %s' % (i, token[:4]) + else: + expect = 'Select co-signer #%d file containing round 1 data' % i + expect += '. File extension has to be "%s"' % suffix + assert expect in story + press_select() + menu_item = bsms_sr1_fname(token, encryption_type == "2", suffix, i) + pick_menu_item(menu_item) + + time.sleep(0.1) + _, story = cap_story() + if way == "sd": + if "Press (1) to save BSMS descriptor template file(s) to SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "nfc": + if f"{KEY_NFC if is_q1 else '(3)'} to share via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "3") + else: + # virtual disk + if "(2) to save to Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("2") + + descriptor_templates = [] + if way == "nfc": + # not implemented because of the fake nfc limit + # pytest skip will be raised before we can get here + if encryption_type == "2": + for i, token in enumerate(tokens, start=1): + time.sleep(.1) + title, story = cap_story() + expect = "Exporting data for co-signer #%d with token %s" % (i, token[:4]) + assert expect in story + press_select() + time.sleep(.5) + rv = nfc_read_text() + time.sleep(.5) + descriptor_templates.append(rv) + press_cancel() # exit animation + + time.sleep(.1) + title, story = cap_story() + assert "All done" in story + press_select() + else: + time.sleep(.5) + rv = nfc_read_text() + time.sleep(.5) + descriptor_templates.append(rv) + press_cancel() # exit animation + else: + if way == "sd": + path_fn = microsd_path + else: + path_fn = virtdisk_path + time.sleep(0.1) + _, story = cap_story() + assert "BSMS descriptor template file(s) written." in story + fnames = story.split("\n\n")[1:] + if encryption_type == "2": + for fname, token in zip(fnames, tokens): + assert token[:4] in fname + + for fname in fnames: + with open(path_fn(fname), "rt" if encryption_type == "3" else "rb") as f: + desc_temp = f.read() + descriptor_templates.append(desc_temp) + + assert descriptor_templates + if encryption_type == "2": + # each file encrypted with different token/key + templates = set() + for token, desc_template in zip(tokens, descriptor_templates): + plaintext = decrypt( + key_derivation_function(token), + desc_template if isinstance(desc_template, str) else desc_template.hex() + ) + assert plaintext + templates.add(plaintext) + assert len(templates) == 1 + # pick last to be the template + the_template = plaintext + elif encryption_type == "1": + # just one template but encrypted + assert len(descriptor_templates) == 1 + plaintext = decrypt( + key_derivation_function(get_token(0)), + descriptor_templates[0] if isinstance(descriptor_templates[0], str) else descriptor_templates[0].hex() + ) + assert plaintext + the_template = plaintext + else: + assert len(descriptor_templates) == 1 + the_template = descriptor_templates[0] + + version, descriptor, pth_restrictions, addr = the_template.split("\n") + assert version == BSMS_VERSION + try: + MultisigDescriptor.checksum_check(descriptor) + descriptor = descriptor.split("#")[0] + except ValueError: + pass + # replace /** so we can parse it + descriptor = descriptor.replace("/**", "/0/*") + descriptor = append_checksum(descriptor) + desc_obj = MultisigDescriptor.parse(descriptor) + assert len(desc_obj.keys) == N + assert pth_restrictions == ALLOWED_PATH_RESTRICTIONS + # bsms lib test ms address + address = ms_address_from_descriptor_bsms(desc_obj) + assert addr == address + + +@pytest.mark.parametrize("refuse", [True, False]) +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk"]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("with_checksum", [True, False]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +def test_signer_round2(refuse, way, encryption_type, M_N, addr_fmt, clear_miniscript, goto_home, need_keypress, pick_menu_item, + cap_menu, cap_story, microsd_path, settings_remove, nfc_read_text, request, settings_get, + make_coordinator_round2, nfc_write_text, microsd_wipe, with_checksum, + press_select, press_cancel, is_q1): + if way == "vdisk": + virtdisk_wipe = request.getfixturevalue("virtdisk_wipe") + virtdisk_path = request.getfixturevalue("virtdisk_path") + virtdisk_wipe() + M, N = M_N + clear_miniscript() + microsd_wipe() + desc_template, token = make_coordinator_round2(M, N, addr_fmt, encryption_type, way=way, add_checksum=with_checksum) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + menu = cap_menu() + assert len(menu) == 2 + assert "Round 1" in menu + menu_item = "1 %s" % token[:4] + assert menu_item in menu + pick_menu_item(menu_item) + menu = cap_menu() + assert len(menu) == 3 + assert "Detail" in menu + assert "Delete" in menu + assert "Round 2" in menu + pick_menu_item("Detail") + time.sleep(0.1) + _, story = cap_story() + assert token in story + assert str(int(token, 16)) in story + press_select() + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if way == "sd": + if "Press (1) to import descriptor template file from SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "vdisk": + if "(2) to import from Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("2") + else: + # NFC + if f"{KEY_NFC if is_q1 else '(3)'} to import via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "3") + + if way == "nfc": + time.sleep(0.1) + nfc_write_text(desc_template.hex() if isinstance(desc_template, bytes) else desc_template) + time.sleep(0.3) + else: + suffix = ".txt" if encryption_type == "3" else ".dat" + time.sleep(0.1) + menu_item = bsms_cr2_fname(token, encryption_type == "2", suffix) + pick_menu_item(menu_item) + + time.sleep(0.5) + _, story = cap_story() + assert "Create new multisig wallet?" in story + assert "bsms" in story # part of the name + policy = "Policy: %d of %d" % (M, N) + assert policy in story + assert addr_fmt.upper() in story + ms_wal_name = story.split("\n\n")[1].split("\n")[-1].strip() + if refuse: + press_cancel() + time.sleep(0.1) + menu = cap_menu() + assert ms_wal_name not in menu + bsms_settings = settings_get(BSMS_SETTINGS) + # signer round 2 NOT removed + assert bsms_settings.get(BSMS_SIGNER_SETTINGS) + else: + press_select() + time.sleep(0.1) + menu = cap_menu() + assert ms_wal_name in menu + bsms_settings = settings_get(BSMS_SETTINGS) + # signer round 2 removed + assert not bsms_settings.get(BSMS_SIGNER_SETTINGS, None) + + +@pytest.mark.parametrize("token", [ + "f" * 15, + "f" * 17, + "0" * 31, + "0" * 33, +]) +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk", "manual"]) +def test_invalid_token_signer_round1(token, way, pick_menu_item, cap_story, need_keypress, + nfc_write_text, microsd_path, virtdisk_path, goto_home, + press_select, is_q1): + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + pick_menu_item('Round 1') + time.sleep(0.1) + title, story = cap_story() + if way == "manual": + need_keypress("2") # manual + need_keypress("2") # decimal + for num in str(int(token, 16)): + need_keypress(num) + press_select() + else: + if way != "nfc": + token_fname = "error.token" + path_func = virtdisk_path if way == "vdisk" else microsd_path + with open(path_func(token_fname), "w") as f: + f.write(token) + if way == "sd": + if "Press (1) to import token file from SD Card" in story: + need_keypress("1") + # else no prompt if both NFC and vdisk disabled + elif way == "nfc": + if f"{KEY_NFC if is_q1 else '(4)'} to import via NFC" not in story: + pytest.skip("NFC disabled") + else: + need_keypress(KEY_NFC if is_q1 else "4") + time.sleep(0.1) + nfc_write_text(token) + time.sleep(0.4) + else: + # virtual disk + if "(6) to import from Virtual Disk" not in story: + pytest.skip("Vdisk disabled") + else: + need_keypress("6") + + if way != "nfc": + time.sleep(0.2) + pick_menu_item(token_fname) + + time.sleep(0.1) + title, story = cap_story() + assert title == "FAILURE" + assert "BSMS signer round1 failed" in story + assert "Invalid token length. Expected 64 or 128 bits (16 or 32 hex characters)" in story + + +@pytest.mark.parametrize("failure", ["wrong_sig", "bsms_version"]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +def test_failure_coordinator_round2(encryption_type, make_coordinator_round1, make_signer_round1, microsd_wipe, cap_menu, + pick_menu_item, press_select, goto_home, cap_story, failure, + need_keypress): + microsd_wipe() + + def get_token(index): + if len(tokens) == 1 and encryption_type == "1": + token = tokens[0] + elif len(tokens) == 2 and encryption_type == "2": + token = tokens[index] + else: + token = "00" + return token + + if failure == "bsms_version": + kws = {failure: "BSMS 1.1"} + else: + kws = {failure: True} + tokens = make_coordinator_round1(2, 2, "p2wsh", encryption_type, way="sd", tokens_only=True) + for i in range(2): + token = get_token(i) + index = None + if encryption_type != "2": + index = i + 1 + make_signer_round1(token, "sd", purge_bsms=False, index=index, **kws) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 2 + coord_menu_item = coordinator_label(2, 2, "p2wsh", encryption_type, index=1) + assert coord_menu_item in menu + pick_menu_item(coord_menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import co-signer round 1 files from SD Card" in story: + need_keypress("1") + press_select() # continue with manual file selection + suffix = ".txt" if encryption_type == "3" else ".dat" + for i, _ in enumerate(range(2), start=1): + token = get_token(i - 1) + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "2": + expect = 'Select co-signer #%d file containing round 1 data for token starting with %s' % (i, token[:4]) + else: + expect = 'Select co-signer #%d file containing round 1 data' % i + expect += '. File extension has to be "%s"' % suffix + assert expect in story + press_select() + menu_item = bsms_sr1_fname(token, encryption_type == "2", suffix, i) + pick_menu_item(menu_item) + time.sleep(0.1) + title, story = cap_story() + assert title == "FAILURE" + assert "BSMS coordinator round2 failed" in story + if failure == "wrong_sig": + failure_msg = "Recovered key from signature does not equal key provided. Wrong signature?" + else: + failure_msg = "Incompatible BSMS version. Need BSMS 1.0 got BSMS 1.1" + assert failure_msg in story + + +# TODO do this for NFC too when length requirements are lifted from 250 +@pytest.mark.parametrize("encryption_type", ["1", "2"]) +def test_wrong_encryption_coordinator_round2(encryption_type, make_coordinator_round1, make_signer_round1, microsd_wipe, + cap_menu, pick_menu_item, need_keypress, goto_home, cap_story, + press_cancel, press_select): + def get_token(index): + if len(tokens) == 1 and encryption_type == "1": + token = tokens[0] + elif len(tokens) == 2 and encryption_type == "2": + token = tokens[index] + else: + token = "00" + return token + + microsd_wipe() + tokens = make_coordinator_round1(2, 2, "p2wsh", encryption_type, way="sd", tokens_only=True) + for i in range(2): + token = get_token(i) + index = None + if encryption_type == "1": + index = i + 1 + make_signer_round1(token, "sd", purge_bsms=False, index=index, wrong_encryption=True) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 2 + coord_menu_item = coordinator_label(2, 2, "p2wsh", encryption_type, index=1) + assert coord_menu_item in menu + pick_menu_item(coord_menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import co-signer round 1 files from SD Card" in story: + need_keypress("1") + press_select() # continue with manual file selection + suffix = ".txt" if encryption_type == "3" else ".dat" + for i, _ in enumerate(range(2), start=1): + for attempt in range(2): + token = get_token(i - 1) + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "2": + expect = 'Select co-signer #%d file containing round 1 data for token starting with %s' % (i, token[:4]) + else: + expect = 'Select co-signer #%d file containing round 1 data' % i + expect += '. File extension has to be "%s"' % suffix + assert expect in story + press_select() + menu_item = bsms_sr1_fname(token, encryption_type == "2", suffix, i) + pick_menu_item(menu_item) + time.sleep(0.1) + _, story = cap_story() + expect_story = "Decryption failed for co-signer #%d" % i + if encryption_type == 2: + expect_story += " with token %s" % token[:4] + assert expect_story in story + if attempt == 0: + assert "Try again?" in story + press_select() + else: + assert "Try again?" not in story + press_cancel() + break + break + + +@pytest.mark.parametrize("failure", [ + "wrong_address", "path_restrictions", "bsms_version", "sortedmulti", "has_ours", "ours_no", + "wrong_encryption", "wrong_chain", "wrong_checksum" +]) +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +def test_failure_signer_round2(encryption_type, goto_home, press_select, pick_menu_item, cap_menu, cap_story, + microsd_path, settings_remove, nfc_read_text, virtdisk_path, settings_get, microsd_wipe, + make_coordinator_round2, failure, need_keypress): + microsd_wipe() + if failure == "wrong_address": + kws = {failure: True} + failure_msg = "Address mismatch!" + elif failure == "path_restrictions": + kws = {failure: "5/*,4/*"} + failure_msg = "Only '/0/*,/1/*' allowed as path restrictions." + elif failure == "bsms_version": + kws = {failure: "BSMS 2.0"} + failure_msg = "Incompatible BSMS version. Need BSMS 1.0 got BSMS 2.0" + elif failure == "sortedmulti": + kws = {failure: False} + failure_msg = "sortedmulti required" + elif failure == "has_ours": + kws = {failure: False} + failure_msg = "My key 0F056943 missing in descriptor." + elif failure == "ours_no": + kws = {failure: 2} + failure_msg = "Multiple 0F056943 keys in descriptor (2)" + elif failure == "wrong_chain": + kws = {failure: True} + failure_msg = "wrong chain" + elif failure == "wrong_checksum": + kws = {failure: True} + failure_msg = "Wrong checksum" + else: + assert failure == "wrong_encryption" + if encryption_type == "3": + pytest.skip("Cannot test wrong encryption on unencrypted BSMS") + kws = {failure: True} + failure_msg = "Decryption with token {token} failed." + + desc_template, token = make_coordinator_round2(2, 2, "p2wsh", encryption_type, way="sd", **kws) + failure_msg = failure_msg.format(token=token[:4]) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + menu_item = "1 %s" % token[:4] + pick_menu_item(menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import descriptor template file from SD Card" in story: + need_keypress("1") + + suffix = ".txt" if encryption_type == "3" else ".dat" + time.sleep(0.1) + menu_item = bsms_cr2_fname(token, encryption_type == "2", suffix) + pick_menu_item(menu_item) + time.sleep(0.1) + title, story = cap_story() + assert title == "FAILURE" + assert "BSMS signer round2 failed" in story + assert failure_msg in story + + +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +def test_integration_signer(encryption_type, M_N, addr_fmt, clear_miniscript, microsd_wipe, goto_home, pick_menu_item, cap_story, + press_select, settings_remove, microsd_path, settings_get, cap_menu, use_mainnet, + need_keypress): + # test CC signer full with bsms lib coordinator (test just SD card no need to retest IO paths again - tested above) + def get_token(index): + if len(tokens) == 1 and encryption_type == "1": + token = tokens[0] + elif len(tokens) == N and encryption_type == "2": + token = tokens[index] + else: + token = "00" + return token + + M, N = M_N + settings_remove(BSMS_SETTINGS) + use_mainnet() + clear_miniscript() + microsd_wipe() + coordinator = CoordinatorSession(M, N, addr_fmt, et_map[encryption_type]) + session_data = coordinator.generate_token_key_pairs() + tokens = [x[0] for x in session_data] + cc_token = get_token(0) + other_signers = [] + for i in range(1, N): + other_signers.append(Signer(token=get_token(i), key_description="Other signer %d" % i)) + # ROUND 1 + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + pick_menu_item('Round 1') + time.sleep(0.1) + _, story = cap_story() + if encryption_type == "3": + need_keypress("3") # no token (unencrypted BSMS) + else: + fname = "bsms_%s.token" % cc_token[:4] if cc_token != "00" else "1" + with open(microsd_path(fname), "w") as f: + f.write(cc_token) + if "Press (1) to import token file from SD Card" in story: + need_keypress("1") + time.sleep(0.2) + fname = "bsms_%s.token" % cc_token[:4] + pick_menu_item(fname) + + time.sleep(0.1) + title, story = cap_story() + assert "You have entered token:\n%s" % cc_token in story + press_select() + time.sleep(0.1) + _, story = cap_story() + # address format a.k.a. SLIP derivation path - ignore and use SLIP agnostic + assert "Choose co-signer address format for correct SLIP derivation path" in story + press_select() + # account number prompt + press_select() + time.sleep(0.1) + _, story = cap_story() + # textual key description + assert "Choose key description" in story + press_select() # default + time.sleep(0.1) + title, story = cap_story() + suffix = ".txt" if encryption_type == "3" else ".dat" + mode = "rt" if encryption_type == "3" else "rb" + if "Press (1) to save BSMS signer round 1 file to SD Card" in story: + need_keypress("1") + time.sleep(0.2) + _, story = cap_story() + assert 'BSMS signer round 1 file written' in story + fname = story.split('\n\n')[-1] + assert suffix in fname + path = microsd_path(fname) + with open(path, mode) as f: + signer_r1 = f.read() + + bsms = settings_get(BSMS_SETTINGS) + assert len(bsms[BSMS_SIGNER_SETTINGS]) == 1 + assert bsms[BSMS_SIGNER_SETTINGS][0] == cc_token + + # ROUND 2 + all_r1_data = [signer_r1.hex() if encryption_type != "3" else signer_r1] + for s in other_signers: + all_r1_data.append(s.round_1()) + + descriptor_templates = coordinator.round_2(all_r1_data) + if encryption_type == "2": + assert len(descriptor_templates) == N + for signer, tmplt in zip(other_signers, descriptor_templates[1:]): + signer.round_2(tmplt) + else: + assert len(descriptor_templates) == 1 + for signer in other_signers: + signer.round_2(descriptor_templates[0]) + + cc_desc_template = descriptor_templates[0] # zeroeth as our token is zero too + suffix = ".txt" if encryption_type == "3" else ".dat" + mode = "wt" if encryption_type == "3" else "wb" + fname = bsms_cr2_fname(cc_token, encryption_type == "2", suffix) + with open(microsd_path(fname), mode) as f: + f.write(bytes.fromhex(cc_desc_template) if mode == "wb" else cc_desc_template) + time.sleep(0.1) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Signer') + menu_item = "1 %s" % cc_token[:4] + pick_menu_item(menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import descriptor template file from SD Card" in story: + need_keypress("1") + time.sleep(0.1) + menu_item = bsms_cr2_fname(cc_token, encryption_type == "2", suffix) + pick_menu_item(menu_item) + time.sleep(0.1) + title, story = cap_story() + assert "Create new multisig wallet?" in story + assert "bsms" in story # part of the name + policy = "Policy: %d of %d" % (M, N) + assert policy in story + assert addr_fmt.upper() in story + ms_wal_name = story.split("\n\n")[1].split("\n")[-1].strip() + press_select() + time.sleep(0.1) + menu = cap_menu() + assert ms_wal_name in menu + bsms_settings = settings_get(BSMS_SETTINGS) + # signer round 2 removed + assert not bsms_settings.get(BSMS_SIGNER_SETTINGS, None) + + +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2,2), (3, 5), (15, 15)]) +@pytest.mark.parametrize("addr_fmt", ["p2wsh", "p2sh-p2wsh"]) +@pytest.mark.parametrize("cr1_shortcut", [True, False]) +def test_integration_coordinator(encryption_type, M_N, addr_fmt, clear_miniscript, microsd_wipe, goto_home, pick_menu_item, + cap_story, need_keypress, settings_remove, microsd_path, settings_get, cap_menu, + use_mainnet, cr1_shortcut, press_select): + M, N = M_N + settings_remove(BSMS_SETTINGS) + use_mainnet() + clear_miniscript() + microsd_wipe() + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 1 # nothing should be in menu at this point but round 1 + pick_menu_item('Create BSMS') + # choose number of signers N + for num in str(N): + need_keypress(num) + press_select() + # choose threshold M + for num in str(M): + need_keypress(num) + press_select() + if addr_fmt == "p2wsh": + press_select() + else: + need_keypress("2") + time.sleep(0.1) + title, story = cap_story() + assert story == "Choose encryption type. Press (1) for STANDARD encryption, (2) for EXTENDED, and (3) for no encryption" + need_keypress(encryption_type) + time.sleep(0.1) + title, story = cap_story() + assert_coord_summary(title, story, M, N, addr_fmt, encryption_type) + press_select() # confirm summary + time.sleep(0.1) + title, story = cap_story() + assert "Press (1) to participate as co-signer in this BSMS" in story + if cr1_shortcut: + _start_idx = 1 + need_keypress("1") + press_select() # slip + press_select() # acct num 0 + press_select() # default textual key description + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to save BSMS signer round 1 file to SD Card" in story: + need_keypress("1") + time.sleep(0.2) + _, story = cap_story() + shortcut_fname = story.split("\n\n")[-1] + press_select() # looking at save sr1 filename + else: + _start_idx = 0 + press_select() # continue normally + + time.sleep(0.1) + title, story = cap_story() + read_tokens = [] + if encryption_type == "3": + assert story == "Success. Coordinator round 1 saved." + else: + if "Press (1) to save BSMS token file(s) to SD Card" in story: + need_keypress("1") + time.sleep(0.2) + _, story = cap_story() + assert 'BSMS token file(s) written' in story + fnames = story.split('\n\n')[2:] + for fname in fnames: + path = microsd_path(fname) + with open(path, 'rt') as f: + tok = f.read().strip() + read_tokens.append(tok) + + all_signers = [] + if encryption_type == "1": + assert len(read_tokens) == 1 + for i in range(_start_idx, N): + all_signers.append(Signer(read_tokens[0], "key %d" % i)) + elif encryption_type == "2": + assert len(read_tokens) == (N - _start_idx) + for i in range(N - _start_idx): + all_signers.append(Signer(read_tokens[i], "key %d" % i)) + else: + assert len(read_tokens) == 0 + for i in range(N - _start_idx): + all_signers.append(Signer("00", "key %d" % i)) + + press_select() # confirm success or files written story + time.sleep(0.1) + menu = cap_menu() + assert len(menu) == 2 + current_coord_menu_item = coordinator_label(M, N, addr_fmt, encryption_type, index=1) + assert menu[0] == current_coord_menu_item + # check correct coord tuple saved + bsms_settings = settings_get(BSMS_SETTINGS) + if BSMS_SIGNER_SETTINGS in bsms_settings: + if cr1_shortcut: + assert len(bsms_settings[BSMS_SIGNER_SETTINGS]) == 1 + shortcut_token = bsms_settings[BSMS_SIGNER_SETTINGS][0] + else: + assert bsms_settings[BSMS_SIGNER_SETTINGS] == [] + shortcut_token = None + coord_settings = bsms_settings[BSMS_COORD_SETTINGS] + assert len(coord_settings) == 1 + if read_tokens: + expect_tokens = [tok.split(" ")[-1] for tok in read_tokens] + if cr1_shortcut and encryption_type == "2": + expect_tokens = [shortcut_token] + expect_tokens + else: + expect_tokens = [] + assert coord_settings[0] == (M, N, af_map[addr_fmt], encryption_type, expect_tokens) + + # ROUND 2 + def get_token(index): + if len(read_tokens) == 1 and encryption_type == "1": + token = read_tokens[0] + elif encryption_type == "2": + token = read_tokens[index] + else: + token = "00" + return token + + all_r1_signer_data = [s.round_1() for s in all_signers] + mode = "wt" if encryption_type == "3" else "wb" + suffix = ".txt" if encryption_type == "3" else ".dat" + for i, data in enumerate(all_r1_signer_data, start=1): + token = get_token(i - 1) + fname = bsms_sr1_fname(token, encryption_type == "2", suffix, i) + with open(microsd_path(fname), mode) as f: + f.write(bytes.fromhex(data) if mode == "wb" else data) + + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + menu = cap_menu() + assert len(menu) == 2 + coord_menu_item = coordinator_label(M, N, addr_fmt, encryption_type, index=1) + assert coord_menu_item in menu + pick_menu_item(coord_menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import co-signer round 1 files from SD Card" in story: + need_keypress("1") + press_select() # continue with manual file selection + if cr1_shortcut: + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "2": + expect = 'Select co-signer #1 file containing round 1 data for token starting with %s' % shortcut_token[:4] + else: + expect = 'Select co-signer #1 file containing round 1 data' + assert expect in story + press_select() + pick_menu_item(shortcut_fname) + for i in range(_start_idx, N): + token = get_token(i - _start_idx) + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "2": + expect = 'Select co-signer #%d file containing round 1 data for token starting with %s' % (i + 1, token[:4]) + else: + expect = 'Select co-signer #%d file containing round 1 data' % (i + 1) + expect += '. File extension has to be "%s"' % suffix + assert expect in story + press_select() + fname = bsms_sr1_fname(token, encryption_type == "2", suffix, i + 1 - _start_idx) + pick_menu_item(fname) + + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to save BSMS descriptor template file(s) to SD Card" in story: + need_keypress("1") + time.sleep(0.1) + _, story = cap_story() + assert "BSMS descriptor template file(s) written." in story + fnames = story.split("\n\n")[1:] + if encryption_type == "2": + if cr1_shortcut: + read_tokens = [shortcut_token] + read_tokens + for fname, token in zip(fnames, read_tokens): + assert token[:4] in fname + descriptor_templates = [] + for fname in fnames: + with open(microsd_path(fname), "rt" if encryption_type == "3" else "rb") as f: + desc_temp = f.read() + descriptor_templates.append(desc_temp) + if len(descriptor_templates) == 1: + target = descriptor_templates[0] + if isinstance(target, bytes): + target = target.hex() + for signer in all_signers: + signer.round_2(target) + else: + if cr1_shortcut: + _, descriptor_templates = descriptor_templates[0], descriptor_templates[1:] + for signer, desc_tmplt in zip(all_signers, descriptor_templates): + if isinstance(desc_tmplt, bytes): + desc_tmplt = desc_tmplt.hex() + signer.round_2(desc_tmplt) + if cr1_shortcut: + # still need to add our signer + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + press_select() + pick_menu_item('Signer') + menu_item = "1 %s" % shortcut_token[:4] + pick_menu_item(menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import descriptor template file from SD Card" in story: + need_keypress("1") + time.sleep(0.1) + pick_menu_item(fnames[0]) + time.sleep(0.1) + title, story = cap_story() + assert "Create new multisig wallet?" in story + assert "bsms" in story # part of the name + policy = "Policy: %d of %d" % (M, N) + assert policy in story + assert addr_fmt.upper() in story + ms_wal_name = story.split("\n\n")[1].split("\n")[-1].strip() + press_select() + time.sleep(0.1) + menu = cap_menu() + assert ms_wal_name in menu + bsms_settings = settings_get(BSMS_SETTINGS) + # signer round 2 removed + assert not bsms_settings.get(BSMS_SIGNER_SETTINGS, None) + + + +@pytest.mark.parametrize("encryption_type", ["1", "2", "3"]) +@pytest.mark.parametrize("M_N", [(2, 2), (3, 5), (15, 15)]) +def test_auto_collection_coordinator_r2(encryption_type, M_N, goto_home, need_keypress, pick_menu_item, microsd_wipe, + cap_story, microsd_path,make_coordinator_round1, make_signer_round1, + press_select): + M, N = M_N + microsd_wipe() + + def get_token(index): + if len(tokens) == 1 and encryption_type == "1": + token = tokens[0] + elif len(tokens) == N and encryption_type == "2": + token = tokens[index] + else: + token = "00" + return token + + # add twice as many files with different tokens - should be still able to collect the correct ones + f_pattern = "bsms_sr1" + if encryption_type == "2": + suffix = ".dat" + for i in range(N): + token = os.urandom(16).hex() + s = Signer(token=token, key_description="key%d" % i) + r1 = s.round_1() + fname = "%s_%s%s" % (f_pattern, token[:4], suffix) + with open(microsd_path(fname), "wb") as f: + f.write(bytes.fromhex(r1)) + + elif encryption_type == "1": + suffix = ".dat" + for i in range(N): + token = os.urandom(8).hex() + s = Signer(token=token, key_description="key%d" % i) + r1 = s.round_1() + fname = "%s%s" % (f_pattern, suffix) + with open(microsd_path(fname), "wb") as f: + f.write(bytes.fromhex(r1)) + + else: + suffix = ".txt" + for i in range(N): + s = Signer(token="00", key_description="key%d" % i) + r1 = s.round_1() + fname = "%s%s" % (f_pattern, suffix) + with open(microsd_path(fname), "w") as f: + f.write(r1) + + tokens = make_coordinator_round1(M, N, "p2wsh", encryption_type, way="sd", tokens_only=True) + all_data = [] + for i in range(N): + token = get_token(i) + index = None + if encryption_type == "1": + index = i + 1 + all_data.append(make_signer_round1(token, "sd", purge_bsms=False, index=index)) + goto_home() + pick_menu_item('Settings') + pick_menu_item('Multisig/Miniscript') + pick_menu_item('BSMS (BIP-129)') + title, story = cap_story() + assert "Bitcoin Secure Multisig Setup (BIP-129) is a mechanism to securely create multisig wallets." in story + assert "WARNING: BSMS is an EXPERIMENTAL and BETA feature" in story + press_select() + pick_menu_item('Coordinator') + coord_menu_item = coordinator_label(M, N, "p2wsh", encryption_type, index=1) + pick_menu_item(coord_menu_item) + pick_menu_item("Round 2") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import co-signer round 1 files from SD Card" in story: + need_keypress("1") + need_keypress("1") # auto-collection + time.sleep(0.1) + title, story = cap_story() + if encryption_type == "3": + # we need exact number of files for unencrypted as we would have no idea which are part of this multisig setup + assert "Auto-collection failed. Defaulting to manual selection of files." in story + else: + if "Press (1) to save BSMS descriptor template file(s) to SD Card" in story: + # if NFC or Vdisk enabled - but means auto-collection was successful and we are prompted where to + # save the resulting descriptor (coordinator round2 data) + assert True + else: + # NFC and Vdisk disabled, automatically written to SD card - success + assert "BSMS descriptor template file(s) written" in story diff --git a/testing/test_ccc.py b/testing/test_ccc.py new file mode 100644 index 000000000..d882a2458 --- /dev/null +++ b/testing/test_ccc.py @@ -0,0 +1,1267 @@ +# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# tests related to CCC feature +# +# run simulator without --eff +# +# +import pytest, pdb, requests, re, time, random, json, glob, os, hashlib, base64, uuid +from base64 import urlsafe_b64encode +from onetimepass import get_totp +from helpers import prandom, slip132undo +from pysecp256k1.ecdh import ecdh, ECDH_HASHFP_CLS +from pysecp256k1 import ec_seckey_verify, ec_pubkey_parse, ec_pubkey_serialize, ec_pubkey_create +from mnemonic import Mnemonic +from bip32 import BIP32Node +from constants import AF_P2WSH +from charcodes import KEY_QR, KEY_DELETE +from bbqr import split_qrs +from psbt import BasicPSBT + +# pubkey for production server. +SERVER_PUBKEY = '0231301ec4acec08c1c7d0181f4ffb8be70d693acccc86cccb8f00bf2e00fcabfd' + +@pytest.fixture +def goto_ccc_menu(goto_home, pick_menu_item, is_mark4): + def doit(): + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Spending Policy") + pick_menu_item("Co-Sign Multi." if is_mark4 else "Co-Sign Multisig (CCC)") + + return doit + +def make_session_key(his_pubkey=None): + + # - second call: given the pubkey of far side, calculate the shared pt on curve + # - creates session key based on that + while True: + my_seckey = prandom(32) + try: + ec_seckey_verify(my_seckey) + break + except: continue + + my_pubkey = ec_pubkey_create(my_seckey) + + his_pubkey = ec_pubkey_parse(bytes.fromhex(SERVER_PUBKEY)) + + # do the D-H thing + + def _py_ckcc_hashfp(output, x, y, data=None): + try: + m = hashlib.sha256() + m.update(x.contents.raw) + m.update(y.contents.raw) + output.contents.raw = m.digest() + return 1 + except: + return 0 + + ckcc_hashfp = ECDH_HASHFP_CLS(_py_ckcc_hashfp) + + shared_key = ecdh(my_seckey, his_pubkey, hashfp=ckcc_hashfp) + + return shared_key, ec_pubkey_serialize(my_pubkey) + + +@pytest.fixture +def make_2fa_url(request): + def doit(shared_secret=b'A'*16, nonce='12345678', + wallet='Example wallet name', is_q=0, encrypted=False): + + lh = request.config.getoption("--localhost") + + base = 'http://127.0.0.1:5070/2fa?' if lh else 'https://coldcard.com/2fa?' + + assert is_q in {0, 1} + assert len(shared_secret) == 16 # base32 + assert isinstance(nonce, str) # hex digits or 8 dec digits in Mk4 mode + + from urllib.parse import quote + + qs = f'ss={shared_secret}&q={is_q}&g={nonce}&nm={quote(wallet)}' + + print(f'2fa URL: {qs}') + + if not encrypted: + return base + qs + + # pick eph key + ses_key, pubkey = make_session_key() + + import pyaes + enc = pyaes.AESModeOfOperationCTR(ses_key, pyaes.Counter(0)).encrypt + + qs = urlsafe_b64encode(pubkey + enc(qs.encode('ascii'))).rstrip(b'=') + + return base + qs.decode('ascii') + + return doit + +@pytest.fixture +def roundtrip_2fa(): + def doit(url, shared_secret, local=False): + if local: + url = url.replace('https://coldcard.com/', 'http://127.0.0.1:5070/') + + if int(time.time() % 30) > 29: + # avoid end of time period + time.sleep(3) + + # build right TOTP answer + answer = '%06d' % get_totp(shared_secret) + assert len(answer) == 6 + + # send both request and answer at same time (we know it works that way) + resp = requests.post(url, data=dict(answer=answer)) + + # server HTML will have this line in response for our use + # + + if ' actual words @@ -392,6 +399,7 @@ def doit(num_words, dice=False, from_main=False, seed_vault=None, testnet=True): assert len(e_seed_words) == num_words need_keypress("6") # skip quiz + time.sleep(.1) press_select() # yes - I'm sure confirm_tmp_seed(seedvault=seed_vault) @@ -787,9 +795,6 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men sim_exec, goto_home, seed_vault_enable, is_q1, enter_text, press_select, press_cancel, press_delete): # Verify "seed vault" feature works as intended - - - reset_seed_words() xfp, entropy, mnemonic = data @@ -849,6 +854,18 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men m = cap_menu() assert m[0] == "AAAA" + pick_menu_item("AAAA") # bug issues/920 + # would be yikes here, if not fixed + time.sleep(.1) + _, story = cap_story() + assert "AAAA" in story + assert xfp in story + if mnemonic: + assert ('%d words' % (6 * (vlen // 8))) in story + else: + assert 'xprv' in story + press_cancel() + # check parent menu - must be updated too press_cancel() m = cap_menu() @@ -884,7 +901,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men e_master_xpub = dev.send_recv(CCProtocolPacker.get_xpub(), timeout=5000) assert e_master_xpub != simulator_fixed_tpub - psbt = fake_txn(2, 2, master_xpub=e_master_xpub, segwit_in=True) + psbt = fake_txn(2, 2, master_xpub=e_master_xpub, addr_fmt=random.choice(ADDR_STYLES_SINGLE)) try_sign(psbt, accept=True, finalize=True) # MUST NOT raise press_select() @@ -1117,16 +1134,21 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item m = cap_menu() assert m[0] == "AAA" pick_menu_item("Delete") + time.sleep(.1) + title, story = cap_story() + # current active does not offer to purge the slot, only to remove from Seed Vault + assert "delete its settings?" not in story press_select() time.sleep(.1) + goto_home() m = cap_menu() - # after we delete from seed vault together with its settings - # we're back to master secret - assert m[0] == "Ready To Sign" + # still in tmp mode + assert m[0] != "Ready To Sign" pick_menu_item("Seed Vault") time.sleep(.1) m = cap_menu() - assert len(m) == 2 + # Ignore Add Current and Restore Master (only SV items are numbered with colon) + assert len([mi for mi in m if ":" in mi]) == 2 press_down() press_select() @@ -1146,7 +1168,10 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item assert "Delete" in m pick_menu_item("Delete") - need_keypress("1") # only delete from seed vault + time.sleep(.1) + _, story = cap_story() + assert "delete its settings?" not in story + press_select() # only delete from seed vault, no other option provided time.sleep(.1) m = cap_menu() assert len(m) == 3 @@ -1164,6 +1189,25 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item # still in ephemeral assert title == m[0] + restore_main_seed() + pick_menu_item("Seed Vault") + press_select() + time.sleep(.1) + m = cap_menu() + assert "Rename" in m + assert "Use This Seed" in m + assert "Delete" in m + + pick_menu_item("Delete") + time.sleep(.1) + _, story = cap_story() + assert "delete its settings?" in story + need_keypress("1") # only remove from seed vault, keep settings + time.sleep(.1) + m = cap_menu() + assert all([":" not in mi for mi in m]) + assert "(none saved yet)" in m + def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv, cap_story, press_cancel, pick_menu_item, cap_menu, @@ -1209,16 +1253,37 @@ def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv, @pytest.mark.parametrize("refuse", [False, True]) def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xprv, goto_home, pick_menu_item, cap_story, cap_menu, - press_cancel, verify_ephemeral_secret_ui, - seed_vault_enable, refuse, press_select): + press_cancel, verify_ephemeral_secret_ui, is_q1, + seed_vault_enable, refuse, press_select, set_bip39_pw, + need_some_notes, need_some_passwords, import_ms_wallet, + restore_main_seed, settings_get, clear_miniscript): ADD_MI = "Add current tmp" reset_seed_words() goto_home() seed_vault_enable(True) + # clear settings_set("seeds", []) + clear_miniscript() + settings_set("notes", []) + + if not refuse: + # add something to seed vault + sv_pass_xfp = set_bip39_pw('dogsNcats', seed_vault=True, reset=False) + restore_main_seed(seed_vault=True) + + # add secure notes and passwords + if is_q1: + need_some_notes() + need_some_passwords() + + # save multisig wallet to master settings + ms_name = "aaa" + import_ms_wallet(2,3,"p2wsh", name=ms_name, accept=True) + + time.sleep(.2) + goto_home() - time.sleep(.2) # in master - do not offer pick_menu_item("Seed Vault") time.sleep(.1) @@ -1249,19 +1314,33 @@ def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xpr else: press_select() verify_ephemeral_secret_ui(xpub=node.hwif(), seed_vault=True) - - -@pytest.mark.parametrize('multisig', [False, 'multisig']) + restore_main_seed(seed_vault=True) + time.sleep(.2) + curr_xfp = settings_get("xfp", None) + assert curr_xfp is not None + assert curr_xfp != 0 + mss = settings_get("miniscript") + assert len(mss) == 1 + assert mss[0][0] == ms_name + if is_q1: + assert len(settings_get("notes")) == 3 + sv = settings_get("seeds") + assert len(sv) == 2 + assert sv[0][0] == xfp2str(sv_pass_xfp) # added passphrase wallet + assert sv[1][0] == xfp # added via `Add current tmp` + + +@pytest.mark.parametrize('multisig', [True, False]) @pytest.mark.parametrize('seedvault', [False, True]) @pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA) def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_setting, data, press_select, cap_story, set_encoded_secret, - reset_seed_words, check_and_decrypt_backup, + reset_seed_words, check_and_decrypt_backup, clear_miniscript, goto_eph_seed_menu, pick_menu_item, word_menu_entry, verify_ephemeral_secret_ui, seedvault, settings_set, - seed_vault_enable, confirm_tmp_seed, settings_path, - seed_vault_delete, restore_main_seed, set_seed_words): - + seed_vault_enable, confirm_tmp_seed, set_seed_words, + seed_vault_delete, restore_main_seed, settings_slots): + xfp_str, encoded_str, mnemonic = data if mnemonic: set_seed_words(mnemonic) @@ -1270,12 +1349,15 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se set_encoded_secret(encoded) settings_set("chain", "XTN") + clear_miniscript() if multisig: import_ms_wallet(15, 15, dev_key=True) press_select() time.sleep(.1) - assert len(get_setting('multisig')) == 1 + assert len(get_setting('miniscript')) == 1 + else: + assert get_setting('miniscript') is None # ACTUAL BACKUP bk_pw = backup_system() @@ -1285,6 +1367,14 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se check_and_decrypt_backup(fname, bk_pw) + # remove all saved slots, one of them will be the one where we just created backup + # slot where backup was created needs to be removed - otherwise we will load back to it + # and see multisig wallet there without the need for backup to actually copy it + for s in settings_slots(): + try: + os.remove(s) + except: pass + # restore fixed simulator reset_seed_words() seed_vault_enable(seedvault) @@ -1297,20 +1387,103 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se word_menu_entry(bk_pw, has_checksum=False) + time.sleep(.5) + title, story = cap_story() + assert f"[{xfp_str}]" == title + assert "Above is the master fingerprint of the seed stored in the backup." in story + assert f"load backup as temporary seed" in story + press_select() + confirm_tmp_seed(seedvault) time.sleep(.1) if mnemonic: mnemonic = mnemonic.split(" ") - xfp = verify_ephemeral_secret_ui(mnemonic=mnemonic, xpub=None, # xpub veriphy ephemeral secret not tested here + xfp = verify_ephemeral_secret_ui(mnemonic=mnemonic, xpub=None, # XPUB verify ephemeral secret not tested here seed_vault=seedvault) + # actual bug, multisig key copied with "setting." prefix -> therefore not visible in Multisig menu + assert get_setting("setting.miniscript") is None + # correct multisig was copied during loading backup as tmp seed + ms = get_setting('miniscript') + if multisig: + assert len(ms) == 1 + assert ms[0][-1]["m_n"] == [15,15] + else: + assert ms is None + if seedvault: - seed_vault_delete(xfp, not False) + seed_vault_delete(xfp, True) else: restore_main_seed(False) +@pytest.mark.parametrize('btype', ["classic", "custom_bkpw", "plaintext"]) +def test_temporary_from_backup_usb(backup_system, set_seed_words, cap_story, verify_ephemeral_secret_ui, + settings_slots, reset_seed_words, word_menu_entry, confirm_tmp_seed, + dev, microsd_path, press_select, btype, enter_complex): + + xfp_str, encoded_str, mnemonic = SEEDVAULT_TEST_DATA[0] + set_seed_words(mnemonic) + bkpw = 32*"X" + plaintext = (btype == "plaintext") + password = False + + # ACTUAL BACKUP + if plaintext: + bk_pw = backup_system(ct=True) + elif btype == "custom_bkpw": + # encrypted but with custom pwd + password = True + bk_pw = backup_system(reuse_pw=[bkpw]) + else: + # classic word-based encrypted backup + bk_pw = backup_system() + + time.sleep(.1) + title, story = cap_story() + fname = story.split("\n\n")[1] + + # remove all saved slots, one of them will be the one where we just created backup + # slot where backup was created needs to be removed - otherwise we will load back to it + # and see multisig wallet there without the need for backup to actually copy it + for s in settings_slots(): + try: + os.remove(s) + except: pass + + # restore fixed simulator + reset_seed_words() + + from ckcc_protocol.protocol import CCProtocolPacker + with open(microsd_path(fname), "rb") as f: + file_len, sha = dev.upload_file(f.read()) + + dev.send_recv(CCProtocolPacker.restore_backup(file_len, sha, password, plaintext), timeout=None) + time.sleep(.2) + _, story = cap_story() + assert "Restore uploaded backup as a temporary seed" in story + press_select() + + time.sleep(.1) + if btype == "classic": + word_menu_entry(bk_pw, has_checksum=False) + elif password: + enter_complex(bkpw, apply=False, b39pass=False) + + time.sleep(.5) + title, story = cap_story() + assert f"[{xfp_str}]" == title + assert "Above is the master fingerprint of the seed stored in the backup." in story + assert f"load backup as temporary seed" in story + press_select() + + time.sleep(.1) + confirm_tmp_seed(seedvault=False) + time.sleep(.1) + mnemonic = mnemonic.split(" ") + verify_ephemeral_secret_ui(mnemonic=mnemonic, xpub=None, seed_vault=False) + def test_tmp_upgrade_disabled(reset_seed_words, pick_menu_item, cap_story, cap_menu, goto_home, unit_test, @@ -1353,10 +1526,13 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story, need_keypress, word_menu_entry, settings_set, confirm_tmp_seed, cap_menu, microsd_path, restore_main_seed, get_identity_story, press_select, - press_cancel): + press_cancel, settings_remove): reset_seed_words() + # disable seed vault + settings_remove("seedvault") + settings_remove("seeds") goto_eph_seed_menu() ephemeral_seed_disabled() @@ -1373,6 +1549,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story, title, story = cap_story() assert "FAILED" == title assert 'Cannot use master seed as temporary.' in story + assert 'tested recovery of your master seed' in story press_cancel() # go to ephemeral seed and then try to create new ephemeral seed from master @@ -1401,6 +1578,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story, title, story = cap_story() assert "FAILED" == title assert 'Cannot use master seed as temporary.' in story + assert 'tested recovery of your master seed' in story press_cancel() # now import same seed but represented as master extended key @@ -1442,21 +1620,30 @@ def test_home_menu_xfp(goto_home, pick_menu_item, press_select, cap_story, cap_m pick_menu_item("Always Show") time.sleep(.3) m = cap_menu() - assert m[1] == "Ready To Sign" assert m[0] == "<" + xfp2str(settings_get("xfp")) + ">" + assert m[1] == "Ready To Sign" + goto_eph_seed_menu() pick_menu_item("Generate Words") pick_menu_item(f"12 Words") time.sleep(0.1) - need_keypress("6") # skip words - press_select() + need_keypress("6") # skip quiz press_select() - time.sleep(.3) + + time.sleep(.1) + _, story = cap_story() + if "Press (1) to store temporary seed" in story: + # seed vault enabled + press_select() # do not save + press_select() # new tmp seed + + time.sleep(.2) m = cap_menu() assert m[1] == "Ready To Sign" assert m[0] == "[" + xfp2str(settings_get("xfp")) + "]" pick_menu_item("Restore Master") press_select() + time.sleep(.3) m = cap_menu() assert m[1] == "Ready To Sign" @@ -1464,13 +1651,34 @@ def test_home_menu_xfp(goto_home, pick_menu_item, press_select, cap_story, cap_m # disable now pick_menu_item("Settings") pick_menu_item("Home Menu XFP") + time.sleep(.1) _, story = cap_story() if "Forces display of XFP" in story: press_select() pick_menu_item("Only Tmp") + time.sleep(.3) m = cap_menu() assert m[0] == "Ready To Sign" + +def test_seed_vault_enable_on_tmp(generate_ephemeral_words, reset_seed_words, + goto_eph_seed_menu, ephemeral_seed_disabled, + verify_ephemeral_secret_ui, goto_home, cap_menu, + restore_main_seed, pick_menu_item, settings_remove): + reset_seed_words() + # disable seed vault + settings_remove("seedvault") + settings_remove("seeds") + goto_eph_seed_menu() + ephemeral_seed_disabled() + e_seed_words = generate_ephemeral_words(num_words=12, dice=False, + from_main=True, seed_vault=False) + verify_ephemeral_secret_ui(mnemonic=e_seed_words, seed_vault=False) + goto_home() + pick_menu_item("Advanced/Tools") + m = cap_menu() + assert "Seed Vault" not in m + # EOF diff --git a/testing/test_export.py b/testing/test_export.py index 0ea61eaea..be7fb0897 100644 --- a/testing/test_export.py +++ b/testing/test_export.py @@ -4,16 +4,13 @@ # # Start simulator with: simulator.py --eff --set nfc=1 # -import sys -sys.path.append("../shared") -from descriptor import Descriptor -from mnemonic import Mnemonic import pytest, time, os, json, io, bech32 from bip32 import BIP32Node +from descriptor import Descriptor +from mnemonic import Mnemonic from ckcc_protocol.constants import * from helpers import xfp2str, slip132undo from conftest import simulator_fixed_xfp, simulator_fixed_tprv, simulator_fixed_words, simulator_fixed_xprv -from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH from pprint import pprint from charcodes import KEY_NFC, KEY_QR @@ -85,7 +82,12 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, addrs = [] imm_js = None imd_js = None + imd_js_tr = None + tr = False for ln in fp: + if ln.startswith("p2tr:"): + tr = True + if 'importmulti' in ln: # PLAN: this will become obsolete assert ln.startswith("importmulti '") @@ -93,20 +95,26 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, assert not imm_js, "dup importmulti lines" imm_js = ln[13:-2] elif "importdescriptors '" in ln: + ln = ln.strip() assert ln.startswith("importdescriptors '") - assert ln.endswith("'\n") - assert not imd_js, "dup importdesc lines" - imd_js = ln[19:-2] + if tr: + imd_js_tr = ln[19:-1] + tr = False + else: + imd_js = ln[19:-1] elif '=>' in ln: path, addr = ln.strip().split(' => ', 1) - assert path.startswith(f"m/84h/1h/{acct_num}h/0") - assert addr.startswith('bcrt1q') # TODO here we should differentiate if testnet or smthg sk = BIP32Node.from_wallet_key(simulator_fixed_tprv).subkey_for_path(path) - h20 = sk.hash160() - assert addr == bech32.encode(addr[0:4], 0, h20) # TODO here we should differentiate if testnet or smthg + if path.startswith(f"m/86h/1h/{acct_num}h/0"): + assert addr.startswith('bcrt1p') + assert addr == sk.address(addr_fmt="p2tr", chain="XRT") + else: + assert path.startswith(f"m/84h/1h/{acct_num}h/0") + assert addr.startswith("bcrt1q") + assert addr == sk.address(addr_fmt="p2wpkh", chain="XRT") addrs.append(addr) - assert len(addrs) == 3 + assert len(addrs) == 6 xfp = xfp2str(simulator_fixed_xfp).lower() @@ -140,14 +148,9 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, x = bitcoind_wallet.getaddressinfo(addrs[-1]) pprint(x) assert x['address'] == addrs[-1] - if 'label' in x: - # pre 0.21.? - assert x['label'] == 'testcase' - else: - assert x['labels'] == ['testcase'] - assert x['iswatchonly'] == True - assert x['iswitness'] == True - assert x['hdkeypath'] == f"m/84'/1'/{acct_num}'/0/%d" % (len(addrs)-1) + # assert x['iswatchonly'] == True + assert x['iswitness'] is True + # assert x['hdkeypath'] == f"m/84'/1'/{acct_num}'/0/%d" % (len(addrs)-1) # importdescriptors -- its better assert imd_js @@ -168,26 +171,49 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, assert expect in desc assert expect+f'/{n}/*' in desc - assert 'label' not in d + res = bitcoind_d_wallet.importdescriptors(obj) + assert res[0]["success"] + assert res[1]["success"] + x = bitcoind_d_wallet.getaddressinfo(addrs[2]) + pprint(x) + assert x['address'] == addrs[2] + assert x['iswatchonly'] == False + assert x['iswitness'] == True + assert x['solvable'] == True + assert x['hdmasterfingerprint'] == xfp2str(dev.master_fingerprint).lower() + assert x['hdkeypath'].replace("'", "h") == f"m/84h/1h/{acct_num}h/0/%d" % 2 + + assert imd_js_tr + obj = json.loads(imd_js_tr) + for n, here in enumerate(obj): + assert here['timestamp'] == 'now' + assert here['internal'] == bool(n) + + d = here['desc'] + desc, chk = d.split('#', 1) + assert len(chk) == 8 + + assert desc.startswith(f'tr([{xfp}/86h/1h/{acct_num}h]') + + expect = BIP32Node.from_wallet_key(simulator_fixed_tprv) \ + .subkey_for_path(f"m/86h/1h/{acct_num}h").hwif() + + assert expect in desc + assert expect + f'/{n}/*' in desc # test against bitcoind -- needs a "descriptor native" wallet res = bitcoind_d_wallet.importdescriptors(obj) assert res[0]["success"] assert res[1]["success"] - core_gen = [] - for i in range(3): - core_gen.append(bitcoind_d_wallet.getnewaddress()) - assert core_gen == addrs x = bitcoind_d_wallet.getaddressinfo(addrs[-1]) pprint(x) assert x['address'] == addrs[-1] - assert x['iswatchonly'] == False - assert x['iswitness'] == True - # assert x['ismine'] == True # TODO we have imported pubkeys - it has no idea if it is ours or solvable - # assert x['solvable'] == True - # assert x['hdmasterfingerprint'] == xfp2str(dev.master_fingerprint).lower() - #assert x['hdkeypath'] == f"m/84'/1'/{acct_num}'/0/%d" % (len(addrs)-1) + assert x['iswatchonly'] is False + assert x['iswitness'] is True + assert x['solvable'] is True + assert x['hdmasterfingerprint'] == xfp2str(dev.master_fingerprint).lower() + assert x['hdkeypath'].replace("'", "h") == f"m/86h/1h/{acct_num}h/0/%d" % 2 @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"]) @@ -305,24 +331,25 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca @pytest.mark.parametrize('acct_num', [ None, '99', '1236']) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"]) -@pytest.mark.parametrize('testnet', [True, False]) +@pytest.mark.parametrize('netcode', ["XTN", "BTC"]) @pytest.mark.parametrize('app', [ # no need to run them all - just name check differs ("Generic JSON", "Generic Export"), - ("Nunchuk", "Nunchuk Wallet"), + # ("Nunchuk", "Nunchuk Wallet"), # These differ only in the menu title. If that changes, add them back here... test latest only # ("Lily Wallet", "Lily Wallet"), # ("Sparrow Wallet", "Sparrow Wallet"), - ("Theya", "Theya Wallet"), + # ("Theya", "Theya Wallet"), + ("Bitcoin Safe", "Bitcoin Safe Wallet"), ]) def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, nfc_read_json, virtdisk_path, addr_vs_path, enter_number, - load_export, testnet, use_mainnet, press_select, + load_export, netcode, use_mainnet, press_select, skip_if_useless_way, expect_acctnum_captured): skip_if_useless_way(way) - if not testnet: + if netcode == "BTC": use_mainnet() export_mi, app_f_name = app @@ -377,8 +404,8 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap addr = v.get('first', None) if fn == 'bip44': - assert first.address(netcode="XTN" if testnet else "BTC") == v['first'] - addr_vs_path(addr, v['deriv'] + '/0/0', AF_CLASSIC, testnet=testnet) + assert first.address(chain=netcode) == v['first'] + addr_vs_path(addr, v['deriv'] + '/0/0', AF_CLASSIC, chain=netcode) elif ('bip48_' in fn) or (fn == 'bip45'): # multisig: cant do addrs assert addr == None @@ -389,11 +416,11 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap h20 = first.hash160() if fn == 'bip84': assert addr == bech32.encode(addr[0:2], 0, h20) - addr_vs_path(addr, v['deriv'] + '/0/0', AF_P2WPKH, testnet=testnet) + addr_vs_path(addr, v['deriv'] + '/0/0', AF_P2WPKH, chain=netcode) elif fn == 'bip49': # don't have test logic for verifying these addrs # - need to make script, and bleh - assert first.address(addr_fmt="p2sh-p2wpkh", netcode="XTN" if testnet else "BTC") == v['first'] + assert first.address(addr_fmt="p2sh-p2wpkh", chain=netcode) == v['first'] else: assert False @@ -431,7 +458,7 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k press_select() expect_acctnum_captured(acct_num) - obj = load_export(way, label="Unchained", is_json=True, sig_check=False) + obj = load_export(way, label="Unchained", is_json=True) ek = simulator_fixed_tprv if testnet else simulator_fixed_xprv root = BIP32Node.from_wallet_key(ek) @@ -455,15 +482,16 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"]) -@pytest.mark.parametrize('testnet', [True, False]) +@pytest.mark.parametrize('netcode', ["BTC", "XTN"]) def test_export_public_txt(way, dev, pick_menu_item, goto_home, press_select, microsd_path, addr_vs_path, virtdisk_path, nfc_read_text, cap_story, use_mainnet, - load_export, testnet, skip_if_useless_way): + load_export, netcode, skip_if_useless_way): # test UX and values produced. skip_if_useless_way(way) - if not testnet: + if netcode == "BTC": use_mainnet() + goto_home() pick_menu_item('Advanced/Tools') pick_menu_item('File Management') @@ -481,7 +509,7 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, press_select, mi xfp = xfp2str(simulator_fixed_xfp).upper() - ek = simulator_fixed_tprv if testnet else simulator_fixed_xprv + ek = simulator_fixed_tprv if netcode == "XTN" else simulator_fixed_xprv root = BIP32Node.from_wallet_key(ek) for ln in fp: @@ -508,26 +536,28 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, press_select, mi if not f: if rhs[0] in '1mn': f = AF_CLASSIC - elif rhs[0:3] in ['tb1', "bc1"]: + elif rhs[0:4] in ['tb1q', "bc1q"]: f = AF_P2WPKH + elif rhs[0:4] in ['tb1p', "bc1p"]: + f = AF_P2TR elif rhs[0] in '23': f = AF_P2WPKH_P2SH else: raise ValueError(rhs) - addr_vs_path(rhs, path=lhs, addr_fmt=f, testnet=testnet) + addr_vs_path(rhs, path=lhs, addr_fmt=f, chain=netcode) @pytest.mark.qrcode +@pytest.mark.parametrize('chain', ["BTC", "XTN"]) @pytest.mark.parametrize('acct_num', [ None, 0, 99, 8989]) -@pytest.mark.parametrize('use_nfc', [False, True]) -def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home, +def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number, cap_screen_qr, - use_mainnet, nfc_read_text, is_q1, press_select, press_cancel, - press_nfc, expect_acctnum_captured): + settings_set, nfc_read_text, is_q1, press_select, press_cancel, + press_nfc, expect_acctnum_captured, nfc_disabled): # XPUB's via QR - use_mainnet() - + settings_set("chain", chain) + chain_num = 0 if chain == "BTC" else 1 goto_home() pick_menu_item('Advanced/Tools') pick_menu_item('Export Wallet') @@ -537,11 +567,13 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home for m in top_items: is_xfp = False if '-84' in m: - expect = "m/84h/0h/{acct}h" + expect = f"m/84h/{chain_num}h/{{acct}}h" + elif '86' in m: + expect = f"m/86h/{chain_num}h/{{acct}}h" elif '-44' in m: - expect = "m/44h/0h/{acct}h" + expect = f"m/44h/{chain_num}h/{{acct}}h" elif '49' in m: - expect = "m/49h/0h/{acct}h" + expect = f"m/49h/{chain_num}h/{{acct}}h" elif 'Master' in m: expect = "m" elif 'XFP' in m: @@ -551,17 +583,22 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home time.sleep(0.3) if is_xfp: got = cap_screen_qr().decode('ascii') - if use_nfc: + time.sleep(.1) + if not nfc_disabled(): press_nfc() - assert got == xfp2str(simulator_fixed_xfp).upper() - press_cancel() + time.sleep(.2) + nfc_got = nfc_read_text() + time.sleep(.2) + assert nfc_got == got == xfp2str(simulator_fixed_xfp).upper() + press_cancel() # cancel animation + press_cancel() # cancel QR continue time.sleep(0.3) title, story = cap_story() - assert expect in story + assert expect.format(acct=0) in story - if 'acct' in expect: + if expect != "m": assert "Press (1) to select account" in story if acct_num is not None: need_keypress('1') @@ -571,24 +608,53 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home expect = expect.format(acct=acct_num) title, story = cap_story() assert expect in story - assert "Press (1) to select account" not in story + assert "Press (1) to select account" in story - expect = expect.format(acct=0) - if not use_nfc: - press_select() - got_pub = cap_screen_qr().decode('ascii') - else: - if f'Press {KEY_NFC if is_q1 else "(3)"}' not in story: - raise pytest.skip("NFC disabled") + expect = expect.format(acct=0) + + press_select() + got_pub = cap_screen_qr().decode('ascii') + + if f'Press {KEY_NFC if is_q1 else "(3)"}' in story: assert 'NFC' in story press_nfc() time.sleep(0.2) - got_pub = nfc_read_text() + got_nfc_pub = nfc_read_text() time.sleep(0.1) - #press_select() + press_cancel() # cancel animation + assert got_nfc_pub == got_pub + + press_cancel() # cancel QR + + time.sleep(.1) + _, story = cap_story() + assert got_pub[0] in 'xt' + if "Press (2)" in story: + if chain == "BTC": + assert f"{'z' if expect[:5] == 'm/84h' else 'y'}pub (SLIP-132)" in story + else: + assert f"{'v' if expect[:5] == 'm/84h' else 'u'}pub (SLIP-132)" in story + need_keypress("2") + time.sleep(.1) + _, story = cap_story() + assert ("%spub (BIP-32)" % ("x" if chain == "BTC" else "t")) in story + assert "Press (2)" in story + + press_select() + got_slip_pub = cap_screen_qr().decode('ascii') + got_unslip, *_ = slip132undo(got_slip_pub) + assert got_unslip == got_pub - if got_pub[0] not in 'xt': - got_pub,*_ = slip132undo(got_pub) + if f'Press {KEY_NFC if is_q1 else "(3)"}' in story: + assert 'NFC' in story + press_nfc() + time.sleep(0.2) + got_nfc_slip_pub = nfc_read_text() + time.sleep(0.1) + press_cancel() # cancel animation + assert got_slip_pub == got_nfc_slip_pub + + press_cancel() # cancel QR expect_acctnum_captured(acct_num) @@ -598,18 +664,18 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home if expect != 'm': wallet = wallet.subkey_for_path(expect[2:].replace('h', "'")) assert got.sec() == wallet.sec() - press_cancel() @pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"]) @pytest.mark.parametrize("way", ["sd", "vdisk", "nfc", "qr"]) -@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2WPKH_P2SH, AF_CLASSIC]) +@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2WPKH_P2SH, AF_CLASSIC, AF_P2TR]) @pytest.mark.parametrize("acct_num", [None, 0, 1, (2 ** 31) - 1]) @pytest.mark.parametrize("int_ext", [True, False]) def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress, expect_acctnum_captured, OK, pick_menu_item, way, cap_story, cap_menu, int_ext, settings_get, virtdisk_path, load_export, press_select, skip_if_useless_way): + skip_if_useless_way(way) settings_set('chain', chain) @@ -651,6 +717,10 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, menu_item = "P2SH-Segwit" desc_prefix = "sh(wpkh(" bip44_purpose = 49 + elif addr_fmt == AF_P2TR: + menu_item = "Taproot P2TR" + desc_prefix = "tr(" + bip44_purpose = 86 else: # addr_fmt == AF_CLASSIC: menu_item = "Classic P2PKH" @@ -662,7 +732,11 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, expect_acctnum_captured(acct_num) - contents = load_export(way, label="Descriptor", is_json=False, addr_fmt=addr_fmt) + sig_check = True + if addr_fmt == AF_P2TR: + sig_check = False + contents = load_export(way, label="Descriptor", is_json=False, addr_fmt=addr_fmt, + sig_check=sig_check) descriptor = contents.strip() if int_ext is False: @@ -770,6 +844,47 @@ def test_zeus_descriptor_export(addr_fmt, acct_num, goto_home, need_keypress, pi assert xpub_target in xpub +@pytest.mark.parametrize("chain", ["BTC", "XTN"]) +def test_bullbitcoin_descriptor_export(goto_home, need_keypress, pick_menu_item, + cap_story, cap_menu, nfc_read_text, settings_get, chain, + press_select, skip_if_useless_way, + settings_set, press_cancel, cap_screen_qr, + expect_acctnum_captured): + + settings_set('chain', chain) + chain_num = 1 if chain == "XTN" else 0 + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Export Wallet") + pick_menu_item("Bull Bitcoin") + + time.sleep(.1) + expect_acctnum_captured(0) + + contents = cap_screen_qr().decode('ascii') + descriptor = contents.strip() + + press_cancel() + time.sleep(.1) + assert "Bull Bitcoin" in cap_menu() # back to menu + + assert descriptor.startswith("wpkh(") + desc_obj = Descriptor.parse(descriptor) + assert desc_obj.serialize(int_ext=True) == descriptor + assert desc_obj.addr_fmt == AF_P2WPKH + assert len(desc_obj.keys) == 1 + xfp, derive, xpub = desc_obj.keys[0] + assert xfp == settings_get("xfp") + assert derive == f"m/84h/{chain_num}h/0h" + seed = Mnemonic.to_seed(simulator_fixed_words) + node = BIP32Node.from_master_secret( + seed, netcode="BTC" if chain == "BTC" else "XTN" + ).subkey_for_path(derive) + xpub_target = node.hwif() + assert xpub_target in xpub + + @pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"]) @pytest.mark.parametrize("account", ["Postmix", "Premix"]) def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_home, @@ -796,6 +911,7 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_ pick_menu_item("Segwit P2WPKH") # both postmix and premix are p2wpkh only file_desc_generic = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH) press_select() # written + press_cancel() # leave export options press_cancel() # back to export submenu press_cancel() # back to advanced pick_menu_item("Export Wallet") @@ -811,4 +927,138 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_ file_desc = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH) assert file_desc.strip() == file_desc_generic.strip() + +@pytest.mark.parametrize("chain", ["BTC", "XTN"]) +@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc", "qr"]) +@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2TR, AF_P2WPKH_P2SH, AF_CLASSIC, + AF_P2WSH, AF_P2WSH_P2SH, 1000]) # using 1000 as P2TR multisig +@pytest.mark.parametrize("acct_num", [None, (2 ** 31) - 1]) +def test_key_expression_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress, + pick_menu_item, way, cap_story, cap_menu, virtdisk_path, dev, + load_export, press_select, skip_if_useless_way): + + skip_if_useless_way(way, allow_mk4_qr=True) + + settings_set('chain', chain) + chain_num = 1 if chain in ["XTN", "XRT"] else 0 + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Export Wallet") + pick_menu_item("Key Expression") + time.sleep(.1) + _, story = cap_story() + assert "This saves a extended key expression" in story + assert "Press (1) to enter a non-zero account number" in story + assert "sensitive--in terms of privacy" in story + assert "not compromise your funds directly" in story + + if isinstance(acct_num, int): + need_keypress("1") # chosse account number + for ch in str(acct_num): + need_keypress(ch) # input num + press_select() # confirm selection + else: + press_select() # confirm story + acct_num = 0 + + menu = cap_menu() + if addr_fmt == AF_P2WPKH: + menu_item = "Segwit P2WPKH" + derive = f"m/84h/{chain_num}h/{acct_num}h" + elif addr_fmt == AF_P2TR: + menu_item = "Taproot P2TR" + derive = f"m/86h/{chain_num}h/{acct_num}h" + elif addr_fmt == AF_P2WPKH_P2SH: + menu_item = "P2SH-Segwit" + derive = f"m/49h/{chain_num}h/{acct_num}h" + elif addr_fmt == AF_CLASSIC: + menu_item = "Classic P2PKH" + derive = f"m/44h/{chain_num}h/{acct_num}h" + elif addr_fmt == AF_P2WSH: + menu_item = "Multi P2WSH" + derive = f"m/48h/{chain_num}h/{acct_num}h/2h" + elif addr_fmt == AF_P2WSH_P2SH: + menu_item = "Multi P2SH-P2WSH" + derive = f"m/48h/{chain_num}h/{acct_num}h/1h" + else: + assert addr_fmt == 1000 + menu_item = "Multi P2TR" + derive = f"m/48h/{chain_num}h/{acct_num}h/3h" + + assert menu_item in menu + pick_menu_item(menu_item) + + contents = load_export(way, label="Key Expression", is_json=False, sig_check=False) + key_exp = contents.strip() + + xfp = dev.master_fingerprint + xfp = xfp2str(xfp).lower() + + seed = Mnemonic.to_seed(simulator_fixed_words) + node = BIP32Node.from_master_secret( + seed, netcode="BTC" if chain == "BTC" else "XTN" + ).subkey_for_path(derive) + + target = f"[{xfp}/{derive.replace('m/', '')}]{node.hwif()}" + assert key_exp == target + + +@pytest.mark.parametrize('path', [ + # NOTE: (2**31)-1 = 0x7fff_ffff = 2147483647 + "m/2147483647/2147483647/2147483647/2147483647/2147483647/2147483647/2147483647/2147483647", + "m/1/2/3/4/5", + "m/1h/2h/3h/4h/5h", + "m/45h", +]) +def test_custom_key_expression_export(path, goto_home, pick_menu_item, cap_menu, need_keypress, + press_select, load_export, use_testnet, dev): + use_testnet() + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Export Wallet") + pick_menu_item("Key Expression") + press_select() # story + pick_menu_item("Custom Path") + + # blind entry, using only first 2 menu items + deeper = path.split("/")[1:] + for depth, part in enumerate(deeper): + time.sleep(.01) + m = cap_menu() + for mi in m: + assert "{idx}" not in mi # ranged values not allowed here + if depth == 0: + assert m[0] == 'm/⋯' + pick_menu_item(m[0]) + else: + assert m[0].endswith("h/⋯") + assert m[1].endswith("/⋯") + assert m[0] != m[1] + + pick_menu_item(m[0 if last_part[-1] == "h" else 1]) + + # enter path component + for d in part: + if d == "h": break + need_keypress(d) + press_select() + + last_part = part + + time.sleep(.01) + m = cap_menu() + pick_menu_item(m[2 if part[-1] == "h" else 3]) + + contents = load_export("sd", label="Key Expression", is_json=False, sig_check=False) + key_exp = contents.strip() + + xfp = dev.master_fingerprint + xfp = xfp2str(xfp).lower() + + seed = Mnemonic.to_seed(simulator_fixed_words) + node = BIP32Node.from_master_secret(seed, netcode="XTN").subkey_for_path(path) + + target = f"[{xfp}/{path.replace('m/', '')}]{node.hwif()}" + assert key_exp == target + # EOF diff --git a/testing/test_hobble.py b/testing/test_hobble.py new file mode 100644 index 000000000..d843ae562 --- /dev/null +++ b/testing/test_hobble.py @@ -0,0 +1,485 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Verify hobble works: a restricted access mode, without export/view of seed and more. +# +# - spending policy menu and txn checks should not be in this file, instead expand +# test_ccc.py or create test_sssp.py +# +# Additional tests, elsewhere: +# +# - test_teleport.py::test_teleport_ms_sign +# - verifies: MS psbt KT should still work in hobbled mode +# +# - test_teleport.py::test_hobble_limited +# - verifies: scan a KT and have it rejected if not PSBT type: so R and E types +# +# - login_settings_tests.py for login/bypass UX +# +# +import pytest, time, os, pdb +from bip32 import BIP32Node +from constants import simulator_fixed_words, simulator_fixed_xprv +from test_ephemeral import SEEDVAULT_TEST_DATA, WORDLISTS +from test_ephemeral import confirm_tmp_seed, verify_ephemeral_secret_ui +from test_ux import word_menu_entry +from charcodes import KEY_QR + +@pytest.fixture +def set_hobble(sim_exec, settings_set, settings_remove, goto_home): + def doit(mode, enabled={}): # okeys, words, notes + assert mode in { True, False, 2 } + + if mode: + v = dict(en=True, pol={}) + for w in enabled: + v[w] = True + settings_set('sssp', v) + print(f'sssp = {v!r}') + else: + settings_remove('sssp') + + sim_exec(f''' +from pincodes import pa; from actions import goto_top_menu +pa.hobbled_mode = {mode!r} +goto_top_menu() +''') + goto_home() # required, not sure why + + yield doit + + doit(False) + +@pytest.mark.parametrize('en_okeys', [ True, False] ) +@pytest.mark.parametrize('en_notes', [ True, False] ) +@pytest.mark.parametrize('en_nfc', [ True, False] ) +@pytest.mark.parametrize('en_miniscript', [ True, False] ) +def test_menu_contents(set_hobble, pick_menu_item, cap_menu, en_okeys, en_notes, settings_set, + need_some_notes, is_q1, is_mark4, en_nfc, sim_exec, en_miniscript, + vdisk_disabled): + + # just enough to pass/fail the menu predicates! + settings_set('seedvault', True) + + #settings_set('nfc', en_nfc) + sim_exec(f'import glob; glob.NFC = {(True if en_nfc else None)!r};') + + settings_set('miniscript', en_miniscript) + + if is_q1: + need_some_notes() + + # main menu basics + expect = {'Ready To Sign', 'Address Explorer', 'Advanced/Tools' } + + if is_q1: + expect.add('Scan Any QR Code') + else: + expect.add('Secure Logout') + + en = set() + if en_okeys: + en.add('okeys') + expect.add('Seed Vault') + expect.add('Passphrase') + + if en_notes: + en.add('notes') + if is_q1: + expect.add('Secure Notes & Passwords') + + # enables hobble and goes to top menu + set_hobble(True, en) + + m = cap_menu() + assert set(m) == expect, 'Main menu wrong' + + # advanced menu + pick_menu_item("Advanced/Tools") + + adv_expect = { 'File Management', + 'Export Wallet', + 'View Identity', + 'Paper Wallets', + 'Destroy Seed', + f'Show {"Firmware" if is_q1 else "FW"} Version' } + + if is_q1 and en_miniscript: + adv_expect.add('Teleport Multisig/Miniscript PSBT') + + if en_nfc: + adv_expect.add('NFC Tools') + + if en_okeys: + adv_expect.add('Temporary Seed') + + m = cap_menu() + assert set(m) == adv_expect, "Adv menu wrong" + + # file management + pick_menu_item("File Management") + + fm_expect = { 'Sign Text File', + 'Batch Sign PSBT', + 'List Files', + 'Export Wallet', + 'Verify Sig File', + 'Format SD Card' } + + if not vdisk_disabled: + fm_expect.add('Format RAM Disk') + + if en_nfc: + fm_expect.add('NFC File Share') + if is_q1: + fm_expect.add('BBQr File Share') + fm_expect.add('QR File Share') + + m = cap_menu() + assert set(m) == fm_expect, "File Mgmt menu wrong" + + +def test_h_notes(only_q1, set_hobble, pick_menu_item, cap_menu, settings_set, need_some_notes, + sim_exec, settings_remove): + ''' + * load a secure note/pw; check readonly once hobbled + * cannot export + * cannot edit + * can view / use for kbd emulation + * check notes not offered if none defined + * check readonly features on notes when note pre-defined before entering hobbled mode + ''' + need_some_notes() + set_hobble(True, {'notes'}) + + pick_menu_item('Secure Notes & Passwords') + + m = cap_menu() + assert m == [ '1: Title Here' ] + pick_menu_item(m[0]) + + m = cap_menu() + assert m == [ '"Title Here"', 'View Note', 'Sign Note Text' ] + + # clear notes, should not be offered + settings_remove('notes') + settings_remove('secnap') + set_hobble(True, {'notes'}) + + m = cap_menu() + assert 'Secure Notes & Passwords' not in m + +def test_kt_limits(only_q1, set_hobble, pick_menu_item, cap_menu, settings_set, need_some_notes, + sim_exec, settings_remove): + ''' + - key teleport + * check KT only offered if MS wallet setup + ''' + settings_remove('multisig') + set_hobble(True) + pick_menu_item("Advanced/Tools") + + assert 'Teleport Multisig/Miniscript PSBT' not in cap_menu() + # converse already tested in test_menu_contents + +@pytest.mark.parametrize('sv_empty', [ True, False] ) +def test_h_seedvault(sv_empty, set_hobble, pick_menu_item, cap_menu, settings_set, sim_exec, + settings_remove, restore_main_seed, settings_get, press_cancel, press_select, + cap_story): + ''' + - seed vault can be accessed, when enabled + - temp seeds are read-only: no create, no rename, etc. + - SV menu item is offered iff SV enabled; can be empty or not. + ''' + + settings_set('seedvault', True) + if sv_empty: + settings_set('seeds', []) + else: + settings_set('seeds', []) + xfp, enc = SEEDVAULT_TEST_DATA[0][0:2] + settings_set("seeds", [(xfp, '80'+enc, f"Menu Label", "meta-source")]) + + set_hobble(True, {'okeys'}) + + assert cap_menu()[0] == 'Ready To Sign', 'restart simulator now' + pick_menu_item('Seed Vault') + + m = cap_menu() + if sv_empty: + assert m == ['(none saved yet)'] + else: + assert m == [' 1: Menu Label'] + + pick_menu_item(m[0]) + m = cap_menu() + assert m == ['Menu Label', 'Use This Seed'] + + pick_menu_item(m[0]) + title, story = cap_story() + assert 'Origin:\nmeta-source' in story + press_cancel() + + pick_menu_item('Use This Seed') + title, story = cap_story() + assert 'temporary master key is in effect' in story + press_select() + + # arrive back in main menu, w/ tmp seed in effect + # - but we are still hobbled. + # - XFP shown + # - Restore master should be offered. + m = cap_menu() + assert m[0] == f'[{xfp}]' + assert m[-1] == 'Restore Master' + assert "Settings" not in m # in hobbled mode + + pick_menu_item("Advanced/Tools") + m = cap_menu() + # we are in tmp seed session, restore master if you want to destroy seed + assert 'Destroy Seed' not in m + press_cancel() + + pick_menu_item("Restore Master") + title, story = cap_story() + assert 'main wallet' in story + press_select() + + + # clear keys from sv, should not be offered in menu, even if okeys set. + settings_remove('seedvault') + set_hobble(True, {'okey'}) + + m = cap_menu() + assert 'Seed Vault' not in m + +@pytest.mark.parametrize('mode', [ 'words', 'qr', 'xprv', 'tapsigner', 'coldcard', 'b39pass']) +def test_h_tempseeds(mode, set_hobble, pick_menu_item, cap_menu, settings_set, is_q1, + press_select, cap_story, word_menu_entry, confirm_tmp_seed, enter_complex, + verify_ephemeral_secret_ui, scan_a_qr, tapsigner_encrypted_backup, + need_keypress, enter_hex, open_microsd, microsd_path, go_to_passphrase): + ''' + - can import and use a key for signing + - NOT offered chance to save into seedvault + ''' + if not is_q1 and mode == 'qr': return + + settings_set('seedvault', True) + settings_set('seeds', []) + + set_hobble(True, {'okeys'}) + + if mode != "b39pass": + pick_menu_item("Advanced/Tools") + pick_menu_item('Temporary Seed') + + m = cap_menu() + assert 'Generate Words' not in m + assert all((i.startswith("Import ") or i.endswith(' Backup') or i == 'Restore Seed XOR') + for i in m), m + + words, expect_xfp = WORDLISTS[12] + + if mode == 'words': + # just quick tests here, not in-depth + # - from test_ephemeral_seed_import_words() + pick_menu_item("Import Words") + pick_menu_item(f"12 Words") + time.sleep(0.1) + word_menu_entry(words.split()) + + elif mode == 'qr': + pick_menu_item("Import from QR Scan") + val = ' '.join(words.split()).upper() + scan_a_qr(val) + time.sleep(0.2) + + elif mode == 'tapsigner': + # like test_ephemeral_seed_import_tapsigner() + fname, backup_key_hex, node = tapsigner_encrypted_backup('sd', testnet=True) + expect_xfp = node.fingerprint().hex().upper() + pick_menu_item("Tapsigner Backup") + time.sleep(0.1) + need_keypress('1') + time.sleep(0.1) + pick_menu_item(fname) + + time.sleep(0.1) + _, story = cap_story() + assert "your TAPSIGNER" in story + + press_select() # yes I have backup key + enter_hex(backup_key_hex) + + elif mode == 'coldcard': + # like test_temporary_from_backup() + # - but skip making new bk file + fn = 'data/tip-index-famous-embark-tobacco-rice-attitude-interest-mask-random-amazing-initial.7z' + pw = fn[5:-3].split('-') + + contents = open(fn, 'rb').read() + with open_microsd('example.7z', 'wb') as fd: + fd.write(contents) + + pick_menu_item("Coldcard Backup") + time.sleep(0.1) + need_keypress('1') + time.sleep(0.1) + pick_menu_item('example.7z') + + word_menu_entry(pw, has_checksum=False) + + time.sleep(.1) + press_select() # confirm loading of the backup + time.sleep(.1) + title, story = cap_story() + assert title == 'FAILED' + assert 'successfully tested recovery' in story + + press_select() + return + + elif mode == 'xprv': + fname = "ek.txt" + node = BIP32Node.from_master_secret(os.urandom(32), netcode="XTN") + expect_xfp = node.fingerprint().hex().upper() + ek = node.hwif(as_private=True) + with open(microsd_path(fname), "w") as f: + f.write(ek) + + pick_menu_item("Import XPRV") + time.sleep(0.1) + _, story = cap_story() + if "Press (1) to import extended private key" in story: + need_keypress("1") + + time.sleep(0.1) + pick_menu_item(fname) + + elif mode == "b39pass": + from mnemonic import Mnemonic + go_to_passphrase() + passphrase = "sssp" + seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=passphrase) + node = BIP32Node.from_master_secret(seed, netcode="XTN") + expect_xfp = node.fingerprint().hex().upper() + + enter_complex(passphrase, apply=True) + time.sleep(.2) + title, story = cap_story() + assert title[1:-1] == expect_xfp + assert "Above is the master key fingerprint of the new wallet" in story + press_select() + time.sleep(.1) + title, story = cap_story() + assert "store temporary seed into Seed Vault" not in story + time.sleep(.1) + + else: + raise pytest.fail(mode) + + if mode != "b39pass": + # different UX for passphrase - verified above + confirm_tmp_seed(seedvault=False, check_sv_not_offered=True) + + # do not verify presence of Seed Vault menu item - irrelevant + verify_ephemeral_secret_ui(expected_xfp=expect_xfp, mnemonic=None, seed_vault=None) + + time.sleep(.1) + m = cap_menu() + if mode in ["words", "qr"]: + # verify okeys is respected in tmp seed + assert "Passphrase" in m + + pick_menu_item("Restore Master") + press_select() + + +@pytest.mark.parametrize('en_okeys', [ True, False]) +def test_h_usbcmds(en_okeys, set_hobble, dev): + # test various usb commands are blocked during hobble + + from ckcc_protocol.protocol import CCProtoError + + set_hobble(True, {'okeys'} if en_okeys else {}) + + block_list = [ 'back', 'enrl', 'bagi', 'hsms', 'user', 'nwur', 'rmur' ] + + if not en_okeys: + block_list.insert(0, 'pass') + + for cmd in block_list: + with pytest.raises(CCProtoError) as ee: + got = dev.send_recv(cmd) + assert 'Spending policy in effect' in str(ee) + + +@pytest.mark.parametrize('en_okeys', [ True, False]) +def test_h_qrscan(en_okeys, set_hobble, scan_a_qr, need_keypress, press_cancel, cap_screen, only_q1, + cap_story, press_select, pick_menu_item): + # verify whitelist of QR types is correct when in hobbled mode + # - no private key material, unless "okeys" is set + # - no teleport starting, except multisig co-signing + # + set_hobble(True, {'okeys'} if en_okeys else {}) + + words, _ = WORDLISTS[12] + keys = [ + ' '.join(w[0:4] for w in words.split()), + simulator_fixed_xprv] + + for ss in keys: + need_keypress(KEY_QR) + scan_a_qr(ss) + time.sleep(1) + + title, story = cap_story() + if en_okeys: + assert 'New temporary master key is in effect' in story + press_select() + + pick_menu_item("Restore Master") + press_select() + else: + assert 'Blocked when Spending Policy is in force.' in story + press_select() + + for dt in 'RSE': + need_keypress(KEY_QR) + tt = f'B$H{dt}0100'+('A'*80) + scan_a_qr(tt) + time.sleep(1) + + if dt == 'E': + title, story = cap_story() + assert 'Incoming PSBT requires miniscript wallet' in story + press_cancel() + else: + scr = cap_screen() # stays in scanning mode + assert 'KT Blocked' in scr + +def test_h_seedxor(set_hobble, need_keypress, press_cancel, cap_screen, + cap_story, press_select, pick_menu_item, settings_set): + # can start import via seed XOR, but cannot include master seed phrase + # as part of it. + + settings_set('seedvault', True) + settings_set('seeds', []) + set_hobble(True, {'okeys'}) + + pick_menu_item("Advanced/Tools") + pick_menu_item('Temporary Seed') + pick_menu_item('Restore Seed XOR') + + title, story = cap_story() + assert 'A/B/C' in story + press_select() # select 24 words + time.sleep(0.1) + + title, story = cap_story() + assert 'Since you have' in story + assert "include this Coldcard's seed" not in story # WEAK: fragile if UX changes + + press_cancel() + + +# EOF diff --git a/testing/test_hsm.py b/testing/test_hsm.py index 815835596..2d73aac8c 100644 --- a/testing/test_hsm.py +++ b/testing/test_hsm.py @@ -71,7 +71,7 @@ def cleanup(type_, value): if type_ == Deriv: rv = [] for orig in value or []: - rv.append(orig if orig in ["any", "p2sh"] else orig.replace('p', "h").replace("'", 'h')) + rv.append(orig if orig in ["any", "msas"] else orig.replace('p', "h").replace("'", 'h')) elif type_ == WhitelistOpts: rv = OrderedDict() rv["mode"] = value.get("mode", "BASIC") @@ -122,7 +122,7 @@ def enable_hsm_commands(dev, sim_exec, only_mk4): sim_exec(cmd) -@pytest.fixture(scope='function') +@pytest.fixture def hsm_reset(dev, sim_exec): # filename for the policy file, as stored on simulated CC @@ -170,10 +170,10 @@ def doit(): (DICT(msg_paths=["any"]), "(any path)"), # data sharing - (DICT(share_addrs=["m/1'/2p/3H"]), ['Address values values will be shared', "m/1h/2h/3h"]), - (DICT(share_addrs=["m/1", "m/2"]), ['Address values values will be shared', "m/1 OR m/2"]), - (DICT(share_addrs=["any"]), ['Address values values will be shared', "(any path)"]), - (DICT(share_addrs=["p2sh", "any"]), ['Address values values will be shared', "(any P2SH)", "(any path"]), + (DICT(share_addrs=["m/1'/2p/3H"]), ['Address values will be shared', "m/1h/2h/3h"]), + (DICT(share_addrs=["m/1", "m/2"]), ['Address values will be shared', "m/1 OR m/2"]), + (DICT(share_addrs=["any"]), ['Address values will be shared', "(any path)"]), + (DICT(share_addrs=["msas", "any"]), ['Address values will be shared', "(any miniscript)", "(any path"]), (DICT(share_xpubs=["m/1'/2p/3H"]), ['XPUB values will be shared', "m/1h/2h/3h"]), (DICT(share_xpubs=["m/1", "m/2"]), ['XPUB values will be shared', "m/1 OR m/2"]), @@ -206,7 +206,7 @@ def doit(): # wallets (DICT(rules=[dict(wallet='1')]), - '(non multisig)'), + '(singlesig only)'), # users (DICT(rules=[dict(users=USERS)]), @@ -476,10 +476,11 @@ def wait_til_signed(dev): return result @pytest.fixture -def attempt_psbt(hsm_status, start_sign, dev): +def attempt_psbt(hsm_status, start_sign, dev, sim_root_dir): def doit(psbt, refuse=None, remote_error=None): - open('debug/attempt.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/attempt.psbt', 'wb') as f: + f.write(psbt) start_sign(psbt) try: @@ -532,54 +533,166 @@ def test_simple_limit(dev, amount, over, start_hsm, fake_txn, attempt_psbt, twea assert 'Rule #2' not in stat.summary # create a transaction - psbt = fake_txn(2, 2, dev.master_xpub, outvals=[amount, 2E8-amount], - change_outputs=[1], fee=0) + psbt = fake_txn(2, [["p2tr", int(amount)],["p2wpkh", int(2E8-amount), True]], + dev.master_xpub, fee=0) attempt_psbt(psbt) - psbt = fake_txn(2, 2, dev.master_xpub, outvals=[amount+over, 2E8-amount-over], - change_outputs=[1], fee=0) + psbt = fake_txn(2, [["p2tr", int(amount+over)],["p2wpkh", int(2E8-amount-over), True]], + dev.master_xpub, fee=0) attempt_psbt(psbt, "amount exceeded") if tweak_rule: tweak_rule(0, dict(max_amount=int(amount+over))) attempt_psbt(psbt) -def test_named_wallets(dev, start_hsm, tweak_rule, make_myself_wallet, hsm_status, - attempt_psbt, fake_txn, fake_ms_txn, amount=5E6, incl_xpubs=False): +def test_named_wallets(dev, start_hsm, tweak_rule, import_ms_wallet, hsm_status, + attempt_psbt, fake_txn, fake_ms_txn, amount=5E6): wname = 'Myself-4' M = 4 stat = hsm_status() assert not stat.active - for retry in range(3): - keys, _ = make_myself_wallet(4) # slow AF + keys = import_ms_wallet(M,M, name=wname, accept=True) + time.sleep(.2) - stat = hsm_status() - if wname in stat.wallets: - break + stat = hsm_status() + assert wname in stat.wallets # policy: only allow multisig w/ that name policy = DICT(rules=[dict(wallet=wname)]) stat = start_hsm(policy) - assert 'Any amount from multisig wallet' in stat.summary + assert 'Any amount from miniscript wallet' in stat.summary assert wname in stat.summary assert 'wallets' not in stat # simple p2pkh should fail - psbt = fake_txn(1, 2, dev.master_xpub, outvals=[amount, 1E8-amount], change_outputs=[1], fee=0) - attempt_psbt(psbt, "not multisig") + psbt = fake_txn(1, [["p2tr", int(amount)],["p2wpkh", int(1E8-amount), True]], + dev.master_xpub, fee=0) + attempt_psbt(psbt, "singlesig only") # but txn w/ multisig wallet should work psbt = fake_ms_txn(1, 2, M, keys, fee=0, outvals=[amount, 1E8-amount], outstyles=['p2wsh'], - change_outputs=[1], incl_xpubs=incl_xpubs) + change_outputs=[1]) attempt_psbt(psbt) # check ms txn not accepted when rule spec's a single signer tweak_rule(0, dict(wallet='1')) - attempt_psbt(psbt, 'wrong wallet') + attempt_psbt(psbt, 'wrong miniscript wallet') + +@pytest.mark.bitcoind +def test_named_wallets_miniscript(dev, start_hsm, tweak_rule, + hsm_status, attempt_psbt, fake_txn, bitcoind, + offer_minsc_import, need_keypress, pick_menu_item, + load_export, goto_home): + stat = hsm_status() + assert not stat.active + + from test_miniscript import CHANGE_BASED_DESCS + for i, desc in enumerate(CHANGE_BASED_DESCS): + name = f"hsm_msc{i}" + xd = json.dumps({"name": name, "desc": desc}) + title, story = offer_minsc_import(xd) + assert "Create new miniscript wallet?" in story + assert name in story + need_keypress("y") + time.sleep(.2) + + core_wallets = [] + for i in range(len(CHANGE_BASED_DESCS)): + name = f"hsm_msc{i}" + wo = bitcoind.create_wallet(wallet_name=name, disable_private_keys=True, blank=True, + passphrase=None, avoid_reuse=False, descriptors=True) + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(name) + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export("sd", label="Bitcoin Core Miniscript", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + res = wo.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"] + + af = "bech32" + if i > 1: + af = "bech32m" + + addr = wo.getnewaddress("", af) + bitcoind.supply_wallet.sendtoaddress(addr, 1.0) + core_wallets.append(wo) + + # mine above txns + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + for w in core_wallets: + assert len(w.listunspent()) > 0, "nu funds" + + stat = hsm_status() + for i in range(len(CHANGE_BASED_DESCS)): + assert f"hsm_msc{i}" in stat.wallets + + # policy: only allow miniscript 0 + wname = "hsm_msc0" + policy = DICT(share_addrs=["any"], rules=[dict(wallet=wname)]) + + stat = start_hsm(policy) + assert 'Any amount from miniscript wallet' in stat.summary + assert wname in stat.summary + assert 'wallets' not in stat + + # simple p2pkh should fail + psbt = fake_txn(1, [["p2tr", int(5E6)],["p2wpkh", int(1E8-5E6), True]], fee=0) + attempt_psbt(psbt, "singlesig only") + + # but txn from target miniscript wallet 0 must work + wal0 = core_wallets[0] + psbt_res = wal0.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.2}], 0, {"fee_rate": 20}) + attempt_psbt(base64.b64decode(psbt_res["psbt"])) + + # WRONG + wal2 = core_wallets[2] + psbt_res = wal2.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.2}], 0, {"fee_rate": 18}) + attempt_psbt(base64.b64decode(psbt_res["psbt"]), 'wrong miniscript wallet') + + wal1 = core_wallets[1] + psbt_res = wal1.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.2}], 0, {"fee_rate": 12}) + attempt_psbt(base64.b64decode(psbt_res["psbt"]), 'wrong miniscript wallet') + + # works + psbt_res = wal0.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.3}], 0, {"fee_rate": 15}) + attempt_psbt(base64.b64decode(psbt_res["psbt"])) + + wname = "hsm_msc3" + tweak_rule(0, dict(wallet=wname)) + + # this worked before but now, after tweak, it does not + psbt_res = wal0.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.1}], 0, {"fee_rate": 13}) + attempt_psbt(base64.b64decode(psbt_res["psbt"]), 'wrong miniscript wallet') + + # correct wallet 3 + wal3 = core_wallets[3] + psbt_res = wal3.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.6}], 0, {"fee_rate": 10}) + attempt_psbt(base64.b64decode(psbt_res["psbt"])) + + psbt_res = wal3.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 0.15}], 0, {"fee_rate": 15}) + last_correct = base64.b64decode(psbt_res["psbt"]) + attempt_psbt(last_correct) + + # check ms txn not accepted when rule spec's a single signer + tweak_rule(0, dict(wallet='1')) + attempt_psbt(last_correct, 'wrong miniscript wallet') + + stat = hsm_status() + assert stat.approvals == 4 + assert stat.refusals == 5 @pytest.mark.parametrize('with_whitelist_opts', [ False, True]) def test_whitelist_single(dev, start_hsm, tweak_rule, attempt_psbt, fake_txn, with_whitelist_opts, amount=5E6): @@ -594,13 +707,11 @@ def test_whitelist_single(dev, start_hsm, tweak_rule, attempt_psbt, fake_txn, wi start_hsm(policy) # try all addr types - for style in ['p2wpkh', 'p2wsh', 'p2sh', 'p2pkh', 'p2wsh-p2sh', 'p2wpkh-p2sh']: + for style in ['p2wpkh', 'p2wsh', 'p2sh', 'p2pkh', 'p2wsh-p2sh', 'p2wpkh-p2sh', 'p2tr']: dests = [] - psbt = fake_txn(1, 2, dev.master_xpub, - outstyles=[style, 'p2wpkh'], - outvals=[amount, 1E8-amount], change_outputs=[1], fee=0, - capture_scripts=dests) + psbt = fake_txn(1, [[style, int(amount)], ["p2wpkh", int(1E8-amount), True]], + dev.master_xpub, fee=0, capture_scripts=dests) dest = render_address(dests[0]) tweak_rule(0, dict(whitelist=[dest])) @@ -622,8 +733,7 @@ def test_whitelist_multi(dev, start_hsm, tweak_rule, attempt_psbt, fake_txn, amo # make a txn that sends to every type of output styles = ['p2wpkh', 'p2wsh', 'p2sh', 'p2pkh', 'p2wsh-p2sh', 'p2wpkh-p2sh'] dests = [] - psbt = fake_txn(1, len(styles), dev.master_xpub, - outstyles=styles, capture_scripts=dests) + psbt = fake_txn(1, [[outs] for outs in styles], dev.master_xpub, capture_scripts=dests) dests = [render_address(s) for s in dests] @@ -758,7 +868,7 @@ def test_big_txn(num_in, num_out, dev, quick_start_hsm, hsm_status, is_simulator attempt_psbt(psbt) -@pytest.mark.veryslow +@pytest.mark.manual def test_multiple_signings(dev, quick_start_hsm, is_simulator, attempt_psbt, fake_txn, load_hsm_users, auth_user): @@ -768,19 +878,20 @@ def test_multiple_signings(dev, quick_start_hsm, is_simulator, quick_start_hsm(policy) for count in range(400): - psbt = fake_txn(2, 2, dev.master_xpub, change_outputs=[0]) + psbt = fake_txn(2, [["p2wpkh", None, True],["p2sh-p2wpkh", None]], + dev.master_xpub, addr_fmt="p2wpkh") auth_user.psbt_hash = sha256(psbt).digest() auth_user("pw") attempt_psbt(psbt) -@pytest.mark.veryslow +@pytest.mark.manual @pytest.mark.parametrize("cc_first", [True, False]) @pytest.mark.parametrize("M_N", [(2,3), (3,5), (15,15)]) def test_multiple_signings_multisig(cc_first, M_N, dev, quick_start_hsm, is_simulator, attempt_psbt, fake_txn, load_hsm_users, auth_user, bitcoind, - request): + request, sim_root_dir): # signs 400 different PSBTs in loop beaing one leg of multisig # CC must be on regtest if testing with real thing af = "bech32" @@ -850,7 +961,9 @@ def test_multiple_signings_multisig(cc_first, M_N, dev, quick_start_hsm, # uploading only external to CC file_len, sha = dev.upload_file(desc_ext.encode('ascii')) - open('debug/last-config.txt', 'wt').write(desc_ext) + with open(f'{sim_root_dir}/debug/last-config.txt', 'wt') as f: + f.write(desc_ext) + dev.send_recv(CCProtocolPacker.multisig_enroll(file_len, sha), timeout=30000) time.sleep(.2) @@ -1075,7 +1188,7 @@ def test_storage_locker(package, count, start_hsm, dev): def test_usb_cmds_block(quick_start_hsm, dev): # check these commands return errors (test whitelist) block_list = [ - 'rebo', 'dfu_', 'enrl', 'enok', + 'rebo', 'dfu_', 'enrl', 'enok', 'rest', 'back', 'pass', 'bagi', 'hsms', 'nwur', 'rmur', 'pwok', 'bkok', ] @@ -1083,8 +1196,8 @@ def test_usb_cmds_block(quick_start_hsm, dev): for cmd in block_list: with pytest.raises(CCProtoError) as ee: - got = dev.send_recv(cmd) - assert 'HSM' in str(ee) + dev.send_recv(cmd) + assert 'Not allowed in HSM mode' in str(ee) def test_unit_local_conf(sim_exec, enter_local_code, quick_start_hsm): # just testing our fixture really @@ -1126,36 +1239,30 @@ def doit(path, addr_fmt): path = path.replace('*', '73') addr = doit(path, addr_fmt) -def test_show_p2sh_addr(dev, hsm_reset, start_hsm, change_hsm, make_myself_wallet, addr_vs_path): - # MULTISIG addrs - from test_multisig import HARD, make_redeem - M = 4 - pm = lambda i: [HARD(45), i, 0,0] - - # can't amke ms wallets inside HSM mode - hsm_reset() - keys, _ = make_myself_wallet(M) # slow AF - - permit = ['p2sh', 'm/73'] - start_hsm(DICT(share_addrs=permit)) - - - scr, pubkeys, xfp_paths = make_redeem(M, keys, path_mapper=pm) - assert len(scr) <= 520, "script too long for standard!" +def test_show_miniscript_addr(dev, offer_minsc_import, start_hsm, + change_hsm, need_keypress, clear_miniscript): + clear_miniscript() + from test_miniscript import CHANGE_BASED_DESCS + name = "hsm_msc_msas" + xd = json.dumps({"name": name, "desc": CHANGE_BASED_DESCS[0]}) + title, story = offer_minsc_import(xd) + assert "Create new miniscript wallet?" in story + assert name in story + need_keypress("y") + time.sleep(.2) - got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address( - M, xfp_paths, scr, addr_fmt=AF_P2WSH)) - addr_vs_path(got_addr, addr_fmt=AF_P2WSH, script=scr) + policy = DICT(share_addrs=["any"], rules=[dict(wallet=name)]) + start_hsm(policy) - # turn it off; p2sh must be explicitly allowed - for allow in ['m', 'any']: - change_hsm(DICT(share_addrs=[allow])) - dev.send_recv(CCProtocolPacker.show_address('m', AF_CLASSIC)) + with pytest.raises(CCProtoError) as ee: + dev.send_recv(CCProtocolPacker.miniscript_address(name, False, 0)) + assert "Not allowed in HSM mode" in ee.value.args[0] - with pytest.raises(CCProtoError) as ee: - got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address( - M, xfp_paths, scr, addr_fmt=AF_P2WSH)) - assert 'Not allowed in HSM mode' in str(ee) + # change policy to allow miniscript address show + policy = DICT(share_addrs=["any", "msas"], rules=[dict(wallet=name)]) + change_hsm(policy) + addr = dev.send_recv(CCProtocolPacker.miniscript_address(name, False, 0)) + assert addr[2:4] == "1q" def test_xpub_sharing(dev, start_hsm, change_hsm, addr_fmt=AF_CLASSIC): # xpub sharing, but only at certain derivations @@ -1219,7 +1326,7 @@ def test_velocity(dev, start_hsm, fake_txn, attempt_psbt, fast_forward, hsm_stat psbt = fake_txn(2, 10, dev.master_xpub) attempt_psbt(psbt, 'would exceed period spending') - psbt = fake_txn(2, 2, dev.master_xpub, outvals=[level, 2E8-level], change_outputs=[1]) + psbt = fake_txn(2, [["p2wpkh", level], ["p2tr", int(2E8-level), True]], dev.master_xpub) attempt_psbt(psbt) # exactly the limit s = hsm_status() @@ -1238,7 +1345,7 @@ def test_velocity(dev, start_hsm, fake_txn, attempt_psbt, fast_forward, hsm_stat assert 'has_spend' not in s amt = 0.30E8 - psbt = fake_txn(1, 2, dev.master_xpub, outvals=[amt, 1E8-amt], change_outputs=[1]) + psbt = fake_txn(1, [["p2tr", int(amt)], ["p2wpkh", int(1E8-amt), True]], dev.master_xpub) attempt_psbt(psbt) # 1/3rd of limit attempt_psbt(psbt) # 1/3rd of limit attempt_psbt(psbt) # 1/3rd of limit @@ -1253,16 +1360,16 @@ def test_min_pct_self_transfer(dev, start_hsm, fake_txn, attempt_psbt): start_hsm(policy) - psbt = fake_txn(1, 2, invals = [1000], outvals = [500, 500], change_outputs = [], fee = 0) + psbt = fake_txn([["p2pkh", None, 1000]], [["p2tr", 500], ["p2pkh", 500]], fee = 0) attempt_psbt(psbt, 'does not meet self transfer threshold, expected: %.2f, actual: %.2f' % (75, 0)) - psbt = fake_txn(1, 2, invals = [1000], outvals = [750, 250], change_outputs = [1], fee = 0) + psbt = fake_txn([["p2tr", None, 1000]], [["p2pkh", 750], ["p2tr", 250, True]], fee = 0) attempt_psbt(psbt, 'does not meet self transfer threshold, expected: %.2f, actual: %.2f' % (75, 25)) - psbt = fake_txn(1, 2, invals = [1000], outvals = [250, 750], change_outputs = [1], fee = 0) + psbt = fake_txn([["p2wpkh", None, 1000]], [["p2tr", 250], ["p2wpkh", 750, True]], fee = 0) attempt_psbt(psbt) # exact threshold - psbt = fake_txn(1, 2, invals = [1000], outvals = [1, 999], change_outputs = [1], fee = 0) + psbt = fake_txn([["p2sh-p2wpkh", None, 1000]], [["p2tr", 1], ["p2sh-p2wpkh", 999, True]], fee = 0) attempt_psbt(psbt) # exceeding the threshold @pytest.mark.parametrize('pattern', ['EQ_NUM_INS_OUTS', 'EQ_NUM_OWN_INS_OUTS', 'EQ_OUT_AMOUNTS'] ) @@ -1282,17 +1389,17 @@ def test_patterns(pattern, dev, start_hsm, fake_txn, attempt_psbt): psbt = fake_txn(2, 2) attempt_psbt(psbt, 'unequal number of own inputs and outputs') - psbt = fake_txn(2, 2, change_outputs = [0]) + psbt = fake_txn(2, [["p2pkh", None, True], ["p2tr"]]) attempt_psbt(psbt, 'unequal number of own inputs and outputs') - psbt = fake_txn(2, 2, change_outputs = [0, 1]) + psbt = fake_txn(2, [["p2pkh", None, True], ["p2tr", None, True]]) attempt_psbt(psbt) # equal number of own ins and outs if pattern == 'EQ_OUT_AMOUNTS': - psbt = fake_txn(1, 2, invals = [1500], outvals = [1000, 500], fee = 0) + psbt = fake_txn([["p2wpkh", None, 1500]], [["p2tr", 1000], ["p2pkh", 500]], fee=0) attempt_psbt(psbt, 'not all output amounts are equal') - psbt = fake_txn(1, 2, invals = [2000], outvals = [1000, 1000], fee = 0) + psbt = fake_txn([["p2tr", None, 2000]], [["p2wpkh", 1000], ["p2tr", 1000]], fee=0) attempt_psbt(psbt) # all output amounts are equal def test_user_subset(dev, start_hsm, tweak_rule, load_hsm_users, fake_txn, attempt_psbt, auth_user): @@ -1436,7 +1543,7 @@ def worst_case_policy(): addrs = [render_address(b'\x00\x14' + prandom(20)) for i in range(5)] - p = DICT(period=30, share_xpubs=paths, share_addrs=paths+['p2sh'], msg_paths=paths, + p = DICT(period=30, share_xpubs=paths, share_addrs=paths+['msas'], msg_paths=paths, warnings_ok=False, must_log=True) p.rules = [dict( local_conf=True, @@ -1518,7 +1625,9 @@ def test_priv_over_ux(quick_start_hsm, hsm_status, load_hsm_users): @pytest.mark.parametrize("allow_op_return", [False, True]) def test_op_return_output_local(op_return_data, start_hsm, attempt_psbt, fake_txn, allow_op_return): dests = [] - psbt = fake_txn(2, 2, op_return=[(0, op_return_data)], capture_scripts=dests) + psbt = fake_txn(2, [["p2tr", 10000], ["p2tr", 10000], ["op_return", 0, None, op_return_data]], + input_amount=10000, capture_scripts=dests) + if allow_op_return: policy = DICT(rules=[dict(whitelist=[render_address(d) for d in dests[0:2]], whitelist_opts=dict(allow_zeroval_outs=True))]) @@ -1537,7 +1646,8 @@ def test_op_return_output_local(op_return_data, start_hsm, attempt_psbt, fake_tx def test_op_return_output_bitcoind(op_return_data, start_hsm, attempt_psbt, bitcoind_d_sim_watch, bitcoind, hsm_reset): cc = bitcoind_d_sim_watch dest_address = cc.getnewaddress() - bitcoind.supply_wallet.generatetoaddress(101, dest_address) + bitcoind.supply_wallet.sendtoaddress(dest_address, 49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) psbt = cc.walletcreatefundedpsbt([], [{dest_address: 1.0}, {"data": op_return_data.hex()}], 0, {"fee_rate": 20})["psbt"] policy = DICT(rules=[dict(max_amount=10)]) start_hsm(policy) @@ -1557,7 +1667,8 @@ def test_hsm_commands_disabled(dev, goto_home, pick_menu_item, hsm_reset, start_ # disable HSM related commands (now enabled because module scope fixture 'enable_hsm_commands') goto_home() pick_menu_item("Advanced/Tools") - pick_menu_item("Enable HSM") + pick_menu_item("Spending Policy") + pick_menu_item("HSM Mode") pick_menu_item("Default Off") goto_home() try: diff --git a/testing/test_miniscript.py b/testing/test_miniscript.py new file mode 100644 index 000000000..ebca6935e --- /dev/null +++ b/testing/test_miniscript.py @@ -0,0 +1,3488 @@ +# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Miniscript-related tests. +# +import pytest, json, time, itertools, struct, random, os, base64 +from ckcc.protocol import CCProtocolPacker +from constants import AF_P2TR +from psbt import BasicPSBT +from charcodes import KEY_QR, KEY_RIGHT, KEY_CANCEL, KEY_DELETE +from bbqr import split_qrs +from bip32 import BIP32Node + + +H = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" # BIP-0341 +TREE = { + 1: '%s', + 2: '{%s,%s}', + 3: random.choice(['{{%s,%s},%s}','{%s,{%s,%s}}']), + 4: '{{%s,%s},{%s,%s}}', + 5: random.choice(['{{%s,%s},{%s,{%s,%s}}}', '{{{%s,%s},%s},{%s,%s}}']), + 6: '{{%s,{%s,%s}},{{%s,%s},%s}}', + 7: '{{%s,{%s,%s}},{%s,{%s,{%s,%s}}}}', + 8: '{{{%s,%s},{%s,%s}},{{%s,%s},{%s,%s}}}', + # more than MAX (4) for test purposes + 9: '{{{%s,{%s,%s}},{%s,%s}},{{%s,%s},{%s,%s}}}', + 10: '{{{{%s,%s},{%s,%s}},{%s,%s}},{{%s,%s},{%s,%s}}}', + 11: '{{{{%s,%s},{%s,%s}},{%s,%s}},{{%s,%s},{%s,{%s,%s}}}}', + 12: '{{{{%s,%s},{%s,%s}},{%s,%s}},{{%s,%s},{{%s,%s},{%s,%s}}}}', +} + + +def ranged_unspendable_internal_key(chain_code=32 * b"\x01", subderiv="/<0;1>/*"): + # provide ranged provably unspendable key in serialized extended key format for core to understand it + # core does NOT understand 'unspend(' + pk = b"\x02" + bytes.fromhex(H) + node = BIP32Node.from_chaincode_pubkey(chain_code, pk) + return node.hwif() + subderiv + + +@pytest.fixture +def offer_minsc_import(cap_story, dev, sim_root_dir): + def doit(config, allow_non_ascii=False): + # upload the file, trigger import + file_len, sha = dev.upload_file(config.encode('utf-8' if allow_non_ascii else 'ascii')) + + with open(f'{sim_root_dir}/debug/last-config-msc.txt', 'wt') as f: + f.write(config) + dev.send_recv(CCProtocolPacker.miniscript_enroll(file_len, sha)) + + time.sleep(.2) + title, story = cap_story() + return title, story + + return doit + + +@pytest.fixture +def import_miniscript(request, is_q1, need_keypress, offer_minsc_import, press_cancel): + def doit(fname=None, way="sd", data=None, name=None): + assert fname or data + + if fname: + if way == "sd": + microsd_path = request.getfixturevalue("microsd_path") + fpath = microsd_path(fname) + else: + virtdisk_path = request.getfixturevalue("virtdisk_path") + fpath = virtdisk_path(fname) + with open(fpath, 'r') as f: + config = f.read() + else: + config = data + + if way in ("usb", None): + return offer_minsc_import(config) + else: + # only get those simulator related fixtures here, to be able to + # use this with real HW + cap_menu = request.getfixturevalue('cap_menu') + cap_story = request.getfixturevalue('cap_story') + goto_home = request.getfixturevalue('goto_home') + press_nfc = request.getfixturevalue('press_nfc') + pick_menu_item = request.getfixturevalue('pick_menu_item') + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(.1) + + pick_menu_item('Import') + time.sleep(.2) + _, story = cap_story() + if way == "nfc": + if "via NFC" not in story: + press_cancel() + pytest.skip("nfc disabled") + + press_nfc() + time.sleep(.1) + if isinstance(config, dict): + config = json.dumps(config) + + nfc_write_text = request.getfixturevalue('nfc_write_text') + nfc_write_text(config) + time.sleep(1) + return cap_story() + elif way == "qr": + scan_a_qr = request.getfixturevalue('scan_a_qr') + if isinstance(data, dict): + data = json.dumps(data) + + need_keypress(KEY_QR) + try: + scan_a_qr(data) + except: + # always as text - even if it is json + actual_vers, parts = split_qrs(data, 'U', max_version=20) + random.shuffle(parts) + + for p in parts: + scan_a_qr(p) + time.sleep(1) # just so we can watch + + time.sleep(1) + return cap_story() + + if not fname: + microsd_path = request.getfixturevalue("microsd_path") + virtdisk_path = request.getfixturevalue("virtdisk_path") + path_f = microsd_path if way == "sd" else virtdisk_path + fname = (name or "ms_wal") + ".txt" + with open(path_f(fname), "w") as f: + f.write(config) + + if "Press (1) to import miniscript wallet file from SD Card" in story: + # in case Vdisk or NFC is enabled + if way == "sd": + need_keypress("1") + + elif way == "vdisk": + if "ress (2)" not in story: + press_cancel() + pytest.xfail(way) + + need_keypress("2") + else: + if way != "sd": + pytest.xfail(way) + + time.sleep(.3) + pick_menu_item(fname) + time.sleep(.1) + return cap_story() + + return doit + +@pytest.fixture +def import_duplicate(import_miniscript, press_cancel, virtdisk_path, microsd_path): + def doit(fname, way="sd", data=None): + new_fpath = None + new_fname = None + path_f = microsd_path + if way == "vdisk": + path_f = virtdisk_path + + time.sleep(.2) + title, story = import_miniscript(fname, way, data=data) + if "unique names" in story: + # trying to import duplicate with same name + # cannot get over name uniqueness requirement + # need to duplicate + if way in ["qr", "nfc"]: + data["name"] = data["name"] + "-new" + else: + with open(path_f(fname), "r") as f: + res = f.read() + + basename, ext = fname.split(".", 1) + new_fname = basename + "-new" + "." + ext + new_fpath = path_f(basename+"-new"+"."+ext) + with open(new_fpath, "w") as f: + f.write(res) + + press_cancel() + title, story = import_miniscript(new_fname, way, data=data) + time.sleep(.2) + + assert "Duplicate wallet" in story + assert "OK to approve" not in story + press_cancel() + + if new_fpath: + os.remove(new_fpath) + + return doit + +@pytest.fixture +def miniscript_descriptors(goto_home, pick_menu_item, need_keypress, cap_story, + microsd_path, is_q1, readback_bbqr, cap_screen_qr, + garbage_collector): + + def doit(minsc_name): + qr_data = None + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(minsc_name) + pick_menu_item("Descriptors") + pick_menu_item("Export") + time.sleep(.1) + if is_q1: + # check QR + need_keypress(KEY_QR) + try: + file_type, data = readback_bbqr() + assert file_type == "U" + qr_data = data.decode().strip() + except: + qr_data = cap_screen_qr().decode('ascii').strip() + + need_keypress(KEY_CANCEL) + + pick_menu_item("Export") + time.sleep(.2) + + title, story = cap_story() + if "Press (1)" in story: + need_keypress("1") + time.sleep(.2) + title, story = cap_story() + + assert "Miniscript file written" in story + assert "signature file written" in story + fname = story.split("\n\n")[1] + fpath = microsd_path(fname) + garbage_collector.append(fpath) + with open(fpath, "r") as f: + cont = f.read().strip() + + if qr_data: + assert qr_data == cont + return cont + + return doit + + +@pytest.fixture +def usb_miniscript_get(dev): + def doit(name): + dev.check_mitm() + resp = dev.send_recv(CCProtocolPacker.miniscript_get(name)) + return json.loads(resp) + + return doit + + +@pytest.fixture +def usb_miniscript_policy(dev): + def doit(name): + dev.check_mitm() + resp = dev.send_recv(CCProtocolPacker.miniscript_policy(name)) + return json.loads(resp) + + return doit + +@pytest.fixture +def usb_miniscript_delete(dev): + def doit(name): + dev.check_mitm() + dev.send_recv(CCProtocolPacker.miniscript_delete(name)) + + return doit + + +@pytest.fixture +def usb_miniscript_ls(dev): + def doit(): + dev.check_mitm() + resp = dev.send_recv(CCProtocolPacker.miniscript_ls()) + return json.loads(resp) + + return doit + + +@pytest.fixture +def usb_miniscript_addr(dev): + def doit(name, index, change=False): + dev.check_mitm() + resp = dev.send_recv(CCProtocolPacker.miniscript_address(name, change, index)) + return resp + + return doit + + +@pytest.fixture +def get_cc_key(dev): + def doit(path, subderiv=None): + # cc device key + master_xfp_str = struct.pack('/*'}" + return doit + + +@pytest.fixture +def bitcoin_core_signer(bitcoind): + def doit(name="core_signer"): + # core signer + signer = bitcoind.create_wallet(wallet_name=name, disable_private_keys=False, + blank=False, passphrase=None, avoid_reuse=False, + descriptors=True) + target_desc = "" + bitcoind_descriptors = signer.listdescriptors()["descriptors"] + for d in bitcoind_descriptors: + if d["desc"].startswith("pkh(") and d["internal"] is False: + target_desc = d["desc"] + break + core_desc, checksum = target_desc.split("#") + core_key = core_desc[4:-1] + return signer, core_key + return doit + + +@pytest.fixture +def address_explorer_check(goto_home, pick_menu_item, need_keypress, cap_menu, + cap_story, miniscript_descriptors, load_export, + usb_miniscript_addr, cap_screen_qr, press_select): + def doit(way, addr_fmt, wallet, cc_minsc_name, export_check=True): + goto_home() + pick_menu_item("Address Explorer") + need_keypress('4') # warning + m = cap_menu() + wal_name = m[-1] + pick_menu_item(wal_name) + + time.sleep(1) + if way == "qr": + need_keypress(KEY_QR) + cc_addrs = [] + for i in range(10): + cc_addrs.append(cap_screen_qr().decode()) + need_keypress(KEY_RIGHT) + time.sleep(.2) + need_keypress(KEY_CANCEL) + else: + contents = load_export(way, label="Address summary", is_json=False) + addr_cont = contents.strip() + press_select() + + + time.sleep(1) + title, story = cap_story() + + assert "change addresses." in story and "(0)" in story + need_keypress("0") + + time.sleep(1) + title, story = cap_story() + + assert "(0)" not in story + assert "change addresses." not in story + + if way == "qr": + need_keypress(KEY_QR) + cc_addrs_change = [] + for i in range(10): + cc_addrs_change.append(cap_screen_qr().decode()) + need_keypress(KEY_RIGHT) + time.sleep(.2) + need_keypress(KEY_CANCEL) + else: + contents_change = load_export(way, label="Address summary", is_json=False) + addr_cont_change = contents_change.strip() + + if way == "nfc": + addr_range = [0, 9] + cc_addrs = addr_cont.split("\n") + cc_addrs_change = addr_cont_change.split("\n") + part_addr_index = 0 + elif way == 'qr': + addr_range = [0, 9] + part_addr_index = 0 + else: + addr_range = [0, 249] + cc_addrs_split = addr_cont.split("\n") + cc_addrs_split_change = addr_cont_change.split("\n") + + cc_addrs = cc_addrs_split[1:] + cc_addrs_change = cc_addrs_split_change[1:] + part_addr_index = 1 + + internal_desc = None + external_desc = None + descriptors = wallet.listdescriptors()["descriptors"] + for desc in descriptors: + if desc["internal"]: + internal_desc = desc["desc"] + else: + external_desc = desc["desc"] + + time.sleep(1) + + if export_check: + desc_export = miniscript_descriptors(cc_minsc_name) + + def remove_minisc_syntactic_sugar(descriptor, a, b): + # syntactic sugar https://bitcoin.sipa.be/miniscript/ + target_len = len(a) + idx = 0 + while idx != -1: + idx = descriptor.find(a, idx) + if idx == -1: break + # needs colon more identities than just 'c' + rep = f":{b}" if descriptor[idx-1] in "asctdvjnlu" else f"{b}" + descriptor = descriptor[:idx] + rep + descriptor[idx+target_len:] + + return descriptor + + desc_export = remove_minisc_syntactic_sugar(desc_export, "c:pk_k(", "pk(") + desc_export = remove_minisc_syntactic_sugar(desc_export, "c:pk_h(", "pkh(") + # TODO format with and without multipath expression + # assert desc_export.split("#")[0] == external_desc.split("#")[0].replace("'", "h") + + bitcoind_addrs = wallet.deriveaddresses(external_desc, addr_range) + bitcoind_addrs_change = wallet.deriveaddresses(internal_desc, addr_range) + + for cc, core in [(cc_addrs, bitcoind_addrs), (cc_addrs_change, bitcoind_addrs_change)]: + for idx, cc_item in enumerate(cc): + if way == "nfc": + address = cc_item + elif way == "qr": + if cc_item.startswith("BC"): + cc_item = cc_item.lower() + address = cc_item + else: + cc_item = cc_item.split(",") + address = cc_item[part_addr_index] + address = address[1:-1] + assert core[idx] == address + + # check few USB addresses + for i in range(5): + addr = usb_miniscript_addr(cc_minsc_name, i, change=False) + time.sleep(.1) + title, story = cap_story() + assert addr in story + assert addr == bitcoind_addrs[i] + + for i in range(5): + addr = usb_miniscript_addr(cc_minsc_name, i, change=True) + time.sleep(.1) + title, story = cap_story() + assert addr in story + assert addr == bitcoind_addrs_change[i] + + return doit + + +@pytest.fixture +def create_core_wallet(goto_home, pick_menu_item, load_export, bitcoind): + def doit(name, addr_type, way="sd", funded=True): + try: + pick_menu_item(name) # pick imported descriptor multisig wallet + except: + # probably not in Miniscript + goto_home() + pick_menu_item('Settings') + pick_menu_item("Multisig/Miniscript") + pick_menu_item(name) + + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export(way, label="Bitcoin Core Miniscript", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + + # watch only wallet where miniscript descriptor will be imported + ms = bitcoind.create_wallet( + wallet_name=name, disable_private_keys=True, + blank=True, passphrase=None, avoid_reuse=False, descriptors=True + ) + + # import descriptors to watch only wallet + res = ms.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"] + + if funded: + addr = ms.getnewaddress("", addr_type) + if addr_type == "bech32": + sw = "bcrt1q" + elif addr_type == "bech32m": + sw = "bcrt1p" + else: + sw = "2" + assert addr.startswith(sw) + # get some coins and fund above multisig address + bitcoind.supply_wallet.sendtoaddress(addr, 49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + return ms + return doit + + +@pytest.fixture +def bitcoind_miniscript(bitcoind, need_keypress, cap_story, load_export, + pick_menu_item, goto_home, cap_menu, microsd_path, + use_regtest, get_cc_key, import_miniscript, + bitcoin_core_signer, import_duplicate, press_select, + virtdisk_path, garbage_collector, create_core_wallet): + def doit(M, N, script_type, internal_key=None, cc_account=0, funded=True, + tapscript_threshold=False, add_own_pk=False, same_account=False, way="sd"): + + use_regtest() + bitcoind_signers = [] + bitcoind_signers_xpubs = [] + for i in range(N - 1): + s, core_key = bitcoin_core_signer(f"bitcoind--signer{i}") + s.keypoolrefill(10) + bitcoind_signers.append(s) + bitcoind_signers_xpubs.append(core_key) + + me_pth = f"m/48h/1h/{cc_account}h/3h" + me = get_cc_key(me_pth) + ik = internal_key or ranged_unspendable_internal_key() + + if tapscript_threshold: + signers_xp = [me] + bitcoind_signers_xpubs + assert len(signers_xp) == N + desc = f"tr({ik},%s)" + + scripts = [] + for c in itertools.combinations(signers_xp, M): + tmplt = f"sortedmulti_a({M},{','.join(c)})" + scripts.append(tmplt) + + if len(scripts) > 8: + while True: + # just some of them but at least one has to have my key + x = random.sample(scripts, 8) + if any(me in s for s in x): + scripts = x + break + + if add_own_pk: + if len(scripts) < 8: + if same_account: + cc_key = get_cc_key(me_pth, subderiv="/<2;3>/*") + else: + cc_key = get_cc_key("m/86h/1h/1000h") + cc_pk_leaf = f"pk({cc_key})" + scripts.append(cc_pk_leaf) + else: + pytest.skip("Scripts full") + + temp = TREE[len(scripts)] + temp = temp % tuple(scripts) + + desc = desc % temp + + else: + if add_own_pk: + if same_account: + ss = [get_cc_key(me_pth, subderiv="/<4;5>/*")] + bitcoind_signers_xpubs + cc_key = get_cc_key(me_pth, subderiv="/<6;7>/*") + else: + ss = [get_cc_key("m/86h/1h/0h")] + bitcoind_signers_xpubs + cc_key = get_cc_key("m/86h/1h/1000h") + + tmplt = f"sortedmulti_a({M},{','.join(ss)})" + cc_pk_leaf = f"pk({cc_key})" + desc = f"tr({ik},{{{tmplt},{cc_pk_leaf}}})" + else: + desc = f"tr({ik},sortedmulti_a({M},{me},{','.join(bitcoind_signers_xpubs)}))" + + name = "minisc" + fname = None + if way in ["sd", "vdisk"]: + data = None + fname = f"{name}.txt" + path_f = microsd_path if way == 'sd' else virtdisk_path + fpath = path_f(fname) + with open(fpath, "w") as f: + f.write(desc + "\n") + garbage_collector.append(fpath) + else: + data = dict(name=name, desc=desc) + + _, story = import_miniscript(fname, way=way, data=data) + assert "Create new miniscript wallet?" in story + assert name in story + assert "Press (1) to see extended public keys" in story + if script_type == "p2wsh": + af = "bech32" + assert "P2WSH" in story + elif script_type == "p2sh": + af = "legacy" + assert "P2SH" in story + elif script_type == "p2tr": + af = "bech32m" + assert "P2TR" in story + else: + af = "p2sh-segwit" + assert "P2SH-P2WSH" in story + # assert "Derivation:\n Varies (2)" in story + press_select() # approve multisig import + import_duplicate(fname, way=way, data=data) + ms = create_core_wallet(name, af, way, funded) + + return ms, bitcoind_signers + + return doit + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("addr_fmt", ["bech32", "p2sh-segwit"]) +@pytest.mark.parametrize("lt_type", ["older", "after"]) # this is actually not generated by liana (liana is relative only) +@pytest.mark.parametrize("recovery", [True, False]) +@pytest.mark.parametrize("way", ["qr", "nfc", "sd", "vdisk"]) +@pytest.mark.parametrize("minisc", [ + "or_d(pk(@A),and_v(v:pkh(@B),locktime(N)))", + + "or_d(pk(@A),and_v(v:pk(@B),locktime(N)))", # this is actually not generated by liana + + "or_d(multi(2,@A,@C),and_v(v:pkh(@B),locktime(N)))", + + "or_d(pk(@A),and_v(v:multi(2,@B,@C),locktime(N)))", +]) +def test_liana_miniscripts_simple(addr_fmt, recovery, lt_type, minisc, clear_miniscript, + pick_menu_item, cap_story, microsd_path, way, dev, + use_regtest, bitcoind, microsd_wipe, load_export, + address_explorer_check, get_cc_key, import_miniscript, + bitcoin_core_signer, import_duplicate, press_select, + virtdisk_path, skip_if_useless_way, garbage_collector, + create_core_wallet, goto_home): + skip_if_useless_way(way) + normal_cosign_core = False + recovery_cosign_core = False + if "multi(" in minisc.split("),", 1)[0]: + normal_cosign_core = True + if "multi(" in minisc.split("),", 1)[-1]: + recovery_cosign_core = True + + if lt_type == "older": + sequence = 5 + locktime = 0 + # 101 blocks are mined by default + to_replace = "older(5)" + else: + sequence = None + locktime = 105 + to_replace = "after(105)" + + minisc = minisc.replace("locktime(N)", to_replace) + + if addr_fmt == "bech32": + desc = f"wsh({minisc})" + else: + desc = f"sh(wsh({minisc}))" + + # core signer + signer0, core_key0 = bitcoin_core_signer("s0") + + # cc device key + cc_key = get_cc_key("84h/0h/0h") + + if recovery: + # recevoery path is always B + desc = desc.replace("@B", cc_key) + desc = desc.replace("@A", core_key0) + else: + desc = desc.replace("@A", cc_key) + desc = desc.replace("@B", core_key0) + + if "@C" in desc: + signer1, core_key1 = bitcoin_core_signer("s1") + desc = desc.replace("@C", core_key1) + + use_regtest() + clear_miniscript() + goto_home() + name = "core-miniscript" + fname = f"{name}.txt" + if way in ["qr", "nfc"]: + data = dict(name=name, desc=desc) + fname = None + else: + path_f = microsd_path if way == "sd" else virtdisk_path + data = None + fpath = path_f(fname) + garbage_collector.append(fpath) + with open(fpath, "w") as f: + f.write(desc) + + _, story = import_miniscript(fname, way=way, data=data) + time.sleep(.2) + assert "Create new miniscript wallet?" in story + press_select() + # import_duplicate(fname, way=way, data=data) + + wo = create_core_wallet(name, addr_fmt, way, True) + + all_of_it = wo.getbalance() + unspent = wo.listunspent() + assert len(unspent) == 1 + addr_dest = wo.getnewaddress("", addr_fmt) # self-spend + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + if recovery and sequence: + inp["sequence"] = sequence + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{addr_dest: all_of_it - 1}], + locktime if recovery else 0, + {"fee_rate": 20, "change_type": addr_fmt, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + if normal_cosign_core or recovery_cosign_core: + psbt = signer1.walletprocesspsbt(psbt, True, "ALL")["psbt"] + + name = f"{name}.psbt" + fpath = microsd_path(name) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(name) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = wo.testmempoolaccept([tx_hex]) + if recovery: + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' if sequence else "non-final" + bitcoind.supply_wallet.generatetoaddress(6, bitcoind.supply_wallet.getnewaddress()) + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + else: + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + # check addresses + address_explorer_check(way, addr_fmt, wo, "core-miniscript") + + +@pytest.mark.parametrize("addr_fmt", ["bech32", "p2sh-segwit"]) +@pytest.mark.parametrize("way", ["qr", "sd"]) +@pytest.mark.parametrize("minsc", [ + ("or_i(and_v(v:pkh($0),older(10)),or_d(multi(3,@A,@B,@C),and_v(v:thresh(2,pkh($1),a:pkh($2),a:pkh($3)),older(5))))", 0), + ("or_i(and_v(v:pkh(@A),older(10)),or_d(multi(3,$0,$1,$2),and_v(v:thresh(2,pkh($3),a:pkh($4),a:pkh($5)),older(5))))", 10), + ("or_i(and_v(v:pkh($0),older(10)),or_d(multi(3,$1,$2,$3),and_v(v:thresh(2,pkh(@A),a:pkh(@B),a:pkh($4)),older(5))))", 5), +]) +def test_liana_miniscripts_complex(addr_fmt, minsc, bitcoind, use_regtest, clear_miniscript, + microsd_path, pick_menu_item, cap_story, + load_export, goto_home, address_explorer_check, cap_menu, + get_cc_key, import_miniscript, bitcoin_core_signer, + import_duplicate, press_select, way, skip_if_useless_way, + garbage_collector, create_core_wallet): + skip_if_useless_way(way) + use_regtest() + clear_miniscript() + goto_home() + + minsc, to_gen = minsc + signer_keys = minsc.count("@") + bsigners = signer_keys - 1 + random_keys = minsc.count("$") + bitcoind_signers = [] + for i in range(random_keys + bsigners): + s, core_key = bitcoin_core_signer(f"co-signer-{i}") + bitcoind_signers.append((s, core_key)) + + cc_key = get_cc_key("m/84h/1h/0h") + minsc = minsc.replace("@A", cc_key) + + use_signers = [] + if bsigners == 2: + for ph, (s, key) in zip(["@B", "@C"], bitcoind_signers[:2]): + use_signers.append(s) + minsc = minsc.replace(ph, key) + for i, (s, key) in enumerate(bitcoind_signers[2:]): + ph = f"${i}" + minsc = minsc.replace(ph, key) + elif bsigners == 1: + use_signers.append(bitcoind_signers[0][0]) + minsc = minsc.replace("@B", bitcoind_signers[0][1]) + for i, (s, key) in enumerate(bitcoind_signers[1:]): + ph = f"${i}" + minsc = minsc.replace(ph, key) + elif bsigners == 0: + for i, (s, key) in enumerate(bitcoind_signers): + ph = f"${i}" + minsc = minsc.replace(ph, key) + else: + assert False + + if addr_fmt == "bech32": + desc = f"wsh({minsc})" + else: + desc = f"sh(wsh({minsc}))" + + name = "cmplx-miniscript" + + if way in ["qr", "nfc"]: + fname = None + data = dict(name=name, desc=desc) + else: + fname = f"{name}.txt" + data = None + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + + garbage_collector.append(fpath) + + _, story = import_miniscript(fname, way=way, data=data) + time.sleep(.2) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + import_duplicate(fname, way=way, data=data) + + wo = create_core_wallet(name, addr_fmt, way, True) + + addr_dest = wo.getnewaddress("", addr_fmt) # self-spend + unspent = wo.listunspent() + assert len(unspent) == 1 + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + if to_gen: + inp["sequence"] = to_gen + + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{addr_dest: 1}], + 0, + {"fee_rate": 20, "change_type": addr_fmt, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + # cosingers signing first + for s in use_signers: + psbt = s.walletprocesspsbt(psbt, True, "ALL")["psbt"] + + pname = f"{name}.psbt" + ppath = microsd_path(pname) + with open(ppath, "w") as f: + f.write(psbt) + garbage_collector.append(ppath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(pname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = wo.testmempoolaccept([tx_hex]) + if to_gen: + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + bitcoind.supply_wallet.generatetoaddress(to_gen, bitcoind.supply_wallet.getnewaddress()) + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + else: + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + # check addresses + address_explorer_check(way, addr_fmt, wo, name) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("cc_first", [True, False]) +@pytest.mark.parametrize("add_pk", [True, False]) +@pytest.mark.parametrize("same_acct", [None, True, False]) +@pytest.mark.parametrize("way", ["qr", "sd"]) +@pytest.mark.parametrize("M_N", [(3,4),(5,6)]) +def test_tapscript(M_N, cc_first, clear_miniscript, goto_home, pick_menu_item, + cap_menu, cap_story, microsd_path, use_regtest, bitcoind, microsd_wipe, + load_export, bitcoind_miniscript, add_pk, same_acct, get_cc_key, + press_select, way, skip_if_useless_way, garbage_collector): + skip_if_useless_way(way) + M, N = M_N + clear_miniscript() + microsd_wipe() + internal_key = None + if same_acct is None: + internal_key = ranged_unspendable_internal_key() + elif same_acct: + # provide internal key with same account derivation (change based derivation) + internal_key = get_cc_key("m/86h/1h/0h", subderiv='/<10;11>/*') + + wo, signers = bitcoind_miniscript(M, N, "p2tr", tapscript_threshold=True, + add_own_pk=add_pk, internal_key=internal_key, + same_account=same_acct, way=way) + addr = wo.getnewaddress("", "bech32m") + bitcoind.supply_wallet.sendtoaddress(addr, 49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + conso_addr = wo.getnewaddress("conso", "bech32m") + psbt = wo.walletcreatefundedpsbt([], [{conso_addr:25}], 0, {"fee_rate": 2})["psbt"] + if not cc_first: + for s in signers[0:M-1]: + psbt = s.walletprocesspsbt(psbt, True, "DEFAULT")["psbt"] + + psbt_fpath = microsd_path("ts_tree.psbt") + with open(psbt_fpath, "w") as f: + f.write(psbt) + + garbage_collector.append(psbt_fpath) + time.sleep(2) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item("ts_tree.psbt") + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + press_select() + time.sleep(0.1) + title, story = cap_story() + assert title == "PSBT Signed" + fname = [i for i in story.split("\n\n") if ".psbt" in i][0] + fpath = microsd_path(fname) + with open(fpath, "r") as f: + psbt = f.read().strip() + garbage_collector.append(fpath) + if cc_first: + # we MUST be able to finalize this without anyone else if add pk + if not add_pk: + for s in signers[0:M-1]: + psbt = s.walletprocesspsbt(psbt, True, "DEFAULT")["psbt"] + res = wo.finalizepsbt(psbt) + assert res["complete"] is True + accept_res = wo.testmempoolaccept([res["hex"]])[0] + assert accept_res["allowed"] is True + txid = wo.sendrawtransaction(res["hex"]) + assert len(txid) == 64 + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("csa", [True, False]) +@pytest.mark.parametrize("add_pk", [True, False]) +@pytest.mark.parametrize('M_N', [(3, 15), (2, 2), (3, 5)]) +@pytest.mark.parametrize('way', ["qr", "sd", "vdisk", "nfc"]) +def test_bitcoind_tapscript_address(M_N, clear_miniscript, bitcoind_miniscript, + use_regtest, way, csa, address_explorer_check, + add_pk, skip_if_useless_way): + skip_if_useless_way(way) + use_regtest() + clear_miniscript() + M, N = M_N + + ik = ranged_unspendable_internal_key(os.urandom(32), subderiv=f"/<22;23>/*") + + ms_wo, _ = bitcoind_miniscript(M, N, "p2tr", funded=False, tapscript_threshold=csa, + add_own_pk=add_pk, way=way, internal_key=ik) + address_explorer_check(way, "bech32m", ms_wo, "minisc") + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("cc_first", [True, False]) +@pytest.mark.parametrize("m_n", [(2,3), (32, 32)]) +@pytest.mark.parametrize("way", ["qr", "sd"]) +@pytest.mark.parametrize("internal_key_spendable", [ + True, + False, + "tpubD6NzVbkrYhZ4WhUnV3cPSoRWGf9AUdG2dvNpsXPiYzuTnxzAxemnbajrATDBWhaAVreZSzoGSe3YbbkY2K267tK3TrRmNiLH2pRBpo8yaWm/<2;3>/*", +]) +def test_tapscript_multisig(cc_first, m_n, internal_key_spendable, use_regtest, bitcoind, goto_home, cap_menu, + pick_menu_item, cap_story, microsd_path, load_export, microsd_wipe, dev, way, + bitcoind_miniscript, clear_miniscript, get_cc_key, press_cancel, press_select, + skip_if_useless_way, garbage_collector, file_tx_signing_done): + skip_if_useless_way(way) + M, N = m_n + clear_miniscript() + microsd_wipe() + internal_key = None + if internal_key_spendable is True: + internal_key = get_cc_key("86h/0h/3h") + + elif isinstance(internal_key_spendable, str): + internal_key = internal_key_spendable + + tapscript_wo, bitcoind_signers = bitcoind_miniscript( + M, N, "p2tr", internal_key=internal_key, + way=way + ) + + dest_addr = tapscript_wo.getnewaddress("", "bech32m") + psbt = tapscript_wo.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 20})["psbt"] + fname = "tapscript.psbt" + if not cc_first: + # bitcoind cosigner sigs first + for i in range(M - 1): + signer = bitcoind_signers[i] + psbt = signer.walletprocesspsbt(psbt, True, "DEFAULT", True)["psbt"] + + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + + garbage_collector.append(fpath) + goto_home() + # bug in goto_home ? + press_cancel() + time.sleep(0.1) + # CC signing + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + press_select() + time.sleep(0.1) + title, story = cap_story() + + signed_psbt, signed_txn, cc_tx_id = file_tx_signing_done(story) + + garbage_collector.append(fpath) + if cc_first: + for signer in bitcoind_signers: + signed_psbt = signer.walletprocesspsbt(signed_psbt, True, "DEFAULT", True)["psbt"] + res = tapscript_wo.finalizepsbt(signed_psbt, True) + assert res['complete'] + tx_hex = res["hex"] + res = bitcoind.supply_wallet.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + if cc_tx_id: + assert tx_hex == signed_txn + assert txn_id == cc_tx_id + assert len(txn_id) == 64 + + +@pytest.mark.parametrize("num_leafs", [1, 2, 5, 8]) +@pytest.mark.parametrize("internal_key_spendable", [True, False]) +def test_tapscript_pk(num_leafs, use_regtest, clear_miniscript, microsd_wipe, bitcoind, + internal_key_spendable, dev, microsd_path, get_cc_key, + pick_menu_item, cap_story, goto_home, cap_menu, load_export, + import_miniscript, bitcoin_core_signer, import_duplicate, + press_select, garbage_collector, create_core_wallet): + use_regtest() + clear_miniscript() + microsd_wipe() + tmplt = TREE[num_leafs] + bitcoind_signers_xpubs = [] + bitcoind_signers = [] + for i in range(num_leafs): + s, core_key = bitcoin_core_signer(f"bitcoind--signer{i}") + bitcoind_signers.append(s) + bitcoind_signers_xpubs.append(core_key) + + bitcoin_signer_leafs = [f"pk({k})" for k in bitcoind_signers_xpubs] + + cc_key = get_cc_key("86h/0h/100h") + cc_leaf = f"pk({cc_key})" + + if internal_key_spendable: + desc = f"tr({cc_key},{tmplt % (*bitcoin_signer_leafs,)})" + else: + internal_key = bitcoind_signers_xpubs[0] + leafs = bitcoin_signer_leafs[1:] + [cc_leaf] + random.shuffle(leafs) + desc = f"tr({internal_key},{tmplt % (*leafs,)})" + + fname = "ts_pk.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc + "\n") + + garbage_collector.append(fpath) + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + assert fname.split(".")[0] in story + assert "Press (1) to see extended public keys" in story + assert "P2TR" in story + + press_select() + import_duplicate(fname) + + goto_home() + pick_menu_item('Settings') + pick_menu_item("Multisig/Miniscript") + menu = cap_menu() + + ts = create_core_wallet(menu[0], "bech32m", "sd", True) + + dest_addr = ts.getnewaddress("", "bech32m") # selfspend + psbt = ts.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 2})["psbt"] + fname = "ts_pk.psbt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + + garbage_collector.append(fpath) + + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + + garbage_collector.append(fpath_psbt) + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = ts.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = ts.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + assert txn_id + + +@pytest.mark.parametrize("desc", [ + "tr(unspend(),{{sortedmulti_a(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*),sortedmulti_a(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)},sortedmulti_a(2,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)})", + "tr(unspend(),{sortedmulti_a(2,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*),{sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*),sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)}})", + "tr(unspend(),{sortedmulti_a(2,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*),{sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*),sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)}})", + "tr(unspend(),{{sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*),sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)},sortedmulti_a(2,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)})", + "tr(unspend(),{{sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*),sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)},or_d(pk([0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*),and_v(v:pkh([30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*),older(500)))})", + "tr(unspend(),{{sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[b7fe820c/48'/1'/0'/3']tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/0/*),sortedmulti_a(2,[0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*,[30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*)},or_d(pk([0f056943/48'/1'/0'/3']tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/0/*),and_v(v:pkh([30afbe54/48'/1'/0'/3']tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/0/*),older(500)))})", +]) +def test_tapscript_import_export(clear_miniscript, pick_menu_item, cap_story, + import_miniscript, load_export, desc, microsd_path, + press_select): + i = random.randint(2, 10) # needs to be disjoint + unspend = ranged_unspendable_internal_key(os.urandom(32), subderiv=f"/<{i};{i+1}>/*") + desc = desc.replace("unspend()", unspend) + clear_miniscript() + fname = "imdesc.txt" + with open(microsd_path(fname), "w") as f: + f.write(desc) + _, story = import_miniscript(fname) + press_select() # approve miniscript import + pick_menu_item(fname.split(".")[0]) + pick_menu_item("Descriptors") + pick_menu_item("Export") + contents = load_export("sd", label="Miniscript", is_json=False, addr_fmt=AF_P2TR) + descriptor = contents.strip() + assert desc.split("#")[0].replace("<0;1>/*", "0/*").replace("'", "h") == descriptor.split("#")[0].replace("<0;1>/*", "0/*").replace("'", "h") + + +def test_duplicate_tapscript_leaves(use_regtest, clear_miniscript, microsd_wipe, bitcoind, dev, + goto_home, pick_menu_item, microsd_path, import_miniscript, + cap_story, load_export, get_cc_key, garbage_collector, + bitcoin_core_signer, import_duplicate, press_select, + create_core_wallet): + # works in core - but some discussions are ongoing + # https://github.com/bitcoin/bitcoin/issues/27104 + # CC also allows this for now... (experimental branch) + use_regtest() + clear_miniscript() + microsd_wipe() + ss, core_key = bitcoin_core_signer(f"s1_dup_leafs") + + cc_key = get_cc_key("86h/0h/100h") + cc_leaf = f"pk({cc_key})" + + tmplt = TREE[2] + tmplt = tmplt % (cc_leaf, cc_leaf) + desc = f"tr({core_key},{tmplt})" + fname = "dup_leafs.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + assert fname.split(".")[0] in story + assert "Press (1) to see extended public keys" in story + assert "P2TR" in story + + press_select() + import_duplicate(fname) + + ts = create_core_wallet(fname.split(".")[0], "bech32m", "sd", True) + + dest_addr = ts.getnewaddress("", "bech32m") # selfspend + psbt = ts.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 2})["psbt"] + fname = "ts_pk.psbt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = ts.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = ts.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + assert txn_id + + +def test_same_key_account_based_minisc(goto_home, pick_menu_item, cap_story, + clear_miniscript, microsd_path, load_export, bitcoind, + import_miniscript, use_regtest, import_duplicate, + press_select, garbage_collector, create_core_wallet): + clear_miniscript() + use_regtest() + + desc = ("wsh(" + "or_d(pk([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*)," + "and_v(" + "v:pkh([0f056943/84'/1'/9']tpubDC7jGaaSE66QBAcX8TUD3JKWari1zmGH4gNyKZcrfq6NwCofKujNF2kyeVXgKshotxw5Yib8UxLrmmCmWd8NVPVTAL8rGfMdc7TsAKqsy6y/<0;1>/*)," + "older(5))))#qmwvph5c") + + name = "mini-accounts" + fname = f"{name}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + assert fname.split(".")[0] in story + assert "Press (1) to see extended public keys" in story + + press_select() + import_duplicate(fname) + + wo = create_core_wallet(fname.split(".")[0], "bech32", "sd", True) + + dest_addr = wo.getnewaddress("", "bech32") # selfspend + psbt = wo.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 2})["psbt"] + fname = "multi-acct.psbt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + + _psbt = BasicPSBT().parse(final_psbt.encode()) + assert len(_psbt.inputs[0].part_sigs) == 2 + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + assert txn_id + + +CHANGE_BASED_DESCS = [ + ( + "wsh(" + "or_d(" + "pk([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*)," + "and_v(" + "v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*)," + "older(5)" + ")" + ")" + ")#aq0kpuae" + ), + ( + "wsh(or_i(" + "and_v(" + "v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*)," + "older(10)" + ")," + "or_d(" + "multi(" + "3," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*" + ")," + "and_v(" + "v:thresh(" + "2," + "pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*)," + "a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*)," + "a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*)" + ")," + "older(5)" + ")" + ")" + "))#a4nfkskx" + ), + "tr(tpubD6NzVbkrYhZ4WhUnV3cPSoRWGf9AUdG2dvNpsXPiYzuTnxzAxemnbajrATDBWhaAVreZSzoGSe3YbbkY2K267tK3TrRmNiLH2pRBpo8yaWm/<2;3>/*,{or_d(pk([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*),and_v(v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*),older(5))),or_i(and_v(v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*),older(10)),or_d(multi_a(3,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*),and_v(v:thresh(2,pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*),a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*),a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*)),older(5))))})", + "tr([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<66;67>/*,{or_d(pk([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*),and_v(v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*),older(5))),or_i(and_v(v:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2147483646;2147483647>/*),older(10)),or_d(multi_a(3,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<100;101>/*,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<26;27>/*,[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<4;5>/*),and_v(v:thresh(2,pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<20;21>/*),a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<104;105>/*),a:pkh([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<22;23>/*)),older(5))))})#qqcy9jlr", +] + +@pytest.mark.parametrize("desc", CHANGE_BASED_DESCS) +def test_same_key_change_based_minisc(goto_home, pick_menu_item, cap_story, + clear_miniscript, microsd_path, load_export, bitcoind, + import_miniscript, address_explorer_check, use_regtest, + desc, press_select, garbage_collector, create_core_wallet): + clear_miniscript() + use_regtest() + if desc.startswith("tr("): + af = "bech32m" + else: + af = "bech32" + + name = "mini-change" + fname = f"{name}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + assert fname.split(".")[0] in story + assert "Press (1) to see extended public keys" in story + press_select() + + wo = create_core_wallet(name, af, "sd", True) + + dest_addr = wo.getnewaddress("", af) # selfspend + psbt = wo.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 2})["psbt"] + fname = "msc-change-conso.psbt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + assert txn_id + + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + + dest_addr_0 = bitcoind.supply_wallet.getnewaddress() + dest_addr_1 = bitcoind.supply_wallet.getnewaddress() + dest_addr_2 = bitcoind.supply_wallet.getnewaddress() + psbt = wo.walletcreatefundedpsbt( + [], + [{dest_addr_0: 1.0}, {dest_addr_1: 2.56}, {dest_addr_2: 12.99}], + 0, {"fee_rate": 2} + )["psbt"] + fname = "msc-change-send.psbt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + assert txn_id + + # check addresses + address_explorer_check("sd", af, wo, "mini-change") + + +@pytest.mark.parametrize("desc", [ + "wsh(or_d(pk(@A),and_v(v:pkh(@A),older(5))))", + "tr(@ik,multi_a(2,@A,@A))", + "tr(@ik,{sortedmulti_a(2,@A,@A),pk(@A)})", + "tr(@ik,or_d(pk(@A),and_v(v:pkh(@A),older(5))))", +]) +def test_insane_miniscript(get_cc_key, pick_menu_item, cap_story, + microsd_path, desc, import_miniscript, + garbage_collector): + + cc_key = get_cc_key("84h/0h/0h") + desc = desc.replace("@A", cc_key) + desc = desc.replace("@ik", ranged_unspendable_internal_key()) + fname = "insane.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Failed to import" in story + assert "Insane" in story + +def test_tapscript_depth(get_cc_key, pick_menu_item, cap_story, + microsd_path, import_miniscript, garbage_collector): + leaf_num = 9 + scripts = [] + for i in range(leaf_num): + k = get_cc_key(f"84h/0h/{i}h") + scripts.append(f"pk({k})") + + tree = TREE[leaf_num] % tuple(scripts) + desc = f"tr({ranged_unspendable_internal_key()},{tree})" + fname = "9leafs.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + _, story = import_miniscript(fname) + assert "Failed to import" in story + assert "num_leafs > 8" in story + +@pytest.mark.bitcoind +# @pytest.mark.parametrize("lt_type", ["older", "after"]) +@pytest.mark.parametrize("same_acct", [True, False]) +@pytest.mark.parametrize("recovery", [True, False]) +@pytest.mark.parametrize("leaf2_mine", [True, False]) +@pytest.mark.parametrize("minisc", [ + "or_d(pk(@A),and_v(v:pkh(@B),locktime(N)))", + + "or_d(pk(@A),and_v(v:pk(@B),locktime(N)))", + + "or_d(multi_a(2,@A,@C),and_v(v:pkh(@B),locktime(N)))", + + "or_d(pk(@A),and_v(v:multi_a(2,@B,@C),locktime(N)))", +]) +def test_minitapscript(leaf2_mine, recovery, minisc, clear_miniscript, goto_home, + pick_menu_item, cap_menu, cap_story, microsd_path, + use_regtest, bitcoind, microsd_wipe, load_export, dev, + address_explorer_check, get_cc_key, import_miniscript, + bitcoin_core_signer, same_acct, import_duplicate, press_select, + garbage_collector, start_sign, end_sign, create_core_wallet): + lt_type = "older" + # needs bitcoind 26.0 + normal_cosign_core = False + recovery_cosign_core = False + if "multi_a(" in minisc.split("),", 1)[0]: + normal_cosign_core = True + if "multi_a(" in minisc.split("),", 1)[-1]: + recovery_cosign_core = True + + if lt_type == "older": + sequence = 5 + locktime = 0 + # 101 blocks are mined by default + to_replace = "older(5)" + else: + sequence = None + locktime = 105 + to_replace = "after(105)" + + minisc = minisc.replace("locktime(N)", to_replace) + + core_keys = [] + signers = [] + for i in range(3): + # core signers + signer, core_key = bitcoin_core_signer(f"co-signer{i}") + signer.keypoolrefill(25) + core_keys.append(core_key) + signers.append(signer) + + # cc device key + if same_acct: + cc_key = get_cc_key("86h/1h/0h", subderiv="/<4;5>/*") + cc_key1 = get_cc_key("86h/1h/0h", subderiv="/<6;7>/*") + else: + cc_key = get_cc_key("86h/1h/0h") + cc_key1 = get_cc_key("86h/1h/1h") + + if recovery: + # recevoery path is always B + minisc = minisc.replace("@B", cc_key) + minisc = minisc.replace("@A", core_keys[0]) + else: + minisc = minisc.replace("@A", cc_key) + minisc = minisc.replace("@B", core_keys[0]) + + if "@C" in minisc: + minisc = minisc.replace("@C", core_keys[1]) + + ik = ranged_unspendable_internal_key(os.urandom(32)) + + if leaf2_mine: + desc = f"tr({ik},{{{minisc},pk({cc_key1})}})" + else: + desc = f"tr({ik},{{pk({core_keys[2]}),{minisc}}})" + + use_regtest() + clear_miniscript() + name = "minitapscript" + fname = f"{name}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + + garbage_collector.append(fpath) + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + import_duplicate(fname) + + wo = create_core_wallet(name, "bech32m", "sd", True) + + all_of_it = wo.getbalance() + unspent = wo.listunspent() + assert len(unspent) == 1 + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + + if recovery and sequence and not leaf2_mine: + inp["sequence"] = sequence + + # split to + num_outs = 20 + nVal = all_of_it / num_outs + conso_addrs = [{wo.getnewaddress("", "bech32m"): nVal} for _ in range(num_outs)] # self-spend + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + conso_addrs, + locktime if (recovery and not leaf2_mine) else 0, + {"fee_rate": 2, "change_type": "bech32m", "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + if (normal_cosign_core or recovery_cosign_core) and not leaf2_mine: + psbt_res = signers[1].walletprocesspsbt(psbt, True, "DEFAULT") + assert psbt_res["psbt"] != psbt + psbt = psbt_res.get("psbt") + + name = f"{name}.psbt" + fpath = microsd_path(name) + with open(fpath, "w") as f: + f.write(psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(name) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "warning" not in story + assert "1 input" in story + assert "20 outputs" in story + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(microsd_path(fname_psbt), "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath) + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = wo.testmempoolaccept([tx_hex]) + if recovery and not leaf2_mine: + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' if sequence else "non-final" + bitcoind.supply_wallet.generatetoaddress(6, bitcoind.supply_wallet.getnewaddress()) + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + else: + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + unspent = wo.listunspent() + assert len(unspent) == 20 + ins = [{"txid": u["txid"], "vout": u["vout"]} for u in unspent] + + if recovery and sequence and not leaf2_mine: + for i in ins: + i["sequence"] = sequence + + # consolidate multiple inputs to one for us + # BUT also send 1 corn back to supply (so not a consolidation) + outs = [ + {wo.getnewaddress("", "bech32m"): wo.getbalance() - 1}, + {bitcoind.supply_wallet.getnewaddress("", "bech32"): 1}, + ] + psbt_resp = wo.walletcreatefundedpsbt( + ins, + outs, + locktime if (recovery and not leaf2_mine) else 0, + {"fee_rate": 2, "change_type": "bech32m", "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + # now CC first + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "warning" not in story + assert "Consolidating" not in story + assert "20 inputs" in story + assert "2 outputs" in story + final_psbt = end_sign(True) + psbt = base64.b64encode(final_psbt).decode() + + if (normal_cosign_core or recovery_cosign_core) and not leaf2_mine: + # core co-signer second after CC (if needed) + psbt_res = signers[1].walletprocesspsbt(psbt, True, "DEFAULT") + assert psbt_res["psbt"] != psbt + psbt = psbt_res.get("psbt") + + res = wo.finalizepsbt(psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + if recovery and not leaf2_mine: + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' if sequence else "non-final" + bitcoind.supply_wallet.generatetoaddress(6, bitcoind.supply_wallet.getnewaddress()) + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + else: + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + # check addresses + address_explorer_check("sd", "bech32m", wo, "minitapscript") + +@pytest.mark.parametrize("desc", [ + "tr(tpubD6NzVbkrYhZ4WhUnV3cPSoRWGf9AUdG2dvNpsXPiYzuTnxzAxemnbajrATDBWhaAVreZSzoGSe3YbbkY2K267tK3TrRmNiLH2pRBpo8yaWm/<2;3>/*,{{sortedmulti(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*),sortedmulti(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)},sortedmulti(2,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)})", + "wsh(sortedmulti_a(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*))", + "sh(wsh(or_d(pk([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*),and_v(v:multi_a(2,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*),older(500)))))", +]) +def test_multi_mixin(desc, clear_miniscript, microsd_path, pick_menu_item, + cap_story, import_miniscript, garbage_collector): + clear_miniscript() + fname = "imdesc.txt" + fpath = microsd_path(fname) + with open(microsd_path(fname), "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + title, story = import_miniscript(fname) + assert "Failed to import" in story + assert "multi mixin" in story + + +def test_timelock_mixin(): + pass + + +@pytest.mark.parametrize("addr_fmt", ["bech32", "bech32m"]) +@pytest.mark.parametrize("cc_first", [True, False]) +def test_d_wrapper(addr_fmt, bitcoind, get_cc_key, goto_home, pick_menu_item, cap_story, cap_menu, + load_export, microsd_path, use_regtest, clear_miniscript, cc_first, + address_explorer_check, import_miniscript, bitcoin_core_signer, press_select, + garbage_collector, create_core_wallet): + + # check D wrapper u property for segwit v0 and v1 + # https://github.com/bitcoin/bitcoin/pull/24906/files + minsc = "thresh(3,c:pk_k(@A),sc:pk_k(@B),sc:pk_k(@C),sdv:older(5))" + + core_keys = [] + signers = [] + for i in range(2): + # core signers + signer, core_key = bitcoin_core_signer(f"co-signer{i}") + core_keys.append(core_key) + signers.append(signer) + + cc_key = get_cc_key(f"{84 if addr_fmt == 'bech32' else 86}h/1h/0h") + + minsc = minsc.replace("@A", cc_key) + minsc = minsc.replace("@B", core_keys[0]) + minsc = minsc.replace("@C", core_keys[1]) + + if addr_fmt == "bech32": + desc = f"wsh({minsc})" + else: + desc = f"tr({ranged_unspendable_internal_key()},{minsc})" + + name = "d_wrapper" + fname = f"{name}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + clear_miniscript() + use_regtest() + _, story = import_miniscript(fname) + if addr_fmt == "bech32": + assert "Failed to import" in story + assert "thresh: X3 should be du" in story + return + + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + + wo = create_core_wallet(name, addr_fmt, "sd", True) + + addr_dest = wo.getnewaddress("", addr_fmt) # self-spend + all_of_it = wo.getbalance() + unspent = wo.listunspent() + assert len(unspent) == 1 + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + inp["sequence"] = 5 + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{addr_dest: all_of_it - 1}], + 0, + {"fee_rate": 20, "change_type": addr_fmt}, + ) + psbt = psbt_resp.get("psbt") + + if not cc_first: + to_sign_psbt_o = signers[0].walletprocesspsbt(psbt, True) + to_sign_psbt = to_sign_psbt_o["psbt"] + assert to_sign_psbt != psbt + else: + to_sign_psbt = psbt + + name = f"{name}.psbt" + fpath = microsd_path(name) + with open(fpath, "w") as f: + f.write(to_sign_psbt) + garbage_collector.append(fpath) + goto_home() + pick_menu_item("Ready To Sign") + time.sleep(.1) + title, story = cap_story() + if "OK TO SEND?" not in title: + time.sleep(0.1) + pick_menu_item(name) + time.sleep(0.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + press_select() # confirm signing + time.sleep(0.5) + title, story = cap_story() + assert "PSBT Signed" == title + assert "Updated PSBT is:" in story + press_select() + fname_psbt = story.split("\n\n")[1] + # fname_txn = story.split("\n\n")[3] + fpath_psbt = microsd_path(fname_psbt) + with open(fpath_psbt, "r") as f: + final_psbt = f.read().strip() + garbage_collector.append(fpath_psbt) + assert final_psbt != to_sign_psbt + # with open(microsd_path(fname_txn), "r") as f: + # final_txn = f.read().strip() + + if cc_first: + done_o = signers[0].walletprocesspsbt(final_psbt, True) + done = done_o["psbt"] + else: + done = final_psbt + + res = wo.finalizepsbt(done) + assert res["complete"] + tx_hex = res["hex"] + # assert tx_hex == final_txn + res = wo.testmempoolaccept([tx_hex]) + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + bitcoind.supply_wallet.generatetoaddress(6, bitcoind.supply_wallet.getnewaddress()) + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + # check addresses + address_explorer_check("sd", addr_fmt, wo, "d_wrapper") + + +def test_chain_switching(use_mainnet, use_regtest, settings_get, settings_set, + clear_miniscript, goto_home, cap_menu, pick_menu_item, + import_miniscript, microsd_path, press_select, garbage_collector): + clear_miniscript() + use_regtest() + + x = "wsh(or_d(pk([0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*),and_v(v:pkh([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*),older(100))))" + z = "wsh(or_d(pk([0f056943/48'/0'/0'/3']xpub6FQgdFZAHcAeDMVe9KxWoLMxziCjscCExzuKJhRSjM71CA9dUDZEGNgPe4S2SsRumCBXeaTBZ5nKz2cMDiK4UEbGkFXNipHLkm46inpjE9D/0/*),and_v(v:pkh([0f056943/48'/0'/0'/2']xpub6FQgdFZAHcAeAhQX2VvQ42CW2fDdKDhgwzhzXuUhWb4yfArmaZXkLbGS9W1UcgHwNxVESCS1b8BK8tgNYEF8cgmc9zkmsE45QSEvbwdp6Kr/0/*),older(100))))" + y = f"tr({ranged_unspendable_internal_key()},or_d(pk([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*),and_v(v:pk([0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*),after(800000))))" + + fname_btc = "BTC.txt" + fname_xtn = "XTN.txt" + fname_xtn0 = "XTN0.txt" + + for desc, fname in [(x, fname_xtn), (z, fname_btc), (y, fname_xtn0)]: + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + # cannot import XPUBS when testnet/regtest enabled + _, story = import_miniscript(fname_btc) + assert "Failed to import" in story + assert "wrong chain" in story + + import_miniscript(fname_xtn) + press_select() + time.sleep(.1) + res = settings_get("miniscript", []) + assert len(res) == 1 + assert res[0][-1]["ct"] == "XRT" + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert "(none setup yet)" not in m + assert fname_xtn.split(".")[0] in m[0] + goto_home() + settings_set("chain", "BTC") + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + # but not on current active chain + assert "(none setup yet)" in m + import_miniscript(fname_btc) + press_select() + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert fname_btc.split(".")[0] in m[0] + for mi in m: + assert fname_xtn.split(".")[0] not in mi + + _, story = import_miniscript(fname_xtn) + assert "Failed to import" in story + assert "wrong chain" in story + + settings_set("chain", "XTN") + import_miniscript(fname_xtn0) + press_select() + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert "(none setup yet)" not in m + assert fname_xtn.split(".")[0] in m[0] + assert fname_xtn0.split(".")[0] in m[1] + for mi in m: + assert fname_btc not in mi + + +@pytest.mark.parametrize("taproot_ikspendable", [ + (True, False), (True, True), (False, False) +]) +@pytest.mark.parametrize("minisc", [ + "or_d(pk(@A),and_v(v:pkh(@B),after(100)))", + "or_d(multi(2,@A,@C),and_v(v:pkh(@B),after(100)))", +]) +def test_import_same_policy_same_keys_diff_order(taproot_ikspendable, minisc, use_regtest, + clear_miniscript, bitcoin_core_signer, + get_cc_key, settings_get, cap_menu, + offer_minsc_import, bitcoind, press_select): + use_regtest() + clear_miniscript() + taproot, ik_spendable = taproot_ikspendable + if taproot: + minisc = minisc.replace("multi(", "multi_a(") + if ik_spendable: + ik = get_cc_key("84h/1h/100h", subderiv="/0/*") + desc = f"tr({ik},{minisc})" + else: + desc = f"tr({ranged_unspendable_internal_key()},{minisc})" + else: + desc = f"wsh({minisc})" + + cc_key0 = get_cc_key("84h/1h/0h", subderiv="/0/*") + signer0, core_key0 = bitcoin_core_signer("s00") + # recevoery path is always B + desc0 = desc.replace("@A", cc_key0) + desc0 = desc0.replace("@B", core_key0) + + if "@C" in desc: + signer1, core_key1 = bitcoin_core_signer("s11") + desc0 = desc0.replace("@C", core_key1) + + # now just change order of the keys (A,B), but same keys same policy + desc1 = desc.replace("@B", cc_key0) + desc1 = desc1.replace("@A", core_key0) + + if "@C" in desc: + desc1 = desc1.replace("@C", core_key1) + + # checksum required if via USB + desc_info = bitcoind.supply_wallet.getdescriptorinfo(desc0) + desc0 = desc_info["descriptor"] # with checksum + desc_info = bitcoind.supply_wallet.getdescriptorinfo(desc1) + desc1 = desc_info["descriptor"] # with checksum + + title, story = offer_minsc_import(desc0) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + title, story = offer_minsc_import(desc1) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + assert len(settings_get("miniscript", [])) == 2 + + +@pytest.mark.parametrize("cs", [True, False]) +@pytest.mark.parametrize("way", ["usb", "nfc", "sd", "vdisk"]) +def test_import_miniscript_usb_json(use_regtest, cs, way, cap_menu, clear_miniscript, get_cc_key, + bitcoin_core_signer, offer_minsc_import, bitcoind, microsd_path, + virtdisk_path, import_miniscript, goto_home, press_select, + settings_get): + name = "my_minisc" + minsc = f"tr({ranged_unspendable_internal_key()},or_d(multi_a(2,@A,@C),and_v(v:pkh(@B),after(100))))" + use_regtest() + clear_miniscript() + + cc_key = get_cc_key("84h/1h/0h", subderiv="/0/*") + signer0, core_key0 = bitcoin_core_signer("s00") + # recevoery path is always B + desc = minsc.replace("@A", cc_key) + desc = desc.replace("@B", core_key0) + + signer1, core_key1 = bitcoin_core_signer("s11") + desc = desc.replace("@C", core_key1) + + if cs: + desc_info = bitcoind.supply_wallet.getdescriptorinfo(desc) + desc = desc_info["descriptor"] # with checksum + + val = json.dumps({"name": name, "desc": desc}) + + nfc_data = None + fname = "diff_name.txt" # will be ignored as name in the json has preference + if way == "usb": + title, story = offer_minsc_import(val) + else: + if way == "nfc": + nfc_data = val + else: + if way == "sd": + fpath = microsd_path(fname) + else: + fpath = virtdisk_path(fname) + + with open(fpath, "w") as f: + f.write(val) + + title, story = import_miniscript(fname, way, nfc_data) + + assert "Create new miniscript wallet?" in story + assert name in story + press_select() + time.sleep(.2) + msc = settings_get("miniscript", []) + assert len(msc) == 1 + assert msc[0][0] == name + + +@pytest.mark.parametrize("config", [ + # all dummy data there to satisfy badlen check in usb.py + # missing 'desc' key + {"name": "my_miniscript", "random": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, + # name longer than 40 chars + {"name": "a" * 41, "desc": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, + # name too short + {"name": "a", "desc": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, + # desc key empty + {"name": "ab", "desc": "", "random": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, + # name type + {"name": None, "desc": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, + # desc type + {"name": "ab", "desc": None, "random": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}, +]) +def test_json_import_failures(config, offer_minsc_import): + with pytest.raises(Exception): + offer_minsc_import(json.dumps(config)) + + +@pytest.mark.parametrize("way", ["sd", "nfc", "vdisk"]) +@pytest.mark.parametrize("is_json", [True, False]) +def test_unique_name(clear_miniscript, use_regtest, offer_minsc_import, + pick_menu_item, cap_menu, way, goto_home, + microsd_path, virtdisk_path, is_json, + import_miniscript, press_select, press_cancel): + clear_miniscript() + use_regtest() + + name = "my_name" + x = "wsh(or_d(pk([0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*),and_v(v:pkh([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*),older(100))))" + y = f"tr({ranged_unspendable_internal_key()},or_d(pk([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*),and_v(v:pk([0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*),after(800000))))" + + xd = json.dumps({"name": name, "desc": x}) + title, story = offer_minsc_import(xd) + assert "Create new miniscript wallet?" in story + assert name in story + press_select() + time.sleep(.2) + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + m = cap_menu() + assert m[0] == name + assert m[1] == "Import" + + # completely different wallet but with the same name (USB) + yd = json.dumps({"name": name, "desc": y}) + title, story = offer_minsc_import(yd) + assert ("'%s' already exists" % name) in story + assert "MUST have unique names" in story + press_cancel() + # nothing imported + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + m = cap_menu() + assert m[0] == name + assert m[1] == "Import" + + goto_home() + fname = f"{name}.txt" + nfc_data = None + if way == "nfc": + if not is_json: + pytest.xfail("impossible") + + nfc_data = yd + fname = None + else: + if way == "sd": + fpath = microsd_path(fname) + elif way == "vdisk": + fpath = virtdisk_path(fname) + else: + assert False + + with open(fpath, "w") as f: + f.write(yd if is_json else y) + + title, story = import_miniscript(fname=fname, way=way, data=nfc_data) + assert ("'%s' already exists" % name) in story + assert "MUST have unique names" in story + + +@pytest.mark.qrcode +def test_usb_workflow(usb_miniscript_get, usb_miniscript_ls, clear_miniscript, + usb_miniscript_addr, usb_miniscript_delete, use_regtest, + reset_seed_words, offer_minsc_import, need_keypress, + cap_story, cap_screen_qr, press_select): + use_regtest() + reset_seed_words() + clear_miniscript() + assert [] == usb_miniscript_ls() + for i, desc in enumerate(CHANGE_BASED_DESCS): + _, story = offer_minsc_import(json.dumps({"name": f"w{i}", "desc": desc})) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + + msc_wallets = usb_miniscript_ls() + assert len(msc_wallets) == 4 + assert sorted(msc_wallets) == ["w0", "w1", "w2", "w3"] + + # try to get/delete nonexistent wallet + with pytest.raises(Exception) as err: + usb_miniscript_get("w4") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + with pytest.raises(Exception) as err: + usb_miniscript_delete("w4") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + for i, w in enumerate(msc_wallets): + assert usb_miniscript_get(w)["desc"].split("#")[0] == CHANGE_BASED_DESCS[i].split("#")[0].replace("'", 'h') + + #check random address + addr = usb_miniscript_addr("w0", 55, False) + time.sleep(0.1) + need_keypress('4') + time.sleep(0.1) + qr = cap_screen_qr().decode('ascii') + assert qr == addr.upper() + + usb_miniscript_delete("w3") + time.sleep(.2) + _, story = cap_story() + assert "Delete miniscript wallet" in story + assert "'w3'" in story + press_select() + time.sleep(.2) + assert len(usb_miniscript_ls()) == 3 + with pytest.raises(Exception) as err: + usb_miniscript_get("w3") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + usb_miniscript_delete("w2") + time.sleep(.2) + _, story = cap_story() + assert "Delete miniscript wallet" in story + assert "'w2'" in story + press_select() + time.sleep(.2) + assert len(usb_miniscript_ls()) == 2 + with pytest.raises(Exception) as err: + usb_miniscript_get("w2") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + usb_miniscript_delete("w1") + time.sleep(.2) + _, story = cap_story() + assert "Delete miniscript wallet" in story + assert "'w1'" in story + press_select() + time.sleep(.2) + assert len(usb_miniscript_ls()) == 1 + with pytest.raises(Exception) as err: + usb_miniscript_get("w1") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + usb_miniscript_delete("w0") + time.sleep(.2) + _, story = cap_story() + assert "Delete miniscript wallet" in story + assert "'w0'" in story + press_select() + time.sleep(.2) + assert len(usb_miniscript_ls()) == 0 + with pytest.raises(Exception) as err: + usb_miniscript_get("w0") + assert err.value.args[0] == "Coldcard Error: Miniscript wallet not found" + + +def test_miniscript_name_validation(microsd_path, offer_minsc_import): + for tc in ["weê", "eee\teee"]: + with pytest.raises(Exception) as e: + offer_minsc_import(json.dumps({"name": tc, "desc": CHANGE_BASED_DESCS[0]})) + assert "must be ascii" in e.value.args[0] + + +def test_bug_fill_policy(set_seed_words, goto_home, pick_menu_item, need_keypress, + microsd_path, cap_story, press_select, clear_miniscript, + cap_menu, bitcoind, start_sign, end_sign): + clear_miniscript() + mnemonic = "normal useless alpha sphere grid defense feed era farm law hair region" + set_seed_words(mnemonic) + + desc = """tr(tpubD6NzVbkrYhZ4Xjg1aU3fkQSj6yp8d7XNpnpVvjUBqDzMJt7J6QafSCBF5RLY2wwi +Vuhu79MKCKbUxjCxvicdATdc7hMPEejgCkQy3B28MiP/<0;1>/*,{and_v(v:multi_a(1, +[61cd4eb6/48'/1'/0'/2']tpubDE4RRPsyHN6GUsic4hrniYUhTsQ7h1bRQYyDPcWDFjKZ +vhms2nUNwo2j4oRwtuDZNJwwXzeoZ22RjGrueJ3zgAbbSTEM8kZQ8EnyDE79sGK/<2;3>/* +,[c658b283/48'/1'/0'/2']tpubDFL5wzgPBYK5pZ2Kh1T8qrxnp43kjE5CXfguZHHBrZS +WpkfASy5rVfj7prh11XdqkC1P3kRwUPBeX7AHN8XBNx8UwiprnFnEm5jyswiRD4p/<2;3>/ +*),older(65535)),multi_a(2,[c658b283/48'/1'/0'/2']tpubDFL5wzgPBYK5pZ2Kh +1T8qrxnp43kjE5CXfguZHHBrZSWpkfASy5rVfj7prh11XdqkC1P3kRwUPBeX7AHN8XBNx8U +wiprnFnEm5jyswiRD4p/<0;1>/*,[61cd4eb6/48'/1'/0'/2']tpubDE4RRPsyHN6GUsic +4hrniYUhTsQ7h1bRQYyDPcWDFjKZvhms2nUNwo2j4oRwtuDZNJwwXzeoZ22RjGrueJ3zgAb +bSTEM8kZQ8EnyDE79sGK/<0;1>/*,[25f48f59/48'/1'/0'/2']tpubDFRnTG8pxuoQ67w +aXsh1vNLD9c88JcRwEFxKCUsXzR11RkuV4pqFU6ccCZdwnjGY4yw25uCRHh4wCKNquvfgQ3 +zUvcND8MhRQFv8dCFzjNu/<0;1>/*)})#vh0vvyyn""" + + psbt = """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""" + + desc_fname = "minib.txt" + with open(microsd_path(desc_fname), "w") as f: + f.write(desc) + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item("Import") + need_keypress("1") + pick_menu_item(desc_fname) + time.sleep(.1) + _, story = cap_story() + assert "Create new miniscript wallet?" in story + assert "minib" in story # name + press_select() + + goto_home() + start_sign(base64.b64decode(psbt)) + signed = end_sign(accept=True) + assert signed != base64.b64decode(psbt) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("tmplt", [ + "wsh(or_d(multi(2,@0/<0;1>/*,@1/<0;1>/*),and_v(v:thresh(2,pkh(@0/<2;3>/*),a:pkh(@1/<2;3>/*),a:pkh(@2/<0;1>/*)),older(10))))", + # below is same as above with just first two keys swapped in thresh + "wsh(or_d(multi(2,@0/<0;1>/*,@1/<0;1>/*),and_v(v:thresh(2,pkh(@1/<2;3>/*),a:pkh(@0/<2;3>/*),a:pkh(@2/<0;1>/*)),older(10))))", + "tr(unspend()/<0;1>/*,{and_v(v:multi_a(2,@0/<2;3>/*,@1/<2;3>/*,@2/<0;1>/*,@3/<0;1>/*),older(10)),multi_a(2,@0/<0;1>/*,@1/<0;1>/*)})", + # below is same as above with just first two keys swapped in last multi_a + "tr(unspend()/<0;1>/*,{and_v(v:multi_a(2,@0/<2;3>/*,@1/<2;3>/*,@2/<0;1>/*,@3/<0;1>/*),older(10)),multi_a(2,@1/<0;1>/*,@0/<0;1>/*)})", + # internal key is ours + "tr(@0/<0;1>/*,{and_v(v:multi_a(2,@0/<2;3>/*,@1/<2;3>/*,@2/<2;3>/*,@3/<0;1>/*),older(10)),multi_a(2,@1/<0;1>/*,@2/<0;1>/*)})", +]) +def test_expanding_multisig(tmplt, clear_miniscript, goto_home, pick_menu_item, garbage_collector, + cap_menu, cap_story, microsd_path, use_regtest, bitcoind, microsd_wipe, + load_export, dev, address_explorer_check, get_cc_key, import_miniscript, + bitcoin_core_signer, import_duplicate, press_select, start_sign, end_sign, + create_core_wallet): + use_regtest() + clear_miniscript() + sequence = 10 + af = "bech32m" if tmplt.startswith("tr(") else "bech32" + unspend = "tpubD6NzVbkrYhZ4WbzhCs1gLUM8s8LAwTh68xVh1a3nRQyA3tbAJFSE2FEaH2CEGJTKmzcBagpyG35Kjv3UGpTEWbc7qSCX6mswrLQVVPgXECd" + tmplt = tmplt.replace("unspend()", unspend) + + csigner0, ckey0 = bitcoin_core_signer(f"co-signer-0") + ckey0 = ckey0.replace("/0/*", "") + csigner0.keypoolrefill(20) + csigner1, ckey1 = bitcoin_core_signer(f"co-signer-1") + ckey1 = ckey1.replace("/0/*", "") + csigner1.keypoolrefill(20) + csigner2, ckey2 = None, None + + # cc device key + cc_key = get_cc_key("86h/1h/0h").replace('/<0;1>/*', "") + + # fill policy + desc = tmplt.replace("@0", cc_key) + desc = desc.replace("@1", ckey0) + desc = desc.replace("@2", ckey1) + + if "@3" in tmplt: + csigner2, ckey2 = bitcoin_core_signer(f"co-signer-2") + ckey2 = ckey2.replace("/0/*", "") + csigner2.keypoolrefill(20) + desc = desc.replace("@3", ckey2) + + wname = "expand_msc" + fname = f"{wname}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + + wo = create_core_wallet(wname, af, "sd", True) + + # use non-recovery path to split into 5 utxos + 1 going back to supply (not a conso) + unspent = wo.listunspent() + assert len(unspent) == 1 + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + dest_addrs = [wo.getnewaddress(f"a{i}", af) for i in range(5)] + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{a: 5} for a in dest_addrs] + [{bitcoind.supply_wallet.getnewaddress(): 5}], + 0, + {"fee_rate": 20, "change_type": af}, + ) + psbt = psbt_resp.get("psbt") + + # if we have internal key we just spend with it, singlesig on chain + have_internal = "tr(@0," in tmplt + + if not have_internal: + # first sign with cosigner in gucci path (non-recovery) + psbt = csigner0.walletprocesspsbt(psbt, True)["psbt"] + + # now CC + start_sign(base64.b64decode(psbt), finalize=have_internal) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + final_psbt = end_sign(True, finalize=have_internal) + + if have_internal: + tx_hex = final_psbt.hex() # it is final tx actually + else: + # client software finalization + res = wo.finalizepsbt(base64.b64encode(final_psbt).decode()) + assert res["complete"] + tx_hex = res["hex"] + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + unspent = wo.listunspent() + assert len(unspent) == 6 # created 5 txos of 5 btc, one to supply & change back is 6th utxo + + # consolidation - consolidate 3 utxo into one bigger + to_spend = [{"txid": o["txid"], "vout": o["vout"]} for o in unspent if float(o["amount"]) == 5.0][:3] + psbt_resp = wo.walletcreatefundedpsbt( + to_spend, + [{wo.getnewaddress("conso", af): 15}], + 0, + {"fee_rate": 20, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + # now CC signing first + start_sign(base64.b64decode(psbt), finalize=have_internal) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + updated_psbt = end_sign(True, finalize=have_internal) + + if not have_internal: + # now cosigner (still on non-recovery path) + updated_psbt = base64.b64encode(updated_psbt).decode() + final_psbt = csigner0.walletprocesspsbt(updated_psbt, True, + "DEFAULT"if "tr(" == tmplt[:3] else "ALL")["psbt"] + + # client software finalization + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + else: + # actually a final tx + tx_hex = updated_psbt.hex() + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + unspent = wo.listunspent() + assert len(unspent) == 4 + + # now we lost our non-recovey path cosigner + del csigner0 + # use recovery key to consolidate all our outputs and send them to other wallet + dest = bitcoind.supply_wallet.getnewaddress() + all_of_it = wo.getbalance() + # need to bump sequence here + psbt_resp = wo.walletcreatefundedpsbt( + [ {"txid": o["txid"], "vout": o["vout"], "sequence": sequence} for o in unspent], + [{dest: all_of_it}], + 0, + {"fee_rate": 10, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + # now cosigner (on recovery path) + psbt = csigner1.walletprocesspsbt(psbt, True)["psbt"] + + if have_internal: + final_psbt = csigner2.walletprocesspsbt(psbt, True)["psbt"] + else: + # CC + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + final_psbt = end_sign(True) + final_psbt = base64.b64encode(final_psbt).decode() + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + # timelocked + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + + # mines some blocks to release the lock + bitcoind.supply_wallet.generatetoaddress(sequence, bitcoind.supply_wallet.getnewaddress()) + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + assert len(wo.listunspent()) == 0 + + # check addresses + address_explorer_check("sd", af, wo, wname) + + +@pytest.mark.parametrize("blinded", [True, False]) +def test_big_boy(use_regtest, clear_miniscript, bitcoin_core_signer, get_cc_key, microsd_path, + garbage_collector, pick_menu_item, bitcoind, import_miniscript, press_select, + cap_story, cap_menu, load_export, start_sign, end_sign, blinded, + create_core_wallet): + # keys (@0,@4,@5) are more important (primary) than keys (@1,@2,@3) (secondary) + # currently requires to tweak MAX_TR_SIGNERS = 33 + # with blinded=True, all co-signer keys are blinded (have no key origin info) + tmplt = ( + "tr(" + "tpubD6NzVbkrYhZ4XgXS51CV3bhoP5dJeQqPhEyhKPDXBgEs64VdSyAfku99gtDXQzY6HEXY5Dqdw8Qud1fYiyewDmYjKe9gGJeDx7x936ur4Ju/<0;1>/*," # unspendable + "{{{and_v(v:multi_a(3,@5/<8;9>/*,@1/<8;9>/*,@2/<8;9>/*,@3/<8;9>/*),older(1000))," # after 1000 blocks one of primary keys can sign with 2 secondary + "and_v(v:multi_a(3,@0/<8;9>/*,@1/<10;11>/*,@2/<10;11>/*,@3/<10;11>/*),older(1000))}," # after 1000 blocks one of primary keys can sign with 2 secondary + "{{and_v(v:multi_a(5,@4/<2;3>/*,@5/<2;3>/*,@0/<2;3>/*,@1/<2;3>/*,@2/<2;3>/*,@3/<2;3>/*),older(20))," # 5of6 after 20 blocks + "and_v(v:multi_a(4,@4/<4;5>/*,@5/<4;5>/*,@0/<4;5>/*,@1/<4;5>/*,@2/<4;5>/*,@3/<4;5>/*),older(60))}," # 4of6 after 60 blocks + "{and_v(v:multi_a(2,@4/<6;7>/*,@5/<6;7>/*,@0/<6;7>/*),older(120))," # after 120 blocks it is enough to have 2 of (@0,@4,@5) + "and_v(v:multi_a(3,@4/<8;9>/*,@1/<6;7>/*,@2/<6;7>/*,@3/<6;7>/*),older(1000))}}}," # after 1000 blocks one of primary keys can sign with 2 secondary + "multi_a(6,@1/<0;1>/*,@2/<0;1>/*,@3/<0;1>/*,@4/<0;1>/*,@5/<0;1>/*,@0/<0;1>/*)})" # 6of6 primary path + ) + + use_regtest() + clear_miniscript() + af = "bech32m" + + cc_key = get_cc_key("86h/1h/0h").replace('/<0;1>/*', "") + desc = tmplt.replace("@0", cc_key) + + cosigners = [] + for i in range(1, 6): + csigner, ckey = bitcoin_core_signer(f"co-signer-{i}") + ckey = ckey.replace("/0/*", "") + + if blinded: + ckey = ckey.split("]")[-1] + + csigner.keypoolrefill(20) + cosigners.append(csigner) + desc = desc.replace(f"@{i}", ckey) + + wname = "bigboy" + fname = f"{wname}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(desc) + + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + + wo = create_core_wallet(wname, af, "sd", True) + + unspent = wo.listunspent() + assert len(unspent) == 1 + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]} + # split to 10 utxos + dest_addrs = [wo.getnewaddress(f"a{i}", af) for i in range(10)] + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{a: 4} for a in dest_addrs] + [{bitcoind.supply_wallet.getnewaddress(): 5}], + 0, + {"fee_rate": 3, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + # sign with all cosigners + for s in cosigners: + psbt = s.walletprocesspsbt(psbt, True)["psbt"] + + # now CC + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + final_psbt = end_sign(True) + final_psbt = base64.b64encode(final_psbt).decode() + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + unspent = wo.listunspent() + assert len(unspent) == 11 + + +@pytest.mark.parametrize("af", ["bech32", "bech32m"]) +def test_single_key_miniscript(af, settings_set, clear_miniscript, goto_home, get_cc_key, + garbage_collector, microsd_path, bitcoind, import_miniscript, + press_select, cap_menu, pick_menu_item, load_export, cap_story, + start_sign, end_sign, create_core_wallet): + sequence = 10 + goto_home() + clear_miniscript() + settings_set("chain", "XRT") + policy = "and_v(v:pk(@0/<0;1>/*),older(10))" + + if af == "bech32m": + tmplt = f"tr(tpubD6NzVbkrYhZ4XgXS51CV3bhoP5dJeQqPhEyhKPDXBgEs64VdSyAfku99gtDXQzY6HEXY5Dqdw8Qud1fYiyewDmYjKe9gGJeDx7x936ur4Ju/<0;1>/*,{policy})" + else: + tmplt = f"wsh({policy})" + + cc_key = get_cc_key("m/99h/0h/0h").replace('/<0;1>/*', '') + tmplt = tmplt.replace("@0", cc_key) + + wname = "single_key_mini" + fname = f"{wname}.txt" + fpath = microsd_path(fname) + with open(fpath, "w") as f: + f.write(tmplt) + + garbage_collector.append(fpath) + + _, story = import_miniscript(fname) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + + wo = create_core_wallet(wname, af, "sd", True) + + unspent = wo.listunspent() + assert len(unspent) == 1 + + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "sequence": sequence} + # split to 10 utxos + dest_addrs = [wo.getnewaddress(f"a{i}", af) for i in range(10)] + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{a: 4} for a in dest_addrs] + [{bitcoind.supply_wallet.getnewaddress(): 5}], + 0, + {"fee_rate": 3, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + final_psbt = end_sign(True) + final_psbt = base64.b64encode(final_psbt).decode() + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + # timelocked + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + + # mines some blocks to release the lock + bitcoind.supply_wallet.generatetoaddress(sequence, bitcoind.supply_wallet.getnewaddress()) + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + unspent = wo.listunspent() + assert len(unspent) == 11 + + # now consolidate to one output + psbt_resp = wo.walletcreatefundedpsbt( + [{"txid": o["txid"], "vout": o["vout"], "sequence": sequence} for o in unspent], + [{wo.getnewaddress("", af): wo.getbalance()}], + 0, + {"fee_rate": 3, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" in story + final_psbt = end_sign(True) + final_psbt = base64.b64encode(final_psbt).decode() + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + # timelocked + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + + # mines some blocks to release the lock + bitcoind.supply_wallet.generatetoaddress(sequence, bitcoind.supply_wallet.getnewaddress()) + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mine above + + unspent = wo.listunspent() + assert len(unspent) == 1 + + +@pytest.mark.parametrize("tmplt", [ + "wsh(or_d(pk(@0),and_v(v:pkh(@1),older(100))))", + f"tr({ranged_unspendable_internal_key()},or_d(pk(@0),and_v(v:pk(@1),older(100))))" +]) +@pytest.mark.parametrize("cc_sign", [False, True]) +@pytest.mark.parametrize("has_orig", [False, True]) +def test_originless_keys(tmplt, offer_minsc_import, get_cc_key, bitcoin_core_signer, bitcoind, + pick_menu_item, load_export, goto_home, cap_menu, clear_miniscript, + use_regtest, press_select, start_sign, end_sign, cap_story, cc_sign, + has_orig, address_explorer_check, create_core_wallet): + # can be both: + # a.) just ranged xpub without origin info -> xpub1/<0;1>/* + # b.) ranged xpub with its fp -> [xpub1_fp]xpub1/<0;1>/* + sequence = 100 + use_regtest() + clear_miniscript() + af = "bech32m" if "tr(" in tmplt else "bech32" + name = "originless" + + cc_key = get_cc_key("m/84h/1h/0h") + cs, ck = bitcoin_core_signer(name+"_signer") + originless_ck = ck.split("]")[-1] + + n = BIP32Node.from_hwif(originless_ck.split("/")[0]) # just extended key + fp_str = "[" + n.fingerprint().hex() + "]" + if has_orig: + originless_ck = fp_str + originless_ck + + desc = tmplt.replace("@0", cc_key) + desc = desc.replace("@1", originless_ck) + to_import = {"desc": desc, "name": name} + offer_minsc_import(json.dumps(to_import)) + press_select() + + wo = create_core_wallet(name, af, "sd", True) + + unspent = wo.listunspent() + assert len(unspent) == 1 + + if cc_sign: + inputs = [] + else: + inputs = [{"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "sequence": sequence}] + + # split to 10 utxos + dest_addrs = [wo.getnewaddress(f"a{i}", af) for i in range(10)] + psbt_resp = wo.walletcreatefundedpsbt( + inputs, + [{a: 4} for a in dest_addrs] + [{bitcoind.supply_wallet.getnewaddress(): 5}], + 0, + {"fee_rate": 3, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + if cc_sign: + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + final_psbt = end_sign(True) + final_psbt = base64.b64encode(final_psbt).decode() + else: + final_psbt_o = cs.walletprocesspsbt(psbt, True, "DEFAULT" if af == "bech32m" else "ALL") + final_psbt = final_psbt_o["psbt"] + assert psbt != final_psbt + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + if not cc_sign: + # timelocked + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + + # mines some blocks to release the lock + bitcoind.supply_wallet.generatetoaddress(sequence, bitcoind.supply_wallet.getnewaddress()) + + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + # check addresses + address_explorer_check("sd", af, wo, name) + + +@pytest.mark.parametrize("internal_key", [ + H, + "r=@", + "r=dfed64ff493dca2ab09eadefaa0c88be8404908fa6eff869ff71c0d359d086b9", + "f19573a10866ee9881769e24464f9a0e989c2cb8e585db385934130462abed90" +]) +def test_static_internal_key(internal_key, clear_miniscript, microsd_path, pick_menu_item, + cap_story, import_miniscript, garbage_collector): + clear_miniscript() + desc = "tr(@ik,{{sortedmulti(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*),sortedmulti(2,[0f056943/48h/1h/0h/3h]tpubDF2rnouQaaYrY6CUWTapYkeFEs3h3qrzL4M52ZGoPeU9dkarJMtrw6VF1zJRGuGuAFxYS3kXtavfAwQPTQkU5dyNYpbgxcpftrR8H3U85Ez/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)},sortedmulti(2,[b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*,[30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)})" + desc = desc.replace("@ik", internal_key) + fname = "imdesc.txt" + fpath = microsd_path(fname) + with open(microsd_path(fname), "w") as f: + f.write(desc) + garbage_collector.append(fpath) + + title, story = import_miniscript(fname) + assert "Failed to import" in story + assert "only extended pubkeys allowed" in story + + +# @pytest.mark.bitcoind +# def test_csa_tapscript(clear_miniscript, bitcoin_core_signer, get_cc_key, +# use_regtest, address_explorer_check, bitcoind, +# offer_minsc_import, create_core_wallet, press_select): +# use_regtest() +# clear_miniscript() +# M, N = 11, 12 +# +# bitcoind_signers = [] +# bitcoind_signers_xpubs = [] +# for i in range(N - 1): +# s, core_key = bitcoin_core_signer(f"bitcoind--signer{i}") +# s.keypoolrefill(10) +# bitcoind_signers.append(s) +# bitcoind_signers_xpubs.append(core_key) +# +# me = get_cc_key(f"m/48h/1h/0h/3h") +# ik = ranged_unspendable_internal_key() +# +# signers_xp = [me] + bitcoind_signers_xpubs +# assert len(signers_xp) == N +# desc = f"tr({ik},%s)" +# +# scripts = [] +# for c in itertools.combinations(signers_xp, M): +# tmplt = f"multi_a({M},{','.join(c)})" +# scripts.append(tmplt) +# +# assert len(scripts) == 12 +# temp = TREE[len(scripts)] +# temp = temp % tuple(scripts) +# +# desc = desc % temp +# +# title, story = offer_minsc_import(desc) +# name = story.split("\n")[3].strip() +# assert "Create new miniscript wallet?" in story +# press_select() +# ms_wo = create_core_wallet(name, "bech32m", "sd", False) +# address_explorer_check("sd", "bech32m", ms_wo, "minisc") + + +# @pytest.mark.parametrize("desc", [ +# +# # "wsh(or_i(and_v(v:pkh(@A),older(100)),or_d(multi(3,@A,@B,@C),and_v(v:thresh(2,pkh(@A),a:pkh(@B),a:pkh(@C)),older(500)))))" +# ]) +def test_tapscript_disjoint_derivation(cap_story, offer_minsc_import, microsd_path, + get_cc_key, bitcoin_core_signer): + desc = "tr(unspend(),{{sortedmulti_a(2,@A,@B),sortedmulti_a(2,@AA,@C)},sortedmulti_a(2,@AAA,@BB,@CC)})" + + # internal key is OK + unspend = ranged_unspendable_internal_key(os.urandom(32), subderiv=f"/<0;1>/*") + desc = desc.replace("unspend()", unspend) + + # @A, @AA & @AAA is us - all OK + kA = get_cc_key("m/999h/1h/66h") + kAA = kA.replace("/<0;1>/*", "/<2;3>/*") + kAAA = kA.replace("/<0;1>/*", "/<4;5>/*") + + desc = desc.replace("@AAA", kAAA) + desc = desc.replace("@AA", kAA) + desc = desc.replace("@A", kA) + + s0, kB = bitcoin_core_signer("B") + # this is problematic - as it is nto disjoint + kB = kB.replace("/0/*", "/<1;2>/*") + kBB = kB.replace("/<1;2>/*", "/<0;1>/*") + + s1, kC = bitcoin_core_signer("C") + kC = kC.replace("/0/*", "/<0;1>/*") + kCC = kC.replace("/<0;1>/*", "/<2;3>/*") + + desc = desc.replace("@BB", kBB) + desc = desc.replace("@B", kB) + desc = desc.replace("@CC", kCC) + desc = desc.replace("@C", kC) + + with pytest.raises(Exception) as e: + offer_minsc_import(desc) + assert "Non-disjoint multipath" in e.value.args[0] + + # now make internal key non-disjoint + desc = desc.replace(unspend, ranged_unspendable_internal_key(os.urandom(32), subderiv=f"/<3;4>/*")) + # previously invalid key + desc = desc.replace(kB, kB.replace("/<1;2>/*", "/<2;3>/*")) + + with pytest.raises(Exception) as e: + offer_minsc_import(desc) + assert "Non-disjoint multipath" in e.value.args[0] + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("way", ["usb", "nfc", "sd", "vdisk", "qr"]) +def test_same_key_set_miniscript(get_cc_key, bitcoin_core_signer, create_core_wallet, way, + offer_minsc_import, press_select, bitcoind, start_sign, + cap_story, end_sign, clear_miniscript, goto_home, scan_a_qr, + pick_menu_item, microsd_path, garbage_collector, press_cancel, + need_keypress, press_nfc, nfc_write_text, nfc_read, + readback_bbqr, virtdisk_path, skip_if_useless_way): + # same keys in miniscript, impossible to match correct wallet with auto-match + skip_if_useless_way(way) + goto_home() + clear_miniscript() + + msc1 = "wsh(andor(pk(@D),after(1767225600),multi(2,@A,@B,@C)))" + msc2 = "wsh(or_d(pk(@D),and_v(v:multi(2,@A,@B,@C),older(65535))))" + + ak = get_cc_key("m/48h/1h/0h/2h") + bs, bk = bitcoin_core_signer("bb") + cs, ck = bitcoin_core_signer("cc") + ds, dk = bitcoin_core_signer("dd") + + bk = bk.replace("/0/*", "/<0;1>/*") + ck = ck.replace("/0/*", "/<0;1>/*") + dk = dk.replace("/0/*", "/<0;1>/*") + + msc1 = msc1.replace("@A", ak) + msc1 = msc1.replace("@B", bk) + msc1 = msc1.replace("@C", ck) + msc1 = msc1.replace("@D", dk) + + msc2 = msc2.replace("@A", ak) + msc2 = msc2.replace("@B", bk) + msc2 = msc2.replace("@C", ck) + msc2 = msc2.replace("@D", dk) + + title, story = offer_minsc_import(json.dumps(dict(name="msc1", desc=msc1))) + assert "msc1" in story + assert "Create new miniscript wallet?" in story + press_select() + + title, story = offer_minsc_import(json.dumps(dict(name="msc2", desc=msc2))) + assert "msc2" in story + assert "Create new miniscript wallet?" in story + press_select() + + m1 = create_core_wallet("msc1", "bech32") + m2 = create_core_wallet("msc2", "bech32") + + # now try to sign (via Ready To Sign) PSBT from msc2 + # this will not work, as msc1 has same key set and was imported first + # so we match msc1 + psbt = m2.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1.0}], + 0, {"fee_rate": 2})["psbt"] + + if way == "usb": + # first try classic way without specifying wallet name + # will fail with scriptPubKey mismatch + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert "spk mismatch" in story + + # now with name specified via USB + start_sign(base64.b64decode(psbt), miniscript="msc2") + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "msc2" in story + end_sign(accept=True) + + else: + fname = "the_txn.psbt" + fpath = microsd_path(fname) + garbage_collector.append(fpath) + with open(fpath, "w") as f: + f.write(psbt) + + goto_home() + # just try SD for normal matching without name + pick_menu_item("Ready To Sign") + title, story = cap_story() + if 'OK TO SEND' not in title: + try: + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + except: pass + + assert "spk mismatch" in story + press_select() # exit + + # now correct way via miniscript wallet + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item("msc2") + pick_menu_item("Sign PSBT") + title, story = cap_story() + if way == "nfc": + if "import via NFC" not in story: + raise pytest.skip("NFC disabled") + + press_nfc() + nfc_write_text(psbt) + time.sleep(1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "msc2" in story + press_select() # confirm signing + time.sleep(0.1) + got = nfc_read() + time.sleep(1) + assert got + press_cancel() # exit NFC loop + + elif way == "qr": + if "scan QR code" not in story: + raise pytest.skip("Mk4 no QR") + + need_keypress(KEY_QR) + # base64 PSBT as text + actual_vers, parts = split_qrs(psbt, 'U', max_version=20) + random.shuffle(parts) + + for p in parts: + scan_a_qr(p) + time.sleep(1) # just so we can watch + + title, story = cap_story() + assert title == "OK TO SEND?" + assert "msc2" in story + press_select() # confirm signing + time.sleep(.2) + file_type, rb = readback_bbqr() + assert file_type == 'P' + press_cancel() + else: + if way == "sd": + assert "Press (1)" in story + need_keypress("1") + else: + assert way == "vdisk" + if "import from Virtual Disk" not in story: + raise pytest.skip("Virtual Disk disabled") + + fpath = virtdisk_path(fname) + garbage_collector.append(fpath) + with open(fpath, "w") as f: + f.write(psbt) + + need_keypress("2") + + title, story = cap_story() + if 'OK TO SEND' not in title: + pick_menu_item(fname) + time.sleep(0.1) + title, story = cap_story() + + assert title == "OK TO SEND?" + assert "msc2" in story + press_select() # confirm signing + time.sleep(0.1) + title, story = cap_story() + assert title == 'PSBT Signed' + + +@pytest.mark.parametrize("desc", CHANGE_BASED_DESCS) +@pytest.mark.parametrize("way", ["usb", "sd", "vdisk", "nfc", "qr"]) +def test_bip388_policies(desc, way, offer_minsc_import, press_select, pick_menu_item, goto_home, + clear_miniscript, microsd_path, virtdisk_path, garbage_collector, + need_keypress, cap_story, load_export, press_cancel, usb_miniscript_get, + skip_if_useless_way, scan_a_qr, press_nfc, nfc_write_text, + usb_miniscript_policy): + + skip_if_useless_way(way) + clear_miniscript() + title, story = offer_minsc_import(json.dumps(dict(name="msc1", desc=desc))) + assert "msc1" in story + assert "Create new miniscript wallet?" in story + press_select() + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item("msc1") + pick_menu_item("Descriptors") + pick_menu_item("BIP-388 Policy") + + if way == "usb": + contents = usb_miniscript_policy("msc1") + else: + contents = load_export(way, "BIP-388 Wallet Policy", is_json=True) + press_cancel() + press_cancel() + if way != "nfc": + press_cancel() + + pick_menu_item("Import") + + # try import - must raise duplicate + new_name = "b388_reimport" + # change name - make it harder + contents["name"] = new_name + to_import = json.dumps(contents) + + if way == "nfc": + press_nfc() + nfc_write_text(to_import) + time.sleep(1) + title, story = cap_story() + assert "Duplicate wallet. Wallet 'msc1' is the same." in story + assert "b388_reimport" in story + press_cancel() + + clear_miniscript() + pick_menu_item("Import") + press_nfc() + + nfc_write_text(to_import) + time.sleep(1) + + elif way == "qr": + need_keypress(KEY_QR) + # base64 PSBT as text + actual_vers, parts = split_qrs(to_import, 'U', max_version=20) + random.shuffle(parts) + + for p in parts: + scan_a_qr(p) + time.sleep(1) # just so we can watch + + title, story = cap_story() + assert "Duplicate wallet. Wallet 'msc1' is the same." in story + assert "b388_reimport" in story + press_cancel() + + clear_miniscript() + pick_menu_item("Import") + need_keypress(KEY_QR) + + for p in parts: + scan_a_qr(p) + time.sleep(1) # just so we can watch + + elif way == "usb": + goto_home() + title, story = offer_minsc_import(to_import) + assert "Duplicate wallet. Wallet 'msc1' is the same." in story + assert "b388_reimport" in story + press_cancel() + + clear_miniscript() + offer_minsc_import(to_import) + + else: + path_f = microsd_path if way == "sd" else virtdisk_path + fname = "b388_reimport.json" + fpath = path_f(fname) + garbage_collector.append(fpath) + with open(path_f(fname), "w") as f: + f.write(to_import) + + if way == "sd": + assert "Press (1)" in story + need_keypress("1") + else: + assert way == "vdisk" + if "import from Virtual Disk" not in story: + raise pytest.skip("Virtual Disk disabled") + + need_keypress("2") + + # try to import duplicate + time.sleep(.1) + pick_menu_item(fname) + time.sleep(.1) + title, story = cap_story() + assert "Duplicate wallet. Wallet 'msc1' is the same." in story + assert "b388_reimport" in story + + press_cancel() + # now clear imported miniscript and import + clear_miniscript() + pick_menu_item("Import") + need_keypress("1" if way == "sd" else "2") + time.sleep(.1) + pick_menu_item(fname) + + + time.sleep(.1) + title, story = cap_story() + assert "Duplicate wallet" not in story + assert "Create new miniscript wallet?" in story + assert "b388_reimport" in story + press_select() + + # verify that the descriptor matches + assert usb_miniscript_get(new_name)["desc"].split("#")[0] == desc.split("#")[0].replace("'", 'h') + + +def test_miniscript_rename(offer_minsc_import, clear_miniscript, press_select, goto_home, + pick_menu_item, enter_complex, cap_menu, cap_screen, is_q1, + need_keypress, press_cancel): + clear_miniscript() + name = "old_name" + title, story = offer_minsc_import(json.dumps(dict(name=name, desc=CHANGE_BASED_DESCS[0]))) + assert "old_name" in story + assert "Create new miniscript wallet?" in story + press_select() + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(name) + pick_menu_item("Rename") + if is_q1: + # old name is filled in input field + # same for Mk4, just not possible with cap_screen, or cap_story + time.sleep(.1) + scr = cap_screen() + assert name in scr + + new_name = 35 * "0" + # first delete old one + for _ in range(len(name) - (0 if is_q1 else 1)): + need_keypress(KEY_DELETE if is_q1 else "x") + + if is_q1: + # attempt to use empty string as a name + # on Mk4 it is not possible to not have at least one char + press_select() + time.sleep(.1) + scr = cap_screen() + assert "Need 1" in scr + + # it is not possible to input more than 30 characters + enter_complex(new_name, apply=False, b39pass=False) + + real_name = new_name[:30] + + # specific wallet menu has changed + time.sleep(.1) + m = cap_menu() + assert name not in m + assert real_name == m[0] + + # miniscript wallets menu has changed + press_cancel() # one back + + time.sleep(.1) + m = cap_menu() + assert name not in m + assert real_name == m[0] + + +def test_legacy_sh_miniscript(offer_minsc_import, press_select, create_core_wallet, clear_miniscript): + clear_miniscript() + desc = ("sh(" + "or_d(pk([0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*)," + "and_v(" + "v:pkh([0f056943/84'/1'/9']tpubDC7jGaaSE66QBAcX8TUD3JKWari1zmGH4gNyKZcrfq6NwCofKujNF2kyeVXgKshotxw5Yib8UxLrmmCmWd8NVPVTAL8rGfMdc7TsAKqsy6y/<0;1>/*)," + "older(5))))") + + name = "legacy_sh_msc" + with pytest.raises(Exception) as e: + offer_minsc_import(json.dumps(dict(name=name, desc=desc))) + + assert "Miniscript in legacy P2SH not allowed" in str(e) + + +@pytest.mark.parametrize("lock", [ + ("older", 0), + ("after", 0), + ("older", 65536), + ("after", 2147483648), + # time-based relative locks + ("older", 4194304), + ("older", 4259840), +]) +def test_timelocks_without_consesnsus_meaning(lock, clear_miniscript, goto_home, get_cc_key, + offer_minsc_import, press_select): + goto_home() + clear_miniscript() + policy = "and_v(v:pk(@0/<0;1>/*),locktime())" + + # not allowed to import on CC + _type, val = lock + to_replace = f"{_type}({val})" + + policy = policy.replace("locktime()", to_replace) + + tmplt = f"wsh({policy})" + + cc_key = get_cc_key("m/88h/0h/0h",).replace('/<0;1>/*', '') + desc = tmplt.replace("@0", cc_key) + + wname = "locks_oob" + + with pytest.raises(Exception) as e: + offer_minsc_import(json.dumps(dict(name=wname, desc=desc))) + + if _type == "older": + if val & (1 << 22): + what = "Time-based " + x = 4194305 + y = 4259839 + else: + what = "Block-based " + x = 1 + y = (2**16)-1 + else: + what = "" + x = 1 + y = (2**31)-1 + + assert f"{what}{lock[0]} out of range [{x}, {y}]" in e.value.args[0] + press_select() + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("taproot", [True, False]) +def test_thresh_with_multiple_rel_locks(taproot, get_cc_key, create_core_wallet, offer_minsc_import, + cap_story, press_select, clear_miniscript, start_sign, + end_sign, bitcoind): + + clear_miniscript() + # we do not know private keys to our co-signer keys + # but that is the whole point, we let both locks expire & then we can sign alone + tmplt = (f"thresh(" + f"3," + f"pk({get_cc_key('m/48h/1h/0h/3h')})," + f"s:pk([30afbe54/48h/1h/0h/3h]tpubDFLVv7cuiLjn3QcsCend5kn3yw5sx6Czazy7hZvdGX61v8pkU95k2Byz9M5jnabzeUg7qWtHYLeKQyCWWAHhUmQQMeZ4Dee2CfGR2TsZqrN/<0;1>/*)," + f"s:pk([b7fe820c/48h/1h/0h/3h]tpubDFdQ1sNV53TbogAMPEd2egY5NXfbdKD1Mnr2iBrJrcwRHJbKC7tuuUMHT8SSHJ2VEKdCf5WYBMfevvWCnyJV53gYUT2wFyxEV8SuUTedBp7/<0;1>/*)," + f"snl:older(10)," + f"snl:older(20))") + + if taproot: + ik = ranged_unspendable_internal_key() + desc = f"tr({ik},{tmplt})" + af = "bech32m" + else: + af = "bech32" + desc = f"wsh({tmplt})" + + wname = "double_lock" + title, story = offer_minsc_import(json.dumps(dict(name=wname, desc=desc))) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + + wo = create_core_wallet(wname, af) + + unspent = wo.listunspent() + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "sequence": 20} + psbt = wo.walletcreatefundedpsbt([inp], [{bitcoind.supply_wallet.getnewaddress(): 1.0}], + 0, {"fee_rate": 2})["psbt"] + + start_sign(base64.b64decode(psbt), miniscript=wname) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story # not consolidation tx + assert wname in story + final_psbt = end_sign(accept=True) + + fin_res = wo.finalizepsbt(base64.b64encode(final_psbt).decode()) + assert fin_res["complete"] + + tx_hex = fin_res["hex"] + res = wo.testmempoolaccept([tx_hex]) + # timelocked + assert not res[0]["allowed"] + assert res[0]["reject-reason"] == 'non-BIP68-final' + + # 20 is the highest of the 2 locks - release by minig + bitcoind.supply_wallet.generatetoaddress(20, bitcoind.supply_wallet.getnewaddress()) # mine above + + fin_res = wo.finalizepsbt(base64.b64encode(final_psbt).decode()) + assert fin_res["complete"] + + tx_hex = fin_res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 + +# EOF \ No newline at end of file diff --git a/testing/test_msg.py b/testing/test_msg.py index 8540f6ad5..332010f28 100644 --- a/testing/test_msg.py +++ b/testing/test_msg.py @@ -2,13 +2,40 @@ # # Message signing. # -import pytest, time, os, itertools, hashlib +import pytest, time, os, itertools, hashlib, json from bip32 import BIP32Node from msg import verify_message, RFC_SIGNATURE_TEMPLATE, sign_message, parse_signed_message from base64 import b64encode, b64decode from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError, CCUserRefused from ckcc_protocol.constants import * from constants import addr_fmt_names, msg_sign_unmap_addr_fmt +from charcodes import KEY_QR, KEY_NFC +from helpers import addr_from_display_format + + +def addr_fmt_from_subpath(subpath): + if not subpath: + af = AF_CLASSIC + elif subpath[:4] == "m/84": + af = AF_P2WPKH + elif subpath[:4] == "m/49": + af = AF_P2WPKH_P2SH + else: + af = AF_CLASSIC + return af + +def default_derivation_by_af(addr_fmt, testnet=True): + b44ct = "1" if testnet else "0" + if addr_fmt == AF_CLASSIC: + path = "m/44h/{chain}h/0h/0/0" + elif addr_fmt == AF_P2WPKH_P2SH: + path = "m/49h/{chain}h/0h/0/0" + elif addr_fmt == AF_P2WPKH: + path = "m/84h/{chain}h/0h/0/0" + else: + assert False, "unsupported address format" + + return path.format(chain=b44ct) @pytest.mark.parametrize('msg', [ 'aZ', 'hello', 'abc def eght', "x"*140, 'a'*240]) @@ -54,7 +81,199 @@ def test_sign_msg_refused(dev, press_cancel): done = dev.send_recv(CCProtocolPacker.get_signed_msg(), timeout=None) -@pytest.mark.parametrize('path,expect', [ +@pytest.fixture +def verify_msg_sign_story(): + def doit(story, msg, subpath=None, addr_fmt=None, testnet=True, addr=None): + assert story.startswith('Ok to sign this?') + assert msg in story + assert 'Using the key associated' in story + + if addr: + assert addr == addr_from_display_format(story.split("\n\n")[2].split("\n")[-1]) + + if not subpath: + assert 'm =>' not in story + subpath = default_derivation_by_af(addr_fmt or AF_CLASSIC, testnet) + else: + subpath = subpath.lower().replace("'", "h") + + assert ('%s =>' % subpath) in story + return subpath + + return doit + + +@pytest.fixture +def msg_sign_export(cap_story, press_nfc, nfc_read_text, press_select, press_cancel, + readback_bbqr, cap_screen_qr, need_keypress, microsd_path, + virtdisk_path, is_q1, OK): + def doit(way, qr_only=False): + time.sleep(.1) + title, story = cap_story() + + if way == "sd": + if "Press (1) to save Signed Msg" in story: + need_keypress("1") + + elif way == "nfc": + if f"press {KEY_NFC if is_q1 else '(3)'} to share via NFC" not in story: + pytest.xfail("NFC disabled") + else: + press_nfc() + time.sleep(0.2) + signed_msg = nfc_read_text() + time.sleep(0.3) + press_cancel() + time.sleep(.1) + press_cancel() + + elif way == "qr": + if not is_q1: + pytest.xfail("QR disabled") + + if not qr_only: + need_keypress(KEY_QR) + + time.sleep(.1) + title, story = cap_story() + assert "Press ENTER to export signature QR only" in story + assert "(0) to export full RFC template" in story + press_select() + time.sleep(.1) + sig_only = cap_screen_qr().decode('ascii') + press_select() + time.sleep(.1) + need_keypress("0") + time.sleep(.1) + file_type, signed_msg = readback_bbqr() + signed_msg = signed_msg.decode() + assert file_type == "U" + assert sig_only in signed_msg + press_select() + press_cancel() + + else: + # virtual disk + if "press (2) to save to Virtual Disk" not in story: + pytest.xfail("Vdisk disabled") + else: + need_keypress("2") + + if way in ("sd", "vdisk"): + path_f = microsd_path if way == "sd" else virtdisk_path + time.sleep(.1) + title, story = cap_story() + fname = story.split("\n\n")[-1] + with open(path_f(fname), "r") as f: + signed_msg = f.read() + + return signed_msg + + return doit + + +@pytest.fixture +def sign_msg_from_text(pick_menu_item, enter_number, press_select, + cap_story, need_keypress, settings_set, is_q1, + addr_vs_path, bitcoind, msg_sign_export, + verify_msg_sign_story, OK): + # used when signing note/passwords misc content + # used after simple text QR scan + # expects to start at menu which offers different single sig address formats + + def doit(msg, addr_fmt, acct, change, idx, way, chain="XTN", qr_only=False): + settings_set("chain", chain) + path = "m" + # pick address format from menu + if addr_fmt == AF_CLASSIC: + path += "/44h" + af_label = "Classic P2PKH" + elif addr_fmt == AF_P2WPKH: + path += "/84h" + af_label = "Segwit P2WPKH" + else: + path += "/49h" + af_label = "P2SH-Segwit" + + pick_menu_item(af_label) + + # chain - no user input - depends on current active settings + if chain == "BTC": + path += "/0h" + else: + path += "/1h" + + # pick account + if acct is None: + path += "/0h" + press_select() + else: + path += ("/%dh" % acct) + enter_number(acct) + + time.sleep(.1) + title, story = cap_story() + assert title == "Change?" + assert "Press (0) to use internal/change address" in story + assert f"{OK} to use external/receive address" in story + if change: + path += "/1" + need_keypress("0") + else: + path += "/0" + press_select() + + # index num + if idx is None: + path += "/0" + press_select() + else: + path += ("/%d" % idx) + enter_number(idx) + + time.sleep(.1) + title, story = cap_story() + path = verify_msg_sign_story(story, msg, path, addr_fmt, testnet=True if chain == "XTN" else False) + press_select() + + signed_msg = msg_sign_export(way, qr_only) + + ret_msg, addr, sig = parse_signed_message(signed_msg) + addr_vs_path(addr, path, addr_fmt, chain=chain) + assert verify_message(addr, sig, ret_msg) is True + if addr_fmt == AF_CLASSIC and chain == "XTN": + res = bitcoind.rpc.verifymessage(addr, sig, ret_msg) + assert res is True + + return doit + + +@pytest.fixture +def sign_msg_from_address(need_keypress, scan_a_qr, press_select, enter_complex, cap_story, + addr_vs_path, verify_msg_sign_story, msg_sign_export): + def doit(msg, addr, subpath, addr_fmt, way=None, chain="XTN"): + if way == 'qr': + # scan text via QR + need_keypress(KEY_QR) + scan_a_qr(msg) + time.sleep(1) + press_select() + else: + enter_complex(msg, b39pass=False) + + time.sleep(.1) + title, story = cap_story() + verify_msg_sign_story(story, msg, subpath, addr_fmt, chain=="XTN", addr) + press_select() + time.sleep(.1) + signed_msg = msg_sign_export(way) + ret_msg, addr, sig = parse_signed_message(signed_msg) + addr_vs_path(addr, subpath, addr_fmt, chain=chain) + + return doit + + +@pytest.mark.parametrize('path,expect', [ ('1/1hard/2', 'invalid characters'), ('m/m/m/1/1hard/2', 'invalid characters'), ('m/', 'empty path component'), @@ -78,24 +297,36 @@ def test_bad_paths(dev, path, expect): @pytest.fixture def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home, - press_select, microsd_path): + press_select, microsd_path, verify_msg_sign_story): # sign a file on the microSD card - def doit(msg, subpath=None, addr_fmt=None, expect_fail=False): - fname = 't-msgsign.txt' + def doit(msg, subpath="", addr_fmt=None, expect_fail=False, testnet=True, + use_json=False): + + suffix = "json" if use_json else "txt" + fname = f't-msgsign.{suffix}' result_fname = 't-msgsign-signed.txt' # cleanup try: os.unlink(microsd_path(result_fname)) except OSError: pass + with open_microsd(fname, 'wt') as sd: - sd.write(msg + '\n') - if subpath is not None: - sd.write(subpath + '\n') - if addr_fmt is not None: - sd.write(addr_fmt_names[addr_fmt] + '\n') + if use_json: + res = {"msg": msg} + if subpath: + res["subpath"] = subpath + if addr_fmt is not None: + res["addr_fmt"] = addr_fmt_names[addr_fmt] + sd.write(json.dumps(res)) + else: + sd.write(msg + '\n') + if subpath or addr_fmt: + sd.write((subpath or "") + '\n') + if addr_fmt is not None: + sd.write(addr_fmt_names[addr_fmt]) goto_home() pick_menu_item('Advanced/Tools') @@ -115,17 +346,8 @@ def doit(msg, subpath=None, addr_fmt=None, expect_fail=False): assert not story.startswith('Ok to sign this?') return story - assert story.startswith('Ok to sign this?') - - assert msg in story - assert 'Using the key associated' in story - if not subpath: - assert 'm =>' in story - else: - x_subpath = subpath.lower().replace("'", "h") - assert ('%s =>' % x_subpath) in story - - press_select() + verify_msg_sign_story(story, msg, subpath, addr_fmt, testnet) + press_select() # confirm msg sign # wait for it to finish for r in range(10): @@ -135,26 +357,23 @@ def doit(msg, subpath=None, addr_fmt=None, expect_fail=False): else: assert False, 'timed out' - lines = [i.strip() for i in open_microsd(result_fname, 'rt').readlines()] - - assert lines[0] == '-----BEGIN BITCOIN SIGNED MESSAGE-----' - assert lines[1:-4] == [msg] - assert lines[-4] == '-----BEGIN BITCOIN SIGNATURE-----' - addr = lines[-3] - sig = lines[-2] - assert lines[-1] == '-----END BITCOIN SIGNATURE-----' + with open_microsd(result_fname, 'rt') as f: + res = f.read() - return sig, addr + ret_msg, addr, sig = parse_signed_message(res) + assert ret_msg == msg + return sig, addr, msg return doit -@pytest.mark.parametrize('msg', [ 'ab', 'hello', 'abc def eght', "x"*140, 'a'*240]) +@pytest.mark.bitcoind # only for testnet and p2pkh +@pytest.mark.parametrize("use_json", [True, False]) +@pytest.mark.parametrize('msg', [ 'ab', 'abc def eght', 'a'*240]) @pytest.mark.parametrize('path', [ "m/84'/0'/22'", None, 'm', "m/1/2", - "m/1'/100'", 'm/23h/22h', ]) @pytest.mark.parametrize('addr_fmt', [ @@ -163,29 +382,54 @@ def doit(msg, subpath=None, addr_fmt=None, expect_fail=False): AF_CLASSIC, AF_P2WPKH_P2SH, ]) -def test_sign_msg_microsd_good(sign_on_microsd, msg, path, addr_vs_path, addr_fmt): - - if (path is None) and (addr_fmt is not None): - # must give path if addr fmt is to be specified - return +@pytest.mark.parametrize("testnet", [True, False]) +def test_sign_msg_microsd_good(sign_on_microsd, msg, path, addr_vs_path, + addr_fmt, testnet, settings_set, bitcoind, + use_json): + chain = "XTN" if testnet else "BTC" + settings_set("chain", chain) # cases we expect to work - sig, addr = sign_on_microsd(msg, path, addr_fmt) + sig, addr, ret_msg = sign_on_microsd(msg, path, addr_fmt, testnet=testnet, + use_json=use_json) + assert msg == ret_msg raw = b64decode(sig) assert 40 <= len(raw) <= 65 - if path is None: - path = 'm' + if addr_fmt is None: + addr_fmt = addr_fmt_from_subpath(path) + + if not path: + path = default_derivation_by_af(addr_fmt, testnet=testnet) # check expected addr was used - addr_vs_path(addr, path, addr_fmt) + addr_vs_path(addr, path, addr_fmt, chain=chain) assert verify_message(addr, sig, msg) is True + if addr_fmt == AF_CLASSIC and testnet: + res = bitcoind.rpc.verifymessage(addr, sig, ret_msg) + assert res is True @pytest.fixture -def sign_using_nfc(goto_home, pick_menu_item, nfc_write_text, cap_story): - def doit(body, expect_fail=True): +def sign_using_nfc(goto_home, pick_menu_item, nfc_write_text, cap_story, press_select, + nfc_read_text, addr_vs_path, press_cancel, OK, verify_msg_sign_story): + def doit(msg, subpath=None, addr_fmt=None, expect_fail=False, use_json=False, + testnet=True): + if use_json: + res = {"msg": msg} + if subpath: + res["subpath"] = subpath + if addr_fmt is not None: + res["addr_fmt"] = addr_fmt_names[addr_fmt] + body = json.dumps(res) + else: + body = msg + "\n" + if subpath or addr_fmt: + body += ((subpath or "") + '\n') + if addr_fmt is not None: + body += addr_fmt_names[addr_fmt] + goto_home() pick_menu_item('Advanced/Tools') pick_menu_item('NFC Tools') @@ -194,49 +438,113 @@ def doit(body, expect_fail=True): time.sleep(0.5) if expect_fail: return cap_story() - raise NotImplementedError + + if not addr_fmt: + addr_fmt = addr_fmt_from_subpath(subpath) + + if not subpath: + subpath = default_derivation_by_af(addr_fmt, testnet=testnet) + + _, story = cap_story() + subpath = verify_msg_sign_story(story, msg, subpath, addr_fmt, testnet) + press_select() + signed_msg = nfc_read_text() + if "BITCOIN SIGNED MESSAGE" not in signed_msg: + # missed it? again + signed_msg = nfc_read_text() + press_select() # exit NFC animation + pmsg, addr, sig = parse_signed_message(signed_msg) + assert pmsg == msg + addr_vs_path(addr, subpath, addr_fmt, chain="XTN" if testnet else "BTC") + assert verify_message(addr, sig, msg) is True + time.sleep(0.5) + press_select() + signed_msg_again = nfc_read_text() + assert signed_msg == signed_msg_again + press_cancel() # exit NFC animation + + return sig, addr, msg return doit -@pytest.mark.parametrize('msg,concern,no_file', [ - ('', 'too short', 0), # zero length not supported - ('a'*1000, 'too long', 1), # too big, won't even be offered as a file - ('a'*300, 'too long', 0), # too big - ('a'*241, 'too long', 0), # too big - ('hello%20sworld'%'', 'many spaces', 0), # spaces - ('hello%10sworld'%'', 'many spaces', 0), # spaces - ('hello%5sworld'%'', 'many spaces', 0), # spaces - ('test\ttest', "must be ascii printable", 0), - ('testêtest', "must be ascii printable", 0), + +@pytest.mark.bitcoind +@pytest.mark.parametrize("way", ["nfc", "sd"]) +@pytest.mark.parametrize("msg", ['test\ttest', "\n\n\tmsg\n\n\tsigning"]) +def test_sign_msg_with_ascii_non_printable_chars(msg, way, sign_on_microsd, addr_vs_path, + settings_set, bitcoind, sign_using_nfc): + # only works with the JSON format + settings_set("chain", "XTN") + if way == "sd": + sig, addr, ret_msg = sign_on_microsd(msg, "", None, use_json=True) + else: + sig, addr, ret_msg = sign_using_nfc(msg, "", None, use_json=True) + + assert ret_msg == msg + raw = b64decode(sig) + assert 40 <= len(raw) <= 65 + + addr_fmt = AF_CLASSIC + path = default_derivation_by_af(addr_fmt, testnet=True) + + # check expected addr was used + addr_vs_path(addr, path, addr_fmt) + assert verify_message(addr, sig, msg) is True + res = bitcoind.rpc.verifymessage(addr, sig, msg) + assert res is True + + +@pytest.mark.parametrize('msg,subpath,addr_fmt,concern,no_file,no_json', [ + ('', "m", AF_CLASSIC, 'too short', 0, 0), # zero length not supported + ('a'*1000, "m/1", AF_P2WPKH,'too long', 1, 0), # too big, won't even be offered as a file + ('a'*241, "m/400", AF_P2WPKH_P2SH, 'too long', 0, 0), # too big + ('hello%20sworld'%'', "m", AF_CLASSIC, 'many spaces', 0, 0), # spaces + ('hello%10sworld'%'', "m/1h/3h", AF_P2WPKH_P2SH, 'many spaces', 0, 0), # spaces + ('hello%5sworld'%'', "m", AF_CLASSIC, 'many spaces', 0, 0), # spaces + ("coinkite", "m", AF_P2WSH, "Unsupported address format: 'p2wsh'", 0, 0), # invalid address format + ("coinkite", "m", AF_P2WSH_P2SH, "Unsupported address format", 0, 0), # invalid address format + ("coinkite", " m", AF_P2TR, "Unsupported address format: 'p2tr'", 0, 0), # invalid address format + ("coinkite", "m/0/0/0/0/0/0/0/0/0/0/0/0/0", AF_CLASSIC, "too deep", 0, 0), # invalid path + ("coinkite", "m/0/0/0/0/0/q/0/0/0", AF_P2WPKH, "invalid characters in path", 0, 0), # invalid path + ("coinkite ", "m", AF_CLASSIC, "trailing space(s)", 0, 0), # invalid msg - trailing space + (" coinkite", "m", AF_P2WPKH_P2SH, "leading space(s)", 0, 0), # invalid msg - leading space + ('testêtest', "m", AF_P2WPKH, "must be ascii", 0, 0), + # below works only with the JSON format + ('test\ttest', "m", AF_CLASSIC, "must be ascii printable", 0, 1), ]) +@pytest.mark.parametrize("use_json", [True, False]) @pytest.mark.parametrize('transport', ['sd', 'usb', 'nfc']) -def test_sign_msg_fails(dev, sign_on_microsd, msg, concern, no_file, transport, sign_using_nfc, path='m/12/34'): - +def test_sign_msg_fails(dev, sign_on_microsd, msg, subpath, addr_fmt, concern, + no_file, no_json, transport, sign_using_nfc, use_json): + if use_json and no_json: + # special cases with ascii non printable characters - can be present in json + raise pytest.skip("json can contain ASCII non-printable in msg") if transport == 'usb': with pytest.raises(CCProtoError) as ee: try: encoded_msg = msg.encode('ascii') except UnicodeEncodeError: encoded_msg = msg.encode() - dev.send_recv(CCProtocolPacker.sign_message(encoded_msg, path), timeout=None) + dev.send_recv(CCProtocolPacker.sign_message(encoded_msg, subpath, addr_fmt), timeout=None) story = ee.value.args[0] elif transport == 'sd': try: - story = sign_on_microsd(msg, path, expect_fail=True) + story = sign_on_microsd(msg, subpath, addr_fmt, expect_fail=True, use_json=use_json) assert story.startswith('Problem: ') except AssertionError as e: if no_file: assert ("No suitable files found" in str(e)) or story == 'NO-FILE' return elif transport == 'nfc': - title, story = sign_using_nfc(msg, expect_fail=True) + title, story = sign_using_nfc(msg, subpath, addr_fmt, expect_fail=True, use_json=use_json) assert title == 'ERROR' or "Problem" in story else: raise ValueError(transport) assert concern in story -@pytest.mark.parametrize('msg,num_iter,expect', [ + +@pytest.mark.parametrize('msg,num_iter,expect', [ ('Test2', 1, 'IHra0jSywF1TjIJ5uf7IDECae438cr4o3VmG6Ri7hYlDL+pUEXyUfwLwpiAfUQVqQFLgs6OaX0KsoydpuwRI71o='), ('Test', 2, 'IDgMx1ljPhLHlKUOwnO/jBIgK+K8n8mvDUDROzTgU8gOaPDMs+eYXJpNXXINUx5WpeV605p5uO6B3TzBVcvs478='), ('Test1', 3, 'IEt/v9K95YVFuRtRtWaabPVwWOFv1FSA/e874I8ABgYMbRyVvHhSwLFz0RZuO87ukxDd4TOsRdofQwMEA90LCgI='), @@ -280,32 +588,16 @@ def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet, assert sig == expect -@pytest.mark.parametrize("body", [ - "coinkite\nm\np2wsh", # invalid address format - "coinkite\nm\np2sh-p2wsh", # invalid address format - "coinkite\nm\np2tr", # invalid address format - "coinkite\nm/0/0/0/0/0/0/0/0/0/0/0/0/0\np2pkh", # invalid path - "coinkite\nm/0/0/0/0/0/q/0/0/0\np2pkh", # invalid path - "coinkite yes!\nm\np2pkh", # invalid msg - too many spaces - "c\nm\np2pkh", # invalid msg - too short - "coinkite \nm\np2pkh", # invalid msg - trailing space - " coinkite\nm\np2pkh", # invalid msg - leading space -]) -def test_nfc_msg_signing_invalid(body, goto_home, pick_menu_item, nfc_write_text, cap_story): - goto_home() - pick_menu_item('Advanced/Tools') - pick_menu_item('NFC Tools') - pick_menu_item('Sign Message') - nfc_write_text(body) - time.sleep(0.5) - title, story = cap_story() - assert title == 'ERROR' or "Problem" in story -@pytest.mark.parametrize("msg", ["coinkite", "Coldcard Signing Device!", 200 * "a"]) -@pytest.mark.parametrize("path", ["", "m/84'/0'/0'/300/0", "m/800h/0h", "m/0/0/0/0/1/1/1"]) -@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"]) -def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item, - goto_home, cap_story, press_select, press_cancel, addr_vs_path, OK): +@pytest.mark.bitcoind # only for testnet and p2pkh +@pytest.mark.parametrize("testnet", [True, False]) +@pytest.mark.parametrize("use_json", [True, False]) +@pytest.mark.parametrize("msg", ["Coldcard Signing Device!", 200 * "a"]) +@pytest.mark.parametrize("path", ["", "m/84h/0h/0h/300/0", "m/0/0/0/0/1/1/1"]) +@pytest.mark.parametrize("addr_fmt", [AF_CLASSIC, None, AF_P2WPKH, AF_P2WPKH_P2SH]) +def test_nfc_msg_signing(msg, path, addr_fmt, testnet, settings_set, bitcoind, use_json, + sign_using_nfc, goto_home): + settings_set("chain", "XTN" if testnet else "BTC") for _ in range(5): # need to wait for ApproveMessageSign to be popped from ux stack @@ -315,43 +607,14 @@ def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text, except: time.sleep(0.5) - pick_menu_item('Advanced/Tools') - pick_menu_item('NFC Tools') - pick_menu_item('Sign Message') - if str_addr_fmt != "": - addr_fmt = msg_sign_unmap_addr_fmt[str_addr_fmt] - body = "\n".join([msg, path, str_addr_fmt]) - else: - addr_fmt = AF_CLASSIC - body = "\n".join([msg, path]) - - nfc_write_text(body) - time.sleep(0.5) - _, story = cap_story() - assert "Ok to sign this?" in story - assert msg in story - assert path.replace("'", "h") in story - press_select() - signed_msg = nfc_read_text() - if "BITCOIN SIGNED MESSAGE" not in signed_msg: - # missed it? again - signed_msg = nfc_read_text() - press_select() # exit NFC animation - pmsg, addr, sig = parse_signed_message(signed_msg) - assert pmsg == msg - addr_vs_path(addr, path, addr_fmt) - assert verify_message(addr, sig, msg) is True - time.sleep(0.5) - _, story = cap_story() - assert f"Press {OK} to share again" in story - press_select() - signed_msg_again = nfc_read_text() - assert signed_msg == signed_msg_again - press_cancel() # exit NFC animation - press_cancel() # do not want to share again + addr, sig, ret_msg = sign_using_nfc(msg, path, addr_fmt, testnet=testnet, use_json=use_json) + assert msg == ret_msg + if addr_fmt == AF_CLASSIC and testnet: + res = bitcoind.rpc.verifymessage(sig, addr, ret_msg) + assert res is True @pytest.fixture -def verify_armored_signature(pick_menu_item, nfc_write_text, press_select, +def verify_armored_signature(pick_menu_item, nfc_write_text, cap_story, goto_home): def doit(way, fname=None, signed_msg=None): goto_home() @@ -383,7 +646,8 @@ def test_verify_signature_file(way, addr_fmt, path, msg, sign_on_microsd, goto_h cap_story, bitcoind, microsd_path, nfc_write_text, verify_armored_signature, chain, settings_set): settings_set("chain", chain) - sig, addr = sign_on_microsd(msg, path, msg_sign_unmap_addr_fmt[addr_fmt]) + sig, addr, ret_msg = sign_on_microsd(msg, path, msg_sign_unmap_addr_fmt[addr_fmt]) + assert ret_msg == msg fname = 't-msgsign-signed.txt' should = RFC_SIGNATURE_TEMPLATE.format(addr=addr, sig=sig, msg=msg) with open(microsd_path(fname), "r") as f: @@ -392,7 +656,7 @@ def test_verify_signature_file(way, addr_fmt, path, msg, sign_on_microsd, goto_h title, story = verify_armored_signature(way, fname, should) assert title == "CORRECT" assert "Good signature" in story - assert addr in story + assert addr == addr_from_display_format(story.split("\n")[-1]) if (addr_fmt == "p2pkh") and (chain != "BTC"): res = bitcoind.rpc.verifymessage(addr, sig, msg) assert res is True @@ -669,6 +933,112 @@ def test_verify_signature_file_truncated(way, microsd_path, cap_story, verify_ar else: assert title == "FAILURE" assert "Armor text MUST be surrounded by exactly five (5) dashes" in story - assert "auth.py" in story + + +@pytest.mark.parametrize("msg", ["this is the message to sign", "this is meessage to sign\n with newline", "a"*200]) +@pytest.mark.parametrize("addr_fmt", [AF_CLASSIC, AF_P2WPKH]) +@pytest.mark.parametrize("acct", [None, 5555]) +def test_sign_scanned_text(msg, addr_fmt, acct, goto_home, need_keypress, scan_a_qr, + sign_msg_from_text, cap_story, skip_if_useless_way): + skip_if_useless_way("qr") + goto_home() + need_keypress(KEY_QR) + scan_a_qr(msg) + time.sleep(1) + title, story = cap_story() + assert title == "Simple Text" + assert "Press (0) to sign the text" in story + need_keypress("0") + sign_msg_from_text(msg, addr_fmt, acct, False, 999, "qr", "XTN", True) + + +@pytest.mark.parametrize("data", [ + {"msg": "msg to be signed via QR"}, + {"msg": "msg with some\n\t\n control characters", "addr_fmt": "p2sh-p2wpkh"}, + {"msg": 100*"CC", "addr_fmt": "p2wpkh", "subpath": "m/900h/0"}, + {"msg": "This is my address! @twiiter_nick", "subpath": "m/84h/1h/0h/0/0"}, + {"msg": "This is my address! @twiiter_nick", "subpath": "m/49'/0'/5'/1/100"}, +]) +@pytest.mark.parametrize("way", ["sd", "nfc", "qr"]) +def test_sign_scanned_json(data, way, goto_home, need_keypress, scan_a_qr, + cap_story, msg_sign_export, press_select, + addr_vs_path, bitcoind, skip_if_useless_way, + verify_msg_sign_story): + skip_if_useless_way(way) + goto_home() + af = data.get("addr_fmt", None) + if not af: + addr_fmt = addr_fmt_from_subpath(data.get("subpath", None)) + else: + addr_fmt = msg_sign_unmap_addr_fmt[af] + + need_keypress(KEY_QR) + scan_a_qr(json.dumps(data)) + time.sleep(1) + title, story = cap_story() + + subpath = verify_msg_sign_story(story, data["msg"], data.get("subpath", None), addr_fmt) + press_select() + + signed_msg = msg_sign_export(way) + ret_msg, addr, sig = parse_signed_message(signed_msg) + assert ret_msg == data["msg"] + # check expected addr was used + addr_vs_path(addr, subpath, addr_fmt) + assert verify_message(addr, sig, ret_msg) is True + if addr_fmt == AF_CLASSIC: + res = bitcoind.rpc.verifymessage(addr, sig, ret_msg) + assert res is True + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("msg", ["an an an an an an an an", 240*"a"]) +@pytest.mark.parametrize("path", ["m/84h/0", "m/44h/0", "m/49h/0", "m"]) +def test_sparrow_qr_sign_msg(msg, path, skip_if_useless_way, need_keypress, scan_a_qr, cap_story, + verify_msg_sign_story, press_select, msg_sign_export, addr_vs_path, + bitcoind): + skip_if_useless_way("qr") + + tmplt = "signmessage %s ascii:%s" + data = tmplt % (path, msg) + + addr_fmt = addr_fmt_from_subpath(path) + + need_keypress(KEY_QR) + scan_a_qr(data) + time.sleep(1) + title, story = cap_story() + subpath = verify_msg_sign_story(story, msg, path, addr_fmt) + press_select() + + signed_msg = msg_sign_export("qr") + ret_msg, addr, sig = parse_signed_message(signed_msg) + assert ret_msg == msg + # check expected addr was used + addr_vs_path(addr, subpath, addr_fmt) + assert verify_message(addr, sig, ret_msg) is True + if addr_fmt == AF_CLASSIC: + res = bitcoind.rpc.verifymessage(addr, sig, ret_msg) + assert res is True + + +@pytest.mark.parametrize("msg", [(50*"a")+"\n\n"+(100*"b"), "Balance replenish 564565456254"]) +def test_verify_scanned_signed_msg(msg, scan_a_qr, need_keypress, goto_home, cap_story, + skip_if_useless_way): + skip_if_useless_way("qr") + wallet = BIP32Node.from_master_secret(os.urandom(32)) + addr = wallet.address() + sk = bytes(wallet.node.private_key) + sig = sign_message(sk, msg.encode()) + armored = RFC_SIGNATURE_TEMPLATE.format(addr=addr, sig=sig, msg=msg) + + goto_home() + need_keypress(KEY_QR) + scan_a_qr(armored) + time.sleep(1) + title, story = cap_story() + assert title == "CORRECT" + assert "Good signature by address" in story + assert addr == addr_from_display_format(story.split("\n")[-1]) # EOF diff --git a/testing/test_multisig.py b/testing/test_multisig.py index fafa0fdd9..1c74013e3 100644 --- a/testing/test_multisig.py +++ b/testing/test_multisig.py @@ -6,17 +6,14 @@ # # py.test test_multisig.py -m ms_danger --ms-danger # -import sys -sys.path.append("../shared") -from descriptor import MultisigDescriptor, append_checksum, MULTI_FMT_TO_SCRIPT, parse_desc_str import time, pytest, os, random, json, shutil, pdb, io, base64, struct, bech32, itertools, re from psbt import BasicPSBT, BasicPSBTInput, BasicPSBTOutput from ckcc.protocol import CCProtocolPacker, MAX_TXN_LEN from pprint import pprint from base64 import b64encode, b64decode from base58 import encode_base58_checksum -from helpers import B2A, fake_dest_addr, xfp2str, detruncate_address -from helpers import path_to_str, str_to_path, slip132undo, swab32, hash160 +from helpers import B2A, fake_dest_addr, xfp2str, addr_from_display_format +from helpers import path_to_str, str_to_path, slip132undo, swab32, hash160, bitcoind_addr_fmt from struct import unpack, pack from constants import * from bip32 import BIP32Node @@ -24,7 +21,8 @@ from io import BytesIO from hashlib import sha256 from bbqr import split_qrs -from charcodes import KEY_QR +from descriptor import MULTI_FMT_TO_SCRIPT, MultisigDescriptor, parse_desc_str +from charcodes import KEY_QR, KEY_DELETE def HARD(n=0): @@ -45,26 +43,8 @@ def str2ipath(s): yield here -@pytest.fixture(scope='function') -def has_ms_checks(request, sim_exec): - # Add this fixture to any test that should FAIL if ms checks are disabled - # - in other words, tests that test the checks which are disabled. - # - still need to run w/ --ms-danger flag set to test those cases - # - also mark testcase with ms_danger - danger_mode = (request.config.getoption('--ms-danger')) - if danger_mode: - print("Enabling multisig danger mode") - - request.node.add_marker(pytest.mark.xfail(True, strict=True, - reason="check was bypassed, so testcase should fail")) - - sim_exec(f'from multisig import MultisigWallet; MultisigWallet.disable_checks={danger_mode}') - - return danger_mode - - -@pytest.fixture() +@pytest.fixture def bitcoind_p2sh(bitcoind): # Use bitcoind to generate a p2sh addres based on public keys. @@ -86,13 +66,6 @@ def doit(M, pubkeys, fmt): return doit - -@pytest.fixture -def clear_ms(unit_test): - def doit(): - unit_test('devtest/wipe_ms.py') - return doit - @pytest.fixture def make_multisig(dev, sim_execfile): # make a multsig wallet, always with simulator as an element @@ -100,24 +73,32 @@ def make_multisig(dev, sim_execfile): # default is BIP-45: m/45'/... (but no co-signer idx) # - but can provide str format for deriviation, use {idx} for cosigner idx - def doit(M, N, unique=0, deriv=None, dev_key=False): + def doit(M, N, unique=0, deriv=None, dev_key=False, netcode="XTN"): + + if netcode == "XRT": + # makes no sense keys wise + netcode = "XTN" + + def _derive(master, origin_der, idx): + if origin_der == "m": + return master + + d = origin_der.format(idx=idx) if origin_der else "m/45h" + try: + child = master.subkey_for_path(d) + except IndexError: + # some test cases are using bogus paths + child = master + return child + keys = [] for i in range(N-1): - pk = BIP32Node.from_master_secret(b'CSW is a fraud %d - %d' % (i, unique), 'XTN') + pk = BIP32Node.from_master_secret(b'CSW is a fraud %d - %d' % (i, unique), netcode) xfp = unpack("I', xfp_bytes)[0]) else: - pk = BIP32Node.from_wallet_key(simulator_fixed_tprv) + pk = BIP32Node.from_wallet_key(simulator_fixed_tprv if netcode == "XTN" else simulator_fixed_xprv) xfp = simulator_fixed_xfp - if not deriv: - sub = pk.subkey_for_path("m/45h") - else: - path = deriv.format(idx=N-1) - try: - sub = pk.subkey_for_path(path) - except IndexError: - # some test cases are using bogus paths - sub = pk + dev_sim = _derive(pk, deriv, N-1) - keys.append((xfp, pk, sub)) + keys.append((xfp, pk, dev_sim)) return keys return doit @pytest.fixture -def offer_ms_import(cap_story, dev): - def doit(config, allow_non_ascii=False): - # upload the file, trigger import - file_len, sha = dev.upload_file(config.encode('utf-8' if allow_non_ascii else 'ascii')) - - open('debug/last-config.txt', 'wt').write(config) - - dev.send_recv(CCProtocolPacker.multisig_enroll(file_len, sha)) - - time.sleep(.2) - title, story = cap_story() - #print(repr(story)) - - return title, story - - return doit - -@pytest.fixture -def import_multisig(request, is_q1, need_keypress, offer_ms_import): - def doit(fname=None, way="sd", data=None, name=None): - assert fname or data - if fname: - if way == "sd": - microsd_path = request.getfixturevalue("microsd_path") - fpath = microsd_path(fname) - else: - virtdisk_path = request.getfixturevalue("virtdisk_path") - fpath = virtdisk_path(fname) - with open(fpath, 'r') as f: - config = f.read() - else: - config = data - if way is None: # USB - title, story = offer_ms_import(config) - else: - # only get those simulator related fixtures here, to be able to - # use this with real HW - cap_menu = request.getfixturevalue('cap_menu') - cap_story = request.getfixturevalue('cap_story') - goto_home = request.getfixturevalue('goto_home') - pick_menu_item = request.getfixturevalue('pick_menu_item') - - if "Skip Checks?" not in cap_menu(): - # we are not in multisig menu - goto_home() - pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") - time.sleep(.1) - - ms_menu = cap_menu() - if way == "qr": - if "Import from QR" not in ms_menu and not is_q1: - pytest.skip("No QR support") - - scan_a_qr = request.getfixturevalue('scan_a_qr') - pick_menu_item("Import from QR") - - actual_vers, parts = split_qrs(config, 'U', max_version=20) - random.shuffle(parts) - - for p in parts: - scan_a_qr(p) - time.sleep(2.0 / len(parts)) - - elif way == "nfc": - if "Import via NFC" not in ms_menu: - pytest.skip("NFC disabled") - - nfc_write_text = request.getfixturevalue('nfc_write_text') - pick_menu_item("Import via NFC") - nfc_write_text(config) - time.sleep(0.5) - - else: - assert way in ("sd", "vdisk") - if way == "sd": - path_f = request.getfixturevalue('microsd_path') - else: - path_f = request.getfixturevalue('virtdisk_path') - - if not fname: - fname = (name or "ms_wal.txt") + ".txt" - with open(path_f(fname), "w") as f: - f.write(config) - - pick_menu_item("Import from File") - time.sleep(.1) - _, story = cap_story() - if way == "vdisk": - if "(2) to import from Virtual Disk" not in story: - pytest.skip("VDisk disabled") - need_keypress("2") - else: - if "Press (1)" in story: - need_keypress("1") - - pick_menu_item(fname) - - time.sleep(.1) - title, story = cap_story() - return title, story - - return doit - -@pytest.fixture -def import_ms_wallet(dev, make_multisig, offer_ms_import, press_select, - is_q1, request, need_keypress, import_multisig, - settings_set): +def import_ms_wallet(dev, make_multisig, offer_minsc_import, press_select, + is_q1, request, need_keypress, usb_miniscript_get, + settings_set, sim_root_dir, import_miniscript): def doit(M, N, addr_fmt=None, name=None, unique=0, accept=False, common=None, - keys=None, do_import=True, derivs=None, descriptor=False, + keys=None, do_import=True, derivs=None, int_ext_desc=False, dev_key=False, way=None, bip67=True, - force_unsort_ms=True): - # param: bip67 if false, only usable together with descriptor=True - if not bip67: - assert descriptor, "needs descriptor=True" - - if (not bip67) and force_unsort_ms: - settings_set("unsort_ms", 1) + chain="XTN"): keys = keys or make_multisig(M, N, unique=unique, dev_key=dev_key, - deriv=common or (derivs[0] if derivs else None)) - name = name or f'test-{M}-{N}' + deriv=common or (derivs[0] if derivs else None), + netcode=chain) - if not do_import: - return keys + if addr_fmt is None: + addr_fmt = "p2wsh" - if descriptor: - if not derivs: - if not common: - common = "m/45h" - key_list = [(xfp, common, dd.hwif(as_private=False)) for xfp, m, dd in keys] - else: - assert len(derivs) == N - key_list = [(xfp, derivs[idx], dd.hwif(as_private=False)) for idx, (xfp, m, dd) in enumerate(keys)] - desc = MultisigDescriptor(M=M, N=N, keys=key_list, addr_fmt=addr_fmt, is_sorted=bip67) - if int_ext_desc: - desc_str = desc.serialize(int_ext=True) - else: - desc_str = desc.serialize() - config = "%s\n" % desc_str + if not derivs: + if not common: + common = "m/45h" + key_list = [(xfp, common, dd.hwif(as_private=False)) for xfp, m, dd in keys] else: - # render as a file for import - config = f"name: {name}\npolicy: {M} / {N}\n\n" - - if addr_fmt: - if isinstance(addr_fmt, int): - addr_fmt = addr_fmt_names[addr_fmt] - config += f'format: {addr_fmt.title()}\n' + assert len(derivs) == N + key_list = [(xfp, derivs[idx], dd.hwif(as_private=False)) + for idx, (xfp, m, dd) in enumerate(keys)] + + desc = MultisigDescriptor(M=M, N=N, keys=key_list, addr_fmt=addr_fmt, + is_sorted=bip67) + if int_ext_desc: + config = desc.serialize(int_ext=True) + else: + config = desc.serialize() - # not good enuf anymore, but maybe in some cases, just need one at top - if common: - config += f'derivation: {common}\n' + if name: + config = json.dumps({"name": name, "desc": config}) - if not derivs: - config += '\n'.join('%s: %s' % (xfp2str(xfp), dd.hwif(as_private=False)) - for xfp, m, dd in keys) - else: - # for cases where derivation of each leg is not same/simple - assert not common and len(derivs) == N - for idx, (xfp, m, dd) in enumerate(keys): - config += 'Derivation: %s\n%s: %s\n\n' % (derivs[idx], - xfp2str(xfp), dd.hwif(as_private=False)) + if not do_import: + return keys, config - #print(config) - open('debug/last-ms.txt', 'wt').write(config) + with open(f'{sim_root_dir}/debug/last-ms.txt', 'wt') as f: + f.write(config) - title, story = import_multisig(data=config, way=way) + title, story = import_miniscript(data=config, way=way) - assert 'Create new multisig' in story \ + assert 'Create new multisig wallet' in story \ or 'Update existing multisig wallet' in story \ or 'new wallet is similar to' in story - if descriptor is False: - # descriptors wallet does not have a name - assert name in story + + story_name = None + assert addr_fmt.upper() in story assert f'Policy: {M} of {N}\n' in story + if M == N == 1: + assert "The one signer must approve spends." in story + elif M == N: + assert f"All {N} co-signers must approve spends" in story + elif M == 1: + assert f"Any signature from {N} co-signers will approve spends" + else: + assert f"{M} signatures, from {N} possible co-signers, will be required to approve spends" in story + + for ll in story.split("\n\n"): + if ll.startswith("Wallet Name"): + story_name = ll.split("\n")[-1].strip() + + assert story_name + if name: + assert name == story_name + if accept: time.sleep(.1) press_select() - # Test it worked. time.sleep(.1) # required - xor = 0 - for xfp, _, _ in keys: - xor ^= xfp - assert dev.send_recv(CCProtocolPacker.multisig_check(M, N, xor)) == 1 + # below raises if miniscript wallet not enrolled + usb_miniscript_get(story_name) return keys @@ -340,62 +201,39 @@ def doit(M, N, addr_fmt=None, name=None, unique=0, accept=False, common=None, @pytest.mark.parametrize('N', [ 3, 15]) -def test_ms_import_variations(N, make_multisig, offer_ms_import, press_cancel, is_q1): +def test_ms_import_variations(N, offer_minsc_import, press_cancel, is_q1, get_cc_key): # all the different ways... - keys = make_multisig(N, N) - + my_key = get_cc_key(path="").replace("/<0;1>/*", "") + keys = [BIP32Node.from_master_secret(os.urandom(32), "XTN").hwif() for _ in range(N-1)] + keys = [my_key] + keys # bare, no fingerprints # - no xfps # - no meta data - config = '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) - title, story = offer_ms_import(config) + k0 = ','.join(keys) + title, story = offer_minsc_import(f"sh(multi({N},{k0}))") assert f'Policy: {N} of {N}\n' in story press_cancel() # exclude myself (expect fail) - config = '\n'.join(sk.hwif(as_private=False) - for xfp,m,sk in keys if xfp != simulator_fixed_xfp) - + k1 = ','.join(keys[1:]) with pytest.raises(BaseException) as ee: - title, story = offer_ms_import(config) - assert 'my key not included' in str(ee.value) - + title, story = offer_minsc_import(f"wsh(sortedmulti({N-1},{k1}))") + assert "My key 0F056943 missing in descriptor" in str(ee.value) + desc0 = f"wsh(sortedmulti({N},{k0}))" # normal names for name in [ 'Zy', 'Z'*20, 'Vault #3' ]: - config = f'name: {name}\n' - config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) - title, story = offer_ms_import(config) + title, story = offer_minsc_import(json.dumps({"name": name, "desc": desc0})) press_cancel() assert name in story # too long name - config = 'name: ' + ('A'*21) + '\n' - config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) + name = 'A' * 31 with pytest.raises(BaseException) as ee: - title, story = offer_ms_import(config) - assert '20 long' in str(ee.value) - - # comments, blank lines - config = [sk.hwif(as_private=False) for xfp,m,sk in keys] - for i in range(len(config)): - config.insert(i, '# comment') - config.insert(i, ' #') - config.insert(i, ' # ') - config.insert(i, ' # ') - config.insert(i, '') - title, story = offer_ms_import('\n'.join(config)) - assert f'Policy: {N} of {N}\n' in story - press_cancel() + title, story = offer_minsc_import(json.dumps({"name": name, "desc": desc0})) + assert 'name len' in str(ee.value) - # the different addr formats - for af in unmap_addr_fmt.keys(): - config = f'format: {af}\n' - config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) - title, story = offer_ms_import(config) - press_cancel() - assert f'Policy: {N} of {N}\n' in story def make_redeem(M, keys, path_mapper=None, violate_script_key_order=False, tweak_redeem=None, tweak_xfps=None, finalizer_hack=None, @@ -467,8 +305,8 @@ def make_redeem(M, keys, path_mapper=None, violate_script_key_order=False, def make_ms_address(M, keys, idx=0, is_change=0, addr_fmt=AF_P2SH, testnet=1, bip67=True, **make_redeem_args): # Construct addr and script need to represent a p2sh address - if 'path_mapper' not in make_redeem_args: - make_redeem_args['path_mapper'] = lambda cosigner: [HARD(45), cosigner, is_change, idx] + if not make_redeem_args.get('path_mapper'): + make_redeem_args['path_mapper'] = lambda cosigner: [HARD(45), is_change, idx] script, pubkeys, xfp_paths = make_redeem(M, keys, bip67=bip67, **make_redeem_args) @@ -495,47 +333,27 @@ def make_ms_address(M, keys, idx=0, is_change=0, addr_fmt=AF_P2SH, testnet=1, @pytest.fixture -def test_ms_show_addr(dev, cap_story, press_select, addr_vs_path, bitcoind_p2sh, - has_ms_checks, is_q1): - def doit(M, keys, addr_fmt=AF_P2SH, bip45=True, **make_redeem_args): +def test_ms_show_addr(dev, cap_story, press_select, bitcoind, is_q1, + usb_miniscript_addr, usb_miniscript_get): + def doit(name, idx=0, change=False): # test we are showing addresses correctly # - verifies against bitcoind as well - addr_fmt = unmap_addr_fmt.get(addr_fmt, addr_fmt) - - # make a redeem script, using provided keys/pubkeys - if bip45: - make_redeem_args['path_mapper'] = lambda i: [HARD(45), i, 0,0] - - scr, pubkeys, xfp_paths = make_redeem(M, keys, **make_redeem_args) - assert len(scr) <= 520, "script too long for standard!" - got_addr = dev.send_recv( - CCProtocolPacker.show_p2sh_address(M, xfp_paths, scr, addr_fmt=addr_fmt), - timeout=None - ) + got_addr = usb_miniscript_addr(name, idx, change) title, story = cap_story() - #print(story) - - if not has_ms_checks: - assert got_addr in story - assert all((xfp2str(xfp) in story) for xfp,_,_ in keys) - if bip45: - for i in range(len(keys)): - assert ('/_/%d/0/0' % i) in story - else: - assert 'UNVERIFIED' in story + assert got_addr == addr_from_display_format(story.split("\n\n")[0]) press_select() - # check expected addr was generated based on my math - addr_vs_path(got_addr, addr_fmt=addr_fmt, script=scr) - - # also check against bitcoind - core_addr, core_scr = bitcoind_p2sh(M, pubkeys, addr_fmt) - assert B2A(scr) == core_scr - assert core_addr == got_addr + # check against bitcoind + desc_obj = usb_miniscript_get(name) + ext_a, int_a = bitcoind.supply_wallet.deriveaddresses(desc_obj["desc"], [idx, idx]) + if change: + assert int_a[0] == got_addr + else: + assert ext_a[0] == got_addr return doit @@ -543,198 +361,207 @@ def doit(M, keys, addr_fmt=AF_P2SH, bip45=True, **make_redeem_args): @pytest.mark.bitcoind @pytest.mark.parametrize('m_of_n', [(1,3), (2,3), (3,3), (3,6), (10, 15), (15,15)]) @pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ]) -def test_import_ranges(m_of_n, use_regtest, addr_fmt, clear_ms, import_ms_wallet, test_ms_show_addr): +def test_import_ranges(m_of_n, use_regtest, addr_fmt, clear_miniscript, import_ms_wallet, + usb_miniscript_addr, test_ms_show_addr): use_regtest() M, N = m_of_n - keys = import_ms_wallet(M, N, addr_fmt, accept=1) + wname = "my_rand_wal" + import_ms_wallet(M, N, addr_fmt, name=wname, accept=True) #print("imported: %r" % [x for x,_,_ in keys]) try: # test an address that should be in that wallet. time.sleep(.1) - test_ms_show_addr(M, keys, addr_fmt=addr_fmt) + test_ms_show_addr(wname) finally: - clear_ms() + clear_miniscript() @pytest.mark.bitcoind @pytest.mark.ms_danger -def test_violate_bip67(clear_ms, use_regtest, import_ms_wallet, - test_ms_show_addr, has_ms_checks, - fake_ms_txn, try_sign): +def test_violate_bip67(clear_miniscript, use_regtest, import_ms_wallet, + test_ms_show_addr, sim_root_dir, try_sign, + fake_ms_txn): # detect when pubkeys are not in order in the redeem script - clear_ms() + clear_miniscript() M, N = 1, 15 keys = import_ms_wallet(M, N, accept=True) - # test an address that should be in that wallet. - time.sleep(.1) - with pytest.raises(BaseException) as ee: - test_ms_show_addr(M, keys, violate_script_key_order=True) - assert 'BIP-67' in str(ee.value) - psbt = fake_ms_txn(1, 3, M, keys, outstyles=ADDR_STYLES_MS, change_outputs=[1], violate_script_key_order=True) - with open('debug/last.psbt', 'wb') as f: + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: f.write(psbt) with pytest.raises(Exception) as e: try_sign(psbt) - assert 'BIP-67' in e.value.args[0] + assert 'spk mismatch' in e.value.args[0] @pytest.mark.parametrize("has_change", [True, False]) -def test_violate_import_order_multi(has_change, clear_ms, import_ms_wallet, - fake_ms_txn, try_sign, test_ms_show_addr): - clear_ms() +def test_violate_import_order_multi(has_change, clear_miniscript, import_ms_wallet, + fake_ms_txn, try_sign, test_ms_show_addr, + sim_root_dir): + clear_miniscript() M, N = 3, 5 - keys = import_ms_wallet(M, N, accept=True, descriptor=True, bip67=False) + keys = import_ms_wallet(M, N, accept=True, bip67=False) time.sleep(.1) - with pytest.raises(BaseException) as ee: - test_ms_show_addr(M, keys, violate_script_key_order=True) - assert "script key order" in str(ee.value) psbt = fake_ms_txn(4, 2, M, keys, outstyles=ADDR_STYLES_MS, change_outputs=[1] if has_change else [], bip67=False, violate_script_key_order=True) - with open('debug/last.psbt', 'wb') as f: + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: f.write(psbt) with pytest.raises(Exception) as e: try_sign(psbt) - assert "script key order" in e.value.args[0] - - -@pytest.mark.bitcoind -@pytest.mark.parametrize('which_pubkey', [0, 1, 14]) -def test_bad_pubkey(has_ms_checks, use_regtest, clear_ms, import_ms_wallet, - test_ms_show_addr, which_pubkey): - # give incorrect pubkey inside redeem script - M, N = 1, 15 - keys = import_ms_wallet(M, N, accept=True) - - try: - # test an address that should be in that wallet. - time.sleep(.1) - def tweaker(scr): - # corrupt the pubkey - return bytes((s if i != (5 + (34*which_pubkey)) else s^0x1) for i,s in enumerate(scr)) - - with pytest.raises(BaseException) as ee: - test_ms_show_addr(M, keys, tweak_redeem=tweaker) - assert ('pk#%d wrong' % (which_pubkey+1)) in str(ee.value) - finally: - clear_ms() + assert "spk mismatch" in e.value.args[0] @pytest.mark.bitcoind @pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ]) -def test_zero_depth(clear_ms, use_regtest, addr_fmt, import_ms_wallet - , test_ms_show_addr, make_multisig): - # test having a co-signer with "m" only key ... ie. depth=0 - - M, N = 1, 2 - keys = make_multisig(M, N, unique=99) - - # censor first co-signer to look like a master key - from copy import deepcopy - kk = deepcopy(keys[0][1]) - kk.node.depth = 0 - kk.node.index = 0 - kk.node.parsed_parent_fingerprint = None - keys[0] = (keys[0][0], keys[0][1], kk) +@pytest.mark.parametrize('desc_type', ['multi', 'sortedmulti' ]) +def test_zero_depth(dev, clear_miniscript, use_regtest, addr_fmt, offer_minsc_import, + make_multisig, bitcoind, desc_type, settings_set, press_select, + goto_home, pick_menu_item, load_export, goto_address_explorer, + cap_story, need_keypress, try_sign): + + settings_set("chain", "XRT") + ms_name = "zero_depth" + clear_miniscript() + bitcoind.delete_wallet_files(pattern="zero_depth_s") + bitcoind.delete_wallet_files(pattern="zero_depth_wo") + # create multiple bitcoin wallets (N-1) as one signer is CC + cosig = bitcoind.create_wallet(wallet_name="zero_depth_s", disable_private_keys=False, + blank=False, passphrase=None, avoid_reuse=False, + descriptors=True) + cosig.keypoolrefill(100) + descs = cosig.listdescriptors()["descriptors"] + target_desc = None + for desc in descs: + if desc["desc"].startswith("wpkh(") and desc["internal"] is False: + target_desc = desc["desc"] + core_desc, checksum = target_desc.split("#") + # remove wpkh(....) + core_key = core_desc[5:-1] + my_master_xpub = dev.send_recv(CCProtocolPacker.get_xpub("m"), timeout=None) + my_xfp = dev.master_fingerprint + my_xfp = xfp2str(my_xfp).lower() # if any letters - lower them + my_data = f"[{my_xfp}]{my_master_xpub}/0/*" + # watch only wallet where multisig descriptor will be imported + wo = bitcoind.create_wallet( + wallet_name="zero_depth_wo", disable_private_keys=True, + blank=True, passphrase=None, avoid_reuse=False, descriptors=True + ) - try: - keys = import_ms_wallet(M, N, accept=1, keys=keys, - addr_fmt=addr_fmt, derivs=["m", "m/45'"]) - def pm(i): - return [] if i == 0 else [HARD(45), i, 0,0] + if addr_fmt == 'p2wsh': + tmplt = "wsh(%s)" + af = "bech32" + elif addr_fmt == "p2sh-p2wsh": + tmplt = "sh(wsh(%s))" + af = "p2sh-segwit" + else: + assert addr_fmt == "p2sh" + tmplt = "sh(%s)" + af = "legacy" - test_ms_show_addr(M, keys, bip45=False, path_mapper=pm) - finally: - clear_ms() + inner = "%s(2,%s)" % (desc_type, ",".join([core_key, my_data])) + desc = tmplt % inner + desc_info = wo.getdescriptorinfo(desc) + desc_w_checksum = desc_info["descriptor"] # with checksum -@pytest.mark.parametrize('mode', ['wrong-xfp', 'long-path', 'short-path', 'zero-path']) -@pytest.mark.ms_danger -@pytest.mark.bitcoind -def test_bad_xfp(mode, clear_ms, use_regtest, import_ms_wallet - , test_ms_show_addr, has_ms_checks, request): - # give incorrect xfp+path args during show_address - if has_ms_checks and (mode in {'zero-path', 'wrong-xfp'}): - # for these 2 cases, we detect the issue regardless of has_ms_checks mode - request.node.get_closest_marker('xfail').kwargs['strict'] = False + title, story = offer_minsc_import(json.dumps({"desc": desc_w_checksum, "name": ms_name})) + assert "Create new multisig wallet?" in story + press_select() - M, N = 1, 15 - keys = import_ms_wallet(M, N, accept=1) - try: - time.sleep(.1) + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(ms_name) + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export("sd", label="Bitcoin Core Multisig", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + # import descriptors to watch only wallet + res = wo.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"], obj - def tweaker(xfps): - print(f"xfps={xfps}") - if mode == 'wrong-xfp': - # bad XFP => not right multisig wallet - xfps[0][0] ^= 0x55 - elif mode == 'long-path': - # add garbage - xfps[0].extend([69, 69, 69, 69, 69]) - elif mode == 'short-path': - # trim last derivation part - xfps[0] = xfps[0][0:-1] - elif mode == 'zero-path': - # just XFP, no path - xfps[0] = xfps[0][0:1] - else: - raise ValueError + goto_address_explorer() + pick_menu_item(ms_name) + time.sleep(.1) + _, story = cap_story() + ea = [i.replace("\x02", "") for i in story.split("\n") if i and i.startswith("\x02")] + need_keypress("0") # change + time.sleep(.1) + _, story = cap_story() + ia = [i.replace("\x02", "") for i in story.split("\n") if i and i.startswith("\x02")] + + # check both external and internal + eabc, iabc = wo.deriveaddresses(core_desc_object[0]["desc"], [0, 9]) + for i in range(10): + assert eabc[i] == ea[i] + assert iabc[i] == ia[i] + + multi_addr = wo.getnewaddress("", af) + dest_addr = bitcoind.supply_wallet.getnewaddress("") + bitcoind.supply_wallet.sendtoaddress(multi_addr, 2) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress("")) + # create funded PSBT + psbt_resp = wo.walletcreatefundedpsbt([], [{dest_addr: 1.2}], 0, + {"fee_rate": 2, "change_type": af}) + psbt = psbt_resp.get("psbt") - with pytest.raises(BaseException) as ee: - test_ms_show_addr(M, keys, tweak_xfps=tweaker) + _, updated = try_sign(base64.b64decode(psbt), finalize=False) + signed = cosig.walletprocesspsbt(b64encode(updated).decode('ascii'), True, "ALL")["psbt"] - if mode in { 'wrong-xfp', 'zero-path' }: - assert 'with those fingerprints not found' in str(ee.value) - else: - assert 'pk#1 wrong' in str(ee.value) - if ('zero' in mode): - assert 'shallow' in str(ee.value) + # finalize and send + rr = bitcoind.supply_wallet.finalizepsbt(signed, True) + assert rr['complete'] + tx_hex = rr["hex"] + res = bitcoind.supply_wallet.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(rr['hex']) + assert len(txn_id) == 64 - finally: - clear_ms() -@pytest.mark.parametrize('cpp', [ - "m///", - "m/", - "m/1/2/3/4/5/6/7/8/9/10/11/12/13", # assuming MAX_PATH_DEPTH==12 -]) @pytest.mark.bitcoind -def test_bad_common_prefix(cpp, use_regtest, clear_ms, import_ms_wallet, +def test_bad_common_prefix(use_regtest, clear_miniscript, import_ms_wallet, test_ms_show_addr): - # give some incorrect path values as the common prefix derivation - + # assuming MAX_PATH_DEPTH==12 + cpp = "m/1/2/3/4/5/6/7/8/9/10/11/12/13" + clear_miniscript() M, N = 1, 15 with pytest.raises(BaseException) as ee: - keys = import_ms_wallet(M, N, accept=1, common=cpp) - assert 'bad derivation line' in str(ee) + keys = import_ms_wallet(M, N, accept=True, common=cpp) + assert 'origin too deep' in str(ee) @pytest.mark.parametrize("desc", ["multi", "sortedmulti"]) -def test_import_detail(desc, clear_ms, import_ms_wallet, need_keypress, +def test_import_detail(desc, clear_miniscript, import_ms_wallet, need_keypress, cap_story, is_q1, press_cancel): # check all details are shown right M,N = 14, 15 descriptor, bip67 = (True, False) if desc == "multi" else (False, True) - keys = import_ms_wallet(M, N, descriptor=descriptor, bip67=bip67) + keys = import_ms_wallet(M, N, bip67=bip67) time.sleep(.2) title, story = cap_story() assert f'{M} of {N}' in story + + # TODO emitting no warning here if desc == "multi": assert "WARNING" in story assert "BIP-67 disabled" in story @@ -742,16 +569,12 @@ def test_import_detail(desc, clear_ms, import_ms_wallet, need_keypress, assert "WARNING" not in story assert "BIP-67 disabled" not in story + assert f'{M} of {N}' in story + need_keypress('1') time.sleep(.1) title, story = cap_story() - if desc == "sortedmulti": - assert title == f'test-{M}-{N}' - else: - # imported from descriptor - name will be just M N - assert title == f'{M}-of-{N}' - xpubs = [sk.hwif() for _,_,sk in keys] for xp in xpubs: assert xp in story @@ -776,7 +599,7 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu, goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") pick_menu_item('Export XPUB') time.sleep(.1) @@ -794,7 +617,7 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu, need_keypress(n) press_select() - rv = load_export(way, is_json=True, label="Multisig XPUB", fpattern="ccxp-", sig_check=False) + rv = load_export(way, is_json=True, label="Multisig XPUB", fpattern="ccxp-") assert 'xfp' in rv assert len(rv) >= 6 @@ -813,6 +636,7 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu, expect = e.subkey_for_path("m/45'") assert expect.hwif() == n.hwif() + assert rv["p2sh_key_exp"] == f"[{rv['xfp']}/45h]{n.hwif()}" for name, deriv in [ ('p2sh_p2wsh', f"m/48h/{int(testnet)}h/{acct_num}h/1h"), @@ -828,35 +652,36 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu, assert n.node.index & 0xff == int(deriv[-2]) expect = e.subkey_for_path(deriv) assert expect.hwif() == n.hwif() + assert rv[name+"_key_exp"] == f"[{rv['xfp']}/{deriv.replace('m/', '')}]{n.hwif()}" - # TODO add tests for descriptor template @pytest.mark.parametrize('N', [ 3, 15]) @pytest.mark.parametrize('vdisk', [True, False]) def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, - need_keypress, microsd_path, make_multisig, + need_keypress, microsd_path, get_cc_key, virtdisk_path, is_q1, press_cancel, press_select): # test menu-based UX for importing wallet file from SD M = N-1 - keys = make_multisig(M, N) + keys = [BIP32Node.from_master_secret(os.urandom(32)).hwif() for _ in range(M)] + keys.append(get_cc_key("", "")) name = 'named-%d' % random.randint(10000,99999) - config = f'policy: {M} of {N}\n' - config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) + config = {"name": name, "desc": f"wsh(sortedmulti({M},{','.join(keys)}))"} if vdisk: fname = virtdisk_path(f'ms-{name}.txt') else: fname = microsd_path(f'ms-{name}.txt') + with open(fname, 'wt') as fp: - fp.write(config) + fp.write(json.dumps(config)) try: goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - pick_menu_item('Import from File') - time.sleep(0.5) + pick_menu_item("Multisig/Miniscript") + pick_menu_item('Import') + time.sleep(0.1) _, story = cap_story() if vdisk: if "(2) to import from Virtual Disk" not in story: @@ -864,7 +689,7 @@ def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, else: need_keypress("2") else: - if "(1) to import multisig wallet file from SD Card" in story: + if "(1) to import miniscript wallet file from SD Card" in story: need_keypress("1") time.sleep(.1) @@ -885,102 +710,16 @@ def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, try: os.unlink(fname) except: pass -@pytest.mark.parametrize("way", [None, "sd", "vdisk", "nfc", "qr"]) -@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ]) -@pytest.mark.parametrize('comm_prefix', ['m/1/2/3/4/5/6/7/8/9/10/11/12', None, "m/45h"]) -def test_export_single_ux(goto_home, comm_prefix, cap_story, pick_menu_item, cap_menu, press_select, - microsd_path, import_ms_wallet, addr_fmt, clear_ms, way, load_export, is_q1): - - # create a wallet, export to SD card, check file created. - # - checks some values for derivation path, assuming MAX_PATH_DEPTH==12 - - clear_ms() - - name = 'ex-test-%d' % random.randint(10000,99999) - M,N = 3, 5 - keys = import_ms_wallet(M, N, name=name, addr_fmt=addr_fmt, accept=1, - common=comm_prefix, way=way) - - goto_home() - pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - - menu = cap_menu() - item = [i for i in menu if name in i][0] - pick_menu_item(item) - - pick_menu_item('Coldcard Export') - contents = load_export(way or "sd", label="Coldcard multisig setup", is_json=False, sig_check=False) - if way == "qr": - # QR code still displayed on screen - press_select() - - got = set() - for ln in io.StringIO(contents).readlines(): - ln = ln.strip() - if '#' in ln: - assert ln[0] == '#' - continue - if not ln: - continue - - assert ':' in ln - label, value = ln.split(': ') - - if label == 'Name': - assert value == name - got.add(label) - elif label == 'Policy': - assert value == f'{M} of {N}' - got.add(label) - elif label == 'Derivation': - assert value == (comm_prefix or "m/45h") - got.add(label) - elif label == 'Format': - assert value == addr_fmt.upper() - assert addr_fmt != 'p2sh' - got.add(label) - else: - assert len(label) == 8, label - xfp = swab32(int(label, 16)) - got.add(xfp) - assert xfp in [x for x,_,_ in keys] - n = BIP32Node.from_wallet_key(value) - - if 'Format' not in got: - assert addr_fmt == 'p2sh' - got.add('Format') - - assert len(got) == 4 + N - - # test delete while we're here - pick_menu_item('Delete') - - time.sleep(.2) - title, story = cap_story() - where = title if is_q1 else story - assert 'you SURE' in where - assert name in story - - press_select() - time.sleep(.1) - menu = cap_menu() - assert not [i for i in menu if name in i] - assert '(none setup yet)' in menu - @pytest.mark.parametrize('N', [ 3, 15]) -def test_overflow(N, import_ms_wallet, clear_ms, press_select, cap_story, mk_num, is_q1): +def test_overflow(N, import_ms_wallet, clear_miniscript, press_select, cap_story, mk_num, is_q1): - clear_ms() + clear_miniscript() M = N - name = 'a'*20 # longest possible + name = 'a'*19 # longest possible for count in range(1, 10): - keys = import_ms_wallet(M, N, name=name, addr_fmt='p2wsh', unique=count, accept=0, - common="m/45h/0h/34h") - - time.sleep(.1) - press_select() + keys = import_ms_wallet(M, N, name=f"{name}{count}", addr_fmt='p2wsh', unique=count, + accept=True, common="m/45h/0h/34h") time.sleep(.2) title, story = cap_story() @@ -990,159 +729,108 @@ def test_overflow(N, import_ms_wallet, clear_ms, press_select, cap_story, mk_num assert 'No space left' in story break - if mk_num >= 4: - assert count == 9 # unlimited now - else: - if N == 3: - assert count == 9, "Expect fail at 9" - if N == 15: - assert count == 2, "Expect fail at 2" + assert count == 9 # unlimited now press_select() - clear_ms() - -@pytest.fixture -def test_make_example_file(microsd_path, make_multisig): - def doit(M, N, addr_fmt=None): - keys = make_multisig(M, N) - - # render as a file for import - name = f'sample-{M}-{N}' - config = f"name: {name}\npolicy: {M} / {N}\n\n" - - if addr_fmt: - config += f'format: {addr_fmt.upper()}\n' - - config += '\n'.join('%s: %s' % (xfp2str(xfp), sk.hwif(as_private=False)) - for xfp,m,sk in keys) + clear_miniscript() - fname = microsd_path(f'{name}.txt') - with open(fname, 'wt') as fp: - fp.write(config+'\n') - - print(f"Created: {fname}") - return fname - return doit @pytest.mark.parametrize('N', [ 5, 10]) -def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, +def test_import_dup_safe(N, clear_miniscript, make_multisig, offer_minsc_import, need_keypress, cap_story, goto_home, pick_menu_item, - cap_menu, is_q1, press_select, OK): + cap_menu, is_q1, press_select, OK, settings_get, enter_text): # import wallet, rename it, (check that indicated, works), attempt same w/ addr fmt different M = N - clear_ms() + clear_miniscript() keys = make_multisig(M, N) # render as a file for import - def make_named(name, af='p2sh', m=M): - config = f"name: {name}\npolicy: {m} / {N}\nformat: {af}\n\n" - config += '\n'.join('%s: %s' % (xfp2str(xfp), sk.hwif(as_private=False)) - for xfp,m,sk in keys) - return config + def make_named(name, af='sh', m=M): + k = ','.join('[%s/45h]%s' % (xfp2str(xfp), sk.hwif()) for xfp, m, sk in keys) + desc_obj = {"name": name, "desc": f"{af}(sortedmulti({m},{k}))"} + return json.dumps(desc_obj) def has_name(name, num_wallets=1): # check worked: look in menu for name goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") menu = cap_menu() - assert f'{M}/{N}: {name}' in menu - # depending if NFC enabled or not, and if Q (has QR) - assert (len(menu) - num_wallets) in [6, 7, 8] + assert name in menu + assert len(settings_get("miniscript")) == num_wallets - title, story = offer_ms_import(make_named('xxx-orig')) + orig_name = "xxx-orig" + title, story = offer_minsc_import(make_named(orig_name)) assert 'Create new multisig wallet' in story - assert 'xxx-orig' in story + assert orig_name in story assert 'P2SH' in story press_select() - has_name('xxx-orig') + has_name(orig_name) + + new_name = "AAAA" + title, story = offer_minsc_import(make_named(new_name)) + assert 'Duplicate wallet' in story + assert f"'{orig_name}' is the same" + assert new_name in story + try: + has_name(new_name) + raise ValueError + except AssertionError: pass + has_name(orig_name, 1) # just simple rename - title, story = offer_ms_import(make_named('xxx-new')) - assert 'update name only' in story.lower() - assert 'xxx-new' in story + pick_menu_item(orig_name) + pick_menu_item("Rename") + for i in range(len(orig_name) if is_q1 else len(orig_name) - 1): + need_keypress(KEY_DELETE if is_q1 else "x") - press_select() - has_name('xxx-new') + if not is_q1: + # below should yield AAAA + need_keypress("1") + for _ in range(3): + need_keypress("9") # next char + need_keypress("1") # letters - assert N < 15, 'cant make more, no space' + press_select() + else: + enter_text(new_name) + + press_select() + has_name(new_name) - newer = make_named('xxx-newer', 'p2wsh') - title, story = offer_ms_import(newer) - assert 'update name only' not in story.lower() - assert 'address type' in story.lower() - assert 'will NOT replace it' in story - assert 'xxx-newer' in story - assert 'WARNING:' in story + newer_name = "xxx-newer" + newer = make_named(newer_name, 'wsh') + title, story = offer_minsc_import(newer) + assert newer_name in story assert 'P2WSH' in story # should be 2 now, slightly different press_select() - has_name('xxx-newer', 2) + has_name(newer_name, 2) - # TODO # repeat last one, should still be two for keys in ['yn', 'n']: - title, story = offer_ms_import(newer) - assert 'Duplicate wallet' in story + title, story = offer_minsc_import(newer) + assert 'unique names' in story assert f'{OK} to approve' not in story - assert 'xxx-newer' in story + assert newer_name in story for key in keys: need_keypress(key) - has_name('xxx-newer', 2) - - clear_ms() - -@pytest.mark.parametrize('N', [ 5]) -def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import, - press_select, cap_story, goto_home, - pick_menu_item, cap_menu, is_q1): - # import wallet, tweak xpub only, check that change detected - clear_ms() - - M = N - keys = make_multisig(M, N) - - # render as a file for import - def make_named(name, af='p2sh', m=M, tweaked=False): - config = f"name: {name}\npolicy: {m} / {N}\nformat: {af}\n\n" - lines = [] - for idx, (xfp,m,sk) in enumerate(keys): - if idx == 1 and tweaked: - x = bytearray(sk.node.key) - x[9] = 254 - sk.node.key = bytes(x) - - hwif = sk.hwif() - lines.append('%s: %s' % (xfp2str(xfp), hwif) ) - config += '\n'.join(lines) - return config - - title, story = offer_ms_import(make_named('xxx-orig')) - assert 'Create new multisig wallet' in story - assert 'xxx-orig' in story - assert 'P2SH' in story - press_select() - - # change one key. - title, story = offer_ms_import(make_named('xxx-new', tweaked=True)) - assert 'WARNING:' in story - assert 'xxx-new' in story - assert 'xpubs' in story + has_name(newer_name, 2) - clear_ms() + clear_miniscript() @pytest.mark.bitcoind @pytest.mark.parametrize('m_of_n', [(2,2), (2,3), (15,15)]) @pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ]) -def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_ms, +def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_miniscript, make_multisig, import_ms_wallet, test_ms_show_addr): M, N = m_of_n @@ -1156,73 +844,18 @@ def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_ms, keys[-1] = (simulator_fixed_xfp, pk, sub) with pytest.raises(Exception) as ee: - import_ms_wallet(M, N, addr_fmt, accept=1, keys=keys) + import_ms_wallet(M, N, addr_fmt, accept=True, keys=keys) #assert 'XFP' in str(ee) assert 'wrong pubkey' in str(ee) -@pytest.mark.parametrize('addr_fmt', [AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH]) -@pytest.mark.parametrize('desc', ["multi", "sortedmulti"]) -def test_ms_cli(dev, addr_fmt, clear_ms, import_ms_wallet, addr_vs_path, desc): - # exercise the p2sh command of ckcc:cli ... hard to do manually. - from subprocess import check_output - - M, N = 2, 3 - clear_ms() - bip67, descriptor = (False, True) if desc == "multi" else (True, False) - keys = import_ms_wallet(M, N, name='cli-test', accept=True, - addr_fmt=addr_fmt_names[addr_fmt], - descriptor=descriptor, bip67=bip67) - - pmapper = lambda i: [HARD(45), i, 0,3] - - scr, pubkeys, xfp_paths = make_redeem(M, keys, pmapper, bip67=bip67) - - def decode_path(p): - return '/'.join(str(i) if i < 0x80000000 else "%d'"%(i& 0x7fffffff) for i in p) - - if 1: - args = ['ckcc'] - if dev.is_simulator: - args += ['-x'] - - args += ['p2sh', '-q'] - - if addr_fmt == AF_P2WSH: - args += ['-s'] - elif addr_fmt == AF_P2WSH_P2SH: - args += ['-s', '-w'] - - args += [B2A(scr)] - args += [xfp2str(x)+'/'+decode_path(path) for x,*path in xfp_paths] - - import shlex - print('CMD: ' + (' '.join(shlex.quote(i) for i in args))) - - addr = check_output(args, encoding='ascii').strip() - - print(addr) - addr_vs_path(addr, addr_fmt=addr_fmt, script=scr) - - # test case for make_ms_address really. - expect_addr, _, scr2, _ = make_ms_address(M, keys, path_mapper=pmapper, - addr_fmt=addr_fmt, bip67=bip67) - assert expect_addr == addr - assert scr2 == scr - - # need to re-start our connection once ckcc has talked to simulator - dev.start_encryption() - dev.check_mitm() - - clear_ms() - @pytest.fixture -def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, press_select, clear_ms, - reset_seed_words, is_q1): +def make_myself_wallet(dev, set_bip39_pw, offer_minsc_import, press_select, clear_miniscript, + reset_seed_words, is_q1, cap_story, press_cancel): # construct a wallet (M of 4) using different bip39 passwords, and default sim - def doit(M, addr_fmt=None, do_import=True): + def doit(M, addr_fmt="p2wsh", do_import=True, desc="sortedmulti"): passwords = ['Me', 'Myself', 'And I', ''] @@ -1255,31 +888,43 @@ def doit(M, addr_fmt=None, do_import=True): if do_import: # render as a file for import - config = f"name: Myself-{M}\npolicy: {M} / 4\n\n" - - if addr_fmt: - config += f'format: {addr_fmt.upper()}\n' - - config += '\n'.join('%s: %s' % (xfp2str(xfp), sk.hwif()) for xfp, _, sk in keys) - #print(config) + msc = {"name": f"Myself-{M}"} + kk = ','.join('[%s/45h]%s' % (xfp2str(xfp), sk.hwif()) for xfp, _, sk in keys) + + if addr_fmt == "p2wsh": + d = f"wsh({desc}({M},{kk}))" + elif addr_fmt == "p2sh-p2wsh": + d = f"sh(wsh({desc}({M},{kk})))" + elif addr_fmt == "p2sh": + d = f"sh({desc}({M},{kk}))" + else: + raise ValueError("Unknown address format: " + addr_fmt) - title, story = offer_ms_import(config) - #print(story) + msc["desc"] = d + config = json.dumps(msc) + title, story = offer_minsc_import(config) + assert "Create new multisig wallet" in story - # dont care if update or create; accept it. + # don't care if update or create; accept it. time.sleep(.1) press_select() - def select_wallet(idx): + def select_wallet(idx, no_import=False): # select to specific pw + print(f"--- switch to another leg of MS: {idx} ---") xfp = set_bip39_pw(passwords[idx]) - if do_import: - offer_ms_import(config) + if do_import and not no_import: + offer_minsc_import(config) time.sleep(.1) - press_select() + title, story = cap_story() + if ("Duplicate" in story) or ("MUST have unique names" in story): + press_cancel() + else: + press_select() assert xfp == keys[idx][0] + return xfp - return (keys, select_wallet) + return keys, select_wallet yield doit @@ -1292,10 +937,10 @@ def fake_ms_txn(pytestconfig): # - but has UTXO's to match needs from struct import pack - def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, + def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, inp_addr_fmt="p2wsh", outstyles=['p2pkh'], change_outputs=[], incl_xpubs=False, hack_psbt=None, hack_change_out=False, input_amount=1E8, psbt_v2=None, bip67=True, - violate_script_key_order=False): + violate_script_key_order=False, path_mapper=None, netcode="XTN", force_outstyle=None): psbt = BasicPSBT() if psbt_v2 is None: @@ -1326,18 +971,32 @@ def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] + if netcode == "XTN": + net = 1 + elif netcode == "XRT": + net = 2 + else: + net = 0 + + af = unmap_addr_fmt[inp_addr_fmt] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC - # addr where the fake money will be stored. - addr, scriptPubKey, script, details = make_ms_address(M, keys, idx=i, bip67=bip67, - violate_script_key_order=violate_script_key_order) - + addr, scriptPubKey, script, details = make_ms_address( + M, keys, idx=i, bip67=bip67, + violate_script_key_order=violate_script_key_order, + path_mapper=path_mapper, addr_fmt=af, testnet=net + ) # lots of supporting details needed for p2sh inputs - if segwit_in: + if inp_addr_fmt in ["p2wsh", "p2sh-p2wsh", "p2wsh-p2sh"]: + segwit_in = True psbt.inputs[i].witness_script = script + if "p2sh" in inp_addr_fmt: + psbt.inputs[i].redeem_script = b'\x00\x20' + sha256(script).digest() else: + # p2sh + segwit_in = False psbt.inputs[i].redeem_script = script for pubkey, xfp_path in details: @@ -1379,11 +1038,19 @@ def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, style = outstyles[i % len(outstyles)] if i in change_outputs: + # overwrite style, change can only be of THE style + if force_outstyle: + style = force_outstyle + else: + style = inp_addr_fmt + make_redeem_args = dict() if hack_change_out: make_redeem_args = hack_change_out(i) if violate_script_key_order: make_redeem_args["violate_script_key_order"] = True + if path_mapper: + make_redeem_args["path_mapper"] = path_mapper addr, scriptPubKey, scr, details = \ make_ms_address(M, keys, idx=i, addr_fmt=unmap_addr_fmt[style], @@ -1394,7 +1061,7 @@ def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, if 'w' in style: psbt.outputs[i].witness_script = scr - if style.endswith('p2sh'): + if 'p2sh' in style: psbt.outputs[i].redeem_script = b'\0\x20' + sha256(scr).digest() elif style.endswith('sh'): psbt.outputs[i].redeem_script = scr @@ -1433,40 +1100,68 @@ def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, @pytest.mark.veryslow @pytest.mark.unfinalized -@pytest.mark.parametrize('addr_fmt', [AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH]) +@pytest.mark.parametrize('addr_fmt', ["p2wsh", "p2sh-p2wsh", "p2sh"]) @pytest.mark.parametrize('num_ins', [2, 15]) -@pytest.mark.parametrize('incl_xpubs', [False, True, 'no-import']) +@pytest.mark.parametrize('incl_xpubs', [True, False, None]) @pytest.mark.parametrize('transport', ['usb', 'sd']) @pytest.mark.parametrize('has_change', [True, False]) @pytest.mark.parametrize('M_N', [(2, 3), (5, 15)]) -@pytest.mark.parametrize('desc', ["multi", "sortedmulti"]) -def test_ms_sign_simple(M_N, num_ins, dev, addr_fmt, clear_ms, incl_xpubs, import_ms_wallet, +@pytest.mark.parametrize('desc', ["sortedmulti", "multi"]) +def test_ms_sign_simple(M_N, num_ins, dev, addr_fmt, clear_miniscript, import_ms_wallet, addr_vs_path, fake_ms_txn, try_sign, try_sign_microsd, transport, - has_change, settings_set, desc): + has_change, settings_set, desc, sim_root_dir, incl_xpubs): M, N = M_N num_outs = num_ins-1 - descriptor, bip67 = (True, False) if desc == "multi" else (False, True) + bip67 = False if desc == "multi" else True - # trust PSBT if we're doing "no-import" case - settings_set('pms', 2 if (incl_xpubs == 'no-import') else 0) - - clear_ms() + clear_miniscript() - if incl_xpubs != "no-import": - do_import = True + if addr_fmt == "p2sh": + dd = "m/45h" + elif addr_fmt == "p2wsh": + dd = "m/48h/1h/0h/2h" else: + dd = "m/48h/1h/0h/1h" + + def path_mapper(idx): + kk = str_to_path(dd) + return kk + [0,0] + + def include_xpubs(idx, xfp, m, sk): + kk = str_to_path(dd) + bp = pack('<%dI' % (dd.count("/") + 1), xfp, *kk) + return sk.node.serialize_public(), bp + + if incl_xpubs: + # test enrolling xpubs form PSBT do_import = False + incl_xpubs = include_xpubs + if not bip67: raise pytest.skip("cannot import unsorted multisig from PSBT") + elif incl_xpubs is None: + # test verification of PSBT xpubs against our enrolled wallet + do_import = True + incl_xpubs = include_xpubs + else: + do_import = True + incl_xpubs = None - keys = import_ms_wallet(M, N, name='cli-test', accept=True, addr_fmt=addr_fmt, - do_import=do_import, descriptor=descriptor, bip67=bip67) + # trust PSBT if we're doing "no-import" case + settings_set('pms', 2 if not do_import else 0) - psbt = fake_ms_txn(num_ins, num_outs, M, keys, incl_xpubs=incl_xpubs, - outstyles=ADDR_STYLES_MS, change_outputs=[1] if has_change else [], - bip67=bip67) + keys = import_ms_wallet(M, N, name='ms-sign-simple', accept=True, addr_fmt=addr_fmt, + do_import=do_import, bip67=bip67, common=dd) - open('debug/last.psbt', 'wb').write(psbt) + if do_import is False: + keys = keys[0] + + psbt = fake_ms_txn(num_ins, num_outs, M, keys, inp_addr_fmt=addr_fmt, incl_xpubs=incl_xpubs, + outstyles=[addr_fmt], change_outputs=[1] if has_change else [], + bip67=bip67, netcode="XRT", path_mapper=path_mapper) + + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) if transport == 'sd': try_sign_microsd(psbt, encoding=('binary', 'hex', 'base64')[random.randint(0,2)]) @@ -1476,33 +1171,36 @@ def test_ms_sign_simple(M_N, num_ins, dev, addr_fmt, clear_ms, incl_xpubs, impor @pytest.mark.unfinalized @pytest.mark.bitcoind @pytest.mark.parametrize('num_ins', [ 15 ]) -@pytest.mark.parametrize('M', [ 2, 4, 1]) -@pytest.mark.parametrize('segwit', [True, False]) +@pytest.mark.parametrize('M', [ 2, 4]) @pytest.mark.parametrize('incl_xpubs', [ True, False ]) -def test_ms_sign_myself(M, use_regtest, make_myself_wallet, segwit, num_ins, dev, clear_ms, - fake_ms_txn, try_sign, incl_xpubs, bitcoind): +def test_ms_sign_myself(M, use_regtest, make_myself_wallet, num_ins, dev, incl_xpubs, + clear_miniscript, fake_ms_txn, try_sign, bitcoind, sim_root_dir): - # IMPORTANT: wont work if you start simulator with --ms flag. Use no args + # IMPORTANT: won't work if you start simulator with --ms flag. Use no args - all_out_styles = list(unmap_addr_fmt.keys()) + all_out_styles = [af for af in unmap_addr_fmt.keys() if af != "p2tr"] num_outs = len(all_out_styles) - clear_ms() + clear_miniscript() use_regtest() # create a wallet, with 3 bip39 pw's - keys, select_wallet = make_myself_wallet(M, do_import=(not incl_xpubs)) + keys, select_wallet = make_myself_wallet(M, addr_fmt="p2sh", do_import=(not incl_xpubs)) N = len(keys) assert M<=N - psbt = fake_ms_txn(num_ins, num_outs, M, keys, segwit_in=segwit, incl_xpubs=incl_xpubs, - outstyles=all_out_styles, change_outputs=list(range(1,num_outs))) + psbt = fake_ms_txn(num_ins, num_outs, M, keys, inp_addr_fmt="p2sh", incl_xpubs=incl_xpubs, + outstyles=["p2sh"], change_outputs=list(range(1,num_outs))) - open(f'debug/myself-before.psbt', 'w').write(b64encode(psbt).decode()) + with open(f'{sim_root_dir}/debug/myself-before.psbt', 'w') as f: + f.write(b64encode(psbt).decode()) for idx in range(M): select_wallet(idx) + if incl_xpubs: + clear_miniscript() _, updated = try_sign(psbt, accept_ms_import=incl_xpubs) - open(f'debug/myself-after.psbt', 'w').write(b64encode(updated).decode()) + with open(f'{sim_root_dir}/debug/myself-after.psbt', 'w') as f: + f.write(b64encode(updated).decode()) assert updated != psbt aft = BasicPSBT().parse(updated) @@ -1513,43 +1211,17 @@ def test_ms_sign_myself(M, use_regtest, make_myself_wallet, segwit, num_ins, dev # should be fully signed now. anal = bitcoind.rpc.analyzepsbt(b64encode(psbt).decode('ascii')) - try: - assert not any(inp.get('missing') for inp in anal['inputs']), "missing sigs: %r" % anal - assert all(inp['next'] in {'finalizer','updater'} for inp in anal['inputs']), "other issue: %r" % anal - except: - # XXX seems to be a bug in analyzepsbt function ... not fully studied - pprint(anal, stream=open('debug/analyzed.txt', 'wt')) - decode = bitcoind.rpc.decodepsbt(b64encode(psbt).decode('ascii')) - pprint(decode, stream=open('debug/decoded.txt', 'wt')) - - if M==N or segwit: - # as observed, bug not trigged, so raise if it *does* happen - raise - else: - print("ignoring bug in bitcoind") - - if 0: - # why doesn't this work? - # TODO this does NOT work only if parameter segwit is True - # TODO I have debuged bitcoin core to see why we're still in updater phase, not in desired finalizer - # relevant comment from core code: - # When we're taking our information from a witness UTXO, we can't verify it is actually data from - # the output being spent. This is safe in case a witness signature is produced (which includes this - # information directly in the hash), but not for non-witness signatures. Remember that we require - # a witness signature in this situation. - # - # In our case, witness signature was not produced (but was required) - rv = bitcoind.rpc.finalizepsbt(b64encode(aft.as_bytes()).decode('ascii'), True) - _, txn, is_complete = b64decode(rv.get('psbt', '')), rv.get('hex'), rv['complete'] - assert is_complete + assert not any(inp.get('missing') for inp in anal['inputs']), "missing sigs: %r" % anal + assert all(inp['next'] in {'finalizer','updater'} for inp in anal['inputs']), "other issue: %r" % anal + @pytest.mark.parametrize('addr_fmt', ['p2wsh', 'p2sh-p2wsh']) -@pytest.mark.parametrize('acct_num', [ 0, None, 4321]) +@pytest.mark.parametrize('acct_num', [None, 4321]) @pytest.mark.parametrize('M_N', [(2,3), (8,14)]) @pytest.mark.parametrize('way', ["sd", "qr"]) @pytest.mark.parametrize('incl_self', [True, False, None]) def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu_item, - need_keypress, microsd_path, set_bip39_pw, clear_ms, enter_number, + need_keypress, microsd_path, set_bip39_pw, clear_miniscript, enter_number, get_settings, load_export, is_q1, press_select, press_cancel, cap_screen, way, scan_a_qr, skip_if_useless_way, incl_self): # test UX and math for bip45 export @@ -1560,7 +1232,7 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu for fn in glob(microsd_path('ccxp-*.json')): assert fn os.unlink(fn) - clear_ms() + clear_miniscript() for idx in range(N - int(incl_self is None)): if not idx and (incl_self is True): @@ -1571,7 +1243,7 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu goto_home() time.sleep(0.1) pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") pick_menu_item('Export XPUB') time.sleep(.05) press_select() @@ -1595,12 +1267,12 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") pick_menu_item('Create Airgapped') if is_q1: time.sleep(.1) title, story = cap_story() - assert "scan multisg XPUBs from QR codes" in story + assert "scan multisig XPUBs from QR codes" in story if way == "qr": need_keypress(KEY_QR) else: @@ -1622,22 +1294,6 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu assert 0, addr_fmt if way == "qr": - # first non-json garbage - scan_a_qr("aaaaaaaaaaaaaaaaaaaa") - time.sleep(1) - scr = cap_screen() - assert f"Expected JSON data" in scr - - # JSON but wrong - _, parts = split_qrs('{"json": "but wrong","missing": "important data"}', - 'J', max_version=20) - for p in parts: - scan_a_qr(p) - - time.sleep(1) - scr = cap_screen() - assert f"Missing value: xfp" in scr # missing xfp - # need to scan json XPUBs here for i, fname in enumerate(glob(microsd_path('ccxp-*.json'))): with open(fname, 'r') as f: @@ -1651,7 +1307,7 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu scr = cap_screen() assert f"Number of keys scanned: {i+1}" in scr - press_cancel() # quit QR animation + press_select() # quit QR animation if not incl_self: time.sleep(.1) @@ -1677,30 +1333,30 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu if incl_self is not False: assert "Create new multisig" in story press_select() - # we use clear_ms fixture at the begining of each test + # we use clear_miniscript fixture at the begining of each test # new multisig wallet is first menu item press_select() - pick_menu_item("Coldcard Export") - impf, fname = load_export("sd", label="Coldcard multisig setup", is_json=False, - sig_check=False, ret_fname=True) + pick_menu_item("Descriptors") + pick_menu_item("Export") + impf, fname = load_export("sd", label="Multisig", is_json=False, + ret_fname=True) cc_fname = microsd_path(fname) - assert f'Policy: {M} of {N}' in impf - if addr_fmt != 'p2sh': - assert f'Format: {addr_fmt.upper()}' in impf + strt = "wsh(sortedmulti" if addr_fmt == 'p2wsh' else "sh(wsh(sortedmulti(" + strt += str(M) press_select() press_select() - clear_ms() + clear_miniscript() # test re-importing the wallet from export file goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - pick_menu_item('Import from File') + pick_menu_item("Multisig/Miniscript") + pick_menu_item('Import') time.sleep(0.5) _, story = cap_story() - if "Press (1) to import multisig wallet file from SD Card" in story: + if "Press (1) to import miniscript" in story: need_keypress("1") time.sleep(.05) @@ -1710,10 +1366,6 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu title, story = cap_story() assert "Create new multisig" in story assert f"Policy: {M} of {N}" in story - if acct_num is None: - assert ("Varies (%d)" % N) in story - else: - assert f"/{acct_num}h/" in story need_keypress('1') time.sleep(.1) @@ -1722,8 +1374,7 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu else: # own wallet not included in the mix, can only export resulting descriptor - desc = load_export(way, label="Descriptor multisig setup", - is_json=False, sig_check=False) + desc = load_export(way, label="Multisig", is_json=False, sig_check=False) desc = desc.strip() do = MultisigDescriptor.parse(desc) assert do.M == M @@ -1747,164 +1398,21 @@ def test_make_airgapped(addr_fmt, acct_num, M_N, goto_home, cap_story, pick_menu press_cancel() press_cancel() -@pytest.mark.unfinalized -@pytest.mark.bitcoind -@pytest.mark.parametrize('addr_style', ["legacy", "p2sh-segwit", "bech32"]) -@pytest.mark.parametrize('cc_sign_first', [True, False]) -def test_bitcoind_cosigning(cc_sign_first, dev, bitcoind, import_ms_wallet, clear_ms, try_sign, - press_cancel, addr_style, use_regtest, is_q1): - # Make a P2SH wallet with local bitcoind as a co-signer (and simulator) - # - send an receive various - # - following text of - # - the constructed multisig walelt will only work for a single pubkey on core side - # - before starting this test, have some funds already deposited to bitcoind testnet wallet - - if not bitcoind.has_bdb: - # addmultisigaddress not supported by descriptor wallets - pytest.skip("Needs BDB legacy wallet") - - from bip32 import PubKeyNode - from binascii import a2b_hex - - use_regtest() - if addr_style == 'legacy': - addr_fmt = AF_P2SH - elif addr_style == 'p2sh-segwit': - addr_fmt = AF_P2WSH_P2SH - elif addr_style == 'bech32': - addr_fmt = AF_P2WSH - - addr = bitcoind.supply_wallet.getnewaddress("sim-cosign") - - info = bitcoind.supply_wallet.getaddressinfo(addr) - - assert info['address'] == addr - bc_xfp = swab32(int(info['hdmasterfingerprint'], 16)) - bc_deriv = info['hdkeypath'] # example: "m/0'/0'/3'" - bc_pubkey = info['pubkey'] # 02f75ae81199559c4aa... - - node = BIP32Node(PubKeyNode( - key=a2b_hex(bc_pubkey), - chain_code=b'\x23'*32, - depth=len(bc_deriv.split('/'))-1, - parent_fingerprint=a2b_hex('%08x' % bc_xfp), - testnet=True - )) - # No means to export XPUB from bitcoind! Still. In 2019. - # - this fake will only work for one pubkey value, the first/topmost - keys = [ - (bc_xfp, None, node), - (simulator_fixed_xfp, None, BIP32Node.from_hwif('tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')), # simulator: m/45' - ] - - M,N=2,2 - - clear_ms() - import_ms_wallet(M, N, keys=keys, accept=1, name="core-cosign", - addr_fmt=addr_fmt_names[addr_fmt], derivs=[bc_deriv, "m/45h"]) - - cc_deriv = "m/45h/55" - cc_pubkey = B2A(BIP32Node.from_hwif(simulator_fixed_tprv).subkey_for_path(cc_deriv).sec()) - - # NOTE: bitcoind doesn't seem to implement pubkey sorting. We have to do it. - resp = bitcoind.supply_wallet.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])), - 'shared-addr-'+addr_style, addr_style) - ms_addr = resp['address'] - bc_redeem = a2b_hex(resp['redeemScript']) - - assert bc_redeem[0] == 0x52 - - def mapper(cosigner_idx): - return list(str2ipath(cc_deriv if cosigner_idx else bc_deriv)) - - scr, pubkeys, xfp_paths = make_redeem(M, keys, mapper) - - assert scr == bc_redeem - - # check Coldcard calcs right address to match - got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address( - M, xfp_paths, scr, addr_fmt=addr_fmt), timeout=None) - assert got_addr == ms_addr - time.sleep(.1) - press_cancel() # clear screen / start over - - print(f"Will be signing an input from {ms_addr}") - - if xfp2str(bc_xfp) in ('5380D0ED', 'EDD08053'): - # my own expected values - assert ms_addr in ( '2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4', - '2N1hZJ5mazTX524GQTPKkCT4UFZn5Fqwdz6', - 'tb1qpcv2rkc003p5v8lrglrr6lhz2jg8g4qa9vgtrgkt0p5rteae5xtqn6njw9') - - # fund multisig address - bitcoind.supply_wallet.importaddress(ms_addr, 'shared-addr-'+addr_style, True) - bitcoind.supply_wallet.sendtoaddress(address=ms_addr, amount=5) - bitcoind.supply_wallet.generatetoaddress(101, bitcoind.supply_wallet.getnewaddress()) # mining - unspent = bitcoind.supply_wallet.listunspent(addresses=[ms_addr]) - ret_addr = bitcoind.supply_wallet.getrawchangeaddress() - - resp = bitcoind.supply_wallet.walletcreatefundedpsbt([dict(txid=unspent[0]["txid"], vout=unspent[0]["vout"])], - [{ret_addr: 2}], 0, - {'subtractFeeFromOutputs': [0], 'includeWatching': True}, True) - - if not cc_sign_first: - # signing first with bitcoind - resp = bitcoind.supply_wallet.walletprocesspsbt(resp["psbt"]) - - # assert resp['changepos'] == -1 - psbt = b64decode(resp['psbt']) - - open('debug/funded.psbt', 'wb').write(psbt) - - # patch up the PSBT a little ... bitcoind doesn't know the path for the CC's key - ex = BasicPSBT().parse(psbt) - cxpk = a2b_hex(cc_pubkey) - for i in ex.inputs: - # issues/47 in secret - from 24.0 core does not add out key into PSBT input bip32 paths - no need to check - # assert cxpk in i.bip32_paths, 'input not to be signed by CC?' - i.bip32_paths[cxpk] = pack('<3I', keys[1][0], *str2ipath(cc_deriv)) - - psbt = ex.as_bytes() - - open('debug/patched.psbt', 'wb').write(psbt) - - _, updated = try_sign(psbt, finalize=False) - - open('debug/cc-updated.psbt', 'wb').write(updated) - - if cc_sign_first: - # cc signed first - bitcoind is now second - rr = bitcoind.supply_wallet.walletprocesspsbt(b64encode(updated).decode('ascii'), True, "ALL") - assert rr["complete"] - both_signed = rr["psbt"] - else: - both_signed = b64encode(updated).decode('ascii') - - # finalize and send - rr = bitcoind.supply_wallet.finalizepsbt(both_signed, True) - open('debug/bc-final-txn.txn', 'wt').write(rr['hex']) - assert rr['complete'] - tx_hex = rr["hex"] - res = bitcoind.supply_wallet.testmempoolaccept([tx_hex]) - assert res[0]["allowed"] - txn_id = bitcoind.supply_wallet.sendrawtransaction(rr['hex']) - assert len(txn_id) == 64 - @pytest.mark.parametrize('addr_fmt', [AF_P2WSH] ) @pytest.mark.parametrize('num_ins', [ 3]) -@pytest.mark.parametrize('incl_xpubs', [ False]) @pytest.mark.parametrize('out_style', ['p2wsh']) @pytest.mark.parametrize('bitrot', list(range(0,6)) + [98, 99, 100] + list(range(-5, 0))) @pytest.mark.ms_danger -def test_ms_sign_bitrot(num_ins, dev, addr_fmt, clear_ms, incl_xpubs, import_ms_wallet, addr_vs_path, - fake_ms_txn, start_sign, end_sign, out_style, cap_story, bitrot, has_ms_checks): +def test_ms_sign_bitrot(num_ins, dev, addr_fmt, clear_miniscript, import_ms_wallet, + addr_vs_path, fake_ms_txn, start_sign, end_sign, out_style, cap_story, + bitrot, sim_root_dir): M = 1 N = 3 num_outs = 2 - clear_ms() - keys = import_ms_wallet(M, N, accept=1, addr_fmt=out_style) + clear_miniscript() + keys = import_ms_wallet(M, N, accept=True, addr_fmt=out_style) # given script, corrupt it a little or a lot def rotten(track, bitrot, scr): @@ -1922,45 +1430,45 @@ def rotten(track, bitrot, scr): return rv track = [] - psbt = fake_ms_txn(num_ins, num_outs, M, keys, incl_xpubs=incl_xpubs, - outstyles=[out_style], change_outputs=[0], - hack_change_out=lambda idx: dict(finalizer_hack= - lambda scr: rotten(track, bitrot, scr))) + psbt = fake_ms_txn( + num_ins, num_outs, M, keys, outstyles=[out_style], change_outputs=[0], + hack_change_out=lambda idx: dict(finalizer_hack=lambda scr: rotten(track, bitrot, scr)) + ) assert len(track) == 1 - open('debug/last.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) start_sign(psbt) with pytest.raises(Exception) as ee: - signed = end_sign(accept=None) + end_sign(accept=None) assert 'Output#0:' in str(ee) - assert 'change output script' in str(ee) + assert 'p2wsh change output is fraudulent' in str(ee) # Check error details are shown time.sleep(.01) title, story = cap_story() - assert story.strip() in str(ee) - assert len(story.split(':')[-1].strip()), story + assert 'Output#0:' in story + assert 'p2wsh change output is fraudulent' -@pytest.mark.parametrize('addr_fmt', [AF_P2WSH, AF_P2SH] ) -@pytest.mark.parametrize('num_ins', [ 1]) -@pytest.mark.parametrize('incl_xpubs', [ True]) -@pytest.mark.parametrize('out_style', ['p2wsh']) -@pytest.mark.parametrize('pk_num', range(4)) +@pytest.mark.parametrize('addr_fmt', ["p2wsh", "p2sh-p2wsh", "p2sh"] ) +@pytest.mark.parametrize('pk_num', range(4)) @pytest.mark.parametrize('case', ['pubkey', 'path']) -def test_ms_change_fraud(case, pk_num, num_ins, dev, addr_fmt, clear_ms, incl_xpubs, make_multisig, - addr_vs_path, fake_ms_txn, start_sign, end_sign, out_style, cap_story): +def test_ms_change_fraud(case, pk_num, dev, addr_fmt, clear_miniscript, make_multisig, + addr_vs_path, fake_ms_txn, start_sign, end_sign, cap_story, + sim_root_dir): M = 1 N = 3 + num_ins = 1 num_outs = 2 - clear_ms() + clear_miniscript() keys = make_multisig(M, N) - # given + # given def tweak(case, pk_num, data): # added from make_redeem() as tweak_pubkeys option #(pk, xfp, path)) @@ -1981,185 +1489,77 @@ def tweak(case, pk_num, data): data[pk_num] = (pk, xfp, path) psbt = fake_ms_txn(num_ins, num_outs, M, keys, incl_xpubs=True, - outstyles=[out_style], change_outputs=[0], + outstyles=[addr_fmt, "p2wpkh"], change_outputs=[0], hack_change_out=lambda idx: dict(tweak_pubkeys= lambda data: tweak(case, pk_num, data))) - open('debug/last.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) with pytest.raises(Exception) as ee: start_sign(psbt) - signed = end_sign(accept=True, accept_ms_import=False) + end_sign(accept=True, accept_ms_import=False) assert 'Output#0:' in str(ee) - assert 'P2WSH or P2SH change output script' in str(ee) + assert f'{addr_fmt} change output is fraudulent' #assert 'Deception regarding change output' in str(ee) # Check error details are shown time.sleep(.5) title, story = cap_story() - assert story.strip() in str(ee.value.args[0]) - assert len(story.split(':')[-1].strip()), story - - -@pytest.mark.parametrize('repeat', range(2) ) -def test_iss6743(repeat, set_seed_words, sim_execfile, try_sign): - # from SomberNight - psbt_b4 = bytes.fromhex('70736274ff0100520200000001bde05be36069e2e0fe44793c68ad8244bb1a52cc37f152e0fa5b75e40169d7f70000000000fdffffff018b1e000000000000160014ed5180f05c7b1dc980732602c50cda40530e00ad4de11c004f01024289ef0000000000000000007dd565da7ee1cf05c516e89a608968fed4a2450633a00c7b922df66b27afd2e1033a0a4fa4b0a997738ac2f142a395c1f02afcb31d7ffd46a90a0c927a4c411fd704094ef7844f01024289ef0431fcbdcc8000000112d4aaea7292e7870c7eeb3565fa1c1fa8f957fa7c4c24b411d5b4f5710d359a023e63d1e54063525bea286ccb2a0ad7b14560aa31ec4be826afa883141dfe1d53145c9e228d300000800100008000000080010000804f01024289ef04e44b38f1800000014a1960f3a3c86ba355a16a66a548cfb62eeb25663311f7cd662a192896f3777e038cc595159a395e4ec35e477c9523a1512f873e74d303fb03fc9a1503b1ba45271434652fae30000080010000800000008001000080000100df02000000000101d1a321707660769c7f8604d04c9ae2db58cf1ec7a01f4f285cdcbb25ce14bdfd0300000000fdffffff02401f00000000000017a914bfd0b8471a3706c1e17870a4d39b0354bcea57b687c864000000000000160014e608b171d63ec24d9fa252d5c1e45624b14e44700247304402205133eb96df167b895f657cce31c6882840a403013682d9d4651aed2730a7dad502202aaacc045d85d9c711af0c84e7f355cc18bf2f8e6d91774d42ba24de8418a39e012103a58d8eb325abb412eaf927cf11d8b7641c4a468ce412057e47892ca2d13ed6144de11c000104220020a3c65c4e376d82fb3ca45596feee5b08313ad64f38590c1b08bc530d1c0bbfea010569522102ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da621034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef6381921039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f53ae220602ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da60c094ef78400000000030000002206034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef638191c5c9e228d3000008001000080000000800100008000000000030000002206039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f1c34652fae3000008001000080000000800100008000000000030000000000') - # pre 3.2.0 result - psbt_wrong = bytes.fromhex('70736274ff0100520200000001bde05be36069e2e0fe44793c68ad8244bb1a52cc37f152e0fa5b75e40169d7f70000000000fdffffff018b1e000000000000160014ed5180f05c7b1dc980732602c50cda40530e00ad4de11c004f01024289ef0000000000000000007dd565da7ee1cf05c516e89a608968fed4a2450633a00c7b922df66b27afd2e1033a0a4fa4b0a997738ac2f142a395c1f02afcb31d7ffd46a90a0c927a4c411fd704094ef7844f01024289ef0431fcbdcc8000000112d4aaea7292e7870c7eeb3565fa1c1fa8f957fa7c4c24b411d5b4f5710d359a023e63d1e54063525bea286ccb2a0ad7b14560aa31ec4be826afa883141dfe1d53145c9e228d300000800100008000000080010000804f01024289ef04e44b38f1800000014a1960f3a3c86ba355a16a66a548cfb62eeb25663311f7cd662a192896f3777e038cc595159a395e4ec35e477c9523a1512f873e74d303fb03fc9a1503b1ba45271434652fae30000080010000800000008001000080000100df02000000000101d1a321707660769c7f8604d04c9ae2db58cf1ec7a01f4f285cdcbb25ce14bdfd0300000000fdffffff02401f00000000000017a914bfd0b8471a3706c1e17870a4d39b0354bcea57b687c864000000000000160014e608b171d63ec24d9fa252d5c1e45624b14e44700247304402205133eb96df167b895f657cce31c6882840a403013682d9d4651aed2730a7dad502202aaacc045d85d9c711af0c84e7f355cc18bf2f8e6d91774d42ba24de8418a39e012103a58d8eb325abb412eaf927cf11d8b7641c4a468ce412057e47892ca2d13ed6144de11c002202034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef63819483045022100a85d08eef6675803fe2b58dda11a553641080e07da36a2f3e116f1224201931b022071b0ba83ef920d49b520c37993c039d13ae508a1adbd47eb4b329713fcc8baef01010304010000002206034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef638191c5c9e228d3000008001000080000000800100008000000000030000002206039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f1c34652fae300000800100008000000080010000800000000003000000220602ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da60c094ef78400000000030000000104220020a3c65c4e376d82fb3ca45596feee5b08313ad64f38590c1b08bc530d1c0bbfea010569522102ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da621034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef6381921039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f53ae0000') - # psbt_right = bytes.fromhex('70736274ff0100520200000001bde05be36069e2e0fe44793c68ad8244bb1a52cc37f152e0fa5b75e40169d7f70000000000fdffffff018b1e000000000000160014ed5180f05c7b1dc980732602c50cda40530e00ad4de11c004f01024289ef0000000000000000007dd565da7ee1cf05c516e89a608968fed4a2450633a00c7b922df66b27afd2e1033a0a4fa4b0a997738ac2f142a395c1f02afcb31d7ffd46a90a0c927a4c411fd704094ef7844f01024289ef0431fcbdcc8000000112d4aaea7292e7870c7eeb3565fa1c1fa8f957fa7c4c24b411d5b4f5710d359a023e63d1e54063525bea286ccb2a0ad7b14560aa31ec4be826afa883141dfe1d53145c9e228d300000800100008000000080010000804f01024289ef04e44b38f1800000014a1960f3a3c86ba355a16a66a548cfb62eeb25663311f7cd662a192896f3777e038cc595159a395e4ec35e477c9523a1512f873e74d303fb03fc9a1503b1ba45271434652fae30000080010000800000008001000080000100df02000000000101d1a321707660769c7f8604d04c9ae2db58cf1ec7a01f4f285cdcbb25ce14bdfd0300000000fdffffff02401f00000000000017a914bfd0b8471a3706c1e17870a4d39b0354bcea57b687c864000000000000160014e608b171d63ec24d9fa252d5c1e45624b14e44700247304402205133eb96df167b895f657cce31c6882840a403013682d9d4651aed2730a7dad502202aaacc045d85d9c711af0c84e7f355cc18bf2f8e6d91774d42ba24de8418a39e012103a58d8eb325abb412eaf927cf11d8b7641c4a468ce412057e47892ca2d13ed6144de11c002202034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef63819483045022100ae90a7e4c350389816b03af0af46df59a2f53da04cc95a2abd81c0bbc5950c1d02202f9471d6b0664b7a46e81da62d149f688adc7ba2b3413372d26fa618a8460eba01010304010000002206034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef638191c5c9e228d3000008001000080000000800100008000000000030000002206039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f1c34652fae300000800100008000000080010000800000000003000000220602ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da60c094ef78400000000030000000104220020a3c65c4e376d82fb3ca45596feee5b08313ad64f38590c1b08bc530d1c0bbfea010569522102ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da621034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef6381921039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f53ae0000') - # changed with with introduction of signature grinding - psbt_right = bytes.fromhex('70736274ff0100520200000001bde05be36069e2e0fe44793c68ad8244bb1a52cc37f152e0fa5b75e40169d7f70000000000fdffffff018b1e000000000000160014ed5180f05c7b1dc980732602c50cda40530e00ad4de11c004f01024289ef0000000000000000007dd565da7ee1cf05c516e89a608968fed4a2450633a00c7b922df66b27afd2e1033a0a4fa4b0a997738ac2f142a395c1f02afcb31d7ffd46a90a0c927a4c411fd704094ef7844f01024289ef0431fcbdcc8000000112d4aaea7292e7870c7eeb3565fa1c1fa8f957fa7c4c24b411d5b4f5710d359a023e63d1e54063525bea286ccb2a0ad7b14560aa31ec4be826afa883141dfe1d53145c9e228d300000800100008000000080010000804f01024289ef04e44b38f1800000014a1960f3a3c86ba355a16a66a548cfb62eeb25663311f7cd662a192896f3777e038cc595159a395e4ec35e477c9523a1512f873e74d303fb03fc9a1503b1ba45271434652fae30000080010000800000008001000080000100df02000000000101d1a321707660769c7f8604d04c9ae2db58cf1ec7a01f4f285cdcbb25ce14bdfd0300000000fdffffff02401f00000000000017a914bfd0b8471a3706c1e17870a4d39b0354bcea57b687c864000000000000160014e608b171d63ec24d9fa252d5c1e45624b14e44700247304402205133eb96df167b895f657cce31c6882840a403013682d9d4651aed2730a7dad502202aaacc045d85d9c711af0c84e7f355cc18bf2f8e6d91774d42ba24de8418a39e012103a58d8eb325abb412eaf927cf11d8b7641c4a468ce412057e47892ca2d13ed6144de11c002202034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef6381947304402201008b084f53d3064ee381dfb3ff4373b29d6ae765b2af15a4e217e8d5d049c650220576af95d79b8fc686627da8a534141208b225ceb6085cd93fcaffb153ac016ea01010304010000002206034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef638191c5c9e228d3000008001000080000000800100008000000000030000002206039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f1c34652fae300000800100008000000080010000800000000003000000220602ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da60c094ef78400000000030000000104220020a3c65c4e376d82fb3ca45596feee5b08313ad64f38590c1b08bc530d1c0bbfea010569522102ab84641359fa22461b8461515231da63c196614cd22b26e556ed878e30db4da621034211ab0f75c3a307a2f6bf6f09a9e05d3c8edd0ba7a2ac31f432d1045ef6381921039690cf74941da5db291fa8be7348abe3807786732d969eac5d27e0afa909a55f53ae0000') - seed_words = 'all all all all all all all all all all all all' - expect_xfp = swab32(int('5c9e228d', 16)) - assert xfp2str(expect_xfp) == '5c9e228d'.upper() - - # load specific private key - xfp = set_seed_words(seed_words) - assert xfp == expect_xfp - - # check Coldcard derives expected Upub - derivation = "m/48h/1h/0h/1h" # part of devtest/unit_iss6743.py - expect_xpub = 'Upub5SJWbuhs5tM4mkJST69tnpGGaf8dDTqByx3BLSocWFpq5YLh1fky4DQTFGQVG6nCSqZfUiAAeStdxSQteUcfMsWjDkhniZx4GdwpB18Tnbq' - - pub = sim_execfile('devtest/unit_iss6743.py') - assert pub == expect_xpub - - # verify psbt globals section - tp = BasicPSBT().parse(psbt_b4) - (hdr_xpub, hdr_path), = [(v,k) for v,k in tp.xpubs if k[0:4] == pack(' %s was %d, gonna be %d' % ( - xfp2str(xfp), dp, sk.node.depth, dp.count('/'))) - sk.node.depth = dp.count('/') - config += '%s: %s\n' % (xfp2str(xfp), sk.hwif(as_private=False)) - - title, story = offer_ms_import(config) - assert f'Policy: {M} of {N}\n' in story - assert f'P2SH-P2WSH' in story - assert 'Derivation:\n Varies' in story - assert f' Varies ({len(set(derivs))})\n' in story - press_select() +@pytest.mark.ms_danger +def test_danger_warning(request, clear_miniscript, import_ms_wallet, cap_story, fake_ms_txn, + start_sign, sim_exec, sim_root_dir, goto_home, pick_menu_item, + need_keypress): goto_home() - pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - pick_menu_item(f'{M}/{N}: impmany') + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item("Skip Checks?") + need_keypress("4") + pick_menu_item("Skip Checks") - pick_menu_item('Coldcard Export') - contents = load_export(way, label="Coldcard multisig setup", sig_check=False, is_json=False) - lines = io.StringIO(contents).readlines() + time.sleep(.1) - for xfp,_,_ in keys: - m = xfp2str(xfp) - assert any(m in ln for ln in lines) + clear_miniscript() + M,N = 2,3 + keys = import_ms_wallet(M, N, accept=True, addr_fmt="p2wsh") + psbt = fake_ms_txn(1, 1, M, keys, inp_addr_fmt="p2wsh", incl_xpubs=True) - pick_menu_item('Electrum Wallet') + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) - time.sleep(.25) + start_sign(psbt) title, story = cap_story() - assert 'This saves a skeleton Electrum wallet file' in story - press_select() - - el = load_export(way, label="Electrum multisig wallet", sig_check=False, is_json=True) - - assert el['seed_version'] == 17 - assert el['wallet_type'] == f"{M}of{N}" - for n in range(1, N+1): - kk = f'x{n}/' - assert kk in el - co = el[kk] - assert 'Coldcard' in co['label'] - dd = co['derivation'] - assert (dd in derivs) or (dd == actual) or ("42069h" in dd) or (dd == 'm') - - clear_ms() - - -@pytest.mark.ms_danger -@pytest.mark.parametrize('descriptor', [True, False]) -def test_danger_warning(request, descriptor, clear_ms, import_ms_wallet, cap_story, fake_ms_txn, start_sign, sim_exec): - # note: cant use has_ms_checks fixture here - danger_mode = (request.config.getoption('--ms-danger')) - sim_exec(f'from multisig import MultisigWallet; MultisigWallet.disable_checks={danger_mode}') - clear_ms() - M,N = 2,3 - keys = import_ms_wallet(M, N, accept=1, descriptor=descriptor, addr_fmt="p2wsh") - psbt = fake_ms_txn(1, 1, M, keys, incl_xpubs=True) + assert 'WARNING' in story + assert 'Danger' in story + assert 'Some miniscript checks are disabled' in story - open('debug/last.psbt', 'wb').write(psbt) + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item("Skip Checks?") + pick_menu_item("Normal") start_sign(psbt) title, story = cap_story() - if danger_mode: - assert 'WARNING' in story - assert 'Danger' in story - assert 'Some multisig checks are disabled' in story - else: - assert 'WARNING' not in story + assert 'WARNING' not in story @pytest.mark.parametrize('change', [True, False]) @pytest.mark.parametrize('desc', ["multi", "sortedmulti"]) @pytest.mark.parametrize('start_idx', [1000, MAX_BIP32_IDX, 0]) @pytest.mark.parametrize('M_N', [(2,3), (15,15)]) @pytest.mark.parametrize('addr_fmt', [AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH] ) -def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu, +def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_miniscript, cap_menu, need_keypress, goto_home, pick_menu_item, cap_story, import_ms_wallet, make_multisig, settings_set, - enter_number, set_addr_exp_start_idx, desc): - clear_ms() + enter_number, set_addr_exp_start_idx, desc, + cap_screen_qr, press_cancel, press_right): + clear_miniscript() M, N = M_N wal_name = f"ax{M}-{N}-{addr_fmt}" @@ -2176,14 +1576,11 @@ def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu, derivs = [deriv.format(idx=i) for i in range(N)] - clear_ms() - - descriptor = None bip67 = True if desc == "multi": - descriptor, bip67 = True, False - keys = import_ms_wallet(M, N, accept=1, keys=keys, name=wal_name, derivs=derivs, - addr_fmt=text_a_fmt, descriptor=descriptor, bip67=bip67) + bip67 = False + keys = import_ms_wallet(M, N, accept=True, keys=keys, name=wal_name, + derivs=derivs, addr_fmt=text_a_fmt, bip67=bip67) goto_home() pick_menu_item("Address Explorer") @@ -2191,12 +1588,7 @@ def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu, set_addr_exp_start_idx(start_idx) - m = cap_menu() - if wal_name in m: - pick_menu_item(wal_name) - else: - # descriptor - pick_menu_item(f"{M}-of-{N}") + pick_menu_item(wal_name) time.sleep(.5) title, story = cap_story() @@ -2209,27 +1601,42 @@ def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu, # once change is selected - do not offer this option again assert "change addresses." not in story assert "(0)" not in story + # unwrap text a bit if change: - story = story.replace("=>\n", "=> ").replace('1/0]\n =>', "1/0 =>") + story = story.replace("=>\n", "=> ").replace('1/0]\n =>', "1/0] =>") else: - story = story.replace("=>\n", "=> ").replace('0/0]\n =>', "0/0 =>") + story = story.replace("=>\n", "=> ").replace('0/0]\n =>', "0/0] =>") maps = [] for ln in story.split('\n'): if '=>' not in ln: continue - path,chk,addr = ln.split() + path,chk,addr = ln.split(" ", 2) assert chk == '=>' assert '/' in path + path = path.replace("[", "").replace("]", "") - maps.append( (path, addr) ) + maps.append((path, addr)) if start_idx <= 2147483638: assert len(maps) == 10 else: assert len(maps) == (MAX_BIP32_IDX - start_idx) + 1 + need_keypress(KEY_QR) + qr_addrs = [] + for i in range(10): + addr_qr = cap_screen_qr().decode() + if addr_fmt == AF_P2WSH: + # segwit addresses are case insensitive + addr_qr = addr_qr.lower() + qr_addrs.append(addr_qr) + press_right() + time.sleep(.2) + press_cancel() + + c = 0 for idx, (subpath, addr) in enumerate(maps, start=start_idx): chng_idx = 1 if change else 0 path_mapper = lambda co_idx: str_to_path(derivs[co_idx]) + [chng_idx, idx] @@ -2238,74 +1645,95 @@ def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu, path_mapper=path_mapper, bip67=bip67) assert int(subpath.split('/')[-1]) == idx + # assert int(subpath.split('/')[-2]) == chng_idx #print('../0/%s => \n %s' % (idx, B2A(script))) - start, end = detruncate_address(addr) - assert expect.startswith(start) - assert expect.endswith(end) + addr = addr_from_display_format(addr) + assert addr == expect == qr_addrs[c] + c += 1 def test_dup_ms_wallet_bug(goto_home, pick_menu_item, press_select, import_ms_wallet, - clear_ms, is_q1): + clear_miniscript, is_q1): M = 2 N = 3 deriv = ["m/48h/1h/0h/69h/1"]*N fmts = [ 'p2wsh', 'p2sh-p2wsh'] - clear_ms() + clear_miniscript() for n, ty in enumerate(fmts): - import_ms_wallet(M, N, name=f'name-{n}', accept=1, derivs=deriv, addr_fmt=ty) + import_ms_wallet(M, N, name=f'name-{n}', accept=True, derivs=deriv, addr_fmt=ty) goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") # drill down to second one time.sleep(.1) - pick_menu_item('2/3: name-1') + pick_menu_item('name-1') pick_menu_item('Delete') press_select() # BUG: pre v4.0.3, would be showing a "Yikes" referencing multisig:419 at this point - pick_menu_item('2/3: name-0') + pick_menu_item('name-0') pick_menu_item('Delete') press_select() - clear_ms() + clear_miniscript() -@pytest.mark.parametrize('M_N', [(2, 3), (2, 2), (3, 5), (15, 15)]) -@pytest.mark.parametrize('addr_fmt', [ AF_P2SH, AF_P2WSH, AF_P2WSH_P2SH ]) +@pytest.mark.parametrize('M_N', [(2, 3), (3, 5), (15, 15)]) +@pytest.mark.parametrize('addr_fmt', ["p2wsh", "p2sh-p2wsh", "p2sh"]) @pytest.mark.parametrize('int_ext_desc', [True, False]) +@pytest.mark.parametrize('json_wrapped', [True, False]) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) @pytest.mark.parametrize('desc', ["multi", "sortedmulti"]) -def test_import_desciptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, goto_home, pick_menu_item, - press_select, clear_ms, cap_story, microsd_path, virtdisk_path, - nfc_read_text, load_export, is_q1, desc): - clear_ms() +def test_import_descriptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, goto_home, pick_menu_item, + press_select, clear_miniscript, cap_story, microsd_path, virtdisk_path, + nfc_read_text, load_export, is_q1, desc, sim_root_dir, skip_if_useless_way, + json_wrapped): + skip_if_useless_way(way) M, N = M_N - import_ms_wallet(M, N, addr_fmt=addr_fmt, accept=1, descriptor=True, - int_ext_desc=int_ext_desc, bip67=False if desc == "multi" else True) + if (way == "nfc") and (M == N == 15): + raise pytest.skip("too big for simulated NFC") + + clear_miniscript() goto_home() - pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - press_select() # only one enrolled multisig - choose it + + name = None + if json_wrapped: + # descriptor wrapped in JSON with name key + name = "aaa" + + import_ms_wallet( + M, N, addr_fmt=addr_fmt, accept=True, way=way, name=name, + int_ext_desc=int_ext_desc, bip67=False if desc == "multi" else True, + ) + with open(f'{sim_root_dir}/debug/last-ms.txt', 'r') as f: + desc_import = f.read().strip() + + if json_wrapped: + desc_obj = json.loads(desc_import) + desc_import = desc_obj["desc"] + pick_menu_item(name) + else: + press_select() # only one enrolled multisig - choose it + pick_menu_item('Descriptors') pick_menu_item('Export') - contents = load_export(way, label="Descriptor multisig setup", is_json=False, sig_check=False) + contents = load_export(way, label="Multisig", is_json=False) desc_export = contents.strip() - with open("debug/last-ms.txt", "r") as f: - desc_import = f.read().strip() + normalized = parse_desc_str(desc_export) - # as new format is not widely supported we only allow to import it - no export yet + # needs bitcoin core client at least on 29.0 if int_ext_desc: - # checksum will differ - ignore it - assert desc_import.split("#")[0] == normalized.split("#")[0].replace("0/*", "<0;1>/*") - else: assert desc_import == normalized + else: + # we always export with multipath + assert normalized.split("#")[0] == desc_import.split("#")[0].replace("/0/*", "/<0;1>/*") starts_with = MULTI_FMT_TO_SCRIPT[addr_fmt].split("%")[0] assert normalized.startswith(starts_with) assert f"{desc}(" in desc_export @@ -2317,24 +1745,25 @@ def test_import_desciptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, go @pytest.mark.parametrize("start_idx", [2147483540, MAX_BIP32_IDX, 0]) @pytest.mark.parametrize('M_N', [(2, 2), (3, 5), (15, 15)]) @pytest.mark.parametrize('addr_fmt', [AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH]) -@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) -def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_keypress, +@pytest.mark.parametrize('way', ["sd", "nfc"]) # vdisk +def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_miniscript, goto_home, need_keypress, pick_menu_item, cap_menu, cap_story, make_multisig, import_ms_wallet, microsd_path, bitcoind_d_wallet_w_sk, use_regtest, load_export, way, is_q1, press_select, start_idx, settings_set, set_addr_exp_start_idx, - desc): + desc, garbage_collector, virtdisk_path, skip_if_useless_way): + skip_if_useless_way(way) use_regtest() - clear_ms() + clear_miniscript() bitcoind = bitcoind_d_wallet_w_sk M, N = M_N - # whether to import as descriptor or old school to CC - descriptor = random.choice([True, False]) + path_f = microsd_path if way == "sd" else virtdisk_path + bip67 = True if desc == "multi": bip67 = False - descriptor = True settings_set("aei", True if start_idx else False) + # adding this as parameter doubles the time this runs wal_name = f"ax{M}-{N}-{addr_fmt}" @@ -2349,9 +1778,9 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke derivs = [deriv.format(idx=i) for i in range(N)] - clear_ms() - import_ms_wallet(M, N, accept=1, keys=keys, name=wal_name, derivs=derivs, - addr_fmt=text_a_fmt, descriptor=descriptor, bip67=bip67) + clear_miniscript() + import_ms_wallet(M, N, accept=True, keys=keys, name=wal_name, derivs=derivs, + addr_fmt=text_a_fmt, bip67=bip67) goto_home() pick_menu_item("Address Explorer") @@ -2359,10 +1788,7 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke set_addr_exp_start_idx(start_idx) m = cap_menu() - if descriptor: - wal_name = m[-2 if start_idx else -1] - else: - assert wal_name in m + assert wal_name in m pick_menu_item(wal_name) time.sleep(0.2) @@ -2377,29 +1803,34 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke assert "change addresses." not in story assert "(0)" not in story - contents = load_export(way, label="Address summary", is_json=False, sig_check=False) + if way != "nfc": + contents, exp_fname = load_export(way, label="Address summary", + is_json=False, ret_fname=True) + garbage_collector.append(path_f(exp_fname)) + else: + contents = load_export(way, label="Address summary", is_json=False) addr_cont = contents.strip() goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") press_select() # only one enrolled multisig - choose it pick_menu_item('Descriptors') pick_menu_item("Bitcoin Core") - contents = load_export(way, label="Bitcoin Core multisig setup", is_json=False, sig_check=False) + if way != "nfc": + contents, exp_fname = load_export(way, label="Bitcoin Core Multisig", is_json=False, + ret_fname=True) + garbage_collector.append(path_f(exp_fname)) + else: + contents = load_export(way, label="Bitcoin Core Multisig", is_json=False) text = contents.replace("importdescriptors ", "").strip() # remove junk r1 = text.find("[") r2 = text.find("]", -1, 0) text = text[r1: r2] core_desc_object = json.loads(text) - if change: - # in descriptor.py we always append external descriptor first - desc_export = core_desc_object[1]["desc"] - else: - desc_export = core_desc_object[0]["desc"] + desc_core = core_desc_object[0]["desc"] - if descriptor: - assert f"({desc}(" in desc_export + assert f"({desc}(" in desc_core if way == "nfc": end_idx = start_idx + 9 @@ -2418,24 +1849,22 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke cc_addrs = addr_cont.split("\n")[1:] part_addr_index = 1 - bitcoind_addrs = bitcoind.deriveaddresses(desc_export, addr_range) + ea, ia = bitcoind.deriveaddresses(desc_core, addr_range) + bitcoind_addrs = ia if change else ea for idx, cc_item in enumerate(cc_addrs): cc_item = cc_item.split(",") - partial_address = cc_item[part_addr_index] - _start, _end = partial_address.split("___") + address = cc_item[part_addr_index] if way != "nfc": - _start, _end = _start[1:], _end[:-1] - assert bitcoind_addrs[idx].startswith(_start) - assert bitcoind_addrs[idx].endswith(_end) + address = address[1:-1] + assert bitcoind_addrs[idx] == address @pytest.mark.bitcoind -def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, microsd_wipe, goto_home, need_keypress, +def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_miniscript, microsd_wipe, goto_home, need_keypress, pick_menu_item, cap_story, load_export, microsd_path, cap_menu, try_sign, is_q1, press_select): - use_regtest() - clear_ms() + clear_miniscript() microsd_wipe() M,N = 2,2 cosigner = bitcoind.create_wallet(wallet_name=f"bitcoind--signer-wit-utxo", disable_private_keys=False, blank=False, @@ -2446,7 +1875,7 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m ) goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") pick_menu_item('Export XPUB') time.sleep(0.5) title, story = cap_story() @@ -2454,8 +1883,8 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m press_select() need_keypress("0") # account press_select() - xpub_obj = load_export("sd", label="Multisig XPUB", is_json=True, sig_check=False) - template = xpub_obj["p2sh_desc"] + xpub_obj = load_export("sd", label="Multisig XPUB", is_json=True) + cc_key = xpub_obj["p2sh_key_exp"] # get key from bitcoind cosigner target_desc = "" bitcoind_descriptors = cosigner.listdescriptors()["descriptors"] @@ -2465,7 +1894,7 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m core_desc, checksum = target_desc.split("#") # remove pkh(....) core_key = core_desc[4:-1] - desc = template.replace("M", str(M), 1).replace("...", core_key) + desc = f"sh(sortedmulti({M},{core_key},{cc_key}))" desc_info = ms.getdescriptorinfo(desc) desc_w_checksum = desc_info["descriptor"] # with checksum name = f"core{M}of{N}_legacy.txt" @@ -2473,11 +1902,11 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m f.write(desc_w_checksum + "\n") goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - pick_menu_item('Import from File') + pick_menu_item("Multisig/Miniscript") + pick_menu_item('Import') time.sleep(0.3) _, story = cap_story() - if "Press (1) to import multisig wallet file from SD Card" in story: + if "Press (1) to import miniscript" in story: # in case Vdisk is enabled need_keypress("1") @@ -2487,18 +1916,16 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m assert "Create new multisig wallet?" in story assert name.split(".")[0] in story assert f"{M} of {N}" in story - assert f"All {N} co-signers must approve spends" in story assert "P2SH" in story - assert "Derivation:\n Varies (2)" in story press_select() # approve multisig import goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") menu = cap_menu() pick_menu_item(menu[0]) # pick imported descriptor multisig wallet pick_menu_item("Descriptors") pick_menu_item("Bitcoin Core") - text = load_export("sd", label="Bitcoin Core multisig setup", is_json=False, sig_check=False) + text = load_export("sd", label="Bitcoin Core Multisig", is_json=False) text = text.replace("importdescriptors ", "").strip() # remove junk r1 = text.find("[") @@ -2539,165 +1966,260 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m try_sign(updated) +@pytest.fixture +def get_cc_key(dev): + def doit(path, subderiv=None): + # cc device key + cc_key = dev.send_recv(CCProtocolPacker.get_xpub(path), timeout=None) + if subderiv is None: + cc_key = cc_key + "/<0;1>/*" + + if not path: + return cc_key + + master_xfp_str = struct.pack('/* allowed", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/1/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#sj7lxn0l"), - ("Invalid subderivation path - only 0/* or <0;1>/* allowed", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#fy9mm8dt"), - ("Key origin info is required", "wsh(sortedmulti(2,tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))#ypuy22nw"), - ("Malformed key derivation info", "wsh(sortedmulti(2,[0f056943]tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))#nhjvt4wd"), - ("Invalid subderivation path - only 0/* or <0;1>/* allowed", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))#gs2fqgl6"), - ("Invalid subderivation path - only 0/* or <0;1>/* allowed", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0))#s487stua"), + ("need multipath", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/1/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#sj7lxn0l"), + ("All keys must be ranged", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/0,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#9h02aqg5"), + ("need multipath", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#fy9mm8dt"), + # ("Key origin info is required", "wsh(sortedmulti(2,tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))#ypuy22nw"), + ("wrong pubkey", "wsh(sortedmulti(2,[0f056943]tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))#nhjvt4wd"), + ("deriv len != xpub depth", "wsh(sortedmulti(2,[0f056943/0h]tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M))"), + ("All keys must be ranged", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0))#s487stua"), ("Cannot use hardened sub derivation path", "wsh(sortedmulti(2,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0'/*))#3w6hpha3"), - # ("Unsupported descriptor", "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))#t2zpj2eu"), - ("Unsupported descriptor", "pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)#ml40v0wf"), ("M must be <= N", "wsh(sortedmulti(3,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#uueddtsy"), ]) -def test_exotic_descriptors(desc, clear_ms, goto_home, need_keypress, pick_menu_item, cap_menu, +def test_exotic_descriptors(desc, clear_miniscript, goto_home, need_keypress, pick_menu_item, cap_menu, cap_story, make_multisig, microsd_path, use_regtest, is_q1, press_select): use_regtest() - clear_ms() + clear_miniscript() msg, desc = desc name = "exotic.txt" if os.path.exists(microsd_path(name)): @@ -2828,22 +2392,22 @@ def test_exotic_descriptors(desc, clear_ms, goto_home, need_keypress, pick_menu_ f.write(desc + "\n") goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') - pick_menu_item('Import from File') + pick_menu_item("Multisig/Miniscript") + pick_menu_item('Import') time.sleep(0.1) _, story = cap_story() - if "Press (1) to import multisig wallet file from SD Card" in story: + if "Press (1) to import miniscript wallet file from SD Card" in story: need_keypress("1") time.sleep(0.1) pick_menu_item(name) _, story = cap_story() - assert "Failed to import" in story + assert "Failed to import miniscript" in story assert msg in story press_select() -def test_ms_wallet_ordering(clear_ms, import_ms_wallet, try_sign_microsd, fake_ms_txn): - clear_ms() +def test_ms_wallet_ordering(clear_miniscript, import_ms_wallet, try_sign_microsd, fake_ms_txn): + clear_miniscript() all_out_styles = list(unmap_addr_fmt.keys()) index = all_out_styles.index("p2sh-p2wsh") all_out_styles[index] = "p2wsh-p2sh" @@ -2855,21 +2419,20 @@ def test_ms_wallet_ordering(clear_ms, import_ms_wallet, try_sign_microsd, fake_m # WHY: as we store wallets in list, they are ordered by their addition/import. Iterating over # wallet candindates in psbt.py M are equal N differs --> assertion error name = f'ms1' - import_ms_wallet(3, 6, name=name, accept=1, do_import=True, addr_fmt="p2wsh") + import_ms_wallet(3, 6, name=name, accept=True, do_import=True, addr_fmt="p2wsh") name = f'ms2' - keys3 = import_ms_wallet(3, 5, name=name, accept=1, do_import=True, addr_fmt="p2wsh") - - psbt = fake_ms_txn(5, 5, 3, keys3, outstyles=all_out_styles, segwit_in=True, incl_xpubs=True) + keys3 = import_ms_wallet(3, 5, name=name, accept=True, do_import=True, addr_fmt="p2wsh") - open('debug/last.psbt', 'wb').write(psbt) + psbt = fake_ms_txn(5, 5, 3, keys3, outstyles=all_out_styles, inp_addr_fmt="p2wsh", incl_xpubs=True) try_sign_microsd(psbt, encoding='base64') @pytest.mark.parametrize("descriptor", [True, False]) @pytest.mark.parametrize("m_n", [(2, 3), (3, 5), (5, 10)]) -def test_ms_xpub_ordering(descriptor, m_n, clear_ms, make_multisig, import_ms_wallet, try_sign_microsd, fake_ms_txn): - clear_ms() +def test_ms_xpub_ordering(descriptor, m_n, clear_miniscript, make_multisig, import_ms_wallet, + try_sign_microsd, fake_ms_txn): + clear_miniscript() M, N = m_n all_out_styles = list(unmap_addr_fmt.keys()) index = all_out_styles.index("p2sh-p2wsh") @@ -2878,26 +2441,23 @@ def test_ms_xpub_ordering(descriptor, m_n, clear_ms, make_multisig, import_ms_wa keys = make_multisig(M, N) all_options = list(itertools.combinations(keys, len(keys))) for opt in all_options: - import_ms_wallet(M, N, keys=opt, name=name, accept=1, do_import=True, - addr_fmt="p2wsh", descriptor=descriptor) + import_ms_wallet(M, N, keys=opt, name=name, accept=True, do_import=True, addr_fmt="p2wsh") psbt = fake_ms_txn(5, 5, M, opt, outstyles=all_out_styles, - segwit_in=True, incl_xpubs=True) - open('debug/last.psbt', 'wb').write(psbt) + inp_addr_fmt="p2wsh", incl_xpubs=True) try_sign_microsd(psbt, encoding='base64') for opt_1 in all_options: # create PSBT with original keys order psbt = fake_ms_txn(5, 5, M, opt_1, outstyles=all_out_styles, - segwit_in=True, incl_xpubs=True) - open('debug/last.psbt', 'wb').write(psbt) + inp_addr_fmt="p2wsh", incl_xpubs=True) try_sign_microsd(psbt, encoding='base64') @pytest.mark.parametrize('cmn_pth_from_root', [True, False]) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) -@pytest.mark.parametrize('M_N', [(3, 15), (2, 2), (3, 5), (15, 15)]) +@pytest.mark.parametrize('M_N', [(2, 3), (3, 5), (15, 15)]) @pytest.mark.parametrize('desc', ["multi", "sortedmulti"]) @pytest.mark.parametrize('addr_fmt', [AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH]) -def test_multisig_descriptor_export(M_N, way, addr_fmt, cmn_pth_from_root, clear_ms, make_multisig, +def test_multisig_descriptor_export(M_N, way, addr_fmt, cmn_pth_from_root, clear_miniscript, make_multisig, import_ms_wallet, goto_home, pick_menu_item, cap_menu, nfc_read_text, microsd_path, cap_story, need_keypress, load_export, desc): @@ -2905,12 +2465,12 @@ def test_multisig_descriptor_export(M_N, way, addr_fmt, cmn_pth_from_root, clear def choose_multisig_wallet(): goto_home() pick_menu_item('Settings') - pick_menu_item('Multisig Wallets') + pick_menu_item("Multisig/Miniscript") menu = cap_menu() pick_menu_item(menu[0]) M, N = M_N - wal_name = f"reexport_{M}-{N}-{addr_fmt}" + wal_name = f"reexport" dd = { AF_P2WSH: ("m/48h/1h/0h/2h/{idx}", 'p2wsh'), @@ -2920,37 +2480,23 @@ def choose_multisig_wallet(): deriv, text_a_fmt = dd[addr_fmt] keys = make_multisig(M, N, unique=1, deriv=None if cmn_pth_from_root else deriv) derivs = [deriv.format(idx=i) for i in range(N)] - clear_ms() - import_ms_wallet(M, N, accept=1, keys=keys, name=wal_name, derivs=None if cmn_pth_from_root else derivs, - addr_fmt=text_a_fmt, descriptor=True, common="m/45h" if cmn_pth_from_root else None, + clear_miniscript() + import_ms_wallet(M, N, accept=True, keys=keys, name=wal_name, + derivs=None if cmn_pth_from_root else derivs, + addr_fmt=text_a_fmt, common="m/45h" if cmn_pth_from_root else None, bip67=False if desc == "multi" else True) # get bare descriptor choose_multisig_wallet() pick_menu_item("Descriptors") pick_menu_item("Export") - contents = load_export(way, label="Descriptor multisig setup", is_json=False, sig_check=False) + contents = load_export(way, label="Multisig", is_json=False) bare_desc = contents.strip() - # get pretty descriptor - choose_multisig_wallet() - pick_menu_item("Descriptors") - pick_menu_item("View Descriptor") - for _ in range(5): - _, story = cap_story() - if "Press (1) to export" in story: - need_keypress("1") - break - else: - time.sleep(1) - - contents = load_export(way, label="Descriptor multisig setup", is_json=False, sig_check=False) - pretty_desc = contents.strip() - # get core descriptor json choose_multisig_wallet() pick_menu_item("Descriptors") pick_menu_item("Bitcoin Core") - core_desc_text = load_export(way, label="Bitcoin Core multisig setup", is_json=False, sig_check=False) + core_desc_text = load_export(way, label="Bitcoin Core Multisig", is_json=False) # remove junk text = core_desc_text.replace("importdescriptors ", "").strip() @@ -2959,129 +2505,173 @@ def choose_multisig_wallet(): text = text[r1: r2] core_desc_object = json.loads(text) - # get descriptor from view descriptor - choose_multisig_wallet() - pick_menu_item("Descriptors") - pick_menu_item("View Descriptor") - for _ in range(5): - try: - _, story = cap_story() - if "Press (1)" in story: - break - except: - time.sleep(1) - - view_desc = story.strip().split("\n\n")[1] - # assert that bare and pretty are the same after parse assert f"({desc}(" in bare_desc - assert bare_desc == view_desc - assert parse_desc_str(pretty_desc) == bare_desc - for obj in core_desc_object: - if obj["internal"]: - pass - else: - assert obj["desc"] == bare_desc - clear_ms() + assert core_desc_object[0]["desc"] == bare_desc + clear_miniscript() -def test_multisig_name_validation(microsd_path, offer_ms_import): - with open("data/multisig/export-p2wsh-myself.txt", "r") as f: - config = f.read() +def test_chain_switching(use_mainnet, use_regtest, settings_get, settings_set, + clear_miniscript, goto_home, cap_menu, pick_menu_item, + need_keypress, import_ms_wallet): + clear_miniscript() + use_regtest() - c0 = config.replace("Name: CC-2-of-4", "Name: eê") + # cannot import XPUBS when testnet/regtest enabled + with pytest.raises(Exception): + import_ms_wallet(3, 3, addr_fmt="p2wsh", accept=True, chain="BTC") - with pytest.raises(Exception) as e: - offer_ms_import(c0, allow_non_ascii=True) - assert "must be ascii" in e.value.args[0] + on_regtest = "xtn0" + import_ms_wallet(2, 2, name=on_regtest, addr_fmt="p2wsh", accept=True, chain="XRT") + res = settings_get("miniscript") + assert len(res) == 1 + assert res[0][-1]["ct"] == "XRT" - c0 = config.replace("Name: CC-2-of-4", "Name: eee\teee") + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert "(none setup yet)" not in m + assert on_regtest == m[0] + goto_home() + settings_set("chain", "BTC") + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert "(none setup yet)" in m + on_mainnet = "btc0" + import_ms_wallet(3, 3, addr_fmt="p2wsh", accept=True, chain="BTC", name=on_mainnet) + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert on_mainnet == m[0] + assert on_regtest not in m - with pytest.raises(Exception) as e: - offer_ms_import(c0, allow_non_ascii=True) - assert "must be ascii" in e.value.args[0] + goto_home() + settings_set("chain", "XTN") + import_ms_wallet(4, 4, addr_fmt="p2wsh", accept=True, chain="XTN", name="xtn1") + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + time.sleep(0.1) + m = cap_menu() + assert "(none setup yet)" not in m + assert on_regtest == m[0] + assert "xtn1" == m[1] + assert on_mainnet not in m -def test_multisig_deriv_path_migration(settings_set, clear_ms, import_ms_wallet, - press_cancel, settings_get, make_multisig, - goto_home, start_sign, cap_story, end_sign, - pick_menu_item, cap_menu): - # this test case simulates multisig wallets imported to CC before 5.3.0 - # release; these wallets, saved in user settings, still have "'" in derivation - # paths; 5.3.1 firmware implements migration to "h" in MultisigWallet.deserialize +@pytest.mark.parametrize("desc", [ + ("wsh(sortedmulti(2," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*," + "[0f056943/84'/1'/9']tpubDC7jGaaSE66QBAcX8TUD3JKWari1zmGH4gNyKZcrfq6NwCofKujNF2kyeVXgKshotxw5Yib8UxLrmmCmWd8NVPVTAL8rGfMdc7TsAKqsy6y/<0;1>/*" + "))"), + ("wsh(sortedmulti(2," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*," + "[0f056943/84'/1'/0']tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<2;3>/*" + "))"), +]) +def test_same_key_account_based_multisig(goto_home, need_keypress, pick_menu_item, cap_story, + clear_miniscript, microsd_path, load_export, desc, + offer_minsc_import): + clear_miniscript() + _, story = offer_minsc_import(desc) + # this is allowed now + assert "Create new multisig wallet" in story - clear_ms() - deriv, text_a_fmt = ("m/48h/1h/0h/2h/{idx}", 'p2wsh') - keys = make_multisig(2, 3, unique=1, deriv=deriv) - derivs = [deriv.format(idx=i) for i in range(3)] - import_ms_wallet(2, 3, accept=True, keys=keys, name="ms1", - derivs=derivs, addr_fmt=text_a_fmt) - time.sleep(.1) +def test_multisig_name_validation(microsd_path, offer_minsc_import): + with open("data/multisig/desc-p2wsh-myself.txt", "r") as f: + config = f.read() - import_ms_wallet(3, 5, name="ms2", addr_fmt='p2wsh-p2sh', accept=True) - time.sleep(.1) + with pytest.raises(Exception) as e: + offer_minsc_import(json.dumps({"name": "eê", "desc": config}), allow_non_ascii=True) + assert "must be ascii" in e.value.args[0] - ms = settings_get("multisig") - pths0 = ms[0][3]["d"] - new_pths0 = [p.replace("h", "'") for p in pths0] - ms[0][3]["d"] = new_pths0 - - ms[1][3]["pp"] = ms[1][3]["pp"].replace("h", "'") - - # this matches data/PSBT - ms.append( - ( - 'ms', - (2, 2), - [(2285969762, 0, 'tpubDEy2hd2VTrqbBS8cS2svq12UmjGM2j7FHmocjHzAXfVhmJdhBFVVbmAi13humi49esaAuSmz36NEJ6GL3u58RzNuUkExP9vL4d81PM3s8u6'), - (1130956047, 1, 'tpubDEFX3QojMWh7x4vSAHN17wpsywpP78aSs2t6nyELHuq1k34gub9mQ7QiaHNCBAYjSQ4UCMMpfBkf5np1cTQaStrvvRCxwxZ7kZaGHqYxUv3')], - {'ch': 'XTN', 'ft': 14, 'd': ["m/48'/0'/99'/2'", "m/48'/0'/33'/2'"]} - ) - ) - settings_set("multisig", ms) + with pytest.raises(Exception) as e: + offer_minsc_import(json.dumps({"name": "eee\teee", "desc": config}), allow_non_ascii=True) + assert "must be ascii" in e.value.args[0] - # psbt from nunchuk, with global xpubs belonging to above ms wallet - b64_psbt = "cHNidP8BAF4CAAAAAfkDjXlS32gzOjVhSRArKxvkAecMTnp1g8wwMJTtq74/AAAAAAD9////AekaAAAAAAAAIgAgzs2e4h4vctbFvvauK+QVFAPzCFnMi1H9hTacH7498P8AAAAATwEENYfPBC7g3O2AAAACLvzTgnL7V0DNOnISJdvOgq/6Pw6DAtkPflmZ+Hc04qwC5CShG0rDIlh8gu7gH2NMBLfrIzYSzoSomnVHeMxtxVQUDwVpQzAAAIAAAACAIQAAgAIAAIBPAQQ1h88EkEB8moAAAALv/1L+Cfeg2EPc01pS00f18DIdU5BOeExlGsXyEFOKGwL71tcAiRuL4Bs+uT1JJjU6AbR3j3X60/rI+rTMJmnOgRRiIUGIMAAAgAAAAIBjAACAAgAAgAABAIkCAAAAAZ5Im3CxbYDyByyrr4luss5vr+s0r7Vt8pK+OvicPLO7AAAAAAD9////AnM2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnTmvqUXAAAAACJRIJF/VcIeZ3E4f+ZEjwiUl5AUUxBJgoaEaPaHHJecq18lq+4qAAEBK3M2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnQiAgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3kcwRAIgHNmbk4J9wu4ljq6UouY132eX1i/2jWvJjuuWWyLRFScCIBPyPCuZ/Hmd06h9KtVkSropBonIuqIc/BK8JZ50YKp/AQEDBAEAAAABBUdSIQMBr34TVHrqSk8K6505//5YTOkHmHqF83J8iUURtL/ptCEDUXZhsRMLD/PMbva5f7RgF6u5D5v+99Mk8kOlx0C/w95SriIGAwGvfhNUeupKTwrrnTn//lhM6QeYeoXzcnyJRRG0v+m0HA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAAAAAAiBgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3hxiIUGIMAAAgAAAAIBjAACAAgAAgAAAAAAAAAAAAAEBR1IhAscIZVvBcy3Q0GKO4UqR3gDB3pm/tWas8siH3Ej8MmuCIQN8lTj0MMTpT+Dlk2MbMdAaL93hezzNP3WDsRn/gwlVQlKuIgICxwhlW8FzLdDQYo7hSpHeAMHemb+1ZqzyyIfcSPwya4IcYiFBiDAAAIAAAACAYwAAgAIAAIAAAAAAAQAAACICA3yVOPQwxOlP4OWTYxsx0Bov3eF7PM0/dYOxGf+DCVVCHA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAEAAAAA" - goto_home() - # in time of creatin of PSBT, lopp was making testnet3 unusable... - settings_set("fee_limit", -1) - start_sign(base64.b64decode(b64_psbt)) - title, story = cap_story() - assert title == "OK TO SEND?" - end_sign() - settings_set("fee_limit", 10) # rollback - pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") - m = cap_menu() - for msi in m[:3]: # three wallets imported - pick_menu_item(msi) - pick_menu_item("View Details") - time.sleep(.1) - _, story = cap_story() - assert "'" not in story - press_cancel() - press_cancel() +# def test_multisig_deriv_path_migration(settings_set, clear_miniscript, import_ms_wallet, +# press_cancel, settings_get, make_multisig, +# goto_home, start_sign, cap_story, end_sign, +# pick_menu_item, cap_menu): +# # this test case simulates multisig wallets imported to CC before 5.3.0 +# # release; these wallets, saved in user settings, still have "'" in derivation +# # paths; 5.3.1 firmware implements migration to "h" in MultisigWallet.deserialize +# +# clear_miniscript() +# +# deriv, text_a_fmt = ("m/48h/1h/0h/2h/{idx}", 'p2wsh') +# keys = make_multisig(2, 3, unique=1, deriv=deriv) +# derivs = [deriv.format(idx=i) for i in range(3)] +# import_ms_wallet(2, 3, accept=True, keys=keys, name="ms1", +# derivs=derivs, addr_fmt=text_a_fmt) +# time.sleep(.1) +# +# import_ms_wallet(3, 5, name="ms2", addr_fmt='p2wsh-p2sh', accept=True) +# time.sleep(.1) +# +# ms = settings_get("multisig") +# pths0 = ms[0][3]["d"] +# new_pths0 = [p.replace("h", "'") for p in pths0] +# ms[0][3]["d"] = new_pths0 +# +# ms[1][3]["pp"] = ms[1][3]["pp"].replace("h", "'") +# +# # this matches data/PSBT +# ms.append( +# ( +# 'ms', +# (2, 2), +# [(2285969762, 0, 'tpubDEy2hd2VTrqbBS8cS2svq12UmjGM2j7FHmocjHzAXfVhmJdhBFVVbmAi13humi49esaAuSmz36NEJ6GL3u58RzNuUkExP9vL4d81PM3s8u6'), +# (1130956047, 1, 'tpubDEFX3QojMWh7x4vSAHN17wpsywpP78aSs2t6nyELHuq1k34gub9mQ7QiaHNCBAYjSQ4UCMMpfBkf5np1cTQaStrvvRCxwxZ7kZaGHqYxUv3')], +# {'ch': 'XTN', 'ft': 14, 'd': ["m/48'/0'/99'/2'", "m/48'/0'/33'/2'"]} +# ) +# ) +# settings_set("multisig", ms) +# +# # psbt from nunchuk, with global xpubs belonging to above ms wallet +# b64_psbt = "cHNidP8BAF4CAAAAAfkDjXlS32gzOjVhSRArKxvkAecMTnp1g8wwMJTtq74/AAAAAAD9////AekaAAAAAAAAIgAgzs2e4h4vctbFvvauK+QVFAPzCFnMi1H9hTacH7498P8AAAAATwEENYfPBC7g3O2AAAACLvzTgnL7V0DNOnISJdvOgq/6Pw6DAtkPflmZ+Hc04qwC5CShG0rDIlh8gu7gH2NMBLfrIzYSzoSomnVHeMxtxVQUDwVpQzAAAIAAAACAIQAAgAIAAIBPAQQ1h88EkEB8moAAAALv/1L+Cfeg2EPc01pS00f18DIdU5BOeExlGsXyEFOKGwL71tcAiRuL4Bs+uT1JJjU6AbR3j3X60/rI+rTMJmnOgRRiIUGIMAAAgAAAAIBjAACAAgAAgAABAIkCAAAAAZ5Im3CxbYDyByyrr4luss5vr+s0r7Vt8pK+OvicPLO7AAAAAAD9////AnM2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnTmvqUXAAAAACJRIJF/VcIeZ3E4f+ZEjwiUl5AUUxBJgoaEaPaHHJecq18lq+4qAAEBK3M2AAAAAAAAIgAgvZi0zfKCeBasTet1hNKm73GA4MEkwiSVwCB9cN0/EnQiAgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3kcwRAIgHNmbk4J9wu4ljq6UouY132eX1i/2jWvJjuuWWyLRFScCIBPyPCuZ/Hmd06h9KtVkSropBonIuqIc/BK8JZ50YKp/AQEDBAEAAAABBUdSIQMBr34TVHrqSk8K6505//5YTOkHmHqF83J8iUURtL/ptCEDUXZhsRMLD/PMbva5f7RgF6u5D5v+99Mk8kOlx0C/w95SriIGAwGvfhNUeupKTwrrnTn//lhM6QeYeoXzcnyJRRG0v+m0HA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAAAAAAiBgNRdmGxEwsP88xu9rl/tGAXq7kPm/730yTyQ6XHQL/D3hxiIUGIMAAAgAAAAIBjAACAAgAAgAAAAAAAAAAAAAEBR1IhAscIZVvBcy3Q0GKO4UqR3gDB3pm/tWas8siH3Ej8MmuCIQN8lTj0MMTpT+Dlk2MbMdAaL93hezzNP3WDsRn/gwlVQlKuIgICxwhlW8FzLdDQYo7hSpHeAMHemb+1ZqzyyIfcSPwya4IcYiFBiDAAAIAAAACAYwAAgAIAAIAAAAAAAQAAACICA3yVOPQwxOlP4OWTYxsx0Bov3eF7PM0/dYOxGf+DCVVCHA8FaUMwAACAAAAAgCEAAIACAACAAAAAAAEAAAAA" +# +# goto_home() +# # in time of creatin of PSBT, lopp was making testnet3 unusable... +# settings_set("fee_limit", -1) +# start_sign(base64.b64decode(b64_psbt)) +# title, story = cap_story() +# assert title == "OK TO SEND?" +# end_sign() +# settings_set("fee_limit", 10) # rollback +# pick_menu_item("Settings") +# pick_menu_item("Multisig Wallets") +# m = cap_menu() +# for msi in m[:3]: # three wallets imported +# pick_menu_item(msi) +# pick_menu_item("View Details") +# time.sleep(.1) +# _, story = cap_story() +# assert "'" not in story +# press_cancel() +# press_cancel() @pytest.mark.parametrize("fpath", [ - # CC export format - "data/multisig/export-p2sh-myself.txt", - "data/multisig/export-p2sh-p2wsh-myself.txt", - "data/multisig/export-p2wsh-myself.txt", # descriptors "data/multisig/desc-p2sh-myself.txt", "data/multisig/desc-p2sh-p2wsh-myself.txt", "data/multisig/desc-p2wsh-myself.txt", ]) -def test_scan_any_qr(fpath, is_q1, scan_a_qr, clear_ms, goto_home, +def test_scan_any_qr(fpath, is_q1, scan_a_qr, clear_miniscript, goto_home, pick_menu_item, cap_story, press_cancel): if not is_q1: pytest.skip("No QR support for Mk4") - clear_ms() + clear_miniscript() goto_home() pick_menu_item("Scan Any QR Code") @@ -3101,70 +2691,18 @@ def test_scan_any_qr(fpath, is_q1, scan_a_qr, clear_ms, goto_home, press_cancel() -@pytest.mark.parametrize("N", [3, 15]) -def test_bare_cc_ms_qr_import(N, make_multisig, scan_a_qr, clear_ms, goto_home, - pick_menu_item, cap_story, press_cancel, is_q1): - # bare: - # - no fingerprints - # - no xfps - # - no meta data - - if not is_q1: - raise pytest.skip("No QR support for Mk4") - - keys = make_multisig(N, N) - config = '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys) - actual_vers, parts = split_qrs(config, 'U', max_version=20) - random.shuffle(parts) - - # will not work in scan any qr in main menu (no xfp) - clear_ms() - goto_home() - pick_menu_item("Scan Any QR Code") - - for p in parts: - scan_a_qr(p) - time.sleep(2.0 / len(parts)) - - title, story = cap_story() - assert title == 'Simple Text' - assert "We can't do any more with it." in story - - press_cancel() - - # if someone uses this bare format with keys of depth 1 - # multisig import path needs to be used - pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") - pick_menu_item("Import from QR") - for p in parts: - scan_a_qr(p) - time.sleep(2.0 / len(parts)) - - title, story = cap_story() - assert "Create new multisig wallet?" in story - assert f"{N}-of-{N}" in story - press_cancel() - - -@pytest.mark.parametrize("psbtv2", [True, False]) @pytest.mark.parametrize("desc", ["multi", "sortedmulti"]) -@pytest.mark.parametrize("data", [ +@pytest.mark.parametrize("data,af", [ # (out_style, amount, is_change) - [("p2wsh", 1000000, 0)] * 99, - [("p2sh", 1000000, 1)] * 33, - [("p2wsh-p2sh", 1000000, 1)] * 18 + [("p2wsh", 50000000, 0)] * 12, - [("p2sh", 1000000, 1), ("p2wsh-p2sh", 50000000, 0), ("p2wsh", 800000, 1)] * 14, + # change can only be of the same address type as imported wallet + ([("p2wsh", 1000000, 0)] * 99, "p2wsh"), + ([("p2sh", 1000000, 1)] * 33, "p2sh"), + ([("p2wsh-p2sh", 1000000, 1)] * 18 + [("p2wsh", 50000000, 0)] * 12, "p2sh-p2wsh"), + ([("p2sh", 1000000, 0), ("p2wsh-p2sh", 50000000, 0), ("p2wsh", 800000, 1)] * 14, "p2wsh"), ]) -def test_txout_explorer(psbtv2, data, clear_ms, import_ms_wallet, fake_ms_txn, - start_sign, txout_explorer, desc): - clear_ms() - M, N = 2, 3 - descriptor, bip67 = False, True - if desc == "multi": - descriptor, bip67 = True, False - keys = import_ms_wallet(2, 3, name='ms-test', accept=True, - descriptor=descriptor, bip67=bip67) +def test_txout_explorer(data, af, desc, clear_miniscript, import_ms_wallet, fake_ms_txn, + start_sign, txout_explorer, pytestconfig): + # TODO This test MUST be run with --psbt2 flag on and off outstyles = [] outvals = [] @@ -3176,54 +2714,38 @@ def test_txout_explorer(psbtv2, data, clear_ms, import_ms_wallet, fake_ms_txn, if is_change: change_outputs.append(i) + clear_miniscript() + M, N = 2, 3 + bip67 = True if desc == "multi" else False + keys = import_ms_wallet(2, 3, name='ms-test', accept=True, bip67=bip67, addr_fmt=af) + inp_amount = sum(outvals) + 100000 # 100k sat fee - psbt = fake_ms_txn(1, len(data), M, keys, outstyles=outstyles, + psbt = fake_ms_txn(1, len(data), M, keys, outstyles=outstyles, inp_addr_fmt=af, outvals=outvals, change_outputs=change_outputs, - input_amount=inp_amount, psbt_v2=psbtv2, bip67=bip67) + input_amount=inp_amount, psbt_v2=pytestconfig.getoption('psbt2'), + bip67=bip67) start_sign(psbt) txout_explorer(data) -def test_import_duplicate_shuffled_keys_legacy(clear_ms, make_multisig, import_ms_wallet, - cap_story, press_cancel, OK): - clear_ms() - M, N = 2, 3 - wname = "ms02" - keys = make_multisig(M, N) - import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True, keys=keys, - descriptor=False) - # shuffle - keys[0], keys[1] = keys[1], keys[0] - - with pytest.raises(AssertionError): - import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True, keys=keys, - descriptor=False) - - time.sleep(.1) - title, story = cap_story() - assert 'Duplicate wallet' in story - assert f'{OK} to approve' not in story - press_cancel() @pytest.mark.parametrize("order", list(itertools.product([True, False], repeat=2))) -def test_import_duplicate_shuffled_keys(clear_ms, make_multisig, import_ms_wallet, +def test_import_duplicate_shuffled_keys(clear_miniscript, make_multisig, import_ms_wallet, cap_story, press_cancel, order, OK): # DO NOT allow to import both wsh(sortedmulti(2,A,B,C)) and wsh(sortedmulti(2,B,C,A)) # DO NOT allow to import both wsh(multi(2,A,B,C)) and wsh(multi(2,B,C,A)) # DO NOT allow to import both wsh(sortedmulti(2,A,B,C)) and wsh(multi(2,B,C,A)) # MUST BE treated as duplicates - clear_ms() + clear_miniscript() M, N = 2, 3 A, B = order # defines bip67 - wname = "ms02" keys = make_multisig(M, N) - import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True, keys=keys, - descriptor=True, bip67=A) + import_ms_wallet(M, N, addr_fmt="p2wsh", name="ms0", accept=True, keys=keys, bip67=A) # shuffle keys[0], keys[1] = keys[1], keys[0] with pytest.raises(AssertionError): - import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True, keys=keys, - descriptor=True, bip67=B) + import_ms_wallet(M, N, addr_fmt="p2wsh", name="ms1", accept=True, keys=keys, bip67=B) + time.sleep(.1) title, story = cap_story() assert 'Duplicate wallet' in story @@ -3235,9 +2757,10 @@ def test_import_duplicate_shuffled_keys(clear_ms, make_multisig, import_ms_walle @pytest.mark.parametrize("int_ext", [True, False]) -def test_multi_sortedmulti_duplicate(clear_ms, make_multisig, import_ms_wallet, OK, - cap_story, press_cancel, int_ext, offer_ms_import): - clear_ms() +def test_multi_sortedmulti_duplicate(clear_miniscript, make_multisig, import_ms_wallet, OK, + cap_story, press_cancel, int_ext, offer_minsc_import, + settings_set): + clear_miniscript() M, N = 3, 5 wname = "ms001" fstr = "m/48h/1h/0h/2h/{idx}" @@ -3252,71 +2775,23 @@ def test_multi_sortedmulti_duplicate(clear_ms, make_multisig, import_ms_wallet, d = MultisigDescriptor(M, N, obj_keys, addr_fmt=AF_P2WSH, is_sorted=False) ser_desc = d.serialize(int_ext=int_ext) - title, story = offer_ms_import(ser_desc) + title, story = offer_minsc_import(ser_desc) assert 'Duplicate wallet' in story assert f'{OK} to approve' not in story assert "BIP-67 clash" in story press_cancel() -def test_unsort_multisig_setting(settings_set, import_ms_wallet, goto_home, - pick_menu_item, cap_story, need_keypress, - settings_get, clear_ms, press_select, is_q1): - clear_ms() - mi = "Unsorted Multisig" if is_q1 else "Unsorted Multi" - settings_set("unsort_ms", 0) # OFF by default - with pytest.raises(Exception) as e: - import_ms_wallet(2, 3, "p2wsh", descriptor=True, bip67=False, - accept=True, force_unsort_ms=False) - assert '"multi(...)" not allowed' in e.value.args[0] - - goto_home() - pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") - pick_menu_item(mi) - time.sleep(.1) - title, story = cap_story() - assert '"multi(...)" unsorted multisig wallets that do not follow BIP-67.' in story - assert 'preserve order of the keys' in story - assert 'USE AT YOUR OWN RISK' in story - assert 'Press (4)' in story - need_keypress("4") - time.sleep(.1) - pick_menu_item("Allow") - time.sleep(.3) - assert settings_get("unsort_ms") == 1 - import_ms_wallet(2, 3, "p2wsh", descriptor=True, bip67=False, - accept=True, force_unsort_ms=False) - assert len(settings_get("multisig")) == 1 - pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") - pick_menu_item(mi) - time.sleep(.1) - title, story = cap_story() - assert "Remove already saved multi(...) wallets first" in story - assert "2-of-3" in story # wallet that needs to be removed - press_select() - assert len(settings_get("multisig")) == 1 - clear_ms() - pick_menu_item(mi) - pick_menu_item("Do Not Allow") - time.sleep(.3) - with pytest.raises(Exception) as e: - import_ms_wallet(2, 3, "p2wsh", descriptor=True, bip67=False, - accept=True, force_unsort_ms=False) - assert '"multi(...)" not allowed' in e.value.args[0] - - @pytest.mark.bitcoind @pytest.mark.parametrize("cs", [True, False]) @pytest.mark.parametrize("way", ["usb", "nfc", "sd", "vdisk", "qr"]) -def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_ms, +def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_miniscript, pick_menu_item, goto_home, need_keypress, - offer_ms_import, bitcoind, microsd_path, - virtdisk_path, import_multisig): + offer_minsc_import, bitcoind, microsd_path, + virtdisk_path, import_miniscript): name = "my_ms_wal" use_regtest() - clear_ms() + clear_miniscript() with open("data/multisig/desc-p2wsh-myself.txt", "r") as f: desc = f.read().strip() @@ -3329,7 +2804,7 @@ def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_ms, data = None fname = None if way == "usb": - title, story = offer_ms_import(val) + title, story = offer_minsc_import(val) else: if way in ["nfc", "qr"]: data = val @@ -3343,7 +2818,7 @@ def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_ms, with open(fpath, "w") as f: f.write(val) - title, story = import_multisig(fname=fname, way=way, data=data) + title, story = import_miniscript(fname=fname, way=way, data=data) assert "Create new multisig wallet?" in story assert name in story @@ -3351,7 +2826,7 @@ def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_ms, time.sleep(.2) goto_home() pick_menu_item("Settings") - pick_menu_item("Multisig Wallets") + pick_menu_item("Multisig/Miniscript") m = cap_menu() assert name in m[0] @@ -3379,9 +2854,631 @@ def test_import_multisig_usb_json(use_regtest, cs, way, cap_menu, clear_ms, {"name": "ab", "desc": None, "random": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} ), ]) -def test_json_import_failures(err, config, offer_ms_import): +def test_json_import_failures(err, config, offer_minsc_import): with pytest.raises(Exception) as e: - offer_ms_import(json.dumps(config)) + offer_minsc_import(json.dumps(config)) assert err in e.value.args[0] + +@pytest.mark.bitcoind +def test_cc_root_key(import_ms_wallet, bitcoind, use_regtest, clear_miniscript, microsd_wipe, goto_home, + pick_menu_item, cap_story, press_select, need_keypress, offer_minsc_import, + cap_menu, load_export, try_sign, goto_address_explorer, settings_set): + # only CC has root key here, not practical to attempt get xpub from core, if possible + use_regtest() + clear_miniscript() + microsd_wipe() + M, N = 2, 2 + cosigner = bitcoind.create_wallet(wallet_name=f"bds", disable_private_keys=False, blank=False, + passphrase=None, avoid_reuse=False, descriptors=True) + ms = bitcoind.create_wallet( + wallet_name=f"watch_only_roots", disable_private_keys=True, + blank=True, passphrase=None, avoid_reuse=False, descriptors=True + ) + goto_home() + target_first_der = [] + + # get key from bitcoind cosigner + target_desc = "" + bitcoind_descriptors = cosigner.listdescriptors()["descriptors"] + for desc in bitcoind_descriptors: + if desc["desc"].startswith("pkh(") and desc["internal"] is False: + target_desc = desc["desc"] + core_desc, checksum = target_desc.split("#") + # remove pkh(....) + core_key = core_desc[4:-1] + + _idx = core_key.find("]") + assert _idx != -1 + inner = core_key[1:_idx].split("/") + # xfp to upper + inner[0] = inner[0].upper() + core_der_base = f"[{'/'.join(inner)}/0/%d]" + cc_der_base = f"[{xfp2str(simulator_fixed_xfp)}/0/%d]" + target_first_der.append(core_der_base % 0) + target_first_der.append(cc_der_base % 0) + + desc = f"wsh(sortedmulti(2,{core_key},[{xfp2str(simulator_fixed_xfp).lower()}]{simulator_fixed_tpub}/0/*))" + desc_info = ms.getdescriptorinfo(desc) + desc_w_checksum = desc_info["descriptor"] # with checksum + + name = "cc_root_key" + title, story = offer_minsc_import(json.dumps({"name": name, "desc": desc_w_checksum})) + + assert "Create new multisig wallet?" in story + assert name in story + # assert f"All {N} co-signers must approve spends" in story + assert "P2WSH" in story + press_select() # approve multisig import + goto_home() + pick_menu_item('Settings') + pick_menu_item("Multisig/Miniscript") + menu = cap_menu() + pick_menu_item(menu[0]) # pick imported descriptor multisig wallet + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export("sd", label="Bitcoin Core Multisig", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + # bump range to be able to verify multisig scripts against bitcoind + # default exported range from us is just 100 addresses + for i in range(len(core_desc_object)): + core_desc_object[i]["range"] = [0,250] + + # import descriptors to watch only wallet + res = ms.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"], obj + + addr_type = "bech32" + multi_addr = ms.getnewaddress("", addr_type) + bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) # mining + dest_addr = ms.getnewaddress("", addr_type) + # create funded PSBT + psbt_resp = ms.walletcreatefundedpsbt( + [], [{dest_addr: 5}], 0, {"fee_rate": 2, "change_type": addr_type} + ) + + _, updated = try_sign(base64.b64decode(psbt_resp.get("psbt"))) + + done = cosigner.walletprocesspsbt(base64.b64encode(updated).decode(), True)["psbt"] + + rr = ms.finalizepsbt(done) + + assert rr['complete'] + tx_hex = rr["hex"] + res = bitcoind.supply_wallet.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + txn_id = bitcoind.supply_wallet.sendrawtransaction(rr['hex']) + assert len(txn_id) == 64 + + bitcoind_addrs = ms.deriveaddresses(desc_w_checksum, [0,250]) + + goto_address_explorer() + pick_menu_item(name) + # TODO + # _, story = cap_story() + # # 2of2 - full paths shown for first address + # der_paths = story.split("\n\n")[1].split("\n")[:N] + # assert der_paths == target_first_der + + need_keypress('1') # SD + contents = load_export("sd", label="Address summary", is_json=False) + cc_addrs = contents.strip().split("\n")[1:] + + # Generate the addresses file and get each line in a list + for i, line in enumerate(cc_addrs): + split_line = line.split(",") + addr = split_line[1][1:-1] + # TODO + # script_hex = split_line[2][1:-1] + # cc_der = split_line[-1][1:-1] + # core_der = split_line[-2][1:-1] + # assert cc_der == (cc_der_base % i) + # assert core_der == (core_der_base % i) + assert addr == bitcoind_addrs[i] + addr_info = ms.getaddressinfo(addr) + assert addr_info["ismine"] + # assert addr_info["hex"] == script_hex + + +@pytest.mark.parametrize("way", ["nfc", "qr"]) +def test_multisig_nfc_qr_finalization(way, clear_miniscript, make_multisig, import_ms_wallet, + cap_story, press_cancel, OK, settings_set, + fake_ms_txn, try_sign_nfc, settings_remove, + try_sign_bbqr): + clear_miniscript() + settings_remove("ptxurl") # tesing above parameter, ptxurl needs to be off + M, N = 1, 2 + wname = "finms-%s" % way + keys = import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True) + + psbt = fake_ms_txn(2, 2, M, keys, outstyles=['p2wsh', 'p2wsh-p2sh'], + change_outputs=[0], inp_addr_fmt="p2wsh") + + if way == "nfc": + ip, result, txid = try_sign_nfc(psbt, expect_finalize=True, + nfc_tools=True, encoding="hex") + is_fin = bool(txid) + else: + assert way == "qr" + ip, ft, result = try_sign_bbqr(psbt) + is_fin = (ft == "T") + + assert is_fin + + +@pytest.mark.parametrize("has_orig", [False, True]) +def test_originless_keys(get_cc_key, bitcoin_core_signer, bitcoind, offer_minsc_import, + pick_menu_item, load_export, goto_home, cap_menu, clear_miniscript, + use_regtest, press_select, start_sign, end_sign, cap_story, + has_orig, need_keypress): + # can be both: + # a.) just ranged xpub without origin info -> xpub1/<0;1>/* + # b.) ranged xpub with its fp -> [xpub1_fp]xpub1/<0;1>/* + + use_regtest() + clear_miniscript() + af = "bech32" + name = "originless_multlisig" + + cc_key = get_cc_key("m/84h/1h/0h") + cs, ck = bitcoin_core_signer(name+"_signer") + originless_ck = ck.split("]")[-1] + + n = BIP32Node.from_hwif(originless_ck.split("/")[0]) # just extended key + fp_str = "[" + n.fingerprint().hex() + "]" + if has_orig: + originless_ck = fp_str + originless_ck + + tmplt = "wsh(sortedmulti(2,@0,@1))" + desc = tmplt.replace("@0", cc_key) + desc = desc.replace("@1", originless_ck) + to_import = {"desc": desc, "name": name} + offer_minsc_import(json.dumps(to_import)) + press_select() + + wo = bitcoind.create_wallet(wallet_name=name, disable_private_keys=True, blank=True, + passphrase=None, avoid_reuse=False, descriptors=True) + + goto_home() + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(name) # pick imported descriptor miniscript wallet + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export("sd", label="Bitcoin Core Multisig", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + res = wo.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"] + + # fund wallet + addr = wo.getnewaddress("", af) + assert bitcoind.supply_wallet.sendtoaddress(addr, 49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + + unspent = wo.listunspent() + assert len(unspent) == 1 + + # split to 10 utxos + dest_addrs = [wo.getnewaddress(f"a{i}", af) for i in range(10)] + psbt_resp = wo.walletcreatefundedpsbt( + [], + [{a: 4} for a in dest_addrs] + [{bitcoind.supply_wallet.getnewaddress(): 5}], + 0, + {"fee_rate": 3, "change_type": af, "subtractFeeFromOutputs": [0]}, + ) + psbt = psbt_resp.get("psbt") + + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert title == "OK TO SEND?" + assert "Consolidating" not in story + cc_signed = end_sign(True) + cc_signed = base64.b64encode(cc_signed).decode() + + final_psbt_o = cs.walletprocesspsbt(cc_signed, True, "ALL") + final_psbt = final_psbt_o["psbt"] + assert psbt != final_psbt + + res = wo.finalizepsbt(final_psbt) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + +def test_input_script_type(clear_miniscript, import_ms_wallet, start_sign, end_sign, cap_story, + press_cancel, settings_set, fake_ms_txn): + + def sign_check(psbt): + # start sign MUST raise scriptPubKey mismatch on inputs or change outputs + # it does not in current master + start_sign(psbt) + _, story = cap_story() + # HWI blocker + # try: + # end_sign() + # assert False, story + # except Exception as e: + # assert e.args[0] == 'Coldcard Error: Nothing to sign here' + # return + + assert "(1 warning below)" in story + assert "WARNING" in story + assert "Limited Signing: We are not signing these inputs" in story + res = end_sign() + po = BasicPSBT().parse(res) + for inp in po.inputs: + assert not inp.part_sigs # no signatures added + + + clear_miniscript() + M, N = 2, 3 + wname = "bugg" + # import wallet with script type p2wsh + keys = import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True) + + # create txn with p2sh inputs + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, inp_addr_fmt="p2sh", + change_outputs=[0,1]) + sign_check(psbt) + + # create txn with p2sh-p2wsh + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, + change_outputs=[0,1], inp_addr_fmt="p2sh-p2wsh") + + sign_check(psbt) + + # ============================ + + clear_miniscript() + # import wallet with script type p2sh-p2wsh + keys = import_ms_wallet(M, N, addr_fmt="p2sh-p2wsh", name=wname, accept=True) + + # create txn with p2wsh inputs + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, + change_outputs=[0,1], inp_addr_fmt="p2wsh") + + sign_check(psbt) + + # create txn with p2sh inputs + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, + change_outputs=[0,1], inp_addr_fmt="p2sh") + + sign_check(psbt) + + # ============================ + + clear_miniscript() + # import wallet with script type p2sh + keys = import_ms_wallet(M, N, addr_fmt="p2sh", name=wname, accept=True) + + # create txn with p2wsh inputs + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, + change_outputs=[0,1], inp_addr_fmt="p2wsh") + + sign_check(psbt) + + # create txn with p2sh-p2wsh inputs + # we shouldn't even recognize these input as ours + psbt = fake_ms_txn(2, 2, M, keys, + change_outputs=[0,1], inp_addr_fmt="p2sh-p2wsh") + + sign_check(psbt) + + +def test_change_output_script_type(clear_miniscript, import_ms_wallet, start_sign, end_sign, + cap_story, press_cancel, settings_set, fake_ms_txn): + + def sign_check(psbt): + # start sign MUST raise scriptPubKey mismatch on inputs or change outputs + # it does not in current master + start_sign(psbt) + _, story = cap_story() + assert "change output is fraudulent" in story + assert "spk mismatch" in story + + clear_miniscript() + M, N = 2, 3 + wname = "bugg" + # import wallet with script type p2wsh + keys = import_ms_wallet(M, N, addr_fmt="p2wsh", name=wname, accept=True) + + # inputs correct, change outputs wrong address format + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2sh", inp_addr_fmt="p2wsh", + change_outputs=[0,1]) + sign_check(psbt) + + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2sh-p2wsh", + change_outputs=[0,1], inp_addr_fmt="p2wsh") + + sign_check(psbt) + + # ============================ + + clear_miniscript() + # import wallet with script type p2sh-p2wsh + keys = import_ms_wallet(M, N, addr_fmt="p2sh-p2wsh", name=wname, accept=True) + + # inputs correct, change outputs wrong address format + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2wsh", + change_outputs=[0,1], inp_addr_fmt="p2sh-p2wsh") + + sign_check(psbt) + + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2sh", + change_outputs=[0,1], inp_addr_fmt="p2sh-p2wsh") + + sign_check(psbt) + + # ============================ + + clear_miniscript() + M, N = 2, 3 + wname = "bugg" + # import wallet with script type p2sh + keys = import_ms_wallet(M, N, addr_fmt="p2sh", name=wname, accept=True) + + # inputs correct, change outputs wrong address format + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2wsh", + change_outputs=[0,1], inp_addr_fmt="p2sh") + + sign_check(psbt) + + psbt = fake_ms_txn(2, 2, M, keys, force_outstyle="p2sh-p2wsh", + change_outputs=[0,1], inp_addr_fmt="p2sh") + + sign_check(psbt) + + +def test_sh_vs_wrapped_segwit_psbt(clear_miniscript, import_ms_wallet, start_sign, end_sign, + cap_story, press_cancel, settings_set, fake_ms_txn): + + clear_miniscript() + M, N = 2, 3 + wname = "spk_check_sh_shwsh" + # import wallet with script type p2sh + keys = import_ms_wallet(M, N, addr_fmt="p2sh", name=wname, accept=True) + + def hack(psbt_in): + for inp in psbt_in.inputs: + # switch scripts so it looks like bare p2sh instead wrapped segwit script hash + # it even has our keys, and script is correct + inp.redeem_script = inp.witness_script + inp.witness_script = None + + # PSBT has p2sh-p2wsh inputs & outputs + # but PSBT creator made a mistake and filled redeem/witness like in p2sh (see hack) + psbt = fake_ms_txn(2, 2, M, keys, inp_addr_fmt="p2sh-p2wsh", hack_psbt=hack) + + start_sign(psbt) + time.sleep(.1) + title, story = cap_story() + assert "OK TO SEND?" not in title + assert "spk mismatch" in story + + +def test_wrapped_segwit_vs_sh_psbt(clear_miniscript, import_ms_wallet, start_sign, end_sign, + cap_story, press_cancel, settings_set, fake_ms_txn): + + clear_miniscript() + M, N = 2, 3 + wname = "spk_check_shwsh_sh" + # import wallet with script type p2sh-p2wsh + keys = import_ms_wallet(M, N, addr_fmt="p2sh-p2wsh", name=wname, accept=True) + + def hack(psbt_in): + for inp in psbt_in.inputs: + # switch scripts so it looks like bare p2sh instead wrapped segwit script hash + # it even has our keys, and script is correct + inp.witness_script = inp.redeem_script + inp.redeem_script = b"\x00\x20" + sha256(inp.witness_script).digest() + + # PSBT has p2sh inputs & outputs + # but PSBT creator made a mistake and filled redeem/witness like in p2sh (see hack) + psbt = fake_ms_txn(2, 2, M, keys, inp_addr_fmt="p2sh", hack_psbt=hack) + + start_sign(psbt) + time.sleep(.1) + title, story = cap_story() + assert "OK TO SEND?" not in title + assert "spk mismatch" in story + + +@pytest.mark.parametrize("pms", [0, 1, 2]) +def test_psbt_xpubs_slip132(pms, clear_miniscript, settings_set, start_sign, end_sign, cap_story, + set_seed_words, press_select, offer_minsc_import): + + set_seed_words("cannon budget unknown inhale select virtual absurd chapter inch firm inquiry valley") + clear_miniscript() + settings_set("pms", pms) + # got this PSBT directly form Casa (part of their test suite) + psbt = 'cHNidP8BAFMBAAAAAeB18EjWQ2J8kHcbWSOWLZ4XG9TROiK2EqIAn2a5pe+PAAAAAAD9////AS9EAgAAAAAAF6kURuQXuB5Udus5+DWGg/ZP1bK5/5mHAAAAAE8BAkKJ7wM+PJ4JAAAAAOIDwvV5ejMJ0rSyNey8cKbskf4kk73yRvCe8cUEiNhiA4E0IkUc+Xmx5ndEYFbZ9sHkOnOXJWeSjxIN6Go1AMfiEM3yQGYxAAAAAQAAAAAAAABPAQJCie8DR+pdIQAADTESbO7YkHNCwPnMVS6sXbxDRiMahe6Eil9h9RzUx1aiKQL+RIAGlCJ8PIu+x5O+oSdz9kSY/1vbnZxjm99fMRYWuRAS1W01MQAAAAEAAAAAAAAATwECQonvA+HsXFkAAAAAsdd6QnUkHTmhRlBNy/VQOWcZHfdPJSf4tX6LWUj1VWMCYWPVp4pXPi5mg/AC9ZP4sdbLtwyRwvalwzNO6KfrzaIQXbGC5jEAAAABAAAAAAAAAAABAPgBAAAAAAEB+Qe27L6aqLnQJ4sbxsWvQR6mhcNk0Y1DIbARPdJjSd4BAAAAFxYAFCU3KVhnuRLNeMk85jv3FgbOR9PH/f///wIrcgIAAAAAABepFJanKkFHtvWWwbHNOjPR6NP7RPfqhwoxrQUAAAAAF6kU5xbgzlw1qmkUVzLnuWp6lOPJpImHAkgwRQIhAMtF6v3RgUOxfTs9uGKAV6jjFb3TPlcZSrhRqgO8QlQ2AiANiNAi5rEGfAR0cAp8AadOOIlcQFH+X0Pf98Nz0KF5vQEhAqiLyMuk2fePxFgctRiB5QB/jwBA7q/zWtHgUbskc3rQAAAAAAEBICtyAgAAAAAAF6kUlqcqQUe29ZbBsc06M9Ho0/tE9+qHAQQiACDHvYHyHI3mL9BOaF+AgriPtki9tfeDyUhVBytva0dqmgEFaVIhAnJjmbStmsYp7bb8aAN/aN2hKiLk+6SzNpcjJftG5703IQKO3IofMd3egH0WqIpjS/M3iusXuFuAHA06s2eLBSCs+CECpbdrv+ihGqUyCBYU+K7QgpXuMD7sOt0zcltPV04PJz1TriIGAnJjmbStmsYp7bb8aAN/aN2hKiLk+6SzNpcjJftG5703GM3yQGYxAAAAAQAAAAAAAAAAAAAAAAAAACIGAo7cih8x3d6AfRaoimNL8zeK6xe4W4AcDTqzZ4sFIKz4GBLVbTUxAAAAAQAAAAAAAAAAAAAAAAAAACIGAqW3a7/ooRqlMggWFPiu0IKV7jA+7DrdM3JbT1dODyc9GF2xguYxAAAAAQAAAAAAAAAAAAAAAAAAAAAA' + bpsbt = base64.b64decode(psbt) + start_sign(bpsbt) + time.sleep(.1) + title, story = cap_story() + if pms == 0: + # verify only + assert "Failure" in title + assert "XPUBs in PSBT do not match any existing wallet" + press_select() + title, story = offer_minsc_import("sh(wsh(sortedmulti(2,[cdf24066/49/1/0]Upub5QWbdFzCKPujKUZWDF9mST5iE4VJpaqAqXiS85jYUEaSBtwbFcJwswU2DeWGC6rNBnoKs8rQC9oKGdNTSqKwseHDeaE68YAx2QbgcqX84z6/<0;1>/*,[12d56d35/49/1/0]Upub5QaiwZWYcoJwBw26hGguMiUgmKvqpnzrBR92uVEmmwdAtS5LnpBEUPPavjQgxdakT8MKb96FE2Pn61ogKFT3r6obPZiH8q9Y3NPCFRswq6F/<0;1>/*,[5db182e6/49/1/0]Upub5RiNwTn4EVpghGJx1CWjbt9jfL5d792JRyccZxgtWVhbPDwf1o6A4vK9AyTY4VgGBMvEgM3qHM3mhAKxCiF4idL3nMjdskZNP1hQXD8XPq3/<0;1>/*)))") + assert "Create new multisig wallet" in story + assert "P2SH-P2WSH" + press_select() + start_sign(bpsbt) + time.sleep(.1) + title, story = cap_story() + + elif pms == 1: + # offer import + assert "Create new multisig wallet" in story + assert "P2SH-P2WSH" + press_select() + start_sign(bpsbt) + time.sleep(.1) + title, story = cap_story() + + assert "Invalid PSBT" not in story + res = end_sign(bpsbt) + po = BasicPSBT().parse(res) + assert len(po.inputs[0].part_sigs) == 1 + + +@pytest.mark.parametrize("af", ["p2sh", "p2wsh", "p2sh-p2wsh"]) +def test_af_psbt_input_matching(af, clear_miniscript, fake_ms_txn, import_ms_wallet, goto_home, + cap_story, start_sign, end_sign, settings_set): + M, N = 3, 5 + clear_miniscript() + goto_home() + + # all is matched properly even without pms_af as we're checking PSBT inputs + # assuming PSBT only has own wallets input - matching will always work + settings_set("pms", 2) # Trust PSBT + + # random path that does not match anything + path = "m/21/21/21" + + def path_mapper(idx): + kk = str_to_path(path) + return kk + [0, 0] + + def incl_xpubs(idx, xfp, m, sk): + kk = str_to_path(path) + bp = pack('<%dI' % (path.count("/") + 1), xfp, *kk) + return sk.node.serialize_public(), bp + + keys = import_ms_wallet(M, N, name='psbt_af_match', accept=True, addr_fmt=af, + common=path, do_import=False)[0] + + psbt = fake_ms_txn(1, 2, M, keys, incl_xpubs=incl_xpubs, inp_addr_fmt=af, + outstyles=ADDR_STYLES_MS, change_outputs=[0], path_mapper=path_mapper) + start_sign(psbt) + time.sleep(.1) + title, story = cap_story() + assert "Invalid PSBT" not in story + res = end_sign(accept=True) + po = BasicPSBT().parse(res) + assert len(po.inputs[0].part_sigs) == 1 + + +def test_casa_case(clear_miniscript, settings_set, start_sign, end_sign, cap_story, set_seed_words): + clear_miniscript() + set_seed_words("cannon budget unknown inhale select virtual absurd chapter inch firm inquiry valley") + settings_set("pms", 2) # Trust PSBT + # got this PSBT directly form Casa (part of their test suite) + psbt = 'cHNidP8BAFMBAAAAAeB18EjWQ2J8kHcbWSOWLZ4XG9TROiK2EqIAn2a5pe+PAAAAAAD9////AS9EAgAAAAAAF6kURuQXuB5Udus5+DWGg/ZP1bK5/5mHAAAAAE8BAkKJ7wM+PJ4JAAAAAOIDwvV5ejMJ0rSyNey8cKbskf4kk73yRvCe8cUEiNhiA4E0IkUc+Xmx5ndEYFbZ9sHkOnOXJWeSjxIN6Go1AMfiEM3yQGYxAAAAAQAAAAAAAABPAQJCie8DR+pdIQAADTESbO7YkHNCwPnMVS6sXbxDRiMahe6Eil9h9RzUx1aiKQL+RIAGlCJ8PIu+x5O+oSdz9kSY/1vbnZxjm99fMRYWuRAS1W01MQAAAAEAAAAAAAAATwECQonvA+HsXFkAAAAAsdd6QnUkHTmhRlBNy/VQOWcZHfdPJSf4tX6LWUj1VWMCYWPVp4pXPi5mg/AC9ZP4sdbLtwyRwvalwzNO6KfrzaIQXbGC5jEAAAABAAAAAAAAAAABAPgBAAAAAAEB+Qe27L6aqLnQJ4sbxsWvQR6mhcNk0Y1DIbARPdJjSd4BAAAAFxYAFCU3KVhnuRLNeMk85jv3FgbOR9PH/f///wIrcgIAAAAAABepFJanKkFHtvWWwbHNOjPR6NP7RPfqhwoxrQUAAAAAF6kU5xbgzlw1qmkUVzLnuWp6lOPJpImHAkgwRQIhAMtF6v3RgUOxfTs9uGKAV6jjFb3TPlcZSrhRqgO8QlQ2AiANiNAi5rEGfAR0cAp8AadOOIlcQFH+X0Pf98Nz0KF5vQEhAqiLyMuk2fePxFgctRiB5QB/jwBA7q/zWtHgUbskc3rQAAAAAAEBICtyAgAAAAAAF6kUlqcqQUe29ZbBsc06M9Ho0/tE9+qHAQQiACDHvYHyHI3mL9BOaF+AgriPtki9tfeDyUhVBytva0dqmgEFaVIhAnJjmbStmsYp7bb8aAN/aN2hKiLk+6SzNpcjJftG5703IQKO3IofMd3egH0WqIpjS/M3iusXuFuAHA06s2eLBSCs+CECpbdrv+ihGqUyCBYU+K7QgpXuMD7sOt0zcltPV04PJz1TriIGAnJjmbStmsYp7bb8aAN/aN2hKiLk+6SzNpcjJftG5703GM3yQGYxAAAAAQAAAAAAAAAAAAAAAAAAACIGAo7cih8x3d6AfRaoimNL8zeK6xe4W4AcDTqzZ4sFIKz4GBLVbTUxAAAAAQAAAAAAAAAAAAAAAAAAACIGAqW3a7/ooRqlMggWFPiu0IKV7jA+7DrdM3JbT1dODyc9GF2xguYxAAAAAQAAAAAAAAAAAAAAAAAAAAAA' + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + assert "Invalid PSBT" not in story + res = end_sign(psbt) + po = BasicPSBT().parse(res) + assert len(po.inputs[0].part_sigs) == 1 + + +@pytest.mark.parametrize("af", ["p2sh", "p2wsh", "p2sh-p2wsh"]) +@pytest.mark.parametrize("psbt_v2", [True, False]) +def test_af_matching_convoluted_case(af, psbt_v2, clear_miniscript, fake_ms_txn, import_ms_wallet, + goto_home, pick_menu_item, cap_story, press_select, start_sign, + end_sign, is_q1, settings_set): + # merge two multisig PSBTs, each with one input (two inputs after merge) + # first input is not ours, but has same M, N + # second is ours, but address format matching will be based on first + M, N = 3, 5 + clear_miniscript() + goto_home() + settings_set("pms", 2) # TRUST PSBT + + # random path that does not match anything + path = "m/21/21/21" + + def path_mapper(idx): + kk = str_to_path(path) + return kk + [0, 0] + + def incl_xpubs(idx, xfp, m, sk): + kk = str_to_path(path) + bp = pack('<%dI' % (path.count("/") + 1), xfp, *kk) + return sk.node.serialize_public(), bp + + keys0 = import_ms_wallet(M, N, name='00', accept=True, addr_fmt=af, + common=path, do_import=False)[0] + + psbt0 = fake_ms_txn(1, 2, M, keys0, incl_xpubs=incl_xpubs, inp_addr_fmt=af, + outstyles=ADDR_STYLES_MS, change_outputs=[0], path_mapper=path_mapper) + # max confusion + af1 = { + "p2sh": "p2sh-p2wsh", + "p2wsh": "p2sh-p2wsh", + "p2sh-p2wsh": "p2sh" + }[af] + + keys1 = import_ms_wallet(M, N+1, name='11', accept=True, addr_fmt=af1, + common=path, do_import=False)[0] + + # last key is ours - drop it - as if it has our key, we will fail + keys1 = keys1[:-1] + + psbt1 = fake_ms_txn(1, 2, M, keys1, incl_xpubs=incl_xpubs, inp_addr_fmt=af1, + outstyles=ADDR_STYLES_MS, change_outputs=[0], path_mapper=path_mapper) + + # now combine above PSBT so that one that we wanna sign (and preserve XPUBS is only the second input) + # aka trick our matching algo to be wrong + p0 = BasicPSBT().parse(psbt0) + p1 = BasicPSBT().parse(psbt1) + + # change to PSBT v2 to not need handle txn + p00 = BasicPSBT().parse(p0.to_v2()) + p11 = BasicPSBT().parse(p1.to_v2()) + + combined = BasicPSBT() + combined.version = 2 + combined.txn_version = 2 + + combined.xpubs = p0.xpubs + + combined.input_count = p00.input_count + p11.input_count + combined.output_count = p00.output_count + p11.output_count + combined.fallback_locktime = 0 + + # put the one that we will not be signig first (i.e no matching PSBT_XPUBS) + combined.inputs = p11.inputs + p00.inputs + combined.outputs = p11.outputs + p00.outputs + + # drop xfp paths for input 0 - otherwise failure - correct + combined.inputs[0].bip32_paths = {} + + psbt = combined.to_v2() if psbt_v2 else combined.to_v0() + start_sign(psbt) + time.sleep(.1) + title, story = cap_story() + assert "(1 warning below)" in story + assert "Limited Signing" in story + res = end_sign(accept=True) + po = BasicPSBT().parse(res) + assert len(po.inputs[0].part_sigs) == 0 # considered not ours + assert len(po.inputs[1].part_sigs) == 1 # signature added + # EOF diff --git a/testing/test_nfc.py b/testing/test_nfc.py index 29a9389a8..5a0ef8c84 100644 --- a/testing/test_nfc.py +++ b/testing/test_nfc.py @@ -5,17 +5,18 @@ # - many test "sync" issues here; case is right but gets outs of sync with DUT # - use `./simulator.py --eff --set nfc=1` # -import pytest, time, io, shutil, json, os +import pytest, time, io, shutil, json, os, random from binascii import b2a_hex, a2b_hex from struct import pack, unpack import ndef from hashlib import sha256 from txn import * -from charcodes import KEY_NFC, KEY_QR +from constants import unmap_addr_fmt +from charcodes import KEY_NFC @pytest.mark.parametrize('case', range(6)) -def test_ndef(case, load_shared_mod): +def test_ndef(case, load_shared_mod, src_root_dir): # NDEF unit tests -- runs in cpython def get_body(efile): @@ -36,7 +37,7 @@ def get_body(efile): def decode(msg): return list(ndef.message_decoder(get_body(msg))) - cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') n = cc_ndef.ndefMaker() if case == 0: @@ -101,13 +102,13 @@ def decode(msg): 'short', 'long', ]) -def test_ndef_ccfile(ccfile, load_shared_mod): +def test_ndef_ccfile(ccfile, load_shared_mod, src_root_dir): # NDEF unit tests def decode(body): return list(ndef.message_decoder(body)) - cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') txt_msg = None if ccfile == 'rx': @@ -149,7 +150,8 @@ def decode(body): @pytest.fixture def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec, nfc_read, nfc_write, nfc_block4rf, press_select, - press_cancel, press_nfc): + press_cancel, press_nfc, nfc_read_txn, ndef_parse_txn_psbt, + sim_root_dir): # like "try_sign" but use NFC to send/receive PSBT/results @@ -172,17 +174,18 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, ip = b2a_hex(ip) recs = [ndef.TextRecord(ip)] elif encoding == 'base64': - from base64 import b64encode, b64decode + from base64 import b64encode ip = b64encode(ip) recs = [ndef.TextRecord(ip)] else: assert encoding == 'binary' recs = [ndef.Record(type='urn:nfc:ext:bitcoin.org:psbt', data=ip), ndef.Record(type='urn:nfc:ext:bitcoin.org:sha256', data=sha256(ip).digest()), - ndef.TextRecord('some text here about situ'), + ndef.TextRecord('some text'), ] - open('debug/nfc-sent.psbt', 'wb').write(ip) + with open(f'{sim_root_dir}/debug/nfc-sent.psbt', 'wb') as f: + f.write(ip) # wrap in a CCFile serialized = b''.join(ndef.message_encoder(recs)) @@ -243,7 +246,7 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, for r in range(10): time.sleep(0.1) title, story = cap_story() - if title == 'PSBT Signed': break + if "shared via NFC" in story: break else: assert False, 'timed out' @@ -262,6 +265,20 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, press_select() txid = None + got_psbt, got_txn, got_txid = ndef_parse_txn_psbt(contents, txid, ip, expect_finalize) + + return ip, (got_psbt or got_txn), (txid or got_txid) + + + yield doit + + # cleanup / restore + sim_exec('from pyb import SDCard; SDCard.ejected = False') + +@pytest.fixture +def ndef_parse_txn_psbt(press_cancel, sim_root_dir): + def doit(contents, txid=None, orig=None, expect_finalized=True): + # from NFC data read, what did we get? got_txid = None got_txn = None got_psbt = None @@ -270,7 +287,7 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, if got.type == 'urn:nfc:wkt:T': assert 'Transaction' in got.text or 'PSBT' in got.text if 'Transaction' in got.text and txid: - assert b2a_hex(txid).decode() in got.text + assert txid in got.text elif got.type == 'urn:nfc:ext:bitcoin.org:txid': got_txid = b2a_hex(got.data).decode('ascii') elif got.type == 'urn:nfc:ext:bitcoin.org:txn': @@ -293,23 +310,16 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, if got_txid: assert got_txn assert got_txid == txid - assert expect_finalize + assert expect_finalized result = got_txn - open("debug/nfc-result.txn", 'wb').write(result) + with open(f"{sim_root_dir}/debug/nfc-result.txn", 'wb') as f: + f.write(result) else: - assert not expect_finalize + assert not expect_finalized result = got_psbt - open("debug/nfc-result.psbt", 'wb').write(result) - - if 0: - # check output encoding matches input - if encoding == 'hex' or finalize: - result = a2b_hex(result.strip()) - elif encoding == 'base64': - result = b64decode(result) - else: - assert encoding == 'binary' + with open(f"{sim_root_dir}/debug/nfc-result.psbt", 'wb') as f: + f.write(result) # read back final product if got_txn: @@ -325,45 +335,45 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, assert got_psbt[0:5] == b'psbt\xff' from psbt import BasicPSBT - was = BasicPSBT().parse(ip) + was = BasicPSBT().parse(orig) now = BasicPSBT().parse(got_psbt) assert was.txn == now.txn assert was != now - return ip, (got_psbt or got_txn), txid + press_cancel() # exit re-export animation - yield doit + return got_psbt, got_txn, got_txid - # cleanup / restore - sim_exec('from pyb import SDCard; SDCard.ejected = False') + return doit @pytest.mark.parametrize('num_outs', [ 1, 20, 250]) def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story, is_q1, press_nfc, press_cancel): # Read signing result (transaction) over NFC, decode it. psbt = fake_txn(1, num_outs) - orig, result = try_sign(psbt, accept=True, finalize=True) + orig, result = try_sign(psbt, accept=True, finalize=True, exit_export_loop=False) too_big = len(result) > 8000 if too_big: assert num_outs > 100 - if num_outs > 100: assert too_big time.sleep(.1) title, story = cap_story() - assert 'TXID' in title, story - txid = a2b_hex(story.split()[0]) - assert f'Press {KEY_NFC if is_q1 else "(3)"}' in story + assert 'TXID' in story, story + txid = a2b_hex(story.split("\n")[3]) + assert f'press {KEY_NFC if is_q1 else "(3)"}' in story press_nfc() time.sleep(.2) if too_big: title, story = cap_story() assert 'is too large' in story + press_cancel() return contents = nfc_read() press_cancel() + press_cancel() #print("contents = " + B2A(contents)) for got in ndef.message_decoder(contents): @@ -380,10 +390,15 @@ def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, raise ValueError(got.type) @pytest.mark.unfinalized # iff partial=1 +@pytest.mark.reexport @pytest.mark.parametrize('encoding', ['binary', 'hex', 'base64']) @pytest.mark.parametrize('num_outs', [1,2]) @pytest.mark.parametrize('partial', [1, 0]) -def test_nfc_signing(encoding, num_outs, partial, try_sign_nfc, fake_txn, dev): +def test_nfc_signing(encoding, num_outs, partial, try_sign_nfc, fake_txn, dev, + signing_artifacts_reexport, microsd_wipe): + # clear any possible files on SD - that are created by signing_artifacts_reexport + microsd_wipe() + xp = dev.master_xpub def hack(psbt): @@ -393,9 +408,15 @@ def hack(psbt): pp = psbt.inputs[0].bip32_paths[pk] psbt.inputs[0].bip32_paths[pk] = b'what' + pp[4:] - psbt = fake_txn(2, num_outs, xp, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn([["p2wpkh"],["p2pkh"]], num_outs, xp, psbt_hacker=hack) - _, txn, txid = try_sign_nfc(psbt, expect_finalize=not partial, encoding=encoding) + got_psbt, txn, txid = try_sign_nfc(psbt, expect_finalize=not partial, encoding=encoding) + _psbt, _txn = signing_artifacts_reexport("nfc", tx_final=not partial, txid=txid, + encoding=encoding) + if partial: + assert _psbt == txn + else: + assert _txn == txn def test_rf_uid(rf_interface, cap_story, goto_home, pick_menu_item): # read UID of NFC chip over the air @@ -416,23 +437,25 @@ def test_rf_uid(rf_interface, cap_story, goto_home, pick_menu_item): print(uid) -def test_ndef_roundtrip(load_shared_mod): +def test_ndef_roundtrip(load_shared_mod, src_root_dir): # specific failing case - cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') r = open('data/ms-import.ndef', 'rb').read() assert cc_ndef.ccfile_decode(r) == (12, 399, False, 4096) +@pytest.mark.parametrize('multisig', [True, False]) @pytest.mark.parametrize('num_outs', [2, 5, 100, 250]) @pytest.mark.parametrize('chain', ['BTC', 'XTN']) @pytest.mark.parametrize('way', ['sd', 'nfc', 'usb', 'qr']) def test_nfc_pushtx(num_outs, chain, enable_nfc, settings_set, settings_remove, - try_sign, fake_txn, nfc_block4rf, nfc_read, press_cancel, + try_sign, fake_txn, nfc_block4rf, nfc_read_url, press_cancel, cap_story, cap_screen, has_qwerty, way, try_sign_microsd, try_sign_nfc, scan_a_qr, need_keypress, press_select, - goto_home): + goto_home, multisig, fake_ms_txn, import_ms_wallet, + clear_miniscript, try_sign_bbqr): # check the NFC push Tx feature, validating the URL's it makes # - not the UX # - 100 outs => 5000 or so @@ -441,6 +464,7 @@ def test_nfc_pushtx(num_outs, chain, enable_nfc, settings_set, settings_remove, from base64 import urlsafe_b64decode from urllib.parse import urlsplit, parse_qsl, unquote + clear_miniscript() settings_set('chain', chain) enable_nfc() @@ -451,33 +475,28 @@ def test_nfc_pushtx(num_outs, chain, enable_nfc, settings_set, settings_remove, prefix = 'http://10.0.0.10/pushtx#' settings_set('ptxurl', prefix) - psbt = fake_txn(2, num_outs) + if multisig: + goto_home() + # create 1 of 3 multiig wallet - no need for another signers to make tx final + M, N = 1, 3 + af = random.choice(["p2wsh", "p2sh-p2wsh", "p2sh"]) + keys = import_ms_wallet(M, N, af, name="ms_pushtx", accept=True, way=way, chain=chain) + psbt = fake_ms_txn(2, num_outs, M, keys, inp_addr_fmt=af) + else: + psbt = fake_txn(2, num_outs) + if way == "usb": - _, result = try_sign(psbt, finalize=True) + _, result = try_sign(psbt, finalize=True, exit_export_loop=False) elif way == "sd": ip, result, txid = try_sign_microsd(psbt, finalize=True, nfc_push_tx=True) elif way == "nfc": + if len(psbt) > 1000: + pytest.skip("too big") + ip, result, txid = try_sign_nfc(psbt, expect_finalize=True, nfc_tools=True, - nfc_push_tx=True) + nfc_push_tx=True, encoding="hex") elif way == "qr": - goto_home() - need_keypress(KEY_QR) - from bbqr import split_qrs - actual_vers, parts = split_qrs(psbt, 'P') - for p in parts: - scan_a_qr(p) - time.sleep(4.0 / len(parts)) # just so we can watch - - for r in range(20): - title, story = cap_story() - if 'OK TO SEND' in title: - break - time.sleep(.1) - else: - raise pytest.fail('never saw it?') - - # approve it - press_select() + try_sign_bbqr(psbt, nfc_push_tx=True) # print(f'len = {len(result)}') # @@ -486,10 +505,9 @@ def test_nfc_pushtx(num_outs, chain, enable_nfc, settings_set, settings_remove, time.sleep(.1) title, story = cap_story() if way == "usb": - assert title == 'Final TXID' - assert 'to share signed txn' in story + assert 'TXID' in story elif way == "sd": - assert title == "PSBT Signed" + assert ('Updated PSBT' in story) or ('Finalized transaction' in story) else: assert False return @@ -501,20 +519,12 @@ def test_nfc_pushtx(num_outs, chain, enable_nfc, settings_set, settings_remove, scr = cap_screen() assert 'TXID:' in scr - contents = nfc_read() - - print(f'nfc contents = {len(contents)}') - - press_cancel() # exit NFC animation - - # expect a single record, a URL - got, = ndef.message_decoder(contents) + uri = nfc_read_url() - assert got.type == 'urn:nfc:wkt:U' - assert got.uri.startswith(prefix) - assert got.uri.startswith(prefix + 't') + assert uri.startswith(prefix) + assert uri.startswith(prefix + 't') - parts = urlsplit(got.uri) + parts = urlsplit(uri) args = parse_qsl(unquote(parts.fragment)) assert args[0][0] == 't', 'txn must be first' @@ -597,10 +607,11 @@ def test_share_by_pushtx(goto_home, cap_story, pick_menu_item, settings_set, ]) def test_nfc_share_files(fname, mode, ftype, nfc_read_json, nfc_read_text, need_keypress, goto_home, pick_menu_item, is_q1, - cap_menu, nfc_read, nfc_block4rf, press_select): + cap_menu, nfc_read, nfc_block4rf, press_select, + src_root_dir, sim_root_dir): goto_home() - fpath = "data/" + fname - shutil.copy2(fpath, '../unix/work/MicroSD') + fpath = f"{src_root_dir}/testing/data/" + fname + shutil.copy2(fpath, f'{sim_root_dir}/MicroSD') pick_menu_item("Advanced/Tools") pick_menu_item("File Management") pick_menu_item("NFC File Share") @@ -651,6 +662,6 @@ def test_nfc_share_files(fname, mode, ftype, nfc_read_json, nfc_read_text, res = json.loads(res) assert res == contents - os.remove('../unix/work/MicroSD/' + fname) + os.remove(f'{sim_root_dir}/MicroSD/' + fname) # EOF diff --git a/testing/test_notes.py b/testing/test_notes.py index 659ece1de..b54413287 100644 --- a/testing/test_notes.py +++ b/testing/test_notes.py @@ -5,8 +5,7 @@ import pytest, time, json, random, os, pdb from helpers import prandom from charcodes import * - -from test_bbqr import readback_bbqr +from constants import AF_CLASSIC, AF_P2WPKH_P2SH, AF_P2WPKH from bbqr import split_qrs @@ -41,12 +40,14 @@ def doit(item=None): return doit @pytest.fixture -def need_some_notes(settings_get, settings_set): +def need_some_notes(is_q1, settings_get, settings_set): # create a note or use what's there, provide as obj - def doit(): + def doit(title='Title Here', body='Body'): + assert is_q1 notes = settings_get('notes', []) if not notes: - settings_set('notes', [dict(misc='Body', title='Title Here')]) + settings_set('notes', [dict(misc=body, title=title)]) + settings_set('secnap', True) return notes return doit @@ -54,8 +55,8 @@ def doit(): def need_some_passwords(settings_get, settings_set): def doit(): notes = settings_get('notes', []) - if any(n.get('password', False) for n in notes): - settings_set('notes', [ + if not any(1 for n in notes if n.get('password', False)): + notes.extend([ {'misc': 'More Notes AAAA', 'password': 'fds65fd5f1sd51s', 'site': 'https://a.com', @@ -67,6 +68,8 @@ def doit(): 'title': 'B-Title', 'user': 'Buzzer'} ]) + settings_set('notes', notes) + settings_set('secnap', True) return notes return doit @@ -93,7 +96,7 @@ def doit(n_title): @pytest.fixture def build_note(goto_notes, pick_menu_item, enter_text, cap_menu, cap_story, need_keypress, cap_screen_qr, readback_bbqr, nfc_read_text, - press_select, press_cancel, is_headless): + press_select, press_cancel, is_headless, nfc_disabled): def doit(n_title, n_body): # we don't try to preserve leading/trailing spaces on note bodies @@ -142,12 +145,13 @@ def doit(n_title, n_body): # hidden NFC button on menu feature m = cap_menu() assert m[1] == 'View Note' - need_keypress(KEY_NFC) - time.sleep(.1) - nfc_rb = nfc_read_text() - time.sleep(.1) - assert nfc_rb == n_body - press_cancel() + if not nfc_disabled: + need_keypress(KEY_NFC) + time.sleep(.1) + nfc_rb = nfc_read_text() + time.sleep(.1) + assert nfc_rb == n_body + press_cancel() # export pick_menu_item('Export') @@ -181,7 +185,7 @@ def build_password(goto_notes, pick_menu_item, enter_text, cap_menu, cap_story, cap_text_box, settings_get, settings_set, scan_a_qr, press_select, press_cancel, is_headless): - def doit(n_title, n_user=None, n_pw=None, n_site=None, n_body=None, key_pw=None): + def doit(n_title, n_user=None, n_pw='secret', n_site=None, n_body=None, key_pw=None): goto_notes('New Password') enter_text(n_title) if n_user: @@ -384,7 +388,7 @@ def test_huge_notes(size, encoding, goto_notes, enter_text, cap_menu, need_keypr time.sleep(.5) # decompression time in some cases m = cap_menu() - assert m[-1] == 'Export' + assert m[-2] == 'Export' notes = settings_get('notes') assert len(notes) == 1 @@ -448,6 +452,33 @@ def test_top_export(goto_notes, pick_menu_item, cap_story, need_keypress, settin assert obj['coldcard_notes'] == notes need_keypress(KEY_ENTER) +def test_sort_by_title(goto_notes, pick_menu_item, cap_story, need_keypress, settings_get, + settings_set, build_note, cap_menu, build_password): + + settings_set('notes', []) + + build_note('ZZZ', 'b1') + + goto_notes() + assert 'Sort By Title' not in cap_menu() + + build_note('MMM', 'b2') + build_note('AAA', 'b3') + build_note('mmm', 'b2') + build_note('Aaa', 'b3') + build_password('Bbb') + + notes = settings_get('notes') + + goto_notes() + pick_menu_item('Sort By Title') + + # effect is immedate + after = settings_get('notes', []) + + assert sorted((i['title'] for i in after), key=lambda i:i.lower()) \ + == [i['title'] for i in after] + def test_top_import(goto_notes, cap_menu, cap_story, need_keypress, settings_get, settings_set, scan_a_qr, need_some_notes): # make some @@ -624,4 +655,41 @@ def test_tmp_notes_separation(goto_notes, pick_menu_item, generate_ephemeral_wor assert 'pwd-tmp' not in mm assert 'note-tmp2' not in mm + +@pytest.mark.parametrize("msg", ["COLDCARD rocks!", "cc\nCC"]) +@pytest.mark.parametrize("addr_fmt", [AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH]) +@pytest.mark.parametrize("acct", [None, 0, 9999]) +@pytest.mark.parametrize("way", ["sd", "qr", "nfc", "vdisk"]) +def test_sign_note_body(msg, addr_fmt, acct, need_some_notes, + pick_menu_item, sign_msg_from_text, way, + goto_notes, settings_set): + settings_set("notes", []) + title = "aaa" + need_some_notes(title, msg) + goto_notes() + pick_menu_item(f"1: {title}") + pick_menu_item("Sign Note Text") + sign_msg_from_text(msg, addr_fmt, acct, False, 0, way) + + +@pytest.mark.parametrize("chain", ["BTC", "XTN"]) +@pytest.mark.parametrize("change", [True, False]) +@pytest.mark.parametrize("idx", [None, 0, 9999]) +def test_sign_password_free_form(chain, change, idx, need_some_passwords, settings_set, + goto_notes, pick_menu_item, sign_msg_from_text): + settings_set('notes', []) # clear + title = "A" + msg = 'More Notes AAAA' + settings_set('notes', [ + {'misc': msg, + 'password': 'fds65fd5f1sd51s', + 'site': 'https://a.com', + 'title': title, + 'user': 'AAA'} + ]) + goto_notes() + pick_menu_item(f"1: {title}") + pick_menu_item("Sign Note Text") + sign_msg_from_text(msg, AF_P2WPKH, None, change, idx, "qr", chain) + # EOF diff --git a/testing/test_ownership.py b/testing/test_ownership.py index 181c9f576..708da7a70 100644 --- a/testing/test_ownership.py +++ b/testing/test_ownership.py @@ -2,13 +2,15 @@ # # Address ownership tests. # -import pytest, time, io, csv +import pytest, time, io, csv, json from txn import fake_address from base58 import encode_base58_checksum -from helpers import hash160 +from helpers import hash160, taptweak, addr_from_display_format +from bech32 import encode as bech32_encode from bip32 import BIP32Node -from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH +from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR from constants import simulator_fixed_xprv, simulator_fixed_tprv, addr_fmt_names +from charcodes import KEY_QR @pytest.fixture def wipe_cache(sim_exec): @@ -23,7 +25,7 @@ def doit(): [14, 8, 26, 1, 7, 19] ''' @pytest.mark.parametrize('addr_fmt', [ - AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH + AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR ]) @pytest.mark.parametrize('testnet', [ False, True] ) def test_negative(addr_fmt, testnet, sim_exec): @@ -36,55 +38,64 @@ def test_negative(addr_fmt, testnet, sim_exec): assert 'Explained' in lst -@pytest.mark.parametrize('addr_fmt, testnet', [ - (AF_CLASSIC, True), - (AF_CLASSIC, False), - (AF_P2WPKH, True), - (AF_P2WPKH, False), - (AF_P2WPKH_P2SH, True), - (AF_P2WPKH_P2SH, False), +@pytest.mark.parametrize('addr_fmt, chain', [ + (AF_CLASSIC, "XTN"), + (AF_CLASSIC, "BTC"), + (AF_P2WPKH, "XTN"), + (AF_P2WPKH, "BTC"), + (AF_P2WPKH_P2SH, "XTN"), + (AF_P2WPKH_P2SH, "BTC"), + (AF_P2TR, "XTN"), + (AF_P2TR, "BTC"), # multisig - testnet only - (AF_P2WSH, True), - (AF_P2SH, True), - (AF_P2WSH_P2SH,True), + (AF_P2WSH, "XTN"), + (AF_P2SH, "XTN"), + (AF_P2WSH_P2SH, "XTN"), ]) -@pytest.mark.parametrize('offset', [ 3, 760] ) +@pytest.mark.parametrize('offset', [ 3, 760, 763] ) @pytest.mark.parametrize('subaccount', [ 0, 34] ) @pytest.mark.parametrize('change_idx', [ 0, 1] ) @pytest.mark.parametrize('from_empty', [ True, False] ) -def test_positive(addr_fmt, offset, subaccount, testnet, from_empty, change_idx, - sim_exec, wipe_cache, make_myself_wallet, use_testnet, goto_home, pick_menu_item, - enter_number, press_cancel, settings_set, import_ms_wallet, clear_ms +def test_positive(addr_fmt, offset, subaccount, chain, from_empty, change_idx, + sim_exec, wipe_cache, use_testnet, goto_home, pick_menu_item, + enter_number, press_cancel, settings_set, import_ms_wallet, clear_miniscript, is_q1, ): - from bech32 import encode as bech32_encode # API/Unit test, limited UX - - if not testnet and addr_fmt in { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH }: - # multisig jigs assume testnet - raise pytest.skip('testnet only') - - use_testnet(testnet) - if from_empty: - wipe_cache() # very different codepaths - settings_set('accts', []) - - coin_type = 1 if testnet else 0 + ms_addr_fmts = { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH } + if (addr_fmt in ms_addr_fmts) and subaccount: + raise pytest.skip('multisig with subaccount') + + if chain == "BTC": + use_testnet(False) + testnet = False + if addr_fmt in ms_addr_fmts: + # multisig jigs assume testnet + raise pytest.skip('testnet only') + + coin_type = 0 + if chain == "XTN": + use_testnet(True) + coin_type = 1 + testnet = True + + wipe_cache() # very different codepaths + settings_set('accts', []) if addr_fmt in { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH }: from test_multisig import make_ms_address, HARD M, N = 1, 3 expect_name = f'search-test-{addr_fmt}' - clear_ms() - keys = import_ms_wallet(M, N, name=expect_name, accept=1, addr_fmt=addr_fmt_names[addr_fmt]) + clear_miniscript() + keys = import_ms_wallet(M, N, name=expect_name, accept=True, addr_fmt=addr_fmt_names[addr_fmt]) # iffy: no cosigner index in this wallet, so indicated that w/ path_mapper - addr, scriptPubKey, script, details = make_ms_address(M, keys, - is_change=change_idx, idx=offset, addr_fmt=addr_fmt, testnet=int(testnet), - path_mapper=lambda cosigner: [HARD(45), change_idx, offset]) - + addr, scriptPubKey, script, details = make_ms_address( + M, keys, addr_fmt=addr_fmt, testnet=int(testnet), + is_change=change_idx, idx=offset + ) path = f'.../{change_idx}/{offset}' else: @@ -92,13 +103,15 @@ def test_positive(addr_fmt, offset, subaccount, testnet, from_empty, change_idx, menu_item = expect_name = 'Classic P2PKH' path = "m/44h/{ct}h/{acc}h" elif addr_fmt == AF_P2WPKH_P2SH: - expect_name = 'P2WPKH-in-P2SH' - menu_item = 'P2SH-Segwit' + menu_item = expect_name = 'P2SH-Segwit' path = "m/49h/{ct}h/{acc}h" - clear_ms() + clear_miniscript() elif addr_fmt == AF_P2WPKH: menu_item = expect_name = 'Segwit P2WPKH' path = "m/84h/{ct}h/{acc}h" + elif addr_fmt == AF_P2TR: + menu_item = expect_name = 'Taproot P2TR' + path = "m/86h/{ct}h/{acc}h" else: raise ValueError(addr_fmt) @@ -108,14 +121,18 @@ def test_positive(addr_fmt, offset, subaccount, testnet, from_empty, change_idx, # see addr_vs_path mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv) - sk = mk.subkey_for_path(path[2:].replace('h', "'")) + sk = mk.subkey_for_path(path) if addr_fmt == AF_CLASSIC: - addr = sk.address(netcode="XTN" if testnet else "BTC") + addr = sk.address(chain=chain) elif addr_fmt == AF_P2WPKH_P2SH: pkh = sk.hash160() digest = hash160(b'\x00\x14' + pkh) addr = encode_base58_checksum(bytes([196 if testnet else 5]) + digest) + elif addr_fmt == AF_P2TR: + from bech32 import encode + tweked_xonly = taptweak(sk.sec()[1:]) + addr = encode("tb" if testnet else "bc", 1, tweked_xonly) else: pkh = sk.hash160() addr = bech32_encode('tb' if testnet else 'bc', 0, pkh) @@ -130,49 +147,81 @@ def test_positive(addr_fmt, offset, subaccount, testnet, from_empty, change_idx, pick_menu_item(menu_item) press_cancel() - cmd = f'from ownership import OWNERSHIP; w,path=OWNERSHIP.search({addr!r}); '\ - 'RV.write(repr([w.name, path]))' - lst = sim_exec(cmd) - if 'candidates without finding a match' in lst: - # some kinda timing issue, but don't want big delays, so just retry - print("RETRY search!") - lst = sim_exec(cmd) - - assert 'Traceback' not in lst, lst + cmd = (f'from ownership import OWNERSHIP;' + f'c,w,path=OWNERSHIP.search({addr!r});' + f'RV.write(repr([c, w.name, path]))') + + if not from_empty: + # we expect here to find address from cache + # so we first need to generate proper cache + lst = sim_exec(cmd, timeout=None) + assert 'Traceback' not in lst, lst + lst = eval(lst) + assert len(lst) == 3 + assert lst[0] is False # not from cache, needed to build it + + lst = sim_exec(cmd, timeout=None) + assert 'Traceback' not in lst, lst lst = eval(lst) - assert len(lst) == 2 + assert len(lst) == 3 + + from_cache, got_name, got_path = lst - got_name, got_path = lst - assert expect_name in got_name - if subaccount and '...' not in path: + assert from_cache == (not from_empty) + if is_q1: + assert expect_name in got_name + else: + assert expect_name.split(" ")[-1] in got_name + if subaccount: # not expected for multisig, since we have proper wallet name - assert f'Account#{subaccount}' in got_name + if is_q1: + assert f'Account#{subaccount}' in got_name + else: + assert f'Acct#{subaccount}' in got_name assert got_path == (change_idx, offset) @pytest.mark.parametrize('valid', [ True, False] ) -@pytest.mark.parametrize('testnet', [ True, False] ) +@pytest.mark.parametrize('netcode', [ "BTC", "XTN"] ) @pytest.mark.parametrize('method', [ 'qr', 'nfc'] ) -def test_ux(valid, testnet, method, - sim_exec, wipe_cache, make_myself_wallet, use_testnet, goto_home, pick_menu_item, +@pytest.mark.parametrize('multisig', [ True, False] ) +def test_ux(valid, netcode, method, + sim_exec, wipe_cache, use_testnet, goto_home, pick_menu_item, press_cancel, press_select, settings_set, is_q1, nfc_write, need_keypress, - cap_screen, cap_story, load_shared_mod, scan_a_qr + cap_screen, cap_story, load_shared_mod, scan_a_qr, skip_if_useless_way, + sign_msg_from_address, multisig, import_ms_wallet, clear_miniscript, verify_qr_address, + src_root_dir, sim_root_dir ): - + skip_if_useless_way(method) addr_fmt = AF_CLASSIC + testnet = (netcode == "XTN") + if valid: - mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv) - path = "m/44h/{ct}h/{acc}h/0/3".format(acc=0, ct=(1 if testnet else 0)) - sk = mk.subkey_for_path(path) - addr = sk.address(netcode="XTN" if testnet else "BTC") + if multisig: + from test_multisig import make_ms_address, HARD + M, N = 2, 3 + + expect_name = f'own_ux_test' + clear_miniscript() + keys = import_ms_wallet(M, N, "p2wsh", name=expect_name, accept=1) + + # iffy: no cosigner index in this wallet, so indicated that w/ path_mapper + addr, scriptPubKey, script, details = make_ms_address( + M, keys, is_change=0, idx=50, addr_fmt=AF_P2WSH, + testnet=int(testnet), path_mapper=lambda cosigner: [HARD(45), 0, 50] + ) + addr_fmt = AF_P2WSH + else: + mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv) + path = "m/44h/{ct}h/{acc}h/0/3".format(acc=0, ct=(1 if testnet else 0)) + sk = mk.subkey_for_path(path) + addr = sk.address(chain=netcode) else: - addr = fake_address(addr_fmt, testnet) + addr = fake_address(addr_fmt, testnet) if method == 'qr': - if not is_q1: - raise pytest.skip('no QR on Mk4') goto_home() pick_menu_item('Scan Any QR Code') scan_a_qr(addr) @@ -180,13 +229,13 @@ def test_ux(valid, testnet, method, title, story = cap_story() - assert addr in story + assert addr == addr_from_display_format(story.split("\n\n")[0]) assert '(1) to verify ownership' in story need_keypress('1') elif method == 'nfc': - cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') n = cc_ndef.ndefMaker() n.add_text(addr) ccfile = n.bytes() @@ -196,7 +245,8 @@ def test_ux(valid, testnet, method, pick_menu_item('Advanced/Tools') pick_menu_item('NFC Tools') pick_menu_item('Verify Address') - open('debug/nfc-addr.ndef', 'wb').write(ccfile) + with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f: + f.write(ccfile) nfc_write(ccfile) #press_select() @@ -205,35 +255,58 @@ def test_ux(valid, testnet, method, time.sleep(1) title, story = cap_story() - - assert addr in story + assert addr == addr_from_display_format(story.split("\n\n")[0]) if title == 'Unknown Address' and not testnet: assert 'That address is not valid on Bitcoin Testnet' in story elif valid: - assert title == 'Verified Address' + assert title == ('Verified Address' if is_q1 else "Verified!") assert 'Found in wallet' in story assert 'Derivation path' in story - assert 'P2PKH' in story + + if is_q1: + # check it can display as QR from here + need_keypress(KEY_QR) + verify_qr_address(addr_fmt, addr) + press_cancel() + + if multisig: + assert expect_name in story + assert "Press (0) to sign message with this key" not in story + else: + assert 'P2PKH' in story + assert "Press (0) to sign message with this key" in story + need_keypress('0') + msg = "coinkite CC the most solid HWW" + sign_msg_from_address(msg, addr, path, addr_fmt, method, netcode) + else: assert title == 'Unknown Address' - assert 'Searched ' in story - assert 'candidates without finding a match' in story + assert 'Searched 1528' in story # max + assert "1 wallet(s)" in story + assert 'without finding a match' in story -@pytest.mark.parametrize("af", ["P2SH-Segwit", "Segwit P2WPKH", "Classic P2PKH", "ms0"]) +@pytest.mark.parametrize("af", ["P2SH-Segwit", "Segwit P2WPKH", "Classic P2PKH", "Taproot P2TR", "ms0", "msc0", "msc2"]) def test_address_explorer_saver(af, wipe_cache, settings_set, goto_address_explorer, - pick_menu_item, need_keypress, sim_exec, clear_ms, + pick_menu_item, need_keypress, sim_exec, clear_miniscript, import_ms_wallet, press_select, goto_home, nfc_write, load_shared_mod, load_export_and_verify_signature, - cap_story): + cap_story, load_export, offer_minsc_import, is_q1, + src_root_dir, sim_root_dir): goto_home() wipe_cache() settings_set('accts', []) if af == "ms0": - clear_ms() - import_ms_wallet(2,3, name=af) + clear_miniscript() + import_ms_wallet(2, 3, name=af) press_select() # accept ms import + elif "msc" in af: + from test_miniscript import CHANGE_BASED_DESCS + which = int(af[-1]) + title, story = offer_minsc_import(json.dumps({"name": af, "desc": CHANGE_BASED_DESCS[which]})) + assert "Create new miniscript wallet?" in story + press_select() # accept goto_address_explorer() pick_menu_item(af) @@ -245,22 +318,20 @@ def test_address_explorer_saver(af, wipe_cache, settings_set, goto_address_explo lst = eval(lst) assert lst - if af == "ms0": - return # multisig addresses are blanked - title, body = cap_story() - contents, sig_addr = load_export_and_verify_signature(body, "sd", label="Address summary") + contents, _, _ = load_export_and_verify_signature(body, "sd", label="Address summary") + addr_dump = io.StringIO(contents) cc = csv.reader(addr_dump) hdr = next(cc) - assert hdr == ['Index', 'Payment Address', 'Derivation'] addr = None - for n, (idx, addr, deriv) in enumerate(cc, start=0): + assert hdr[:2] == ['Index', 'Payment Address'] + for n, (idx, addr, *_) in enumerate(cc, start=0): assert int(idx) == n if idx == 200: addr = addr - cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') n = cc_ndef.ndefMaker() n.add_text(addr) ccfile = n.bytes() @@ -270,21 +341,384 @@ def test_address_explorer_saver(af, wipe_cache, settings_set, goto_address_explo pick_menu_item('Advanced/Tools') pick_menu_item('NFC Tools') pick_menu_item('Verify Address') - open('debug/nfc-addr.ndef', 'wb').write(ccfile) + with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f: + f.write(ccfile) + nfc_write(ccfile) time.sleep(1) title, story = cap_story() - assert addr in story - assert title == 'Verified Address' + assert addr == addr_from_display_format(story.split("\n\n")[0]) + assert title == ('Verified Address' if is_q1 else "Verified!") assert 'Found in wallet' in story assert 'Derivation path' in story - if af == "P2SH-Segwit": - assert "P2WPKH-in-P2SH" in story - elif af == "Segwit P2WPKH": - assert " P2WPKH " in story - else: + if is_q1: assert af in story + else: + if "ms" in af: + assert af in story + elif af == "P2SH-Segwit": + assert af in story + else: + part = af.split(" ")[1] + assert part in story + + +def test_ae_saver(wipe_cache, settings_set, goto_address_explorer, cap_story, + pick_menu_item, need_keypress, sim_exec, is_q1, + import_ms_wallet, press_select, goto_home, nfc_write, + load_shared_mod, load_export_and_verify_signature, + set_addr_exp_start_idx, use_testnet): + + cmd = lambda a: ( + f'from ownership import OWNERSHIP;' + f'c,w,path=OWNERSHIP.search({a!r});' + f'RV.write(repr([c, w.name, path]))') + + def cache_check(a, from_cache): + l = sim_exec(cmd(a), timeout=None) + assert 'Traceback' not in l, l + assert eval(l)[0] == from_cache + + use_testnet() + goto_home() + wipe_cache() + settings_set('accts', []) + settings_set('aei', True) + + goto_address_explorer() + set_addr_exp_start_idx(7) # starting from index 7 + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary") + addr_dump = io.StringIO(contents) + cc = csv.reader(addr_dump) + hdr = next(cc) + assert hdr == ['Index', 'Payment Address', 'Derivation'] + addrs = {} + for idx, addr, deriv in cc: + addrs[int(idx)] = addr + + # nothing was created from above as start index was 7 + cache_check(addrs[7], False) + # now we have cached addresses up to 27 + for i in range(8, 28): + cache_check(addrs[i], True) + + # cache file position at 27 (aka count) + goto_address_explorer() + set_addr_exp_start_idx(1) # starting from index 1 + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + load_export_and_verify_signature(body, "sd", label="Address summary") + + # after above we must have first 250 addresses cached + cache_check(addrs[249], True) + + # cache file position at 250 (aka count) + goto_address_explorer() + set_addr_exp_start_idx(250) # starting from index 250 + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary") + addr_dump = io.StringIO(contents) + cc = csv.reader(addr_dump) + hdr = next(cc) + assert hdr == ['Index', 'Payment Address', 'Derivation'] + addrs = {} + for idx, addr, deriv in cc: + addrs[int(idx)] = addr + + # after above we must have first 500 addresses cached + cache_check(addrs[300], True) + cache_check(addrs[400], True) + cache_check(addrs[499], True) + + # now addresses that we already have, does nothing + goto_address_explorer() + set_addr_exp_start_idx(100) # starting from index 100 + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + load_export_and_verify_signature(body, "sd", label="Address summary") + cache_check(addrs[499], True) + + # now move count up via ownership + mk = BIP32Node.from_wallet_key(simulator_fixed_tprv) + sk = mk.subkey_for_path("84h/1h/0h/0/580") + addr = bech32_encode('tb', 0, sk.hash160()) + cache_check(addr, False) + # now count at 600 (580+20) + + # now over the max but with some we already have + goto_address_explorer() + set_addr_exp_start_idx(550) # starting from index 550 (would go up to 800) + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary") + addr_dump = io.StringIO(contents) + cc = csv.reader(addr_dump) + hdr = next(cc) + assert hdr == ['Index', 'Payment Address', 'Derivation'] + addrs = {} + for idx, addr, deriv in cc: + addrs[int(idx)] = addr + + assert 799 in addrs + cache_check(addrs[763], True) # max + + # start idx over max stored addresses + goto_address_explorer() + set_addr_exp_start_idx(764) # starting from index 764 + pick_menu_item("Segwit P2WPKH") + need_keypress("1") # save to SD + + time.sleep(.1) + title, body = cap_story() + load_export_and_verify_signature(body, "sd", label="Address summary") + # does notthing harmful, nothing added + + cache_check(addrs[763], True) # max + l = sim_exec(cmd(addrs[764]), timeout=None) + assert 'Traceback' in l + assert 'Searched 1528' in l # max + assert "1 wallet(s)" in l + assert 'without finding a match' in l + + +def test_regtest_addr_on_mainnet(goto_home, is_q1, pick_menu_item, scan_a_qr, nfc_write, cap_story, + need_keypress, load_shared_mod, use_mainnet, src_root_dir, sim_root_dir): + # testing bug in chains.possible_address_fmt + # allowed regtest addresses to be allowed on main chain + goto_home() + use_mainnet() + addr = "bcrt1qmff7njttlp6tqtj0nq7svcj2p9takyqm3mfl06" + if is_q1: + pick_menu_item('Scan Any QR Code') + scan_a_qr(addr) + time.sleep(1) + + title, story = cap_story() + + assert addr == addr_from_display_format(story.split("\n\n")[0]) + assert '(1) to verify ownership' in story + need_keypress('1') + + else: + cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py') + n = cc_ndef.ndefMaker() + n.add_text(addr) + ccfile = n.bytes() + + # run simulator w/ --set nfc=1 --eff + pick_menu_item('Advanced/Tools') + pick_menu_item('NFC Tools') + pick_menu_item('Verify Address') + with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f: + f.write(ccfile) + nfc_write(ccfile) + # press_select() + + time.sleep(1) + title, story = cap_story() + assert addr == addr_from_display_format(story.split("\n\n")[0]) + + assert title == 'Unknown Address' + assert "not valid on Bitcoin Mainnet" in story + + +def test_20_more_build_after_match(sim_exec, import_ms_wallet, clear_miniscript, wipe_cache, + settings_set): + from test_multisig import make_ms_address, HARD + + cmd = lambda a: ( + f'from ownership import OWNERSHIP;' + f'c,w,path=OWNERSHIP.search({a!r});' + f'RV.write(repr([c, w.name, path]))') + + # create multisig wallet + M, N = 2, 3 + expect_name = 'test20more' + clear_miniscript() + keys = import_ms_wallet(M, N, name=expect_name, accept=True, addr_fmt="p2wsh") + + make_a = lambda index: make_ms_address( + M, keys, + is_change=False, idx=index, addr_fmt=AF_P2WSH, testnet=True, + path_mapper=lambda cosigner: [HARD(45), 0, index]) + + def cache_check(index, from_cache): + a = make_a(index)[0] + l = sim_exec(cmd(a), timeout=None) + assert 'Traceback' not in l, l + assert eval(l)[0] == from_cache + + # clean slate + wipe_cache() + settings_set('accts', []) + + # generate 10th (idx=9) address (external) + # first run, generated first 10 addresses + 20 + cache_check(9, False) + + # now we can go up to index 29 - all must come from cache + for i in range(10, 30): + cache_check(i, True) + + # idx 30 - not in cache + # but will cache next 20 addrs + cache_check(30, False) + + # now we can go up to index 51 - all must come from cache + for i in range(31, 51): + cache_check(i, True) + + # idx 51 - not in cache + # but will cache next 20 addrs + cache_check(51, False) + + cache_check(760, False) + cache_check(761, True) + cache_check(762, True) + cache_check(763, True) + + # after max - not gonna find + addr = make_a(764)[0] + l = sim_exec(cmd(addr), timeout=None) + assert 'Traceback' in l + assert 'Searched 1528' in l # max + assert "1 wallet(s)" in l + assert 'without finding a match' in l + + +def test_named_wallet_search_fail(load_shared_mod, goto_home, pick_menu_item, nfc_write, + cap_story, sim_root_dir): + addr = fake_address(AF_P2WSH, True) + addr = f"{addr}?wallet=unknown" + cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + n = cc_ndef.ndefMaker() + n.add_text(addr) + ccfile = n.bytes() + + # run simulator w/ --set nfc=1 --eff + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('NFC Tools') + pick_menu_item('Verify Address') + with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f: + f.write(ccfile) + nfc_write(ccfile) + + time.sleep(1) + title, story = cap_story() + assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0]) + assert "Wallet 'unknown' not defined." in story + + +@pytest.mark.parametrize('valid', [True, False]) +@pytest.mark.parametrize('method', ["qr", "nfc"]) +@pytest.mark.parametrize('wname', ["msnm", "Longer Wallet Name"]) +def test_named_wallet_search(wname, valid, method, clear_miniscript, import_ms_wallet, is_q1, + load_shared_mod, goto_home, pick_menu_item, scan_a_qr, + cap_story, need_keypress, nfc_write, use_testnet, + wipe_cache, settings_set, sim_root_dir): + + from test_multisig import make_ms_address, HARD + + if method == "qr" and (not is_q1): + raise pytest.skip("QR Mk") + + wipe_cache() # very different codepaths + settings_set('accts', []) + use_testnet() + M, N = 2, 3 + clear_miniscript() + ms_data = {} + # all ms wallets have same address format, different M/N + for i in range(3): + idx = 5 + if i == 2: + idx = 763 + name = f'{wname}{i}' + keys = import_ms_wallet(M+i, N+i, "p2wsh", name=name, accept=True) + # last address + addr, scriptPubKey, script, details = make_ms_address( + M+i, keys, is_change=0, idx=idx, addr_fmt=AF_P2WSH, + testnet=True, path_mapper=lambda cosigner: [HARD(45), 0, idx] + ) + ms_data[name] = (addr, scriptPubKey, script, keys) + + if valid: + # msnw2 -> last added wallet + addr, *_ = ms_data[f"{wname}{i}"] + else: + # will fail, even tho address is present in different wallet + # with wallet= only specified wallet is searched + addr, *_ = ms_data[f"{wname}0"] + + # will only search specified wallet + addr = f"{addr}?wallet={wname}{i}".replace(' ', '%20') + + if method == 'qr': + goto_home() + pick_menu_item('Scan Any QR Code') + scan_a_qr(addr) + time.sleep(1) + + title, story = cap_story() + + assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0]) + assert '(1) to verify ownership' in story + need_keypress('1') + + elif method == 'nfc': + cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py') + n = cc_ndef.ndefMaker() + n.add_text(addr) + ccfile = n.bytes() + + # run simulator w/ --set nfc=1 --eff + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('NFC Tools') + pick_menu_item('Verify Address') + with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f: + f.write(ccfile) + nfc_write(ccfile) + # press_select() + + else: + raise ValueError(method) + + time.sleep(1) + title, story = cap_story() + assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0]) + + if valid: + assert title == ('Verified Address' if is_q1 else "Verified!") + assert 'Found in wallet' in story + assert 'Derivation path' in story + assert f"{wname}" in story + + else: + assert title == 'Unknown Address' + assert 'Searched 1528' in story # max + assert "1 wallet(s)" in story + assert 'without finding a match' in story # EOF diff --git a/testing/test_paper.py b/testing/test_paper.py index 0dc2e0db0..c6c59d343 100644 --- a/testing/test_paper.py +++ b/testing/test_paper.py @@ -6,19 +6,19 @@ # This module can and should be run with `-l` and without it. # -import pytest, time, os, shutil, re, random +import pytest, time, os, shutil, re, random, json from binascii import a2b_hex from hashlib import sha256 from bip32 import PrivateKey from ckcc_protocol.constants import * -@pytest.mark.parametrize('mode', ["classic", 'segwit']) +@pytest.mark.parametrize('mode', ["classic", 'segwit', 'taproot']) @pytest.mark.parametrize('pdf', [False, True]) -@pytest.mark.parametrize('netcode', ["XTN", "BTC"]) +@pytest.mark.parametrize('netcode', ["XRT", "BTC", "XTN"]) def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, verify_detached_signature_file, settings_set, - press_select): + press_select, validate_address, bitcoind, src_root_dir): # test UX and operation of the 'bitcoin core' wallet export mx = "Don't make PDF" @@ -26,10 +26,7 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, goto_home() pick_menu_item('Advanced/Tools') - try: - pick_menu_item('Paper Wallets') - except: - raise pytest.skip('Feature absent') + pick_menu_item('Paper Wallets') time.sleep(0.1) title, story = cap_story() @@ -45,9 +42,14 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, pick_menu_item('Segwit P2WPKH') time.sleep(0.5) + if mode == 'taproot': + pick_menu_item('Classic P2PKH') + pick_menu_item('Taproot P2TR') + time.sleep(0.5) + if pdf: assert mx in cap_menu() - shutil.copy('../docs/paperwallet.pdf', microsd_path('paperwallet.pdf')) + shutil.copy(f'{src_root_dir}/docs/paperwallet.pdf', microsd_path('paperwallet.pdf')) pick_menu_item(mx) time.sleep(0.2) @@ -58,7 +60,7 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, time.sleep(0.1) title, story = cap_story() - if "Press (1) to save paper wallet file to SD Card" in story: + if "Press (1)" in story: need_keypress("1") time.sleep(0.2) title, story = cap_story() @@ -68,20 +70,32 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, story = [i for i in story.split('\n') if i] sig_file = story[-1] if not pdf: - fname = story[-2] - fnames = [fname] + if mode == "taproot": + fname = story[-1] + else: + fname = story[-2] + fnames = [fname] else: - fname = story[-3] - pdf_name = story[-2] - fnames = [fname, pdf_name] + if mode == "taproot": + fname = story[-2] + pdf_name = story[-1] + else: + fname = story[-3] + pdf_name = story[-2] + fnames = [fname, pdf_name] assert pdf_name.endswith('.pdf') assert fname.endswith('.txt') - assert sig_file.endswith(".sig") - verify_detached_signature_file(fnames, sig_file, "sd", - addr_fmt=AF_CLASSIC if mode == "classic" else AF_P2WPKH) + if mode != 'taproot': + assert sig_file.endswith(".sig") + verify_detached_signature_file(fnames, sig_file, "sd", + addr_fmt=AF_CLASSIC if mode == "classic" else AF_P2WPKH) path = microsd_path(fname) + _wif = None + _sk = None + _addr = None + _idesc = None with open(path, 'rt') as fp: hdr = None for ln in fp: @@ -98,27 +112,46 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, val = ln.strip() if 'Deposit address' in hdr: assert val == fname.split('.', 1)[0].split('-', 1)[0] - txt_addr = val - addr = val + _addr = val elif hdr == 'Private key:': # for QR case - assert val == wif + assert val == _wif elif 'Private key' in hdr and 'WIF=Wallet' in hdr: - wif = val - k1 = PrivateKey.from_wif(val) + _wif = val elif 'Private key' in hdr and 'Hex, 32 bytes' in hdr: - k2 = PrivateKey(sec_exp=a2b_hex(val)) + _sk = val elif 'Bitcoin Core command': - assert wif in val - assert 'importmulti' in val or 'importprivkey' in val + assert _wif in val + if 'importdescriptors' in val: + _idesc = val + assert 'importprivkey' in val or 'importdescriptors' in val else: print(f'{hdr} => {val}') raise ValueError(hdr) - assert k1.K.sec() == k2.K.sec() - assert addr == k1.K.address(addr_fmt="p2wpkh" if mode == "segwit" else "p2pkh", - testnet=True if netcode == "XTN" else False) - - os.unlink(path) + if netcode != "XRT": + from bip32 import PrivateKey + k1 = PrivateKey.from_wif(_wif) + k2 = PrivateKey.parse(a2b_hex(_sk)) + assert k1 == k2 + validate_address(_addr, k1) + else: + if mode == "segwit": + assert _addr.startswith("bcrt1q") + elif mode == "taproot": + assert _addr.startswith("bcrt1p") + else: + assert _addr[0] in "mn" + + # bitcoind on regtest + conn = bitcoind.create_wallet(wallet_name="paper", disable_private_keys=False, blank=True, + passphrase=None, avoid_reuse=False, descriptors=True) + desc_obj_s, desc_obj_e = _idesc.find("["), _idesc.find("]") + 1 + desc_obj = json.loads(_idesc[desc_obj_s:desc_obj_e]) + desc = desc_obj[0]["desc"] + res = conn.importdescriptors(desc_obj) + assert res[0]["success"] + assert _addr == conn.deriveaddresses(desc)[0] + bitcoind.delete_wallet_files() if not pdf: return @@ -126,8 +159,8 @@ def test_generate(mode, pdf, netcode, dev, cap_menu, pick_menu_item, goto_home, with open(path, 'rb') as fp: d = fp.read() - assert wif.encode('ascii') in d - assert txt_addr.encode('ascii') in d + assert _wif.encode('ascii') in d + assert _addr.encode('ascii') in d os.unlink(path) @@ -276,7 +309,7 @@ def test_dice_generate(rolls, testnet, dev, cap_menu, pick_menu_item, goto_home, val, = hx k2 = PrivateKey(sec_exp=a2b_hex(val)) - assert addr == k2.K.address(testnet=testnet, addr_fmt="p2pkh") + assert addr == k2.K.address(chain="XTN" if testnet else "BTC", addr_fmt="p2pkh") assert val == sha256(rolls.encode('ascii')).hexdigest() diff --git a/testing/test_pwsave.py b/testing/test_pwsave.py index 41b335ee2..2e158af6f 100644 --- a/testing/test_pwsave.py +++ b/testing/test_pwsave.py @@ -3,11 +3,15 @@ # tests for ../shared/pwsave.py # import pytest, time, os, shutil -from test_ux import word_menu_entry from binascii import a2b_hex from constants import simulator_fixed_tprv -SIM_FNAME = '../unix/work/MicroSD/.tmp.tmp' + +@pytest.fixture +def simulator_db_file(sim_root_dir): + def doit(): + return sim_root_dir + "/MicroSD/.tmp.tmp" + return doit @pytest.fixture def set_pw_phrase(pick_menu_item, word_menu_entry): @@ -32,8 +36,9 @@ def doit(words): 'ab'*25, ]) def test_first_time(pws, need_keypress, cap_story, pick_menu_item, enter_complex, - cap_menu, go_to_passphrase, reset_seed_words, press_select): - try: os.unlink(SIM_FNAME) + cap_menu, go_to_passphrase, reset_seed_words, press_select, + simulator_db_file): + try: os.unlink(simulator_db_file()) except: pass pws = pws.split() @@ -86,7 +91,7 @@ def test_first_time(pws, need_keypress, cap_story, pick_menu_item, enter_complex reset_seed_words() -def test_crypto_unittest(sim_eval, sim_exec, simulator): +def test_crypto_unittest(sim_exec, simulator, simulator_db_file): # unit test for AES key generation from SDCard and master secret card = sim_exec('import files; from h import b2a_hex; cs = files.CardSlot().__enter__(); RV.write(b2a_hex(cs.get_id_hash())); cs.__exit__()') @@ -122,7 +127,7 @@ def test_crypto_unittest(sim_eval, sim_exec, simulator): # check that key works for decrypt and that the file was actually encrypted - with open(SIM_FNAME, 'rb') as fd: + with open(simulator_db_file(), 'rb') as fd: raw = fd.read() import pyaes @@ -138,7 +143,7 @@ def test_crypto_unittest(sim_eval, sim_exec, simulator): assert j[0]['xfp'] def test_delete_one_by_one(go_to_passphrase, pick_menu_item, cap_menu, - cap_story, press_select): + cap_story, press_select, src_root_dir, sim_root_dir): # delete it one by one # when all deleted - we must be back in Passphrase # menu without Restore Saved option visible @@ -146,7 +151,7 @@ def test_delete_one_by_one(go_to_passphrase, pick_menu_item, cap_menu, time.sleep(.1) m = cap_menu() if 'Restore Saved' not in m: - shutil.copy2('data/pwsave.tmp', '../unix/work/MicroSD/.tmp.tmp') + shutil.copy2(f'{src_root_dir}/testing/data/pwsave.tmp', f'{sim_root_dir}/MicroSD/.tmp.tmp') go_to_passphrase() pick_menu_item('Restore Saved') m = cap_menu() diff --git a/testing/test_se2.py b/testing/test_se2.py index 092ea2151..ec6a77ff1 100644 --- a/testing/test_se2.py +++ b/testing/test_se2.py @@ -65,7 +65,7 @@ def decode_slot(data): assert len(data) == 128 return SlotInfo(*struct.unpack(TRICK_FMT, data)) -@pytest.fixture(scope='function') +@pytest.fixture def se2_gate(sim_exec): # not-so-low-level method: include auth data for main PIN def doit(method_num, obj=None, buf=None): @@ -252,7 +252,7 @@ def test_ux_trick_menus(goto_trick_menu, pick_menu_item, cap_menu, # all clear now -@pytest.fixture(scope='function') +@pytest.fixture def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, press_select, cap_story, enter_pin, se2_gate, is_simulator, is_q1): # using menus and UX, setup a new trick PIN @@ -301,8 +301,10 @@ def doit(new_pin, op_mode, expect=None): time.sleep(.1) m = cap_menu() assert m[0] == f'[{new_pin}]' - assert set(m[1:]) == {'Duress Wallet', 'Just Reboot', 'Wipe Seed', \ - 'Delta Mode', 'Look Blank', 'Brick Self', 'Login Countdown'} + assert set(m[1:]) == {'Duress Wallet', 'Just Reboot', 'Wipe Seed', 'Delta Mode', + 'Look Blank', 'Policy Unlock', + 'Policy Unlock & Wipe' if is_q1 else 'P.U. & Wipe', + 'Brick Self', 'Login Countdown'} pick_menu_item(op_mode) @@ -515,7 +517,7 @@ def test_ux_countdown_choices(subchoice, expect, xflags, new_trick_pin, new_pin_ # ( 'Blank Coldcard', 'freshly wiped Coldcard', TC_WIPE|TC_BLANK_WALLET, 0 ), ]) def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs, words12, - reset_seed_words, repl, clear_all_tricks, import_ms_wallet, get_setting, clear_ms, + reset_seed_words, repl, clear_all_tricks, import_ms_wallet, get_setting, clear_miniscript, new_trick_pin, new_pin_confirmed, cap_menu, pick_menu_item, cap_story, need_keypress, press_select, press_cancel, seed_story_to_words, is_q1, set_seed_words, stop_after_activated=False, @@ -529,11 +531,11 @@ def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs, words12, xargs += 1000 # import multisig - clear_ms() + clear_miniscript() import_ms_wallet(2, 2, dev_key=words12) press_select() time.sleep(.1) - assert len(get_setting('multisig')) == 1 + assert len(get_setting('miniscript')) == 1 # after Wipe Seed -> Wipe->Wallet choice, another level clear_all_tricks() @@ -611,7 +613,7 @@ def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs, words12, xp = repl.eval("settings.get('xpub')") assert xp == wallet.hwif(as_private=False) - assert not get_setting('multisig') # multisig is not copied + assert not get_setting('miniscript') # multisig is not copied # re-login to recover normal seed reset_seed_words() @@ -879,7 +881,7 @@ def build_duress_wallets(request, seed_vault=False): # fixtures I need in test_ux_duress_choices args = {f: request.getfixturevalue(f) - for f in ['reset_seed_words', 'repl', 'clear_all_tricks', 'new_trick_pin', 'clear_ms', + for f in ['reset_seed_words', 'repl', 'clear_all_tricks', 'new_trick_pin', 'clear_miniscript', 'import_ms_wallet', 'get_setting', 'press_select', 'press_cancel', 'is_q1', 'new_pin_confirmed', 'cap_menu', 'pick_menu_item', 'cap_story', 'need_keypress', 'seed_story_to_words', 'set_seed_words']} @@ -914,6 +916,14 @@ def build_duress_wallets(request, seed_vault=False): return 4 +def test_deltamode_toggle(get_deltamode, set_deltamode): + # check test fixture works. + assert get_deltamode() == False + set_deltamode(True) + assert get_deltamode() == True + set_deltamode(False) + assert get_deltamode() == False + # TODO # - make trick and do login, check arrives right state? diff --git a/testing/test_seed_xor.py b/testing/test_seed_xor.py index bdd2f929b..d14cf883c 100644 --- a/testing/test_seed_xor.py +++ b/testing/test_seed_xor.py @@ -3,12 +3,11 @@ # test Seed XOR features # -import pytest, time, itertools +import pytest, time, itertools, random from mnemonic import Mnemonic from constants import simulator_fixed_words from xor import prepare_test_pairs, xor from bip32 import BIP32Node -from test_ux import word_menu_entry, pass_word_quiz from charcodes import KEY_QR, KEY_RIGHT, KEY_DOWN wordlist = Mnemonic('english').wordlist @@ -55,7 +54,7 @@ def restore_seed_xor(set_seed_words, goto_home, pick_menu_item, cap_story, word_menu_entry, verify_ephemeral_secret_ui, confirm_tmp_seed, seed_vault_enable, press_select, scan_a_qr, is_q1, cap_screen_qr, cap_screen, OK): - def doit(parts, expect, incl_self=False, save_to_vault=False, + def doit(parts, expect, incl_self=False, save_to_vault=None, is_master_tmp_fail=False, way=None): if expect is None: parts, expect = prepare_test_pairs(*parts) @@ -67,6 +66,9 @@ def doit(parts, expect, incl_self=False, save_to_vault=False, elif incl_self is False: set_seed_words(proper[num_words]) + if save_to_vault is None: + save_to_vault = random.getrandbits(1) + seed_vault_enable(save_to_vault) time.sleep(.2) @@ -172,7 +174,6 @@ def doit(parts, expect, incl_self=False, save_to_vault=False, @pytest.mark.parametrize('way', ["qr", "seedqr", "classic"]) @pytest.mark.parametrize('incl_self', [False, True]) -@pytest.mark.parametrize('seed_vault', [False, True]) @pytest.mark.parametrize('parts, expect', [ # 24words - 3 parts (['romance wink lottery autumn shop bring dawn tongue range crater truth ability miss spice fitness easy legal release recall obey exchange recycle dragon room', @@ -192,10 +193,10 @@ def doit(parts, expect, incl_self=False, save_to_vault=False, # random generated *random_test_cases() ]) -def test_import_xor(seed_vault, incl_self, parts, expect, restore_seed_xor, way, is_q1): +def test_import_xor(incl_self, parts, expect, restore_seed_xor, way, is_q1): if not is_q1 and "qr" in way: raise pytest.skip("Q only") - restore_seed_xor(parts, expect, incl_self, seed_vault, way=way) + restore_seed_xor(parts, expect, incl_self, way=way) @pytest.mark.parametrize('incl_self', [False, True]) @@ -252,7 +253,8 @@ def test_xor_split(num_words, qty, trng, goto_home, pick_menu_item, cap_story, n time.sleep(.01) title, body = cap_story() - assert f'Record these {qty} lists of {num_words}-words' in body + assert "Record these" in title + assert f'{qty} lists of {num_words}-words' in body assert all((f'Part {chr(n+65)}:' in body) for n in range(qty)) if is_q1: diff --git a/testing/test_sign.py b/testing/test_sign.py index 4043da146..c89773358 100644 --- a/testing/test_sign.py +++ b/testing/test_sign.py @@ -4,22 +4,22 @@ # import time, pytest, os, random, pdb, struct, base64, binascii, itertools, datetime -from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError, MAX_TXN_LEN, CCUserRefused +from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError from binascii import b2a_hex, a2b_hex from psbt import BasicPSBT, BasicPSBTInput, BasicPSBTOutput, PSBT_IN_REDEEM_SCRIPT from io import BytesIO -from pprint import pprint, pformat +from pprint import pprint from decimal import Decimal from base64 import b64encode, b64decode from base58 import encode_base58_checksum -from helpers import B2A, U2SAT, prandom, fake_dest_addr, make_change_addr, parse_change_back +from helpers import B2A, fake_dest_addr, parse_change_back, addr_from_display_format from helpers import xfp2str, seconds2human_readable, hash160 from msg import verify_message from bip32 import BIP32Node -from constants import ADDR_STYLES, ADDR_STYLES_SINGLE, SIGHASH_MAP, simulator_fixed_tpub +from constants import ADDR_STYLES, ADDR_STYLES_SINGLE, SIGHASH_MAP, simulator_fixed_xfp, SIGHASH_MAP_NON_TAPROOT from txn import * from ctransaction import CTransaction, CTxOut, CTxIn, COutPoint -from ckcc_protocol.constants import STXN_FINALIZE, STXN_VISUALIZE, STXN_SIGNED +from ckcc_protocol.constants import STXN_VISUALIZE, STXN_SIGNED from charcodes import KEY_QR, KEY_RIGHT @@ -40,7 +40,7 @@ def test_sign1(dev, finalize): #assert 'None of the keys' in str(ee) #assert 'require subpaths' in str(ee) - assert 'PSBT does not contain any key path information' in str(ee) + assert 'PSBT inputs do not contain any key path information' in str(ee) @pytest.mark.parametrize('fn', [ @@ -87,7 +87,7 @@ def test_psbt_parse_good(try_sign, fn, accept): assert ('Missing UTXO' in msg) \ or ('None of the keys' in msg) \ or ('completely signed already' in msg) \ - or ('PSBT does not contain any key path information' in msg) \ + or ('PSBT inputs do not contain any key path information' in msg) \ or ('require subpaths' in msg), msg @@ -119,22 +119,23 @@ def xxx_test_sign_truncated(dev): 'data/worked-combined.psbt', 'data/worked-7.psbt', ]) -def test_psbt_proxy_parsing(fn, sim_execfile, sim_exec): +def test_psbt_proxy_parsing(fn, sim_execfile, sim_exec, src_root_dir, sim_root_dir): # unit test: parsing by the psbt proxy object - sim_exec('import main; main.FILENAME = %r; ' % ('../../testing/'+fn)) + sim_exec('import main; main.FILENAME = %r; ' % (f'{src_root_dir}/testing/'+fn)) rv = sim_execfile('devtest/unit_psbt.py') assert not rv, rv - rb = '../unix/work/readback.psbt' + rb = f'{sim_root_dir}/readback.psbt' oo = BasicPSBT().parse(open(fn, 'rb').read()) rb = BasicPSBT().parse(open(rb, 'rb').read()) assert oo == rb @pytest.mark.unfinalized -def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, - press_select): +@pytest.mark.parametrize("addr_fmt", ["p2tr", "p2wpkh"]) +def test_speed_test(dev, addr_fmt, fake_txn, is_mark3, is_mark4, start_sign, end_sign, + press_select, press_cancel, sim_root_dir): # measure time to sign a larger txn if is_mark4: # Mk4: expect @@ -149,9 +150,11 @@ def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, num_in = 9 num_out = 100 - psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=True) + psbt = fake_txn(num_in, num_out, dev.master_xpub, addr_fmt=addr_fmt) + + with open(f'{sim_root_dir}/debug/speed.psbt', 'wb') as f: + f.write(psbt) - open('debug/speed.psbt', 'wb').write(psbt) dt = time.time() start_sign(psbt, finalize=False) @@ -169,6 +172,7 @@ def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, print(" Tx time: %.1f" % tx_time) print("Sign time: %.1f" % ready_time) + press_cancel() if 0: # TODO: attempt to re-create the mega transaction: 5,569 inputs, one out @@ -190,9 +194,9 @@ def test_mega_txn(fake_txn, is_mark4, start_sign, end_sign, dev): @pytest.mark.bitcoind @pytest.mark.veryslow -@pytest.mark.parametrize('segwit', [True, False]) +@pytest.mark.parametrize('addr_fmt', ["p2wpkh", "p2tr", "p2pkh"]) def test_io_size(request, use_regtest, decode_with_bitcoind, fake_txn, - start_sign, end_sign, dev, segwit, accept = True): + start_sign, end_sign, dev, addr_fmt, sim_root_dir): # try a bunch of different bigger sized txns # - important to test on real device, due to it's limited memory @@ -209,9 +213,11 @@ def test_io_size(request, use_regtest, decode_with_bitcoind, fake_txn, num_in = 250 num_out = 2000 - psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=segwit, outstyles=ADDR_STYLES) + psbt = fake_txn(num_in, num_out, dev.master_xpub, addr_fmt=addr_fmt, + force_full_tx_utxo=True, supply_num_ins=10, supply_num_outs=10) - open('debug/last.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) start_sign(psbt, finalize=True) @@ -224,9 +230,10 @@ def test_io_size(request, use_regtest, decode_with_bitcoind, fake_txn, except: cap_story = None - signed = end_sign(accept, finalize=True) + signed = end_sign(True, finalize=True) - open('debug/signed.txn', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/signed.txn', 'wb') as f: + f.write(signed) decoded = decode_with_bitcoind(signed) @@ -261,13 +268,16 @@ def test_io_size(request, use_regtest, decode_with_bitcoind, fake_txn, @pytest.mark.bitcoind @pytest.mark.parametrize('num_ins', [ 2, 7, 15 ]) -@pytest.mark.parametrize('segwit', [True, False]) -def test_real_signing(fake_txn, use_regtest, try_sign, dev, num_ins, segwit, decode_with_bitcoind): +def test_real_signing(fake_txn, use_regtest, try_sign, dev, num_ins, + decode_with_bitcoind, sim_root_dir): # create a TXN using actual addresses that are correct for DUT xp = dev.master_xpub - psbt = fake_txn(num_ins, 1, xp, segwit_in=segwit) - open('debug/real-%d.psbt' % num_ins, 'wb').write(psbt) + inputs = [["p2tr"] if i % 2 == 0 else ["p2wpkh"] for i in range(num_ins)] + + psbt = fake_txn(inputs, 1, xp) + with open(f'{sim_root_dir}/debug/real-%d.psbt' % num_ins, 'wb') as f: + f.write(psbt) _, txn = try_sign(psbt, accept=True, finalize=True) @@ -278,8 +288,7 @@ def test_real_signing(fake_txn, use_regtest, try_sign, dev, num_ins, segwit, dec #pprint(decoded) assert len(decoded['vin']) == num_ins - if segwit: - assert all(x['txinwitness'] for x in decoded['vin']) + assert all(x['txinwitness'] for x in decoded['vin']) @pytest.mark.unfinalized # iff we_finalize=F @@ -287,7 +296,7 @@ def test_real_signing(fake_txn, use_regtest, try_sign, dev, num_ins, segwit, dec @pytest.mark.parametrize('num_dests', [ 1, 10, 25 ]) @pytest.mark.bitcoind def test_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, - start_sign, end_sign, we_finalize, num_dests): + start_sign, end_sign, we_finalize, num_dests, sim_root_dir): wallet_xfp = match_key use_regtest() @@ -343,7 +352,8 @@ def EncodeDecimal(o): fee = resp['fee'] chg_pos = resp['changepos'] - open('debug/vs.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/vs.psbt', 'wb') as f: + f.write(psbt) # check some basics mine = BasicPSBT().parse(psbt) @@ -365,14 +375,17 @@ def EncodeDecimal(o): assert mine.version == 2 signed = end_sign(accept=True, finalize=we_finalize) - open('debug/vs-signed.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/vs-signed.psbt', 'wb') as f: + f.write(signed) if not we_finalize: b4 = BasicPSBT().parse(psbt) aft = BasicPSBT().parse(signed) assert b4 != aft, "signing didn't change anything?" - open('debug/signed.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/signed.psbt', 'wb') as f: + f.write(signed) + resp = bitcoind.supply_wallet.finalizepsbt(str(b64encode(signed), 'ascii'), True) #combined_psbt = b64decode(resp['psbt']) @@ -384,7 +397,8 @@ def EncodeDecimal(o): # assert resp['complete'] print("Final txn: %r" % network) - open('debug/finalized-by-btcd.txn', 'wb').write(network) + with open(f'{sim_root_dir}/debug/finalized-by-btcd.txn', 'wb') as f: + f.write(network) # try to send it txed = bitcoind.supply_wallet.sendrawtransaction(B2A(network)) @@ -393,7 +407,8 @@ def EncodeDecimal(o): else: assert signed[0:4] != b'psbt', "expecting raw bitcoin txn" #print("Final txn: %s" % B2A(signed)) - open('debug/finalized-by-cc.txn', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/finalized-by-cc.txn', 'wb') as f: + f.write(signed) txed = bitcoind.supply_wallet.sendrawtransaction(B2A(signed)) print("Final txn hash: %r" % txed) @@ -425,7 +440,7 @@ def test_sign_example(set_master_key, sim_execfile, start_sign, end_sign): @pytest.mark.bitcoind @pytest.mark.unfinalized -def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind): +def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind, sim_root_dir): # Check we can finalize p2sh_p2wpkh inputs right. # TODO fix this @@ -441,7 +456,8 @@ def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind start_sign(psbt, finalize=True) signed = end_sign(accept=True) #signed = end_sign(None) - open('debug/p2sh-signed.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/p2sh-signed.psbt', 'wb') as f: + f.write(signed) #print('my finalization: ' + B2A(signed)) @@ -449,7 +465,9 @@ def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind signed_psbt = end_sign(accept=True) # use bitcoind to combine - open('debug/signed.psbt', 'wb').write(signed_psbt) + with open(f'{sim_root_dir}/debug/signed.psbt', 'wb') as f: + f.write(signed_psbt) + resp = bitcoind.rpc.finalizepsbt(str(b64encode(signed_psbt), 'ascii'), True) assert resp['complete'] == True, "bitcoind wasn't able to finalize it" @@ -462,7 +480,8 @@ def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind @pytest.mark.bitcoind @pytest.mark.unfinalized def test_sign_p2sh_example(set_master_key, use_regtest, sim_execfile, start_sign, end_sign, - decode_psbt_with_bitcoind, offer_ms_import, press_select, clear_ms): + decode_psbt_with_bitcoind, offer_minsc_import, press_select, clear_miniscript, + sim_root_dir): # Use the private key given in BIP 174 and do similar signing # as the examples. @@ -486,8 +505,8 @@ def test_sign_p2sh_example(set_master_key, use_regtest, sim_execfile, start_sign xfp = '4F6A0CD9' config += f'{xfp}: {n1}\n{xfp}: {n2}\n' - clear_ms() - offer_ms_import(config) + clear_miniscript() + offer_minsc_import(config) time.sleep(.1) press_select() @@ -503,7 +522,8 @@ def test_sign_p2sh_example(set_master_key, use_regtest, sim_execfile, start_sign start_sign(psbt) part_signed = end_sign(True) - open('debug/ex-signed-part.psbt', 'wb').write(part_signed) + with open(f'{sim_root_dir}/debug/ex-signed-part.psbt', 'wb') as f: + f.write(part_signed) b4 = BasicPSBT().parse(psbt) aft = BasicPSBT().parse(part_signed) @@ -513,7 +533,9 @@ def test_sign_p2sh_example(set_master_key, use_regtest, sim_execfile, start_sign start_sign(part_signed, finalize=False) signed = end_sign(True, finalize=False) - open('debug/ex-signed.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/ex-signed.psbt', 'wb') as f: + f.write(signed) + aft2 = BasicPSBT().parse(signed) decode = decode_psbt_with_bitcoind(signed) @@ -541,7 +563,8 @@ def EncodeDecimal(o): @pytest.mark.bitcoind -def test_change_case(start_sign, use_regtest, end_sign, check_against_bitcoind, cap_story): +def test_change_case(start_sign, use_regtest, end_sign, check_against_bitcoind, cap_story, + sim_root_dir): # is change shown/hidden at right times. no fraud checks # NOTE: out#1 is change: @@ -553,13 +576,16 @@ def test_change_case(start_sign, use_regtest, end_sign, check_against_bitcoind, time.sleep(.1) _, story = cap_story() - assert chg_addr in story + split_sory = story.split("\n\n")[3].split("\n") + assert split_sory[0] == "Change back:" + assert chg_addr == addr_from_display_format(split_sory[-1]) b4 = BasicPSBT().parse(psbt) check_against_bitcoind(B2A(b4.txn), Decimal('0.00000294'), change_outs=[1,]) signed = end_sign(True) - open('debug/chg-signed.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/chg-signed.psbt', 'wb') as f: + f.write(signed) # modify it: remove bip32 path b4.outputs[1].bip32_paths = {} @@ -578,14 +604,17 @@ def test_change_case(start_sign, use_regtest, end_sign, check_against_bitcoind, check_against_bitcoind(B2A(b4.txn), Decimal('0.00000294'), change_outs=[]) signed2 = end_sign(True) - open('debug/chg-signed2.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/chg-signed2.psbt', 'wb') as f: + f.write(signed) + aft = BasicPSBT().parse(signed) aft2 = BasicPSBT().parse(signed2) assert aft.txn == aft2.txn @pytest.mark.parametrize('case', [ 1, 2]) @pytest.mark.bitcoind -def test_change_fraud_path(start_sign, use_regtest, end_sign, case, check_against_bitcoind, cap_story): +def test_change_fraud_path(start_sign, use_regtest, end_sign, case, check_against_bitcoind, + cap_story, sim_root_dir): # fraud: BIP-32 path of output doesn't lead to pubkey indicated # NOTE: out#1 is change: @@ -609,7 +638,8 @@ def test_change_fraud_path(start_sign, use_regtest, end_sign, case, check_agains b4.serialize(fd) mod_psbt = fd.getvalue() - open('debug/mod-%d.psbt' % case, 'wb').write(mod_psbt) + with open(f'{sim_root_dir}/debug/mod-%d.psbt' % case, 'wb') as f: + f.write(mod_psbt) if case == 1: start_sign(mod_psbt) @@ -623,12 +653,13 @@ def test_change_fraud_path(start_sign, use_regtest, end_sign, case, check_agains time.sleep(.1) _, story = cap_story() - assert chg_addr in story + assert chg_addr == addr_from_display_format(story.split("\n\n")[3].split("\n")[-1]) assert 'Change back:' not in story end_sign(True) @pytest.mark.bitcoind -def test_change_fraud_addr(start_sign, end_sign, use_regtest, check_against_bitcoind, cap_story): +def test_change_fraud_addr(start_sign, end_sign, use_regtest, check_against_bitcoind, cap_story, + sim_root_dir): # fraud: BIP-32 path of output doesn't match TXO address # NOTE: out#1 is change: #chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo' @@ -650,24 +681,27 @@ def test_change_fraud_addr(start_sign, end_sign, use_regtest, check_against_bitc b4.serialize(fd) mod_psbt = fd.getvalue() - open('debug/mod-addr.psbt', 'wb').write(mod_psbt) + with open(f'{sim_root_dir}/debug/mod-addr.psbt', 'wb') as f: + f.write(mod_psbt) start_sign(mod_psbt) with pytest.raises(CCProtoError) as ee: signed = end_sign(True) - assert 'Change output is fraud' in str(ee) + assert 'p2pkh change output is fraud' in str(ee) @pytest.mark.parametrize('case', ['p2sh-p2wpkh', 'p2wpkh', 'p2sh', 'p2sh-p2pkh']) @pytest.mark.bitcoind def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, use_regtest, - cap_story, case): + cap_story, case, sim_root_dir): # not fraud: output address encoded in various equiv forms use_regtest() # NOTE: out#1 is change: #chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo' - psbt = open('data/example-change.psbt', 'rb').read() + with open('data/example-change.psbt', 'rb') as f: + psbt = f.read() + b4 = BasicPSBT().parse(psbt) t = CTransaction() @@ -717,7 +751,8 @@ def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, use_re b4.serialize(fd) mod_psbt = fd.getvalue() - open('debug/mod-%s.psbt' % case, 'wb').write(mod_psbt) + with open(f'{sim_root_dir}/debug/mod-%s.psbt' % case, 'wb') as f: + f.write(mod_psbt) start_sign(mod_psbt) @@ -725,13 +760,15 @@ def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, use_re _, story = cap_story() if case in ["p2sh", "p2sh-p2pkh"]: - assert "Output#1: Change output is fraudulent" == story + assert f"Output#1: p2sh-p2wpkh change output is fraudulent" in story return check_against_bitcoind(B2A(b4.txn), Decimal('0.00000294'), change_outs=[1,], dests=[(1, expect_addr)]) - assert expect_addr in story + split_sory = story.split("\n\n")[3].split("\n") + assert split_sory[0] == "Change back:" + assert expect_addr == addr_from_display_format(split_sory[-1]) assert parse_change_back(story) == (Decimal('1.09997082'), [expect_addr]) end_sign(True) @@ -777,7 +814,7 @@ def test_wrong_p2sh_p2wpkh(bitcoind, start_sign, end_sign, bitcoind_d_sim_watch, try: fin = end_sign(True) except Exception as e: - assert "Change output is fraudulent" in e.args[0] + assert "p2sh-p2wpkh change output is fraudulent" in e.args[0] # this is the correct ending return @@ -812,7 +849,8 @@ def test_sign_multisig_partial_fail(start_sign, end_sign): assert 'None of the keys involved' in str(ee) @pytest.mark.unfinalized -def test_sign_wutxo(start_sign, set_seed_words, end_sign, cap_story, sim_exec, sim_execfile): +def test_sign_wutxo(start_sign, set_seed_words, end_sign, cap_story, sim_exec, sim_execfile, + sim_root_dir): # Example from SomberNight: we can sign it, but signature won't be accepted by # network because the PSBT lies about the UTXO amount and tries to give away to miners, @@ -835,7 +873,7 @@ def test_sign_wutxo(start_sign, set_seed_words, end_sign, cap_story, sim_exec, s assert 'Network fee 0.00000500 XTN' in story # check we understood it right - ex = dict( had_witness=False, num_inputs=1, num_outputs=1, sw_inputs=[None], + ex = dict( had_witness=False, num_inputs=1, num_outputs=1, sw_inputs=[False], miner_fee=500, warnings_expected=0, lock_time=1442308, total_value_out=99500, total_value_in=100000) @@ -847,11 +885,13 @@ def test_sign_wutxo(start_sign, set_seed_words, end_sign, cap_story, sim_exec, s signed = end_sign(True, finalize=fin) - open('debug/sn-signed.'+ ('txn' if fin else 'psbt'), 'wt').write(B2A(signed)) + with open(f'{sim_root_dir}/debug/sn-signed.'+ ('txn' if fin else 'psbt'), 'wt') as f: + f.write(B2A(signed)) @pytest.mark.parametrize('fee_max', [ 10, 25, 50]) @pytest.mark.parametrize('under', [ False, True]) -def test_network_fee_amts(fee_max, under, fake_txn, try_sign, start_sign, dev, settings_set, sim_exec, cap_story): +def test_network_fee_amts(fee_max, under, fake_txn, try_sign, start_sign, dev, settings_set, + sim_exec, cap_story, sim_root_dir): settings_set('fee_limit', fee_max) @@ -859,9 +899,10 @@ def test_network_fee_amts(fee_max, under, fake_txn, try_sign, start_sign, dev, s target = (fee_max - 2) if under else fee_max outval = int(1E8 / ((target/100.) + 1.)) - psbt = fake_txn(1, 1, dev.master_xpub, fee=None, outvals=[outval]) + psbt = fake_txn(1, [["p2pkh", outval]], dev.master_xpub, fee=0) - open('debug/fee.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/fee.psbt', 'wb') as f: + f.write(psbt) if not under: with pytest.raises(CCProtoError) as ee: @@ -880,16 +921,16 @@ def test_network_fee_amts(fee_max, under, fake_txn, try_sign, start_sign, dev, s settings_set('fee_limit', 10) -def test_network_fee_unlimited(fake_txn, start_sign, end_sign, dev, settings_set, cap_story): +def test_network_fee_unlimited(fake_txn, start_sign, end_sign, dev, settings_set, cap_story, + sim_root_dir): settings_set('fee_limit', -1) # creat a txn with single 1BTC input, and tiny one output; the rest is fee - outval = 100 - - psbt = fake_txn(1, 1, dev.master_xpub, fee=None, outvals=[outval]) + psbt = fake_txn(1, [["p2wpkh", 100]], dev.master_xpub) - open('debug/fee-un.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/fee-un.psbt', 'wb') as f: + f.write(psbt) # should be able to sign, but get warning start_sign(psbt, False) @@ -907,20 +948,21 @@ def test_network_fee_unlimited(fake_txn, start_sign, end_sign, dev, settings_set @pytest.mark.parametrize('num_outs', [ 2, 7, 15 ]) @pytest.mark.parametrize('act_outs', [ 2, 1, -1]) -@pytest.mark.parametrize('segwit', [True, False]) -@pytest.mark.parametrize('add_xpub', [True, False]) +# @pytest.mark.parametrize('add_xpub', [True, False]) # TODO create test and verify @pytest.mark.parametrize('out_style', ADDR_STYLES_SINGLE) @pytest.mark.parametrize('visualized', [0, STXN_VISUALIZE, STXN_VISUALIZE|STXN_SIGNED]) def test_change_outs(fake_txn, start_sign, end_sign, cap_story, dev, num_outs, master_xpub, - act_outs, segwit, out_style, visualized, add_xpub, num_ins=3): + act_outs, out_style, visualized, sim_root_dir): # create a TXN which has change outputs, which shouldn't be shown to user, and also not fail. + num_ins = 3 xp = dev.master_xpub couts = num_outs if act_outs == -1 else num_ins-act_outs - psbt = fake_txn(num_ins, num_outs, xp, segwit_in=segwit, - outstyles=[out_style], change_outputs=range(couts), add_xpub=add_xpub) + outs = [[out_style, None, True] for _ in range(couts)] + [[out_style] for _ in range(num_outs-couts)] + psbt = fake_txn(num_ins, outs, xp, addr_fmt=out_style) - open('debug/change.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/change.psbt', 'wb') as f: + f.write(psbt) # should be able to sign, but get warning if not visualized: @@ -966,8 +1008,12 @@ def test_change_outs(fake_txn, start_sign, end_sign, cap_story, dev, num_outs, m assert all((i[0] in 'mn') for i in addrs) elif out_style == 'p2wpkh': assert set(i[0:4] for i in addrs) == {'tb1q'} - elif out_style == 'p2wpkh-p2sh': + elif out_style in ('p2wpkh-p2sh', 'p2sh-p2wpkh'): assert set(i[0] for i in addrs) == {'2'} + else: + assert out_style == "p2tr" + assert set(i[0:4] for i in addrs) == {'tb1p'} + def KEEP_test_random_psbt(try_sign, sim_exec, fname="data/ .psbt"): # allow almost any PSBT to run on simulator, at least up until wrong pubkeys detected @@ -996,7 +1042,8 @@ def KEEP_test_random_psbt(try_sign, sim_exec, fname="data/ .psbt"): @pytest.mark.bitcoind @pytest.mark.unfinalized @pytest.mark.parametrize('num_dests', [ 1, 10, 25 ]) -def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, start_sign, end_sign, num_dests): +def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, + start_sign, end_sign, num_dests, sim_root_dir): # Compare how we finalize vs bitcoind ... should be exactly the same txn wallet_xfp = match_key # has to be after match key @@ -1025,7 +1072,8 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind fee = resp['fee'] chg_pos = resp['changepos'] - open('debug/vs.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/vs.psbt', 'wb') as f: + f.write(psbt) # check some basics mine = BasicPSBT().parse(psbt) @@ -1048,13 +1096,15 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind signed_final = end_sign(accept=True, finalize=True) assert signed_final[0:4] != b'psbt', "expecting raw bitcoin txn" - open('debug/finalized-by-ckcc.txn', 'wt').write(B2A(signed_final)) + with open(f'{sim_root_dir}/debug/finalized-by-ckcc.txn', 'wt') as f: + f.write(B2A(signed_final)) # Sign again, but don't finalize it. start_sign(psbt, finalize=False) signed = end_sign(accept=True) - open('debug/vs-signed-unfin.psbt', 'wb').write(signed) + with open(f'{sim_root_dir}/debug/vs-signed-unfin.psbt', 'wb') as f: + f.write(signed) # Use bitcoind to finalize it this time. resp = bitcoind.supply_wallet.finalizepsbt(str(b64encode(signed), 'ascii'), True) @@ -1064,7 +1114,8 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind # assert resp['complete'] #print("Final txn: %r" % network) - open('debug/finalized-by-btcd.txn', 'wt').write(B2A(network)) + with open(f'{sim_root_dir}/debug/finalized-by-btcd.txn', 'wt') as f: + f.write(B2A(network)) assert network == signed_final, "Finalized differently" @@ -1083,10 +1134,10 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind ("45'/1'/0'/1/5", 'diff path prefix'), ("44'/2'/0'/1/5", 'diff path prefix'), ("44'/1'/1'/1/5", 'diff path prefix'), - ("44'/1'/0'/3000/5", '2nd last component'), - ("44'/1'/0'/3/5", '2nd last component'), + # ("44'/1'/0'/3000/5", '2nd last component'), + # ("44'/1'/0'/3/5", '2nd last component'), ]) -def test_change_troublesome(dev, start_sign, cap_story, try_path, expect): +def test_change_troublesome(dev, start_sign, cap_story, try_path, expect, sim_root_dir): # NOTE: out#1 is change: # addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo' # path = (m=4369050F)/44'/1'/0'/1/5 @@ -1110,7 +1161,8 @@ def test_change_troublesome(dev, start_sign, cap_story, try_path, expect): b4.serialize(fd) mod_psbt = fd.getvalue() - open('debug/troublesome.psbt', 'wb').write(mod_psbt) + with open(f'{sim_root_dir}/debug/troublesome.psbt', 'wb') as f: + f.write(mod_psbt) start_sign(mod_psbt) time.sleep(0.1) @@ -1195,43 +1247,61 @@ def spend_outputs(funding_psbt, finalized_txn, tweaker=None): with BytesIO() as rv: nn.serialize(rv) raw = rv.getvalue() - - open('debug/spend_outs.psbt', 'wb').write(raw) return nn, raw @pytest.fixture -def hist_count(sim_exec): +def history_data(sim_exec): def doit(): - return int(sim_exec( - 'import history; RV.write(str(len(history.OutptValueCache.runtime_cache)));')) + return eval(sim_exec( + 'import history; RV.write(str(history.OutptValueCache.runtime_cache));')) + return doit + +@pytest.fixture +def txid_from_export_prompt(cap_story, cap_screen_qr, cap_screen, need_keypress): + def doit(): + time.sleep(.1) + title, story = cap_story() + assert "(6) for QR Code of TXID" in story + need_keypress("6") + time.sleep(.1) + screen_txid = cap_screen().strip().replace("\n", "").replace("~", "") + qr_txid = cap_screen_qr().decode().strip().lower() + assert qr_txid == screen_txid + return qr_txid + return doit @pytest.mark.parametrize('num_utxo', [9, 100]) -@pytest.mark.parametrize('segwit_in', [False, True]) -def test_bip143_attack_data_capture(num_utxo, segwit_in, try_sign, fake_txn, settings_set, - settings_get, cap_story, sim_exec, hist_count): +def test_bip143_attack_data_capture(num_utxo, try_sign, fake_txn, press_cancel, + settings_set, settings_get, cap_story, sim_exec, + history_data, txid_from_export_prompt, sim_root_dir): # cleanup prev runs, if very first time thru sim_exec('import history; history.OutptValueCache.clear()') - hist_b4 = hist_count() - assert hist_b4 == 0 + assert len(history_data()) == 0 # make a txn, capture the outputs of that as inputs for another txn - psbt = fake_txn(1, num_utxo+3, segwit_in=segwit_in, change_outputs=range(num_utxo+2), - outstyles=(['p2wpkh']*num_utxo) + ['p2wpkh-p2sh', 'p2pkh']) - _, txn = try_sign(psbt, accept=True, finalize=True) + outputs = [] + for i in range(num_utxo): + if i: + # change + outputs.append(["p2wpkh", None, True]) + else: + outputs.append(["p2pkh", None, True]) - open('debug/funding.psbt', 'wb').write(psbt) + psbt = fake_txn(1, outputs, addr_fmt="p2wpkh") + _, txn = try_sign(psbt, accept=True, finalize=True, exit_export_loop=False) - num_inp_utxo = (1 if segwit_in else 0) + with open(f'{sim_root_dir}/debug/funding.psbt', 'wb') as f: + f.write(psbt) - time.sleep(.1) - title, story = cap_story() - assert 'TXID' in title, story - txid = story.strip().split()[0] + txid = txid_from_export_prompt() + press_cancel() + press_cancel() - assert hist_count() in {128, hist_b4+num_utxo+num_inp_utxo} + curr = history_data() + assert len(curr) in {128, num_utxo} t = CTransaction() t.deserialize(BytesIO(txn)) @@ -1240,12 +1310,14 @@ def test_bip143_attack_data_capture(num_utxo, segwit_in, try_sign, fake_txn, set # expect all of new "change outputs" to be recorded (none of the non-segwit change tho) # plus the one input we "revealed" after1 = settings_get('ovc') - assert len(after1) == min(30, num_utxo + num_inp_utxo) + assert len(after1) == min(30, num_utxo) - all_utxo = hist_count() - assert all_utxo == hist_b4+num_utxo+num_inp_utxo + all_utxo = history_data() + assert len(all_utxo) == num_utxo # build a new PSBT based on those change outputs psbt2, raw = spend_outputs(psbt, txn) + with open(f'{sim_root_dir}/debug/spend_outs.psbt', 'wb') as f: + f.write(raw) # try to sign that ... should work fine try_sign(raw, accept=True, finalize=True) @@ -1261,53 +1333,51 @@ def value_tweak(spendables): spendables[0][1].nValue += amt psbt3, raw = spend_outputs(psbt, txn, tweaker=value_tweak) + with open(f'{sim_root_dir}/debug/spend_outs.psbt', 'wb') as f: + f.write(raw) with pytest.raises(CCProtoError) as ee: orig, result = try_sign(raw, accept=True, finalize=True) assert 'but PSBT claims' in str(ee), ee -@pytest.mark.parametrize('segwit', [False, True]) +@pytest.mark.parametrize('addr_fmt', ADDR_STYLES_SINGLE) @pytest.mark.parametrize('num_ins', [1, 17]) -def test_txid_calc(num_ins, fake_txn, try_sign, dev, segwit, decode_with_bitcoind, cap_story): +@pytest.mark.parametrize('num_outs', [1, 17]) +def test_txid_calc(num_ins, fake_txn, try_sign, dev, decode_with_bitcoind, cap_story, + txid_from_export_prompt, press_cancel, num_outs, addr_fmt): # verify correct txid for transactions is being calculated xp = dev.master_xpub - psbt = fake_txn(num_ins, 1, xp, segwit_in=segwit) - - _, txn = try_sign(psbt, accept=True, finalize=True) - - #print('Signed; ' + B2A(txn)) + psbt = fake_txn(num_ins, num_outs, xp, addr_fmt=addr_fmt) - time.sleep(.1) - title, story = cap_story() - assert '0' in story - assert 'TXID' in title, story - txid = story.strip().split()[0] + _, txn = try_sign(psbt, accept=True, finalize=True, exit_export_loop=False) + txid = txid_from_export_prompt() + press_cancel() # exit QR + press_cancel() # exit re-export loop - if 1: - t = CTransaction() - t.deserialize(BytesIO(txn)) - assert t.txid().hex() == txid + t = CTransaction() + t.deserialize(BytesIO(txn)) + assert t.txid().hex() == txid - if 1: - # compare to bitcoin core - decoded = decode_with_bitcoind(txn) - pprint(decoded) + # compare to bitcoin core + decoded = decode_with_bitcoind(txn) + pprint(decoded) - assert len(decoded['vin']) == num_ins - if segwit: - assert all(x['txinwitness'] for x in decoded['vin']) + assert len(decoded['vin']) == num_ins + if "w" in addr_fmt: + assert all(x['txinwitness'] for x in decoded['vin']) + assert decoded['txid'] == txid - assert decoded['txid'] == txid @pytest.mark.unfinalized # iff partial=1 +@pytest.mark.reexport @pytest.mark.parametrize('encoding', ['binary', 'hex', 'base64']) -#@pytest.mark.parametrize('num_outs', [1,2,3,4,5,6,7,8]) @pytest.mark.parametrize('num_outs', [1,15]) @pytest.mark.parametrize('del_after', [1, 0]) @pytest.mark.parametrize('partial', [1, 0]) -def test_sdcard_signing(encoding, num_outs, del_after, partial, try_sign_microsd, fake_txn, try_sign, dev, settings_set): +def test_sdcard_signing(encoding, num_outs, del_after, partial, try_sign_microsd, fake_txn, + dev, settings_set, signing_artifacts_reexport): # exercise the txn encode/decode from sdcard xp = dev.master_xpub @@ -1320,15 +1390,22 @@ def hack(psbt): pp = psbt.inputs[0].bip32_paths[pk] psbt.inputs[0].bip32_paths[pk] = b'what' + pp[4:] - psbt = fake_txn(2, num_outs, xp, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn([["p2pkh"], ["p2wpkh"], ["p2tr"]], num_outs, xp, psbt_hacker=hack) _, txn, txid = try_sign_microsd(psbt, finalize=not partial, - encoding=encoding, del_after=del_after) + encoding=encoding, del_after=del_after) + _psbt, _txn = signing_artifacts_reexport("sd", tx_final=not partial, txid=txid, + encoding=encoding, del_after=del_after) + if partial: + assert _psbt == txn + else: + assert _txn == txn @pytest.mark.unfinalized @pytest.mark.parametrize('num_ins', [2,3,8]) @pytest.mark.parametrize('num_outs', [1,2,8]) -def test_payjoin_signing(num_ins, num_outs, fake_txn, try_sign, start_sign, end_sign, cap_story): +def test_payjoin_signing(num_ins, num_outs, fake_txn, try_sign, start_sign, end_sign, + cap_story, sim_root_dir): # Try to simulate a PSBT that might be involved in a Payjoin (BIP-78 txn) @@ -1336,9 +1413,10 @@ def hack(psbt): # change an input to be "not ours" ... but with utxo details psbt.inputs[num_ins-1].bip32_paths.clear() - psbt = fake_txn(num_ins, num_outs, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn(num_ins, num_outs, addr_fmt="p2wpkh", psbt_hacker=hack) - open('debug/payjoin.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/payjoin.psbt', 'wb') as f: + f.write(psbt) ip = start_sign(psbt, finalize=False) time.sleep(.1) @@ -1346,13 +1424,14 @@ def hack(psbt): assert 'warning below' in story assert 'Limited Signing' in story - assert 'because we do not know the key' in story + assert "don't know the key" in story + assert "different wallet" in story assert ': %s' % (num_ins-1) in story txn = end_sign(True, finalize=False) -@pytest.mark.parametrize('segwit', [False, True]) -def test_fully_unsigned(fake_txn, try_sign, segwit): +@pytest.mark.parametrize('addr_fmt', ["p2wpkh", "p2tr"]) +def test_fully_unsigned(fake_txn, try_sign, addr_fmt): # A PSBT which is unsigned but all inputs lack keypaths @@ -1360,16 +1439,17 @@ def hack(psbt): # change all inputs to be "not ours" ... but with utxo details for i in psbt.inputs: i.bip32_paths.clear() + i.taproot_bip32_paths.clear() - psbt = fake_txn(7, 2, segwit_in=segwit, psbt_hacker=hack) + psbt = fake_txn(7, 2, addr_fmt=addr_fmt, psbt_hacker=hack) with pytest.raises(CCProtoError) as ee: orig, result = try_sign(psbt, accept=True) - assert 'does not contain any key path information' in str(ee) + assert 'PSBT inputs do not contain any key path information' in str(ee) -@pytest.mark.parametrize('segwit', [False, True]) -def test_wrong_xfp(fake_txn, try_sign, segwit): +@pytest.mark.parametrize('addr_fmt', ["p2wpkh", "p2tr"]) +def test_wrong_xfp(fake_txn, try_sign, addr_fmt): # A PSBT which is unsigned and doesn't involve our XFP value @@ -1380,17 +1460,19 @@ def hack(psbt): for i in psbt.inputs: for pubkey in i.bip32_paths: i.bip32_paths[pubkey] = wrong_xfp + i.bip32_paths[pubkey][4:] + for xonly_pubkey in i.taproot_bip32_paths: + i.taproot_bip32_paths[xonly_pubkey] = b"\x00" + wrong_xfp + i.taproot_bip32_paths[xonly_pubkey][5:] - psbt = fake_txn(7, 2, segwit_in=segwit, psbt_hacker=hack) + psbt = fake_txn(7, 2, addr_fmt=addr_fmt, psbt_hacker=hack) with pytest.raises(CCProtoError) as ee: orig, result = try_sign(psbt, accept=True) assert 'None of the keys' in str(ee) - assert 'found 12345678' in str(ee) + assert 'need 0F056943' in str(ee) -@pytest.mark.parametrize('segwit', [False, True]) -def test_wrong_xfp_multi(fake_txn, try_sign, segwit): +@pytest.mark.parametrize('addr_fmt', ["p2wpkh", "p2tr"]) +def test_wrong_xfp_multi(fake_txn, try_sign, addr_fmt, sim_root_dir): # A PSBT which is unsigned and doesn't involve our XFP value # - but multiple wrong XFP values @@ -1404,10 +1486,16 @@ def hack(psbt): here = struct.pack(' not now - assert "warning" not in story + if len(op_return_data) > 80: + assert "(1 warning below)" in story # looking for warning at the top + assert "OP_RETURN > 80 bytes" in story + else: + assert "warning" not in story + assert "OP_RETURN" in story + assert "Multiple OP_RETURN outputs:" not in story # always just one - core restriction + try: - expect = op_return_data.decode("ascii") + assert len(op_return_data) <= 160 + try: + expect = op_return_data.decode("ascii") + except: + # not ascii + expect = op_return_data.hex() except: expect = binascii.hexlify(op_return_data).decode() + expect = expect[:160] + "\n ⋯\n" + expect[-160:] + assert expect in story - signed = end_sign(accept=True) - tx = cc.finalizepsbt(base64.b64encode(signed).decode())["hex"] - assert cc.testmempoolaccept([tx])[0]["allowed"] is True - tx_id = cc.sendrawtransaction(tx) - assert isinstance(tx_id, str) and len(tx_id) == 64 + tx = end_sign(accept=True, finalize=True).hex() + + # tx is final at this point and consensus valid + # tx = cc.finalizepsbt(base64.b64encode(signed).decode())["hex"] + res = cc.testmempoolaccept([tx])[0] + + if (bitcoind.version < 300000) and (len(op_return_data) > 80): + # policy + assert res["allowed"] is False + assert res["reject-reason"] == "scriptpubkey" + else: + assert res["allowed"] is True + tx_id = cc.sendrawtransaction(tx) + assert isinstance(tx_id, str) and len(tx_id) == 64 + @pytest.mark.parametrize("unknowns", [ # tuples (unknown_global, unknown_ins, unknown_outs) @@ -1777,7 +1914,7 @@ def test_op_return_signing(op_return_data, dev, fake_txn, bitcoind_d_sim_watch, ({b"x" * 64: b"y" * 128}, {b"q" * 64: b"p" * 128}, {b"w" * 90: b"z" * 256}), ({b"x" * 32: b"y" * 256}, {b"q" * 32: b"p" * 256, b"f" * 15: 32 * b"\x01"}, {b"w": b"z"}), ]) -def test_unknow_values_in_psbt(unknowns, dev, start_sign, end_sign, fake_txn): +def test_unknow_values_in_psbt(unknowns, dev, start_sign, end_sign, fake_txn, sim_root_dir): unknown_global, unknown_ins, unknown_outs = unknowns def hack(psbt): psbt.unknown = unknown_global @@ -1786,8 +1923,9 @@ def hack(psbt): for o in psbt.outputs: o.unknown = unknown_outs - psbt = fake_txn(5, 5, dev.master_xpub, segwit_in=True, psbt_hacker=hack) - open('debug/last.psbt', 'wb').write(psbt) + psbt = fake_txn(5, 5, dev.master_xpub, addr_fmt="p2wpkh", psbt_hacker=hack) + with open(f'{sim_root_dir}/debug/last.psbt', 'wb') as f: + f.write(psbt) psbt_o = BasicPSBT().parse(psbt) assert psbt_o.unknown == unknown_global for inp in psbt_o.inputs: @@ -1804,7 +1942,7 @@ def hack(psbt): for out in res.outputs: assert out.unknown == unknown_outs -def test_read_write_prop_attestation_keys(try_sign, fake_txn): +def test_read_write_prop_attestation_keys(try_sign, fake_txn, sim_root_dir): from psbt import ser_prop_key, PSBT_PROP_CK_ID def attach_attest_to_outs(psbt): for idx, o in enumerate(psbt.outputs): @@ -1813,7 +1951,8 @@ def attach_attest_to_outs(psbt): o.proprietary[key] = value psbt = fake_txn(2, 2, psbt_hacker=attach_attest_to_outs) - open('debug/propkeys.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/propkeys.psbt', 'wb') as f: + f.write(psbt) orig, signed = try_sign(psbt) res = BasicPSBT().parse(signed) @@ -1827,7 +1966,7 @@ def test_duplicate_unknow_values_in_psbt(dev, start_sign, end_sign, fake_txn): # duplicate keys for global unknowns def hack(psbt): psbt.unknown = [(b"xxx", 32 * b"\x00"), (b"xxx", 32 * b"\x01")] - psbt = fake_txn(5, 5, dev.master_xpub, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn(5, 5, dev.master_xpub, addr_fmt="p2wpkh", psbt_hacker=hack) start_sign(psbt) with pytest.raises(Exception): end_sign() @@ -1836,7 +1975,7 @@ def hack(psbt): def hack(psbt): for i in psbt.inputs: i.unknown = [(b"xxx", 32 * b"\x00"), (b"xxx", 32 * b"\x01")] - psbt = fake_txn(5, 5, dev.master_xpub, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn(5, 5, dev.master_xpub, addr_fmt="p2pkh", psbt_hacker=hack) start_sign(psbt) with pytest.raises(Exception): end_sign() @@ -1845,7 +1984,7 @@ def hack(psbt): def hack(psbt): for o in psbt.outputs: o.unknown = [(b"xxx", 32 * b"\x00"), (b"xxx", 32 * b"\x01")] - psbt = fake_txn(5, 5, dev.master_xpub, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn(5, 5, dev.master_xpub, addr_fmt="p2tr", psbt_hacker=hack) start_sign(psbt) with pytest.raises(Exception): end_sign() @@ -1853,12 +1992,19 @@ def hack(psbt): @pytest.fixture def _test_single_sig_sighash(cap_story, press_select, start_sign, end_sign, dev, - bitcoind, bitcoind_d_dev_watch, settings_set, finalize_v2_v0_convert): + bitcoind, bitcoind_d_dev_watch, settings_set, + finalize_v2_v0_convert, pytestconfig, sim_root_dir): def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh_checks=False, - psbt_v2=False, tx_check=True): + psbt_v2=None, tx_check=True): from decimal import Decimal, ROUND_DOWN + if psbt_v2 is None: + # anything passed directly to this function overrides + # pytest flag --psbt2 - only care about pytest flag + # if psbt_v2 is not specified (None) + psbt_v2 = pytestconfig.getoption('psbt2') + if dev.is_simulator: # if running against real HW you need to set CC to correct sighshchk mode # Below test need to run with sighshchk disabled: @@ -1872,7 +2018,10 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh settings_set("sighshchk", int(not sh_checks)) - not_all_ALL = any(sh != "ALL" for sh in sighash) + not_all_ALL = any(sh not in ["ALL", "DEFAULT"] for sh in sighash) + + # this is needed as supply wallet is still legacy bitcoind wallet (no tr support) + dest_wal = bitcoind.create_wallet("dest_wal") bitcoind_d_dev_watch.keypoolrefill(num_inputs + num_outputs) input_val = bitcoind.supply_wallet.getbalance() / num_inputs @@ -1892,7 +2041,7 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh unspent = bitcoind_d_dev_watch.listunspent() output_val = bitcoind_d_dev_watch.getbalance() / num_outputs # consolidation or not? - dest_wal = bitcoind_d_dev_watch if consolidation else bitcoind.supply_wallet + dest_wal = bitcoind_d_dev_watch if consolidation else dest_wal destinations = [ {dest_wal.getnewaddress("", addr_fmt): Decimal(output_val).quantize(Decimal('.0000001'), rounding=ROUND_DOWN)} for _ in range(num_outputs) @@ -1921,8 +2070,9 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh psbt_sh = x.as_b64_str() # make useful reference psbt along the way - open(f'debug/sighash-{sighash[0] if len(sighash) == 1 else "MIX"}.psbt'\ - .replace('|', '-'), 'wt').write(psbt_sh) + with open(f'{sim_root_dir}/debug/sighash-{sighash[0] if len(sighash) == 1 else "MIX"}.psbt'\ + .replace('|', '-'), 'wt') as f: + f.write(psbt_sh) # get story out of CC via visualize feature start_sign(psbt_sh_bytes, False, stxn_flags=STXN_VISUALIZE) @@ -1932,7 +2082,7 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh with pytest.raises(Exception) as e: end_sign(accept=None, expect_txn=False).decode() # assert title == "Failure" - assert "Only sighash ALL is allowed for pure consolidation transaction" in e.value.args[0] + assert "Only sighash ALL/DEFAULT is allowed for pure consolidation transaction" in e.value.args[0] return elif not consolidation and any("NONE" in sh for sh in sighash if isinstance(sh, str)): @@ -1950,7 +2100,7 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh assert "---WARNING---" in story assert "Danger" in story assert "Destination address can be changed after signing (sighash NONE)." in story - elif any(sh != "ALL" for sh in sighash): + elif any(sh not in ["ALL", "DEFAULT"] for sh in sighash): assert "(1 warning below)" in story assert "---WARNING---" in story assert "Caution" in story @@ -1973,12 +2123,32 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh for idx, i in enumerate(y.inputs): if len(sighash) == 1: - assert i.sighash == SIGHASH_MAP[sighash[0]] + target = sighash[0] + sh_num = SIGHASH_MAP[target] + if target == "ALL": + if addr_fmt == "bech32m": + assert i.sighash == sh_num + else: + assert i.sighash is None + elif target == "DEFAULT": + assert i.sighash is None + else: + assert i.sighash == sh_num else: - assert i.sighash == SIGHASH_MAP[sighash[idx]] - # check signature hash correct checkusm appended + target = sighash[idx] + sh_num = SIGHASH_MAP[target] + if target == "ALL": + if addr_fmt == "bech32m": + assert i.sighash == sh_num + else: + assert i.sighash is None + elif target == "DEFAULT": + assert i.sighash is None + else: + assert i.sighash == sh_num + # check signature hash correct checksum appended for _, sig in i.part_sigs.items(): - assert sig[-1] == i.sighash + assert sig[-1] == sh_num resp = finalize_v2_v0_convert(y) @@ -1989,7 +2159,11 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh # sign and get finalized tx ready for broadcast out start_sign(psbt_sh_bytes, finalize=True) cc_tx_hex = end_sign(accept=True, finalize=True) - assert tx_hex == cc_tx_hex.hex() + cc_tx_hex = cc_tx_hex.hex() + if addr_fmt != "bech32m": + # schnorr signatures are not deterministic + # any subsequent sign will produce different witness + assert tx_hex == cc_tx_hex if psbt_v2: # check txn_modifiable properly set @@ -2015,49 +2189,58 @@ def doit(addr_fmt, sighash, num_inputs=2, num_outputs=2, consolidation=False, sh assert mod & 4 == 0 # for PSBTv2 here we check if we correctly finalize - res = bitcoind.supply_wallet.testmempoolaccept([tx_hex]) + res = bitcoind.supply_wallet.testmempoolaccept([cc_tx_hex]) assert res[0]["allowed"] - txn_id = bitcoind.supply_wallet.sendrawtransaction(tx_hex) + txn_id = bitcoind.supply_wallet.sendrawtransaction(cc_tx_hex) assert txn_id return doit +# TODO Sighash test MUST be run with --psbt2 flag on and off +# pytest test_sign.py -k sighash {--psbt2,} @pytest.mark.bitcoind -@pytest.mark.parametrize("addr_fmt", ["legacy", "p2sh-segwit", "bech32"]) -@pytest.mark.parametrize("sighash", [sh for sh in SIGHASH_MAP if sh != 'ALL']) -@pytest.mark.parametrize("num_outs", [1, 3, 5]) -@pytest.mark.parametrize("num_ins", [2, 5]) -@pytest.mark.parametrize("psbt_v2", [True, False]) -def test_sighash_same(addr_fmt, sighash, num_ins, num_outs, psbt_v2, _test_single_sig_sighash): +@pytest.mark.parametrize("addr_fmt", ["legacy", "p2sh-segwit", "bech32", "bech32m"]) +@pytest.mark.parametrize("sighash", [sh for sh in SIGHASH_MAP if sh not in ['ALL', 'DEFAULT']]) +def test_sighash_same(addr_fmt, sighash, _test_single_sig_sighash): # sighash is the same among all inputs - _test_single_sig_sighash(addr_fmt, [sighash], num_inputs=num_ins, num_outputs=num_outs, - psbt_v2=psbt_v2) + _test_single_sig_sighash(addr_fmt, [sighash], num_inputs=4, num_outputs=2) @pytest.mark.bitcoind @pytest.mark.parametrize("addr_fmt", ["legacy", "p2sh-segwit", "bech32"]) -@pytest.mark.parametrize("sighash", list(itertools.combinations(SIGHASH_MAP.keys(), 2))) -@pytest.mark.parametrize("num_outs", [2, 3, 5]) -@pytest.mark.parametrize("psbt_v2", [True, False]) -def test_sighash_different(addr_fmt, sighash, num_outs, psbt_v2, _test_single_sig_sighash): +@pytest.mark.parametrize("sighash", list(itertools.combinations(SIGHASH_MAP_NON_TAPROOT.keys(), 2))) +def test_sighash_different(addr_fmt, sighash, _test_single_sig_sighash): # sighash differ among all inputs - _test_single_sig_sighash(addr_fmt, sighash, num_inputs=2, num_outputs=num_outs, - psbt_v2=psbt_v2) + _test_single_sig_sighash(addr_fmt, sighash, num_inputs=2, num_outputs=5) @pytest.mark.bitcoind -@pytest.mark.parametrize("addr_fmt", ["legacy", "p2sh-segwit", "bech32"]) -@pytest.mark.parametrize("num_outs", [5, 8]) -@pytest.mark.parametrize("psbt_v2", [True, False]) -def test_sighash_fullmix(addr_fmt, num_outs, psbt_v2, _test_single_sig_sighash): +@pytest.mark.parametrize("sighash", [ + ('ALL', 'DEFAULT', 'NONE'), ('ALL', 'DEFAULT', 'SINGLE'), ('ALL', 'DEFAULT', 'ALL|ANYONECANPAY'), + ('DEFAULT', 'ALL', 'NONE|ANYONECANPAY'), ('DEFAULT', 'ALL', 'SINGLE|ANYONECANPAY') +]) +@pytest.mark.parametrize("num_outs", [1, 5]) +def test_sighash_different_taproot(sighash, num_outs, _test_single_sig_sighash): + # sighash differ among all inputs + _test_single_sig_sighash("bech32m", sighash, num_inputs=3, num_outputs=num_outs) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("addr_fmt", ["legacy", "p2sh-segwit", "bech32", "bech32m"]) +def test_sighash_fullmix(addr_fmt, _test_single_sig_sighash): # tx with 6 inputs representing all possible sighashes - _test_single_sig_sighash(addr_fmt, tuple(SIGHASH_MAP.keys()), num_inputs=6, - num_outputs=num_outs, psbt_v2=psbt_v2) + _test_single_sig_sighash(addr_fmt, tuple(SIGHASH_MAP_NON_TAPROOT.keys()), num_inputs=6, num_outputs=8) @pytest.mark.bitcoind -@pytest.mark.parametrize("sighash", [sh for sh in SIGHASH_MAP if sh != 'ALL']) +def test_sighash_fullmix_taproot(_test_single_sig_sighash): + # tx with 6 inputs representing all possible sighashes + _test_single_sig_sighash("bech32m", tuple(SIGHASH_MAP.keys()), num_inputs=7, num_outputs=5) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("sighash", [sh for sh in SIGHASH_MAP if sh not in ['ALL', 'DEFAULT']]) def test_sighash_disallowed_consolidation(sighash, _test_single_sig_sighash): # sighash != ALL blocked for pure consolidations _test_single_sig_sighash("bech32", [sighash], num_inputs=2, num_outputs=2, @@ -2066,10 +2249,11 @@ def test_sighash_disallowed_consolidation(sighash, _test_single_sig_sighash): @pytest.mark.bitcoind @pytest.mark.parametrize("sighash", ["NONE", "NONE|ANYONECANPAY"]) -def test_sighash_disallowed_NONE(sighash, _test_single_sig_sighash): +@pytest.mark.parametrize("num_outs", [1, 3]) +def test_sighash_disallowed_NONE(sighash, num_outs, _test_single_sig_sighash): # sighash is the same among all inputs - _test_single_sig_sighash("bech32", [sighash], num_inputs=2, num_outputs=2, - consolidation=False, sh_checks=True) + _test_single_sig_sighash("bech32", [sighash], num_inputs=2, + num_outputs=num_outs, consolidation=False, sh_checks=True) @pytest.mark.bitcoind @@ -2105,19 +2289,25 @@ def test_no_outputs_tx(fake_txn, microsd_path, goto_home, press_select, pick_men try: os.remove(fpath) except: pass - -def test_send2taproot_addresss(fake_txn , start_sign, end_sign, cap_story, use_testnet): +@pytest.mark.parametrize("num_unknown", [1,3]) +def test_send2unknown_script(fake_txn , start_sign, end_sign, cap_story, use_testnet, num_unknown): use_testnet() - psbt = fake_txn(2, 2, segwit_in=True, change_outputs=[0], outstyles=["p2tr"]) + unknowns = ["unknown"] * num_unknown + num_out = 2 if num_unknown == 1 else 4 + + # OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + hex_str = "049f7b2a5cb17576a914371c20fb2e9899338ce5e99908e64fd30b78931388ac" + + outs = [["p2tr", None, True] if not i else ["unknown", None, False, hex_str] for i in range(num_out)] + psbt = fake_txn(2, outs, addr_fmt="p2tr") start_sign(psbt) title, story = cap_story() assert title == "OK TO SEND?" # we do not understand change in taproot (taproot not supported) + assert "(1 warning below)" in story # unknown script + assert ("Sending to %d not well understood script(s)" % num_unknown) in story assert "Consolidating" not in story - assert "Change back" not in story - # but we should show address - assert "to script" not in story - assert "tb1p" in story + assert "to script" in story signed = end_sign(accept=True, finalize=False) assert signed @@ -2133,7 +2323,7 @@ def test_batch_sign(num_tx, ui_path, action, fake_txn, need_keypress, microsd_wipe() for i in range(num_tx): - psbt = fake_txn(2, 2, segwit_in=random.getrandbits(1)) + psbt = fake_txn(2, 2, addr_fmt=random.choice(["p2tr", "p2wpkh", "p2pkh"])) with open(microsd_path(f"{i}.psbt"), "wb") as f: f.write(psbt) @@ -2185,7 +2375,7 @@ def test_batch_sign(num_tx, ui_path, action, fake_txn, need_keypress, time.sleep(.5) title, story = cap_story() assert "-signed.psbt" in story - press_select() + press_cancel() time.sleep(.5) title, story = cap_story() @@ -2225,12 +2415,9 @@ def test_v2_psbt_bip370_invalid(desc_psbt_hex, start_sign, cap_story): @pytest.mark.bitcoind @pytest.mark.parametrize("outstyle", ADDR_STYLES_SINGLE) -@pytest.mark.parametrize("segwit_in", [True, False]) -@pytest.mark.parametrize("wrapped_segwit_in", [True, False]) -def test_psbt_v2(outstyle, segwit_in, wrapped_segwit_in, fake_txn , start_sign, end_sign, cap_story, +def test_psbt_v2(outstyle, fake_txn , start_sign, end_sign, cap_story, microsd_path, bitcoind, finalize_v2_v0_convert): - psbt = fake_txn(2, 2, segwit_in=segwit_in, wrapped=wrapped_segwit_in, change_outputs=[0], - outstyles=[outstyle], psbt_v2=True) + psbt = fake_txn(2, [[outstyle, None, True], [outstyle]], addr_fmt=outstyle, psbt_v2=True) start_sign(psbt) title, story = cap_story() @@ -2259,8 +2446,7 @@ def test_psbt_v2(outstyle, segwit_in, wrapped_segwit_in, fake_txn , start_sign, assert resp["complete"] is True def test_psbt_v2_tx_modifiable_parse(fake_txn, start_sign, end_sign): - psbt = fake_txn(2, 2, segwit_in=True, wrapped=True, - change_outputs=[0], psbt_v2=True) + psbt = fake_txn(2, [["p2tr", None, True],["p2wpkh"]], addr_fmt="p2tr", psbt_v2=True) p = BasicPSBT().parse(psbt) # 3 = both inputs and outputs are modifiable # need just some value instead of None, in that case flag is ommited @@ -2294,9 +2480,8 @@ def hacker(psbt, way): psbt.output_count = actual_len_o + 1 - psbt = fake_txn(2, 2, segwit_in=True, wrapped=True, change_outputs=[0], - outstyles=["p2pkh", "p2wpkh"], psbt_v2=True, - psbt_hacker=lambda psbt: hacker(psbt, way)) + psbt = fake_txn(2, [["p2pkh", None, True],["p2wpkh"]], addr_fmt="p2sh-p2wpkh", + psbt_v2=True, psbt_hacker=lambda psbt: hacker(psbt, way)) start_sign(psbt) title, story = cap_story() @@ -2315,7 +2500,7 @@ def hacker(psbt, way): ]) def test_locktime_ux(use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, microsd_path, cap_story, goto_home, press_select, - pick_menu_item, bitcoind, locktime): + pick_menu_item, bitcoind, locktime, file_tx_signing_done): use_regtest() sim = bitcoind_d_sim_watch addr = sim.getnewaddress() @@ -2344,12 +2529,15 @@ def test_locktime_ux(use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, psbt_fname = "locktime.psbt" with open(microsd_path(psbt_fname), "w") as f: f.write(psbt) + goto_home() pick_menu_item('Ready To Sign') time.sleep(0.1) - pick_menu_item(psbt_fname) - time.sleep(0.1) title, story = cap_story() + if 'OK TO SEND' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() assert "WARNING" not in story if locktime != 0: @@ -2368,18 +2556,10 @@ def test_locktime_ux(use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, press_select() # confirm signing time.sleep(0.1) title, story = cap_story() - assert title == 'PSBT Signed' assert "Updated PSBT is:" in story assert "Finalized transaction (ready for broadcast)" in story assert "TXID" in story - split_story = story.split("\n\n") - story_txid = split_story[-1].split("\n")[-1] - signed_psbt_fname = split_story[1] - with open(microsd_path(signed_psbt_fname), "r") as f: - signed_psbt = f.read().strip() - signed_txn_fname = split_story[3] - with open(microsd_path(signed_txn_fname), "r") as f: - signed_txn = f.read().strip() + signed_psbt, signed_txn, story_txid = file_tx_signing_done(story) assert signed_psbt != psbt finalize_res = sim.finalizepsbt(signed_psbt) bitcoind_signed_txn = finalize_res["hex"] @@ -2404,7 +2584,7 @@ def test_locktime_ux(use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, def test_nsequence_blockheight_relative_locktime_ux(sequence, use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, microsd_path, cap_story, goto_home, press_select, pick_menu_item, - bitcoind, num_ins, differ): + bitcoind, num_ins, differ, file_tx_signing_done): if differ and (sequence == 0): # this case makes no sense return @@ -2453,12 +2633,15 @@ def test_nsequence_blockheight_relative_locktime_ux(sequence, use_regtest, bitco psbt_fname = "rtl-blockheight.psbt" with open(microsd_path(psbt_fname), "w") as f: f.write(psbt) + goto_home() pick_menu_item('Ready To Sign') time.sleep(0.1) - pick_menu_item(psbt_fname) - time.sleep(0.1) title, story = cap_story() + if 'OK TO SEND' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() assert "WARNING" not in story if sequence: @@ -2480,18 +2663,11 @@ def test_nsequence_blockheight_relative_locktime_ux(sequence, use_regtest, bitco press_select() # confirm signing time.sleep(0.1) title, story = cap_story() - assert title == 'PSBT Signed' assert "Updated PSBT is:" in story assert "Finalized transaction (ready for broadcast)" in story assert "TXID" in story - split_story = story.split("\n\n") - story_txid = split_story[-1].split("\n")[-1] - signed_psbt_fname = split_story[1] - with open(microsd_path(signed_psbt_fname), "r") as f: - signed_psbt = f.read().strip() - signed_txn_fname = split_story[3] - with open(microsd_path(signed_txn_fname), "r") as f: - signed_txn = f.read().strip() + press_select() # exit saved story + signed_psbt, signed_txn, story_txid = file_tx_signing_done(story) assert signed_psbt != psbt finalize_res = sim.finalizepsbt(signed_psbt) bitcoind_signed_txn = finalize_res["hex"] @@ -2516,13 +2692,13 @@ def test_nsequence_blockheight_relative_locktime_ux(sequence, use_regtest, bitco @pytest.mark.bitcoind -@pytest.mark.veryslow @pytest.mark.parametrize("num_ins", [1, 4, 11]) @pytest.mark.parametrize("differ", [True, False]) -@pytest.mark.parametrize("seconds", [512, 10000, 1000000, 33554431]) +@pytest.mark.parametrize("seconds", [512, 10240, 1024000, 33554431]) def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind_d_sim_watch, start_sign, microsd_path, cap_story, goto_home, press_select, - pick_menu_item, bitcoind, end_sign, num_ins, differ): + pick_menu_item, bitcoind, end_sign, num_ins, differ, + file_tx_signing_done): sequence = SEQUENCE_LOCKTIME_TYPE_FLAG | (seconds >> 9) use_regtest() sim = bitcoind_d_sim_watch @@ -2540,18 +2716,22 @@ def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind ins = [] num_ins_locked = 0 + locked_indexes = [] for i, utxo in enumerate(utxos): # time-based RTL - if i and differ: - nSeq = sequence - (sequence * i) + if i and differ and (seconds > 512): + secs = seconds // i + nSeq = SEQUENCE_LOCKTIME_TYPE_FLAG | (secs >> 9) if nSeq < 0: nSeq = 0 else: + secs = seconds nSeq = sequence if nSeq > 0: num_ins_locked += 1 + locked_indexes.append((i, secs)) inp = { "txid": utxo["txid"], @@ -2565,12 +2745,15 @@ def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind psbt_fname = "rtl-time.psbt" with open(microsd_path(psbt_fname), "w") as f: f.write(psbt) + goto_home() - pick_menu_item('Ready To Sign') - time.sleep(0.1) - pick_menu_item(psbt_fname) + pick_menu_item("Ready To Sign") time.sleep(0.1) title, story = cap_story() + if 'OK TO SEND' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() assert "WARNING" not in story assert "TX LOCKTIMES" in story @@ -2580,9 +2763,9 @@ def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind if num_ins_locked == 1: assert ("has " + base_msg) in story else: - if differ: + if differ and (seconds > 512): assert ("%d inputs have relative time-based timelock." % num_ins_locked) in story - for i in range(num_ins_locked): + for i, _ in sorted(locked_indexes, key=lambda i: i[1], reverse=True)[:10]: assert ("%d. " % i) in story else: msg1 = "%d inputs have " % num_ins_locked @@ -2591,18 +2774,10 @@ def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind press_select() # confirm signing time.sleep(0.1) title, story = cap_story() - assert title == 'PSBT Signed' assert "Updated PSBT is:" in story assert "Finalized transaction (ready for broadcast)" in story assert "TXID" in story - split_story = story.split("\n\n") - story_txid = split_story[-1].split("\n")[-1] - signed_psbt_fname = split_story[1] - with open(microsd_path(signed_psbt_fname), "r") as f: - signed_psbt = f.read().strip() - signed_txn_fname = split_story[3] - with open(microsd_path(signed_txn_fname), "r") as f: - signed_txn = f.read().strip() + signed_psbt, signed_txn, story_txid = file_tx_signing_done(story) assert signed_psbt != psbt finalize_res = sim.finalizepsbt(signed_psbt) bitcoind_signed_txn = finalize_res["hex"] @@ -2627,13 +2802,13 @@ def test_nsequence_timebased_relative_locktime_ux(seconds, use_regtest, bitcoind assert txid == story_txid -@pytest.mark.bitcoind @pytest.mark.veryslow +@pytest.mark.bitcoind @pytest.mark.parametrize("abs_lock", [True, False]) -@pytest.mark.parametrize("num_rtl", [(2,3),(4,7),(8,3),(6,7)]) -def test_mixed_locktimes(num_rtl, use_regtest, bitcoind_d_sim_watch, start_sign, - microsd_path, cap_story, goto_home, press_select, - pick_menu_item, bitcoind, end_sign, abs_lock): +@pytest.mark.parametrize("num_rtl", [(2,3),(4,7),(6,7)]) +def test_mixed_locktimes(num_rtl, use_regtest, bitcoind_d_sim_watch, start_sign, microsd_path, + cap_story, goto_home, press_select, pick_menu_item, bitcoind, end_sign, + abs_lock, file_tx_signing_done): tb, bb = num_rtl num_ins = tb + bb sequence = SEQUENCE_LOCKTIME_TYPE_FLAG | (512 >> 9) @@ -2681,9 +2856,11 @@ def test_mixed_locktimes(num_rtl, use_regtest, bitcoind_d_sim_watch, start_sign, goto_home() pick_menu_item('Ready To Sign') time.sleep(0.1) - pick_menu_item(psbt_fname) - time.sleep(0.1) title, story = cap_story() + if 'OK TO SEND' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() assert "WARNING" not in story assert "TX LOCKTIMES" in story @@ -2704,18 +2881,10 @@ def test_mixed_locktimes(num_rtl, use_regtest, bitcoind_d_sim_watch, start_sign, press_select() # confirm signing time.sleep(0.1) title, story = cap_story() - assert title == 'PSBT Signed' assert "Updated PSBT is:" in story assert "Finalized transaction (ready for broadcast)" in story assert "TXID" in story - split_story = story.split("\n\n") - story_txid = split_story[-1].split("\n")[-1] - signed_psbt_fname = split_story[1] - with open(microsd_path(signed_psbt_fname), "r") as f: - signed_psbt = f.read().strip() - signed_txn_fname = split_story[3] - with open(microsd_path(signed_txn_fname), "r") as f: - signed_txn = f.read().strip() + signed_psbt, signed_txn, story_txid = file_tx_signing_done(story) assert signed_psbt != psbt finalize_res = sim.finalizepsbt(signed_psbt) bitcoind_signed_txn = finalize_res["hex"] @@ -2768,65 +2937,65 @@ def random_nLockTime_test_cases(num=10): *random_nLockTime_test_cases() ]) def test_timelocks_visualize(start_sign, end_sign, dev, bitcoind, use_regtest, - bitcoind_d_sim_watch, nLockTime): - # - works on simulator and connected USB real-device - nLockTime, expect_ux = nLockTime - num_ins = 10 - use_regtest() - bitcoind_d_sim_watch.keypoolrefill(20) - for i in range(num_ins): - addr = bitcoind_d_sim_watch.getnewaddress() - bitcoind.supply_wallet.sendtoaddress(addr, 1) + bitcoind_d_sim_watch, nLockTime, sim_root_dir): + # - works on simulator and connected USB real-device + nLockTime, expect_ux = nLockTime + num_ins = 10 + use_regtest() + bitcoind_d_sim_watch.keypoolrefill(20) + for i in range(num_ins): + addr = bitcoind_d_sim_watch.getnewaddress() + bitcoind.supply_wallet.sendtoaddress(addr, 1) - bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) - dest_addr = bitcoind_d_sim_watch.getnewaddress() # self-spend - utxos = bitcoind_d_sim_watch.listunspent() - assert len(utxos) == num_ins - - ins = [] - for i, utxo in enumerate(utxos): - if i % 2 == 0: - nSeq = (SEQUENCE_LOCKTIME_TYPE_FLAG | i) - else: - confirmations = utxo["confirmations"] - nSeq = confirmations + (20*i) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + dest_addr = bitcoind_d_sim_watch.getnewaddress() # self-spend + utxos = bitcoind_d_sim_watch.listunspent() + assert len(utxos) == num_ins + + ins = [] + for i, utxo in enumerate(utxos): + if i % 2 == 0: + nSeq = (SEQUENCE_LOCKTIME_TYPE_FLAG | i) + else: + confirmations = utxo["confirmations"] + nSeq = confirmations + (20*i) - inp = { - "txid": utxo["txid"], - "vout": utxo["vout"], - "sequence": nSeq, - } - ins.append(inp) + inp = { + "txid": utxo["txid"], + "vout": utxo["vout"], + "sequence": nSeq, + } + ins.append(inp) - psbt_resp = bitcoind_d_sim_watch.walletcreatefundedpsbt( - ins, [{dest_addr: (num_ins - 0.1)}], - nLockTime, {"fee_rate": 20} - ) - psbt = base64.b64decode(psbt_resp.get("psbt")) + psbt_resp = bitcoind_d_sim_watch.walletcreatefundedpsbt( + ins, [{dest_addr: (num_ins - 0.1)}], + nLockTime, {"fee_rate": 20} + ) + psbt = base64.b64decode(psbt_resp.get("psbt")) - open('debug/locktimes.psbt', 'wb').write(psbt) + with open(f'{sim_root_dir}/debug/locktimes.psbt', 'wb') as f: + f.write(psbt) - # should be able to sign, but get warning + # should be able to sign, but get warning - # use new feature to have Coldcard return the 'visualization' of transaction - start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) - story = end_sign(accept=None, expect_txn=False) + # use new feature to have Coldcard return the 'visualization' of transaction + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + story = end_sign(accept=None, expect_txn=False) - story = story.decode('ascii') - assert datetime.datetime.utcfromtimestamp(nLockTime).strftime("%Y-%m-%d %H:%M:%S") == expect_ux - assert f"Abs Locktime: This tx can only be spent after {expect_ux} UTC (MTP)" in story - assert "Block height RTL: 5 inputs have relative block height timelock" in story - # when i=0 in loop time based RTL is zero - assert "Time-based RTL: 4 inputs have relative time-based timelock" in story + story = story.decode('ascii') + assert datetime.datetime.utcfromtimestamp(nLockTime).strftime("%Y-%m-%d %H:%M:%S") == expect_ux + assert f"Abs Locktime: This tx can only be spent after {expect_ux} UTC (MTP)" in story + assert "Block height RTL: 5 inputs have relative block height timelock" in story + # when i=0 in loop time based RTL is zero + assert "Time-based RTL: 4 inputs have relative time-based timelock" in story @pytest.mark.parametrize('in_out', [(4,1),(2,2),(2,1)]) @pytest.mark.parametrize('partial', [False, True]) -@pytest.mark.parametrize('segwit', [True, False]) -def test_base64_psbt_qr(in_out, partial, segwit, scan_a_qr, readback_bbqr, +def test_base64_psbt_qr(in_out, partial, scan_a_qr, readback_bbqr, goto_home, use_regtest, cap_story, fake_txn, dev, decode_psbt_with_bitcoind, decode_with_bitcoind, - press_cancel, press_select, need_keypress): + press_cancel, press_select, need_keypress, sim_root_dir): def hack(psbt): if partial: # change first input to not be ours @@ -2836,15 +3005,12 @@ def hack(psbt): num_in, num_out = in_out - if not segwit: - psbt = fake_txn(num_in, num_out, dev.master_xpub, psbt_hacker=hack) - else: - psbt = fake_txn(num_in, num_out, dev.master_xpub, psbt_hacker=hack, - segwit_in=True, outstyles=['p2wpkh']) + psbt = fake_txn(num_in, num_out, dev.master_xpub, addr_fmt="p2wpkh", psbt_hacker=hack) psbt = base64.b64encode(psbt).decode() - open('debug/last.psbt', 'w').write(psbt) + with open(f'{sim_root_dir}/debug/last.psbt', 'w') as f: + f.write(psbt) goto_home() need_keypress(KEY_QR) @@ -2900,8 +3066,7 @@ def test_sorting_outputs_by_size(fake_txn, start_sign, cap_story, use_testnet, 9000000, 179989995, 11000000, 12000000, 13000000, 14000000, 15000000] max_display_num = 10 rest_num = len(out_vals) - max_display_num - psbt = fake_txn(3, 15, segwit_in=True, outstyles=ADDR_STYLES_SINGLE, - outvals=out_vals) + psbt = fake_txn(3, [[random.choice(ADDR_STYLES_SINGLE), i] for i in out_vals], addr_fmt="p2wpkh") start_sign(psbt) time.sleep(.1) title, story = cap_story() @@ -2925,50 +3090,57 @@ def test_sorting_outputs_by_size(fake_txn, start_sign, cap_story, use_testnet, press_cancel() -@pytest.mark.parametrize("psbtv2", [True, False]) @pytest.mark.parametrize("chain", ["BTC", "XTN"]) @pytest.mark.parametrize("data", [ # (out_style, amount, is_change) + [("p2tr", 999999, 1)] + [("p2tr", 888888, 0)] * 12, [("p2pkh", 1000000, 0)] * 99, - [("p2wpkh", 1000000, 1),("p2wpkh-p2sh", 800000, 1)] * 27, + [("p2wpkh", 1000000, 1),("p2wpkh-p2sh", 800000, 1), ("p2tr", 600000, 1)] * 27, [("p2pkh", 1000000, 1)] * 11 + [("p2wpkh", 50000000, 0)] * 16, - [("p2pkh", 1000000, 1), ("p2wpkh", 50000000, 0), ("p2wpkh-p2sh", 800000, 1)] * 11, + [("p2pkh", 1000000, 1), ("p2wpkh", 50000000, 0), ("p2wpkh-p2sh", 800000, 1), ("p2tr", 100000, 0)] * 11, ]) -def test_txout_explorer(psbtv2, chain, data, fake_txn, start_sign, - settings_set, txout_explorer, cap_story): +def test_txout_explorer(chain, data, fake_txn, start_sign, settings_set, txout_explorer, + cap_story, pytestconfig): + # TODO This test MUST be run with --psbt2 flag on and off settings_set("chain", chain) - outstyles = [] - outvals = [] - change_outputs = [] - for i in range(len(data)): - os, ov, is_change = data[i] - outstyles.append(os) - outvals.append(ov) - if is_change: - change_outputs.append(i) - - inp_amount = sum(outvals) + 100000 # 100k sat fee - psbt = fake_txn(1, len(data), segwit_in=True, outstyles=outstyles, - outvals=outvals, change_outputs=change_outputs, - psbt_v2=psbtv2, input_amount=inp_amount) + + out_val = sum(d[1] for d in data) # zero fee + psbt = fake_txn(1, data, addr_fmt="p2tr", input_amount=out_val, + psbt_v2=pytestconfig.getoption('psbt2')) start_sign(psbt) txout_explorer(data, chain) - -def test_txout_explorer_op_return(fake_txn, start_sign, cap_story, is_q1, - need_keypress, press_cancel): - d = [ - (1, b"Coinkite"), - (0, b"Mk1 Mk2 Mk3 Mk4 Q"), - (100, b"binarywatch.org"), - (100, b"a" * 75), - ] - psbt = fake_txn(1, 20, segwit_in=False, op_return=d) - start_sign(psbt) +@pytest.mark.parametrize("finalize", [True, False]) +@pytest.mark.parametrize("data", [ + [(1, b"Coinkite"), (0, b"Mk1 Mk2 Mk3 Mk4 Q"), (100, b"binarywatch.org"), (100, b"a" * 75)], + [(0, b"W"*160), (10000, b"W"*153)], + [(0, b"a" * 300), (10, b"x" * 1000), (0, b"anchor output")], + [(0, b""), (10, b"")], + [(0, os.urandom(32)), (10, os.urandom(64)), (1000, os.urandom(160)), (0, os.urandom(161))], +]) +def test_txout_explorer_op_return(finalize, data, fake_txn, start_sign, cap_story, is_q1, + need_keypress, press_cancel, press_select, end_sign, + cap_screen_qr, cap_screen): + outputs = [["p2tr", 50000, not i] for i in range(20)] + outputs += [["op_return", am, None, d] for am, d in data] + out_val = sum(o[1] for o in outputs) + psbt = fake_txn(1, outputs, addr_fmt="p2tr", input_amount=out_val) + start_sign(psbt, finalize=finalize) time.sleep(.1) title, story = cap_story() assert title == 'OK TO SEND?' + assert "(1 warning below)" in story + if len(data) > 1: + assert ("Multiple OP_RETURN outputs: %d" % len(data)) in story + else: + assert "Multiple OP_RETURN outputs" not in story + + if sum(int(len(x[1]) > 80) for x in data): + assert "OP_RETURN > 80 bytes" in story + else: + assert "OP_RETURN > 80 bytes" not in story + assert "Press (2) to explore txn" in story need_keypress("2") time.sleep(.1) @@ -2980,56 +3152,592 @@ def test_txout_explorer_op_return(fake_txn, start_sign, cap_story, is_q1, time.sleep(.1) _, story = cap_story() ss = story.split("\n\n") - for i, (sa, sb, (amount, data)) in enumerate(zip(ss[:-1:2], ss[1::2], d), start=20): + + # collect QR codes first + need_keypress(KEY_QR if is_q1 else "4") + qr_list = [] + for v, d in data: + try: + qr = cap_screen_qr().decode() + qr_list.append(qr) + except RuntimeError: + scr = cap_screen() + if is_q1: + too_big = 650 + assert "QR too big" in scr + else: + too_big = 158 + assert "QR too" in scr + assert "big" in scr + + assert len(d) > too_big + qr_list.append(None) + + need_keypress(KEY_RIGHT if is_q1 else "9") + time.sleep(.5) + + press_cancel() # QR code on screen - exit + + for i, (sa, sb, (amount, data)) in enumerate(zip(ss[:-1:2], ss[1::2], data), start=20): assert f"Output {i}:" == sa - val, name, dd = sb.split("\n") + try: + val, name, dd = sb.split("\n") + except: + dd = None + val, name, dd0, _, dd1 = sb.split("\n") assert "OP_RETURN" in name assert f'{amount / 100000000:.8f} XTN' == val - hex_str, ascii_str = dd.split(" ", 1) - assert f"(ascii: {data.decode()})" == ascii_str - assert data.hex() == hex_str + if dd == "null-data": + assert qr_list[i - 20] == "" + elif dd: + is_ascii = False + try: + data.decode("ascii") + is_ascii = True + except UnicodeDecodeError: pass + if is_ascii: + hex_str, ascii_str = dd.split(" ", 1) + else: + hex_str = dd + + qr_target = qr_list[i-20] + if qr_target: + assert hex_str in qr_target + assert qr_target.startswith("6a") # OP_RETURN + assert data.hex() == hex_str + if is_ascii: + assert f"(ascii: {data.decode()})" == ascii_str + else: + s = data[:80].hex() + e = data[-80:].hex() + assert s == dd0 + assert e == dd1 - press_cancel() - press_cancel() + press_cancel() # exit txn out explorer + end_sign(finalize=finalize) + +def test_null_data_op_return(fake_txn, start_sign, end_sign, reset_seed_words): + reset_seed_words() + psbt = fake_txn(1, [["p2pkh", 99_999_800], ["op_return", 50, None, b""]]) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + story = end_sign(accept=None, expect_txn=False).decode() + assert "null-data" in story + assert "OP_RETURN" in story + +def test_smallest_txn(fake_txn, start_sign, end_sign, reset_seed_words, settings_set): + # serialized txn has just 62 bytes and is the smallest that we support + # 1 input (iregardless of script type) and 1 zero value null OP_RETURN + reset_seed_words() + settings_set("fee_limit", -1) + psbt = fake_txn(1, [["op_return", 10, None, b""]], addr_fmt="p2tr", input_amount=10) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + story = end_sign(accept=None, expect_txn=False).decode() + assert "null-data" in story + assert "OP_RETURN" in story + + +@pytest.mark.parametrize("num_outs", [1, 12]) +@pytest.mark.parametrize("change", [True, False]) +def test_zero_value_outputs(num_outs, change, fake_txn, start_sign, end_sign, reset_seed_words, + settings_set): + reset_seed_words() + # user needs to disable fee limit checks to be able to do this + settings_set("fee_limit", -1) + psbt = fake_txn(1, num_outs * [[random.choice(ADDR_STYLES_SINGLE), 0, change]], input_amount=1) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + story = end_sign(accept=None, expect_txn=False).decode() + assert "Zero Value: Non-standard zero value output(s)." in story + assert "1 input" in story + assert f"{num_outs} output{'' if num_outs == 1 else 's'}" in story + assert 'Network fee 0.00000001 XTN' in story + + if change: + assert "0.00000000 XTN" in story.split("\n\n")[4] # change back is zero + assert "Consolidating 0.00000000 XTN" in story + assert "Change back" in story + if num_outs > 1: + assert "to addresses" in story + else: + assert "to address" in story + else: + # even + if num_outs == 12: + # even tho we do not see 2 outputs, fee is also 0 and 2 smaller not shown here have also value o 0 + assert story.count('0.00000000 XTN') == 12 + else: + assert story.count('0.00000000 XTN') == 2 + assert "Change back" not in story -def test_low_R_grinding(dev, goto_home, microsd_path, press_select, offer_ms_import, - cap_story, try_sign, reset_seed_words, clear_ms): +@pytest.mark.parametrize("change", [True, False]) +@pytest.mark.parametrize("num_ins", [True, False]) +def test_zero_value_input(change, num_ins, fake_txn, start_sign, end_sign, reset_seed_words, + cap_story): + # 0 value inputs - not allowed reset_seed_words() - clear_ms() - desc = "sh(sortedmulti(2,[6ba6cfd0/45h]tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9/0/*,[747b698e/45h]tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc/0/*,[7bb026be/45h]tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa/0/*,[0f056943/45h]tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n/0/*))#up0sw2xp" - # PSBT created via fake_ms_txn, grinded in test_ms_sign_myself - psbt_fname = "myself-72sig.psbt" - with open(f"data/{psbt_fname}", "r") as f: - b64psbt = f.read() + af = random.choice(ADDR_STYLES_SINGLE) + psbt = fake_txn([[af, None, 0]], [[af, 0, change]], addr_fmt=af, fee=0) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + with pytest.raises(Exception): + end_sign(accept=None, expect_txn=False).decode() + + _, story = cap_story() + assert "zero value txn" in story + + +@pytest.mark.parametrize("num_ins", [1, 12]) +@pytest.mark.parametrize("foreign", [True, False]) +@pytest.mark.parametrize("change", [True, False]) +def test_zero_value_inputs(num_ins, foreign, change, fake_txn, start_sign, end_sign, + reset_seed_words): + # one input is-non zero + # others are zero --> allowed + reset_seed_words() + af = random.choice(ADDR_STYLES_SINGLE) + + inputs = (num_ins - 1 -int(foreign)) * [[af, None, 0]] + if foreign: + inputs += [[af, None, 0, False]] + + inputs += [[af, None, 10000]] # always one input mine + + psbt = fake_txn(inputs, [[af, 9980, change]], addr_fmt=af, fee=20) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + end_sign(accept=None, expect_txn=False).decode() + + +def test_negative_amount_inputs(reset_seed_words, fake_txn, start_sign, end_sign, cap_story): + reset_seed_words() + af = random.choice(ADDR_STYLES_SINGLE) + psbt = fake_txn([[af, None, -1000]], [[af, 200]], addr_fmt=af, fee=0) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + with pytest.raises(Exception): + end_sign(accept=None, expect_txn=False).decode() + + _, story = cap_story() + assert "negative input value: i0" in story + +def test_negative_amount_outputs(reset_seed_words, fake_txn, start_sign, end_sign, cap_story): + reset_seed_words() + af = random.choice(ADDR_STYLES_SINGLE) + psbt = fake_txn([[af, None, 1000]], [[af, -200]], addr_fmt=af, fee=0) + start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) + with pytest.raises(Exception): + end_sign(accept=None, expect_txn=False).decode() + + _, story = cap_story() + assert "negative output value: o0" in story + +def test_mk4_done_signing_infinite_loop(goto_home, try_sign, fake_txn, enable_hw_ux, + settings_get, is_q1): + if is_q1: + raise pytest.skip("Irrelevant on Q as it always provides QR option") goto_home() - passphrase = "Myself" - dev.send_recv(CCProtocolPacker.bip39_passphrase(passphrase), timeout=None) - press_select() + had_nfc = settings_get("nfc", None) + had_vdisk = settings_get("vidsk", None) + enable_hw_ux("nfc", disable=True) + enable_hw_ux("vdisk", disable=True) + psbt = fake_txn(1, [["p2wpkh", None, True], []], addr_fmt="p2wpkh") + try_sign(psbt, accept=True) + # above never returns in unpatched version and fills up the disk + if had_nfc: + enable_hw_ux("nfc") + if had_vdisk: + enable_hw_ux("vdisk") + + +@pytest.mark.bitcoind +def test_finalize_with_foreign_inputs(bitcoind, bitcoind_d_sim_watch, start_sign, end_sign, + cap_story, try_sign_microsd): + # foreign inputs that have partial sigs filled + # we still do not care about final_scriptsig & final_scriptwitness PSBT fields + dest_address = bitcoind.supply_wallet.getnewaddress() + alice = bitcoind.create_wallet(wallet_name="alice") + bob = bitcoind.create_wallet(wallet_name="bob") + cc = bitcoind_d_sim_watch + alice_addr = alice.getnewaddress() + bob_addr = bob.getnewaddress() + cc_addr = cc.getnewaddress() + # fund all addresses + for addr in (alice_addr, bob_addr, cc_addr): + bitcoind.supply_wallet.sendtoaddress(addr, 2.0) + + # mine above sends + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + + psbt_list = [] + for w in (alice, bob, cc): + assert w.listunspent() + psbt = w.walletcreatefundedpsbt([], [{dest_address: 1.0}], 0, {"fee_rate": 20})["psbt"] + psbt_list.append(psbt) + + # join PSBTs to one + the_psbt = bitcoind.supply_wallet.joinpsbts(psbt_list) + + # bitcoin core would just fill finalscriptwitness, we need partial signatures + # just add dummy signatures and remove + pp = BasicPSBT().parse(base64.b64decode(the_psbt)) + for i in pp.inputs: + assert len(i.bip32_paths) == 1 # single sigs + der = list(i.bip32_paths.values())[0] + if der[:4].hex().upper() == xfp2str(simulator_fixed_xfp): + # our key + continue + pubkey = list(i.bip32_paths.keys())[0] + assert not i.part_sigs # empty + i.part_sigs[pubkey] = os.urandom(71) # dummy sig + + # USB works and our signature is added (but only if we do not finalize) + psbt = pp.as_bytes() + start_sign(psbt) + signed = end_sign(accept=True) + assert signed != psbt + for i in BasicPSBT().parse(signed).inputs: + assert i.part_sigs + + try_sign_microsd(psbt, finalize=True, accept=True) + title, story = cap_story() + assert title == "PSBT Signed" + assert "Finalized transaction (ready for broadcast)" in story + +# EOF + +@pytest.mark.bitcoind +def test_taproot_keyspend(use_regtest, bitcoind_d_sim_watch, start_sign, end_sign, microsd_path, + cap_story, goto_home, press_select, pick_menu_item, bitcoind, sim_root_dir): + use_regtest() + sim = bitcoind_d_sim_watch + sim.keypoolrefill(10) + addr = sim.getnewaddress("", "bech32m") + bitcoind.supply_wallet.sendtoaddress(addr, 49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + dest_addr = sim.getnewaddress("", "bech32m") # self-spend + psbt_resp = sim.walletcreatefundedpsbt([], [{dest_addr: 1.0}], 0, {"fee_rate": 20}) + psbt = psbt_resp.get("psbt") + psbt_fname = "tr.psbt" + with open(f'{sim_root_dir}/debug/last.psbt', 'w')as f: + f.write(psbt) + with open(microsd_path(psbt_fname), "w") as f: + f.write(psbt) + goto_home() + pick_menu_item('Ready To Sign') time.sleep(.1) title, story = cap_story() + if 'OK TO SEND?' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() + assert title == 'OK TO SEND?' + assert "Consolidating" in story # self-spend + assert " 1 input\n 2 outputs" in story + addrs = [addr_from_display_format(l) for l in story.split("\n") if l and (l[0] == '\x02')] + assert len(addrs) == 2 + for addr in addrs: + assert addr.startswith("bcrt1p") + press_select() # confirm signing + time.sleep(0.1) + title, story = cap_story() + assert title == 'PSBT Signed' + assert "Updated PSBT is:" in story + assert "Finalized transaction (ready for broadcast)" in story + assert "TXID" in story + split_story = story.split("\n\n") + story_txid = split_story[4].split("\n")[-1] + signed_psbt_fname = split_story[1] + with open(microsd_path(signed_psbt_fname), "r") as f: + signed_psbt = f.read().strip() + with open(f'{sim_root_dir}/debug/last.psbt', 'w') as f: + f.write(psbt) + signed_txn_fname = split_story[3] + with open(microsd_path(signed_txn_fname), "r") as f: + signed_txn = f.read().strip() + assert signed_psbt != psbt + finalize_res = sim.finalizepsbt(signed_psbt) + bitcoind_signed_txn = finalize_res["hex"] + assert finalize_res["complete"] is True + accept_res = sim.testmempoolaccept([bitcoind_signed_txn])[0] + assert accept_res["allowed"] is True + assert signed_txn == bitcoind_signed_txn + txid = sim.sendrawtransaction(signed_txn) + assert len(txid) == 64 + assert txid == story_txid - assert "[747B698E]" in title - press_select() + addr_segwit = sim.getnewaddress("", "bech32") + sim.generatetoaddress(1, addr_segwit) # mine transaction sent and also new coins to p2wpkh + addr_nested_segwit = sim.getnewaddress("", "p2sh-segwit") + sim.generatetoaddress(1, addr_nested_segwit) + addr_legacy = sim.getnewaddress("", "legacy") + sim.generatetoaddress(1, addr_legacy) + # try to sign tx with all input types (legacy, nested segwit, native segwit, taproot) + all_of_it = sim.getbalance() + dest_addr0 = sim.getnewaddress("", "bech32m") # self-spend + dest_addr1 = sim.getnewaddress("", "bech32m") # self-spend + dest_addr2 = sim.getnewaddress("", "bech32m") # self-spend + chunk = round(all_of_it / 3, 6) + psbt_resp = sim.walletcreatefundedpsbt([], [{dest_addr0: chunk}, {dest_addr1: chunk}, {dest_addr2: chunk}], + 0, {'subtractFeeFromOutputs': [0], "fee_rate": 20}) + psbt = psbt_resp.get("psbt") + psbt_fname = "tr-all.psbt" + with open(f'{sim_root_dir}/debug/last.psbt', 'w') as f: + f.write(psbt) + with open(microsd_path(psbt_fname), "w") as f: + f.write(psbt) + goto_home() + pick_menu_item('Ready To Sign') + time.sleep(.1) + title, story = cap_story() + if 'OK TO SEND?' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() + assert title == 'OK TO SEND?' + assert "Consolidating" in story # self-spend + assert " 2 inputs\n 3 outputs" in story + press_select() # confirm signing + time.sleep(0.1) + title, story = cap_story() + assert title == 'PSBT Signed' + assert "Updated PSBT is:" in story + assert "Finalized transaction (ready for broadcast)" in story + assert "TXID" in story + split_story = story.split("\n\n") + story_txid = split_story[4].split("\n")[-1] + signed_psbt_fname = split_story[1] + with open(microsd_path(signed_psbt_fname), "r") as f: + signed_psbt = f.read().strip() + with open(f'{sim_root_dir}/debug/last.psbt', 'w') as f: + f.write(psbt) + signed_txn_fname = split_story[3] + with open(microsd_path(signed_txn_fname), "r") as f: + signed_txn = f.read().strip() + assert signed_psbt != psbt + finalize_res = sim.finalizepsbt(signed_psbt) + bitcoind_signed_txn = finalize_res["hex"] + assert finalize_res["complete"] is True + accept_res = sim.testmempoolaccept([bitcoind_signed_txn])[0] + assert accept_res["allowed"] is True + assert signed_txn == bitcoind_signed_txn + txid = sim.sendrawtransaction(signed_txn) + assert len(txid) == 64 + assert txid == story_txid + # multi p2tr output consolidation + addr_segwit = sim.getnewaddress("", "bech32") + sim.generatetoaddress(1, addr_segwit) # mine transaction sent and also new coins to p2wpkh + all_of_it = sim.getbalance() + dest_addr = sim.getnewaddress("", "bech32m") + psbt_resp = sim.walletcreatefundedpsbt([], [{dest_addr: all_of_it}], + 0, {'subtractFeeFromOutputs': [0], "fee_rate": 20}) + psbt = psbt_resp.get("psbt") + psbt_fname = "tr-multi-out-consolidation.psbt" + with open(microsd_path(psbt_fname), "w") as f: + f.write(psbt) + goto_home() + pick_menu_item('Ready To Sign') time.sleep(.1) - _, story = offer_ms_import(desc) - assert "Create new multisig wallet?" in story + title, story = cap_story() + if 'OK TO SEND?' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() + assert title == 'OK TO SEND?' + assert "Consolidating" in story # self-spend + assert " 3 inputs\n 1 output" in story + press_select() # confirm signing + time.sleep(0.1) + title, story = cap_story() + assert title == 'PSBT Signed' + assert "Updated PSBT is:" in story + assert "Finalized transaction (ready for broadcast)" in story + assert "TXID" in story + split_story = story.split("\n\n") + story_txid = split_story[4].split("\n")[-1] + signed_psbt_fname = split_story[1] + with open(microsd_path(signed_psbt_fname), "r") as f: + signed_psbt = f.read().strip() + with open(f'{sim_root_dir}/debug/last.psbt', 'w') as f: + f.write(psbt) + signed_txn_fname = split_story[3] + with open(microsd_path(signed_txn_fname), "r") as f: + signed_txn = f.read().strip() + assert signed_psbt != psbt + finalize_res = sim.finalizepsbt(signed_psbt) + bitcoind_signed_txn = finalize_res["hex"] + assert finalize_res["complete"] is True + accept_res = sim.testmempoolaccept([bitcoind_signed_txn])[0] + assert accept_res["allowed"] is True + assert signed_txn == bitcoind_signed_txn + txid = sim.sendrawtransaction(signed_txn) + assert len(txid) == 64 + assert txid == story_txid + + # send it all to bob, he's a good guy + bob_w = bitcoind.create_wallet("bob") + dst = bob_w.getnewaddress("", "bech32m") + all_of_it = sim.getbalance() + psbt_resp = sim.walletcreatefundedpsbt([], [{dst: all_of_it}], + 0, {'subtractFeeFromOutputs': [0], "fee_rate": 20}) + psbt = psbt_resp.get("psbt") + psbt_fname = "tr2bob.psbt" + with open(microsd_path(psbt_fname), "w") as f: + f.write(psbt) + goto_home() + pick_menu_item('Ready To Sign') time.sleep(.1) - press_select() + title, story = cap_story() + if 'OK TO SEND?' not in title: + pick_menu_item(psbt_fname) + time.sleep(0.1) + title, story = cap_story() + assert title == 'OK TO SEND?' + assert "Consolidating" not in story # NOT a self-spend + assert "to address" in story + assert dst in story + press_select() # confirm signing + time.sleep(0.1) + title, story = cap_story() + assert title == 'PSBT Signed' + assert "Updated PSBT is:" in story + assert "Finalized transaction (ready for broadcast)" in story + assert "TXID" in story + split_story = story.split("\n\n") + story_txid = split_story[4].split("\n")[-1] + signed_psbt_fname = split_story[1] + with open(microsd_path(signed_psbt_fname), "r") as f: + signed_psbt = f.read().strip() + signed_txn_fname = split_story[3] + with open(microsd_path(signed_txn_fname), "r") as f: + signed_txn = f.read().strip() + assert signed_psbt != psbt + finalize_res = sim.finalizepsbt(signed_psbt) + bitcoind_signed_txn = finalize_res["hex"] + assert finalize_res["complete"] is True + accept_res = sim.testmempoolaccept([bitcoind_signed_txn])[0] + assert accept_res["allowed"] is True + assert signed_txn == bitcoind_signed_txn + txid = sim.sendrawtransaction(signed_txn) + assert len(txid) == 64 + assert txid == story_txid - # below raises for 72 bytes long signature - # only on firmware versions that do only 10 grinding iterations - try_sign(base64.b64decode(b64psbt), accept=True) +@pytest.mark.parametrize('fn_err_msg', [ + ('data/taproot/in_internal_key_len.psbt', 'PSBT_IN_TAP_INTERNAL_KEY length != 32'), + ('data/taproot/in_key_pth_sig_len.psbt', 'PSBT_IN_TAP_KEY_SIG length != 64 or 65'), + ('data/taproot/in_key_pth_sig_len1.psbt', 'PSBT_IN_TAP_KEY_SIG length != 64 or 65'), + ('data/taproot/in_tr_deriv_key_len.psbt', 'PSBT_IN_TAP_BIP32_DERIVATION xonly-pubkey length != 32'), + ('data/taproot/in_script_sig_key_len.psbt', 'PSBT_IN_TAP_SCRIPT_SIG key length != 64'), + ('data/taproot/in_script_sig_sig_len.psbt', 'PSBT_IN_TAP_SCRIPT_SIG signature length != 64 or 65'), + ('data/taproot/in_script_sig_sig_len1.psbt', 'PSBT_IN_TAP_SCRIPT_SIG signature length != 64 or 65'), + ('data/taproot/in_leaf_script_cb_len.psbt', 'PSBT_IN_TAP_LEAF_SCRIPT control block is not valid'), + ('data/taproot/in_leaf_script_cb_len1.psbt', 'PSBT_IN_TAP_LEAF_SCRIPT control block is not valid'), +]) +def test_invalid_input_taproot_psbt(start_sign, fn_err_msg, cap_story): + fn, err_msg = fn_err_msg + start_sign(fn) -def test_null_data_op_return(fake_txn, start_sign, end_sign, reset_seed_words): - reset_seed_words() - psbt = fake_txn(1, 1, op_return=[(50, b"")]) - start_sign(psbt, False, stxn_flags=STXN_VISUALIZE) - story = end_sign(accept=None, expect_txn=False).decode() - assert "null-data" in story - assert "OP_RETURN" in story + title, story = cap_story() + assert title == "Failure" + assert 'Invalid PSBT' in story + # error messages are disabled to save some space - problem file line is still included + # assert err_msg in story + + +def test_invalid_output_taproot_psbt(fake_txn, start_sign, cap_story, dev): + psbt = fake_txn(3, [[],["p2tr", None, True]], master_xpub=dev.master_xpub, addr_fmt="p2tr") + # invalid internal key length + psbt_obj = BasicPSBT().parse(psbt) + for o in psbt_obj.outputs: + o.taproot_internal_key = b"\x03" + b"a" * 32 + psbt0 = BytesIO() + psbt_obj.serialize(psbt0) + start_sign(psbt0.getvalue()) + title, story = cap_story() + assert title == "Failure" + assert 'Invalid PSBT' in story + # error messages are disabled to save some space - problem file line is still included + # assert "PSBT_OUT_TAP_INTERNAL_KEY length != 32" in story + + # invalid internal key length in bip32 taproot paths + psbt_obj = BasicPSBT().parse(psbt) + for o in psbt_obj.outputs: + o.taproot_bip32_paths = {b"\x03" + b"a" * 32: 12 * b"1"} + psbt0 = BytesIO() + psbt_obj.serialize(psbt0) + start_sign(psbt0.getvalue()) + title, story = cap_story() + assert title == "Failure" + assert 'Invalid PSBT' in story + # error messages are disabled to save some space - problem file line is still included + # assert "PSBT_IN_TAP_BIP32_DERIVATION xonly-pubkey length != 32" in story + +@pytest.mark.parametrize("multi", [False, True]) +@pytest.mark.parametrize("ss_af", ["p2wpkh", "p2tr", "p2pkh", "p2sh-p2wpkh"]) +@pytest.mark.parametrize("ms_af", ["p2wsh", "p2sh-p2wsh"]) # p2tr +def test_single_multi_psbt(multi, ss_af, ms_af, dev, fake_txn, fake_ms_txn, import_ms_wallet, + start_sign, end_sign, cap_story, clear_miniscript, use_testnet): + clear_miniscript() + use_testnet() + psbt = fake_txn(1, [[ss_af, int(5E6)], [ss_af, int(1E8 - 5E6), True]], + fee=0, addr_fmt=ss_af) + + wal_name = "msw" + keys = import_ms_wallet(2, 3, ms_af, name=wal_name, accept=True) + ms_psbt = fake_ms_txn(1, 2, 2, keys, outstyles=[ms_af], change_outputs=[0], inp_addr_fmt=ms_af) + + ssp = BasicPSBT().parse(psbt) + msp = BasicPSBT().parse(ms_psbt) + + # change to PSBT v2 to not need handle txn + sspv2 = BasicPSBT().parse(ssp.to_v2()) + mspv2 = BasicPSBT().parse(msp.to_v2()) + + combined = BasicPSBT() + combined.version = 2 + combined.txn_version = 2 + + combined.input_count = sspv2.input_count + mspv2.input_count + combined.output_count = sspv2.output_count + mspv2.output_count + combined.fallback_locktime = 0 + if multi: + cha = render_address(mspv2.outputs[0].script) + combined.inputs = mspv2.inputs + sspv2.inputs + combined.outputs = sspv2.outputs + mspv2.outputs + else: + cha = render_address(sspv2.outputs[1].script) + combined.inputs = sspv2.inputs + mspv2.inputs + combined.outputs = mspv2.outputs + sspv2.outputs -# EOF + for psbt in [combined.to_v2(), combined.to_v0()]: + + start_sign(psbt) + + time.sleep(.1) + _, story = cap_story() + + change_story = story.split("\n\n")[7 if multi else 6] + assert "Change back:" in change_story + split_chstory = change_story.split("\n") + assert len(split_chstory) == 4 # just one address + got = addr_from_display_format(split_chstory[-1]) + assert cha == got, f"{cha} target\n{got} got" + + if multi: + assert f"Wallet: {wal_name}" in story + else: + assert wal_name not in story + + assert "(1 warning below)" in story + assert 'Limited Signing' in story + assert ("We are not signing these inputs, because we either don't " + "know the key, inputs belong to different wallet," + " or we have already signed: 1") in story + + res = end_sign() + r = BasicPSBT().parse(res) + # check only desired signatures were added + if ss_af == "p2tr" and not multi: + assert r.inputs[0].taproot_key_sig + else: + assert r.inputs[0].part_sigs + assert not r.inputs[1].part_sigs \ No newline at end of file diff --git a/testing/test_sssp.py b/testing/test_sssp.py new file mode 100644 index 000000000..36089ab29 --- /dev/null +++ b/testing/test_sssp.py @@ -0,0 +1,783 @@ +# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# tests related to Single Signer Spending Policy feature (SSSP) +# +# run simulator without --eff +# +# +import pytest, time, base64, random, json +from psbt import BasicPSBT +from ckcc.protocol import CCProtocolPacker + + +@pytest.fixture +def goto_sssp_menu(goto_home, pick_menu_item, is_mark4): + def doit(): + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Spending Policy") + pick_menu_item("Single-Signer") + + return doit + + +@pytest.fixture +def setup_sssp(goto_sssp_menu, pick_menu_item, cap_story, press_select, pass_word_quiz, is_q1, + seed_story_to_words, cap_menu, OK, word_menu_entry, press_cancel, press_delete, + enter_number, scan_a_qr, cap_screen, settings_get, need_keypress, microsd_path, + master_settings_get, enter_pin, settings_remove, sim_exec): + + def doit(pin=None, mag=None, vel=None, whitelist=None, w2fa=None, has_violation=None, + word_check=None, notes_and_pws=None, rel_keys=None): + + goto_sssp_menu() + time.sleep(.1) + title, story = cap_story() + + # it is possible that PIN was set beforehand + if title == "Spending Policy": + assert "stops you from signing transactions unless conditions are met" in story + assert "locked into a special mode" in story + assert "First step is to define a new PIN" in story + press_select() + time.sleep(.1) + scr = cap_screen() + if "Spending Policy" in scr: + what = "Enter first part of PIN" if is_q1 else "Enter PIN Prefix" + assert what in scr + + enter_pin(pin) + time.sleep(.1) + scr = cap_screen() + what = "Confirm PIN value"if is_q1 else "CONFIRM PIN VALUE" + assert what in scr + enter_pin(pin) + time.sleep(.1) + + m = cap_menu() + + assert "Edit Policy..." in m + if has_violation is not None: + if has_violation: + assert "Last Violation" in m + else: + assert "last Violation" not in m + + assert "Word Check" in m + assert ("Allow Notes" in m) or not is_q1 + assert "Related Keys" in m + assert "Remove Policy" in m + assert "Test Drive" in m + assert "ACTIVATE" in m + + pick_menu_item("Edit Policy...") + + whitelist_mi = "Whitelist Addresses" if is_q1 else "Whitelist" + mag_mi = "Max Magnitude" + vel_mi = "Limit Velocity" + mi_2fa = "Web 2FA" + + time.sleep(.1) + m = cap_menu() + assert mag_mi in m + assert vel_mi in m + assert whitelist_mi in m + assert mi_2fa in m + + # setting above values here + if mag: + pick_menu_item(mag_mi) + enter_number(mag) + time.sleep(.1) + title, story = cap_story() + assert f"{mag} {'BTC' if int(mag) < 1000 else 'SATS'}" in story + press_select() + + time.sleep(.1) + assert settings_get("sssp")["pol"]["mag"] == mag + + if vel: + if not settings_get("sssp")["pol"].get("mag", None): + pick_menu_item(vel_mi) + title, story = cap_story() + assert 'Velocity limit requires' in story + assert 'starting value' in story + press_select() + else: + pick_menu_item(vel_mi) + + if vel == "Unlimited": + target = 0 + else: + target = int(vel.split()[0]) + + pick_menu_item(vel) # actually a full menu item + time.sleep(.3) + assert settings_get("sssp")["pol"]["vel"] == target + + if whitelist: + pick_menu_item(whitelist_mi) + time.sleep(.1) + m = cap_menu() + assert "(none yet)" in m + assert "Import from File" in m + if is_q1: + assert "Scan QR" in m + pick_menu_item("Scan QR") + for i, addr in enumerate(whitelist, start=1): + scan_a_qr(addr) + + for _ in range(10): + scr = cap_screen() + if (f"Got {i} so far" in scr) and ("ENTER to apply" in scr): + break + time.sleep(.2) + else: + assert False, "updating whitelist failed" + + press_select() + else: + assert "Scan QR" not in m + fname = "ccc_addrs.txt" + with open(microsd_path(fname), "w") as f: + for a in whitelist: + f.write(f"{a}\n") + + pick_menu_item("Import from File") + time.sleep(.1) + _, story = cap_story() + if "Press (1)" in story: + need_keypress("1") + pick_menu_item(fname) + + time.sleep(.1) + _, story = cap_story() + if len(whitelist) == 1: + assert "Added new address to whitelist" in story + else: + assert f"Added {len(whitelist)} new addresses to whitelist" in story + + for addr in whitelist: + assert addr in story + + # check menu correct + press_select() + time.sleep(.1) + m = cap_menu() + mi_addrs = [a for a in m if '⋯' in a] + for mia, addr in zip(mi_addrs, reversed(whitelist)): + _start, _end = mia.split('⋯') + assert addr.startswith(_start) + assert addr.endswith(_end) + + press_cancel() + + assert settings_get("sssp")["pol"]["addrs"] == whitelist + + if w2fa: + pick_menu_item(mi_2fa) + + press_cancel() # leave Edit Policy... (shared settings with CCC) + + # now rest of sssp specific settings + if word_check is not None: + pick_menu_item("Word Check") + time.sleep(.1) + title, story = cap_story() + assert "addition to special PIN" in story + assert "provide the first and last seed words" in story + if word_check: + assert "Enable?" in story + press_select() # confirm action + time.sleep(.1) + assert settings_get("sssp")["words"] + else: + assert "Disable?" in story + pol = settings_get("sssp") + if "words" in pol: + assert not pol["words"] + + if notes_and_pws is not None: + assert is_q1 + pick_menu_item("Allow Notes") + time.sleep(.1) + title, story = cap_story() + assert "Allow (read-only) access to secure notes and passwords?" in story + if notes_and_pws: + assert "Enable?" in story + press_select() # confirm action + time.sleep(.1) + assert settings_get("sssp")["notes"] + else: + assert "Disable?" in story + pol = settings_get("sssp") + if "notes" in pol: + assert not pol["notes"] + + if rel_keys is not None: + pick_menu_item("Related Keys") + time.sleep(.1) + title, story = cap_story() + assert "Allow access to BIP-39 passphrase wallets" in story + assert "and Seed Vault (read-only)" in story + if rel_keys: + assert "Enable?" in story + press_select() # confirm action + time.sleep(.1) + assert settings_get("sssp")["okeys"] + else: + assert "Disable?" in story + pol = settings_get("sssp") + if "okeys" in pol: + assert not pol["okeys"] + + yield doit + + # cleanup code -- all users of this fixture will get this code + + settings_remove("sssp") + sim_exec('from pincodes import pa;pa.hobbled_mode = False; from actions import goto_top_menu; goto_top_menu()') + + + +@pytest.fixture +def policy_sign(start_sign, end_sign, cap_story, get_last_violation): + def doit(wallet, psbt, violation=None): + start_sign(base64.b64decode(psbt)) + time.sleep(.1) + title, story = cap_story() + + if violation: + # assume SSSP cases + assert title == "Failure" + assert 'warning' not in story + assert "Spending Policy violation." in story + assert violation in get_last_violation() + return + + assert 'OK TO SEND?' == title + assert "warning" not in story + + signed = end_sign(accept=True) + po = BasicPSBT().parse(signed) + + tx_hex = None + if violation is None: + assert not get_last_violation() + assert len(po.inputs[0].part_sigs) or po.inputs[0].taproot_key_sig or len(po.inputs[0].taproot_script_sigs) + res = wallet.finalizepsbt(base64.b64encode(signed).decode()) + assert res["complete"] + tx_hex = res["hex"] + res = wallet.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wallet.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + + return signed, tx_hex + + return doit + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("mag_ok", [True, False]) +@pytest.mark.parametrize("mag", [1000000, 2]) +def test_magnitude(mag_ok, mag, setup_sssp, bitcoind, settings_set, pick_menu_item, + bitcoind_d_sim_watch, policy_sign, press_select, + reset_seed_words, settings_path): + + wo = bitcoind_d_sim_watch + + settings_set("chain", "XRT") + + if mag_ok: + # always try limit/border value + if mag is None: + to_send = 1 + else: + to_send = mag / 100000000 if mag > 1000 else mag + else: + if mag is None: + to_send = 1.1 + else: + to_send = ((mag / 100000000)+1) if mag > 1000 else (mag+0.001) + + setup_sssp("11-11", mag=mag) + + pick_menu_item("ACTIVATE") + press_select() + + addr = wo.getnewaddress() + bitcoind.supply_wallet.sendtoaddress(address=addr, amount=5.0) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + # create funded PSBT + psbt_resp = wo.walletcreatefundedpsbt( + [], [{bitcoind.supply_wallet.getnewaddress(): to_send}], 0, {"fee_rate": 2} + ) + psbt = psbt_resp.get("psbt") + + policy_sign(wo, psbt, violation=None if mag_ok else "magnitude") + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("whitelist_ok", [True, False]) +def test_whitelist(whitelist_ok, setup_sssp, bitcoind, settings_set, policy_sign, + bitcoind_d_sim_watch, pick_menu_item, press_select): + + wo = bitcoind_d_sim_watch + + settings_set("chain", "XRT") + + whitelist = [ + "bcrt1qqca9eefwz8tzn7rk6aumhwhapyf5vsrtrddxxp", + "bcrt1q7nck280nje50gzjja3gyguhp2ds6astu5ndhkj", + "bcrt1qhexpvdhwuerqq0h24j06g8y5eumjjdr28ng4vv", + "bcrt1q3ylr55pk7rl0rc06d8th7h25zmcuvvg8wt0yl3", + ] + + if whitelist_ok: + send_to = whitelist[0] + else: + send_to = bitcoind.supply_wallet.getnewaddress() + + setup_sssp("11-11", whitelist=whitelist) + pick_menu_item("ACTIVATE") + press_select() + + multi_addr = wo.getnewaddress() + bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=5.0) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + # create funded PSBT + psbt_resp = wo.walletcreatefundedpsbt( + [], [{send_to: 1}], 0, {"fee_rate": 2} + ) + psbt = psbt_resp.get("psbt") + policy_sign(wo, psbt, violation=None if whitelist_ok else "whitelist") + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("velocity_mi", ['6 blocks (hour)', '48 blocks (8h)']) +def test_velocity(velocity_mi, setup_sssp, bitcoind, settings_set, pick_menu_item, + policy_sign, settings_get, bitcoind_d_sim_watch, press_select): + + wo = bitcoind_d_sim_watch + wo.keypoolrefill(20) + settings_set("chain", "XRT") + + blocks = int(velocity_mi.split()[0]) + + setup_sssp("11-11", vel=velocity_mi) + pick_menu_item("ACTIVATE") + press_select() + + assert "block_h" not in settings_get("sssp")["pol"] + + multi_addr = wo.getnewaddress() + bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=49) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + # create funded PSBT, first tx + init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height + psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}], + init_block_height) # nLockTime set to current block height + psbt = psbt_resp.get("psbt") + po = BasicPSBT().parse(base64.b64decode(psbt)) + assert po.parsed_txn.nLockTime == init_block_height + policy_sign(wo, psbt) # success as this is first tx that sets block height from 0 + + assert settings_get("sssp")["pol"]["block_h"] == init_block_height + + # mine some, BUT not enough to satisfy velocity policy + # - check velocity is exactly right to block number vs. required gap + bitcoind.supply_wallet.generatetoaddress(blocks - 1, bitcoind.supply_wallet.getnewaddress()) + block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] + psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}], + block_height) + psbt = psbt_resp.get("psbt") + po = BasicPSBT().parse(base64.b64decode(psbt)) + assert po.parsed_txn.nLockTime == block_height + policy_sign(wo, psbt, violation="velocity") + + assert settings_get("sssp")["pol"]["block_h"] == init_block_height # still initial block height as above failed + + # mine the remaining one block to satisfy velocity policy + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] + psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}], + block_height) + psbt = psbt_resp.get("psbt") + po = BasicPSBT().parse(base64.b64decode(psbt)) + assert po.parsed_txn.nLockTime == block_height + policy_sign(wo, psbt) # success + + assert settings_get("sssp")["pol"]["block_h"] == block_height # updated block height + + # check txn re-sign fails (if velocity in effect) + policy_sign(wo, psbt, violation="rewound") + # check decreasing nLockTime + policy_sign( + wo, + wo.walletcreatefundedpsbt( + [], [{bitcoind.supply_wallet.getnewaddress(): 1}], block_height - 1 + )["psbt"], + violation="rewound" + ) + # check nLockTime disabled when velocity enabled - fail + policy_sign( + wo, + wo.walletcreatefundedpsbt( + [], [{bitcoind.supply_wallet.getnewaddress(): 1}], 0 + )["psbt"], + violation="no nLockTime" + ) + # unix timestamp + policy_sign( + wo, + wo.walletcreatefundedpsbt( + [], [{bitcoind.supply_wallet.getnewaddress(): 1}], 500000000 + )["psbt"], + violation="nLockTime not height" + ) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("active", [True, False]) +def test_warnings(setup_sssp, bitcoind, settings_set, policy_sign, pick_menu_item, + bitcoind_d_sim_watch, settings_get, press_select, active): + + wo = bitcoind_d_sim_watch + wo.keypoolrefill(20) + + settings_set("chain", "XRT") + + whitelist = ["bcrt1qlk39jrclgnawa42tvhu2n7se987qm96qg8v76e", + "2Mxp1Dy2MyR4w36J2VaZhrFugNNFgh6LC1j", + "mjR14oKxYzRg9RAZdpu3hrw8zXfFgGzLKm"] + + setup_sssp("11-11", mag=10000000, vel='6 blocks (hour)', whitelist=whitelist) + if active: + pick_menu_item("ACTIVATE") + press_select() + else: + # demonstration that policy is in effect from configuration + # user does not need to activate (or test-drive) and policy in effect already + pass + + bitcoind.supply_wallet.sendtoaddress(address=wo.getnewaddress(), amount=2) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + # create funded PSBT, first tx + # whitelist OK, velocity OK, & magnitude OK - but fee high + init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height + psbt_resp = wo.walletcreatefundedpsbt([], [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}], + init_block_height, {"fee_rate":48000}) + psbt = psbt_resp.get("psbt") + po = BasicPSBT().parse(base64.b64decode(psbt)) + assert po.parsed_txn.nLockTime == init_block_height + policy_sign(wo, psbt, violation="has warnings") + + # invalidate nLockTime with use of nSequence max values + utxos = wo.listunspent() + ins = [] + for i, utxo in enumerate(utxos): + # block height based RTL + inp = { + "txid": utxo["txid"], + "vout": utxo["vout"], + "sequence": 0xffffffff, + } + ins.append(inp) + + psbt_resp = wo.walletcreatefundedpsbt(ins, [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}], + 0, {"fee_rate":2, "replaceable": False}) # locktime needs to be zero, otherwise exception from core (contradicting parameters) + po = BasicPSBT().parse(base64.b64decode(psbt_resp.get("psbt"))) + assert po.parsed_txn.nLockTime == 0 + po.parsed_txn.nLockTime = init_block_height # add locktime + po.txn = po.parsed_txn.serialize_with_witness() + # num_warn=2, warn_list=["Bad Locktime"] + policy_sign(wo, po.as_b64_str(), violation="has warnings") + + # exotic sighash warning + settings_set("sighshchk", 1) # needed to only get warning instead of failure + psbt_resp = wo.walletcreatefundedpsbt([], [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}], + init_block_height, {"fee_rate":2, "replaceable": True}) + po = BasicPSBT().parse(base64.b64decode(psbt_resp.get("psbt"))) + for idx, i in enumerate(po.inputs): + i.sighash = 2 # NONE + + # num_warn=2, warn_list=["sighash NONE"] + policy_sign(wo, po.as_b64_str(), violation="has warnings") + + +def test_remove_sssp(setup_sssp, pick_menu_item, press_select, cap_story, cap_menu, settings_get): + setup_sssp("11-11", mag=10000000, vel='6 blocks (hour)') + + # check test drive + pick_menu_item("Test Drive") + time.sleep(.1) + _, story = cap_story() + assert "COLDCARD operation will look like with Spending Policy" in story + press_select() + + time.sleep(.1) + m = cap_menu() + assert "EXIT TEST DRIVE" in m + assert "Settings" not in m + + pick_menu_item("EXIT TEST DRIVE") + time.sleep(.1) + m = cap_menu() + assert "Edit Policy..." in m # back in policy settings + + pick_menu_item("Remove Policy") + time.sleep(.1) + _, story = cap_story() + assert "Bypass PIN will be removed" in story + assert "spending policy settings forgotten" in story + press_select() + + time.sleep(.1) + assert not settings_get("sssp") + + tps = settings_get("tp") + if tps: + assert "11-11" not in tps + + assert not settings_get("sssp") + + +def test_use_main_pin_as_unlock(setup_sssp, cap_story): + # not allowed + # simulator PIN + with pytest.raises(Exception): + setup_sssp("12-12") + + _, story = cap_story() + assert "already in use" in story + assert "PIN codes must be unique" in story + + +@pytest.mark.parametrize("hide", [True, False]) +def test_use_trick_pin_as_unlock(hide, setup_sssp, cap_story, new_trick_pin, pick_menu_item, + press_select, clear_all_tricks): + clear_all_tricks() + pin = "11-11" + new_trick_pin(pin, 'Wipe Seed', 'Wipe the seed and maybe do more') + pick_menu_item('Wipe & Reboot') + press_select() + press_select() + if hide: + pick_menu_item(f"↳{pin}") + pick_menu_item("Hide Trick") + press_select() # confirm + + with pytest.raises(Exception): + setup_sssp(pin) + + _, story = cap_story() + assert "already in use" in story + assert "PIN codes must be unique" in story + + + +@pytest.mark.parametrize("active_policy", [False, True]) +def test_deltamode_signature(active_policy, setup_sssp, bitcoind, settings_set, + start_sign, end_sign, pick_menu_item, press_select, + set_deltamode, bitcoind_d_sim_watch, settings_get): + + # verify that "deltamode" trick pins will work in SSSP mode + # - and that resulting signature is bad + # - device should **not** wipe itself + + dest = "bcrt1qlk39jrclgnawa42tvhu2n7se987qm96qg8v76e" + wo = bitcoind_d_sim_watch + wo.keypoolrefill(20) + + settings_set("chain", "XRT") + + if active_policy: + setup_sssp(f"{random.randint(0,99)}-11", mag=100) + pick_menu_item("ACTIVATE") + press_select() + + bitcoind.supply_wallet.sendtoaddress(address=wo.getnewaddress(), amount=2) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + + # create funded PSBT, first tx + # - within active policy. + init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height + psbt_resp = wo.walletcreatefundedpsbt([], [{dest: 0.06}], + init_block_height, {"fee_rate":2, "replaceable": False}) + psbt = psbt_resp.get("psbt") + + po = BasicPSBT().parse(base64.b64decode(psbt)) + assert po.parsed_txn.nLockTime == init_block_height + + start_sign(base64.b64decode(psbt), finalize=True) + signed = end_sign(accept=True, finalize=True) + + set_deltamode(True) + + start_sign(base64.b64decode(psbt), finalize=True) + signed2 = end_sign(accept=True, finalize=True) + + # check wrong signature happened + assert signed != signed2 + probs = wo.testmempoolaccept([signed2.hex()])[0] + try: + # old bitcoind + assert 'Signature must be zero' in probs['reject-reason'], probs + except AssertionError: + assert 'mandatory-script-verify-flag-failed' in probs['reject-reason'], probs + assert not probs['allowed'] + + # check right signature + no_probs = wo.testmempoolaccept([signed.hex()])[0] + assert no_probs['allowed'] + + +@pytest.mark.bitcoind +def test_sssp_enforce_tmp_seed(setup_sssp, bitcoind, settings_set, settings_get, press_select, + pick_menu_item, cap_menu, go_to_passphrase, enter_complex, + need_keypress, word_menu_entry, fake_txn, start_sign, dev, + cap_story): + tmp_words = "style car win bomb plug raccoon predict warm wrap flush usual seminar" + blocks = 6 # ~1 hour + settings_set("chain", "XRT") + setup_sssp("11-11", mag=2, vel='6 blocks (hour)', rel_keys=True) + assert "block_h" not in settings_get("sssp")["pol"] + pick_menu_item("ACTIVATE") + press_select() + time.sleep(.1) + m = cap_menu() + # check we are in hobbled mode & okeys is respected + assert "Passphrase" in m + assert "Settings" not in m + + # import word-based seed as tmp and check that sssp is enforced + pick_menu_item("Advanced/Tools") + pick_menu_item("Temporary Seed") + need_keypress("4") + pick_menu_item("Import Words") + pick_menu_item("12 Words") + word_menu_entry(tmp_words.split()) + press_select() + time.sleep(.1) + m = cap_menu() + assert "Passphrase" in m # word based + okeys + assert "Settings" not in m + + xpub = dev.send_recv(CCProtocolPacker.get_xpub("m"), timeout=None) + psbt = fake_txn(2,2, input_amount=200000000, master_xpub=xpub) + start_sign(psbt) + time.sleep(.1) + _, story = cap_story() + assert "Spending Policy violation" in story + press_select() + + # recurse deeper, to passphrase wallet, on top of word-based tmp seed + go_to_passphrase() + enter_complex("AAA", apply=True) + + press_select() + m = cap_menu() + assert "Passphrase" not in m # xprv based + assert "Settings" not in m # still in hobbled + + xpub = dev.send_recv(CCProtocolPacker.get_xpub("m"), timeout=None) + psbt = fake_txn(2, 2, input_amount=200000000, master_xpub=xpub) + start_sign(psbt) + time.sleep(.1) + _, story = cap_story() + assert "Spending Policy violation" in story + press_select() + time.sleep(.1) + + pick_menu_item("Restore Master") + press_select() + + time.sleep(.1) + m = cap_menu() + assert "Passphrase" in m + assert "Settings" not in m # still in hobbled + psbt = fake_txn(2, 2, input_amount=200000000) + start_sign(psbt) + time.sleep(.1) + _, story = cap_story() + assert "Spending Policy violation" in story + press_select() + +def test_sssp_notes_enable(only_q1, setup_sssp): + # just test menu item works + setup_sssp("11-11", mag=2, vel='6 blocks (hour)', notes_and_pws=True) + +def test_sssp_word_check(setup_sssp): + # just test menu item works + setup_sssp("11-11", mag=2, vel='6 blocks (hour)', word_check=True) + +@pytest.mark.parametrize("af", ["bech32", "bech32m"]) +def test_miniscript_enforce(af, settings_set, clear_miniscript, goto_home, get_cc_key, bitcoind, + offer_minsc_import, press_select, cap_menu, pick_menu_item, cap_story, + start_sign, end_sign, create_core_wallet, policy_sign, setup_sssp): + sequence = 10 + goto_home() + clear_miniscript() + + settings_set("chain", "XRT") + policy = "and_v(v:pk(@0/<0;1>/*),older(10))" + + if af == "bech32m": + tmplt = f"tr(tpubD6NzVbkrYhZ4XgXS51CV3bhoP5dJeQqPhEyhKPDXBgEs64VdSyAfku99gtDXQzY6HEXY5Dqdw8Qud1fYiyewDmYjKe9gGJeDx7x936ur4Ju/<0;1>/*,{policy})" + else: + tmplt = f"wsh({policy})" + + cc_key = get_cc_key("m/666h/1h/0h").replace('/<0;1>/*', '') + desc = tmplt.replace("@0", cc_key) + + wname = "single_k_mini" + + _, story = offer_minsc_import(json.dumps(dict(name=wname, desc=desc))) + assert "Create new miniscript wallet?" in story + # do some checks on policy --> helper function to replace keys with letters + press_select() + + wo = create_core_wallet(wname, af, "sd", True) + + whitelisted_addr = bitcoind.supply_wallet.getnewaddress() + setup_sssp("11-11", mag=10000000, vel='6 blocks (hour)', whitelist=[whitelisted_addr]) + pick_menu_item("ACTIVATE") + press_select() + + unspent = wo.listunspent() + assert len(unspent) == 1 + + # mines 10 blocks to release script lock (not related to SSSP) + bitcoind.supply_wallet.generatetoaddress(sequence, bitcoind.supply_wallet.getnewaddress()) + + inp = {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "sequence": sequence} + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{bitcoind.supply_wallet.getnewaddress(): 5}], # magnitude violation + wo.getblockchaininfo()["blocks"], + {"fee_rate": 3, "change_type": af}, + ) + psbt = psbt_resp.get("psbt") + + policy_sign(wo, psbt, violation="magnitude") + + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{bitcoind.supply_wallet.getnewaddress(): 0.09}], # whitelist violation + wo.getblockchaininfo()["blocks"], + {"fee_rate": 3, "change_type": af}, + ) + psbt = psbt_resp.get("psbt") + policy_sign(wo, psbt, violation="whitelist") + + psbt_resp = wo.walletcreatefundedpsbt( + [inp], + [{whitelisted_addr: 0.09}], + wo.getblockchaininfo()["blocks"], + {"fee_rate": 3, "change_type": af}, + ) + psbt = psbt_resp.get("psbt") + policy_sign(wo, psbt) # good - in accordance with policy + +# EOF diff --git a/testing/test_teleport.py b/testing/test_teleport.py new file mode 100644 index 000000000..c87b8b2d3 --- /dev/null +++ b/testing/test_teleport.py @@ -0,0 +1,991 @@ +# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Key Teleport (a Q-only feature) +# +# - you'll need v1.0.1 of bbqr library for this to work +# +import pytest, time, re, pdb, os, json, base64 +from mnemonic import Mnemonic +from bip32 import BIP32Node +from helpers import prandom, xfp2str, str2xfp, str_to_path +from bbqr import join_qrs +from charcodes import KEY_QR, KEY_NFC +from base64 import b32encode +from constants import * +from test_ephemeral import SEEDVAULT_TEST_DATA +from test_backup import make_big_notes +from ckcc.protocol import CCProtocolPacker +from test_hobble import set_hobble + +# All tests in this file are exclusively meant for Q +# +@pytest.fixture(autouse=True) +def THIS_FILE_requires_q1(is_q1, is_headless): + if not is_q1 or is_headless: + raise pytest.skip('Q1 only (not headless)') + +@pytest.fixture +def rx_start(grab_payload, goto_home, pick_menu_item): + def doit(**kws): + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('Key Teleport (start)') + + return grab_payload('R', **kws)[0:2] + + return doit + +@pytest.fixture +def main_do_over(unit_test, settings_get, settings_set): + # reset all contents, including master secret ... except ktrx + # - so you can test backup-restore onto blank unit + def doit(): + kp = settings_get('ktrx') + unit_test('devtest/clear_seed.py') + settings_set('ktrx', kp) + + return doit + +@pytest.fixture +def grab_payload(press_select, need_keypress, press_cancel, nfc_read_url, cap_story, nfc_block4rf, + cap_screen_qr, readback_bbqr): + + # started the process; capture pw/code and QR contents, verify NFC works + def doit(tt_code, allow_reuse=True, reset_pubkey=False): + expect_in_title = 'Receive' if tt_code == 'R' else 'Teleport Password' + + title, story = cap_story() + + if 'Reuse' in title and tt_code == 'R': + assert allow_reuse + assert 'press (R)' in story + + if reset_pubkey: + # make a new key anyway + need_keypress('r') + else: + press_select() + + time.sleep(.1) + title, story = cap_story() + + assert 'Teleport' in title + assert expect_in_title in title + + assert 'QR' in story + + code, = re.findall(' (\w{8}) = ', story) + assert len(code) == 8 + + nfc_raw = None + if KEY_NFC in story: + # test NFC case -- when enabled + need_keypress(KEY_NFC) + + # expect NFC animation + nfc_block4rf() + + url = nfc_read_url().replace('%24', '$') + + assert url.startswith('https://keyteleport.com/#') + + nfc_data = url.rsplit('#')[1] + assert nfc_data.startswith(f'B$2{tt_code}0100') + + filetype, nfc_raw = join_qrs([nfc_data]) # update your bbqr install if fails + assert filetype == tt_code + + need_keypress(KEY_QR) + + # will be multi-frame BBQr in case of PSBT, other cases usually one frame + filetype, qr_raw = readback_bbqr() + # this is un-split BBQR which didn't really happen, but useful + qr_data = f'B$2{filetype}0100' + b32encode(qr_raw).decode('ascii').rstrip('=') + + assert filetype == tt_code + + if nfc_raw: assert nfc_raw == qr_raw + + press_cancel() + press_cancel() + + return code, qr_data, qr_raw + + return doit + +@pytest.fixture +def rx_complete(press_select, need_keypress, press_cancel, cap_story, scan_a_qr, enter_complex, + cap_screen, goto_home, split_scan_bbqr): + # finish the teleport by doing QR and getting data + def doit(data, pw, expect_fail=False, expect_xfp=None): + goto_home() + need_keypress(KEY_QR) + time.sleep(.250) # required + + if isinstance(data, tuple): + bbrq_type, raw = data + split_scan_bbqr(raw, bbrq_type, max_version=26) + else: + assert len(data) < 2000 # USB protocol limit + scan_a_qr(data) + + if expect_fail: + time.sleep(.200) + return + + for _ in range(10): + scr = cap_screen() + if 'Teleport Password' in scr: break + time.sleep(.2) + else: + raise RuntimeError("Teleport Password not in screen") + + if expect_xfp: + assert xfp2str(expect_xfp) in scr + + enter_complex(pw) + time.sleep(.150) # required + + + return doit + +@pytest.fixture +def tx_start(press_select, need_keypress, press_cancel, goto_home, pick_menu_item, cap_story, + scan_a_qr, enter_complex, cap_screen): + + # start the Tx process, capturing password and leaving you are picker menu + def doit(rx_qr, rx_code, expect_fail=None, expect_wrong_code=False): + goto_home() + need_keypress(KEY_QR) + time.sleep(.250) # required + scan_a_qr(rx_qr) + + for _ in range(10): + scr = cap_screen() + if expect_fail and expect_fail in scr: + return + elif 'Teleport Password (number)' in scr: + break + time.sleep(.2) + else: + assert False, "Teleport Password not in screen" + + enter_complex(rx_code) + time.sleep(.150) # required + + + title, story = cap_story() + if expect_wrong_code: + # not a sure thing + if 'Incorrect Teleport Pass' in story: + return True + + assert title == 'Key Teleport: Send' + + assert 'Choose what to share' in story + assert 'WARNING' in story + press_select() + + return doit + +def test_rx_reuse(rx_start): + # check rx pubkey re-use logic + code, enc_pubkey = rx_start(allow_reuse=True, reset_pubkey=True) + assert code.isdigit() + code2, enc_pubkey2 = rx_start(allow_reuse=True, reset_pubkey=False) + assert code2 == code + assert enc_pubkey2 == enc_pubkey + + code3, pk3 = rx_start(allow_reuse=True, reset_pubkey=True) + assert code3 != code + +def test_tx_quick_note(rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, grab_payload, + rx_complete, cap_story, press_cancel, press_select): + # Send a quick-note + code, rx_pubkey = rx_start() + pw = tx_start(rx_pubkey, code) + + m = cap_menu() + assert 'Master Seed Words' in m + assert 'Quick Text Message' in m + # other contents require other features to be enabled + + msg = b32encode(prandom(10)).decode('ascii') + + pick_menu_item('Quick Text Message') + + enter_complex(msg) + + time.sleep(.150) # required + pw, data, _ = grab_payload('S') + assert len(pw) == 8 + + # now, send that back + rx_complete(data, pw) + + # should arrive in notes menu + m = cap_menu() + assert m[-1] == 'Import' + mi = [i for i in m if i.endswith(': Quick Note')] + assert mi + pick_menu_item(mi[-1]) # most recent test + + # view note + m = cap_menu() + assert m[0] == '"Quick Note"' + pick_menu_item(m[0]) + + _, body = cap_story() + assert body == msg + + # cleanup + press_cancel() + pick_menu_item('Delete') + press_select() + + +@pytest.mark.parametrize('testcase', [ 'weak', 'strong']) +def test_tx_master_send(testcase, rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, + grab_payload, rx_complete, cap_story, press_cancel, press_select, main_do_over): + # Send master secret, but doesn't really work since same as what we have + code, rx_pubkey = rx_start() + pw = tx_start(rx_pubkey, code) + + # other contents require other features to be enabled + pick_menu_item('Master Seed Words') + + title, body = cap_story() + + assert 'Are you SURE' in title + assert 'MASTER secret' in body + assert '24 words' in body + + press_select() + + time.sleep(.150) # required? + pw, data, _ = grab_payload('S') + + if testcase == 'strong': + # virginized + main_do_over() + + # now, send that back + rx_complete(data, pw) + + title, body = cap_story() + + if testcase == 'weak': + + assert title == 'FAILED' + assert 'Cannot use master seed as temp' in body + assert 'successfully tested' in body + + elif testcase == 'strong': + # real product would reboot; simulator just quietly goes back to top menu? + assert title == '' + m = cap_menu() + assert m[0] == 'Ready To Sign' + + press_cancel() + +@pytest.mark.parametrize('qty', [1, 3]) +def test_tx_notes(qty, rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, grab_payload, + rx_complete, cap_story, press_cancel, press_select, need_some_passwords, + need_some_notes, settings_set, settings_get): + # Send notes. + settings_set('notes', []) + need_some_notes() + notes = need_some_passwords() + + assert len(notes) >= qty + + code, rx_pubkey = rx_start() + pw = tx_start(rx_pubkey, code) + + # other contents require other features to be enabled + if qty == 1: + pick_menu_item('Single Note / Password') + pick_menu_item('1: ' + notes[0]["title"]) + else: + pick_menu_item('Export All Notes & Passwords') + + time.sleep(.150) # required? + pw, data, _ = grab_payload('S') + + # now, send that back + rx_complete(data, pw) + + # arrive in settings menu, on last item (last imported) + m = cap_menu() + assert m[-1] == 'Import' + + after = settings_get('notes', None) + + assert notes[0:qty] == after[-qty:] + + settings_set('notes', []) + press_cancel() + + +@pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA[0:2]) +def test_tx_seedvault(data, rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, + grab_payload, rx_complete, cap_story, press_cancel, press_select, settings_set, + settings_get, goto_home, need_keypress): + # Send seeds from vault + + xfp, entropy, mnemonic = data + + # build stashed encoded secrets + entropy_bytes = bytes.fromhex(entropy) + if mnemonic: + vlen = len(entropy_bytes) + assert vlen in [16, 24, 32] + marker = 0x80 | ((vlen // 8) - 2) + stored_secret = bytes([marker]) + entropy_bytes + else: + stored_secret = entropy_bytes + + pkg = [xfp, stored_secret.hex(), f"[{xfp}]", "from testing"] + + settings_set("seedvault", True) + settings_set("seeds", [pkg]) + + # get ready to send + code, rx_pubkey = rx_start(reset_pubkey=True) + pw = tx_start(rx_pubkey, code) + + pick_menu_item('From Seed Vault') + mi, = (i for i in cap_menu() if i.endswith(f"[{xfp}]")) + pick_menu_item(mi) + + time.sleep(.150) # required? + pw, data, _ = grab_payload('S') + + settings_set("seeds", []) + + rx_complete(data, pw) + + if settings_get("seedvault", False): + time.sleep(.1) + title, body = cap_story() + assert 'Press (1) to store temp' in body + assert 'to continue without saving' in body + need_keypress('1') + + time.sleep(.1) + title, body = cap_story() + assert xfp in body + assert 'Saved to Seed Vault' in body + + assert settings_get('seeds') == [pkg] + + goto_home() + pick_menu_item('Restore Master') + press_select() + + time.sleep(.1) + assert settings_get('xfp', -1) == simulator_fixed_xfp + +def test_rx_truncated(rx_start, tx_start): + # Truncate the RX Code + code, rx_pubkey = rx_start() + tx_start(rx_pubkey[:-3], code, expect_fail='Truncated KT RX') + + +def test_tx_wrong_pub(rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, grab_payload, + rx_complete, cap_story, press_cancel, press_select): + # simulate wrong numeric code only -- sender doesn't know + right_code, rx_pubkey = rx_start() + + for attempt in range(20): + code = '%08d' % attempt + failed = tx_start(rx_pubkey, code, expect_wrong_code=True) + + if failed: + # 50% odds (apx, maybe?) of wrong code being detected. + print(f'{code} => wasnt accepted') + continue + break + else: + raise pytest.fail('huh') + + # other contents require other features to be enabled + pick_menu_item('Master Seed Words') + time.sleep(.150) # required? + press_select() + + time.sleep(.150) # required? + pw, data, _ = grab_payload('S') + + # now, send that back + rx_complete(data, pw, expect_fail=True) + + time.sleep(.1) + title, body = cap_story() + + assert title == 'Teleport Fail' + assert 'password was wrong' in body + assert 'start again' in body + + press_cancel() + +@pytest.mark.unfinalized +@pytest.mark.parametrize('num_ins', [ 15 ]) +@pytest.mark.parametrize('M', [4]) +@pytest.mark.parametrize('hobbled', [True, False]) +def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, num_ins, dev, clear_miniscript, hobbled, + fake_ms_txn, try_sign, bitcoind, cap_story, need_keypress, + cap_menu, pick_menu_item, grab_payload, rx_complete, press_select, + ndef_parse_txn_psbt, press_nfc, nfc_read, settings_get, settings_set, + txid_from_export_prompt, sim_root_dir, set_hobble, readback_bbqr, + nfc_is_enabled, goto_home, restore_main_seed): + + # IMPORTANT: won't work if you start simulator with --ms flag. Use no args + num_outs = 4 + af = "p2wsh" + + clear_miniscript() + use_regtest() + + # create a wallet, with 3 bip39 pw's + keys, select_wallet = make_myself_wallet(M, do_import=True, addr_fmt=af) + N = len(keys) + assert M<=N + + if hobbled: + # we need to import before hobbled mode is enabled + for i in range(3): # 4th is simulator (ignore) + select_wallet(i) + + restore_main_seed(preserve_settings=True) + time.sleep(.1) + + set_hobble(True, {'okeys'}) + goto_home() + + psbt = fake_ms_txn(15, num_outs, M, keys, inp_addr_fmt=af, incl_xpubs=False, + outstyles=["p2sh-p2wsh", af, af, af], + change_outputs=list(range(1,num_outs))) + + with open(f'{sim_root_dir}/debug/myself-before.psbt', 'wb') as f: + f.write(psbt) + + cur_wallet = 0 + my_xfp = select_wallet(cur_wallet, no_import=hobbled) + + _, updated = try_sign(psbt, accept_ms_import=False, exit_export_loop=False) + with open(f'{sim_root_dir}/debug/myself-after-1.psbt', 'wb') as f: + f.write(updated) + assert updated != psbt + + title, body = cap_story() + assert title == "PSBT Signed" + assert '(T) to use Key Teleport to send PSBT to other co-signers' in body + + num_sigs_needed = M - 1 # we have already signed with first at this point + + while 1: + # expect: a menu of other signers to pick from + need_keypress('t') + time.sleep(.1) + + m = cap_menu() + assert len(m) == N + assert 'YOU' in [ln for ln in m if xfp2str(my_xfp) in ln][0] + + unsigned = [ln[1:9] for ln in m if (xfp2str(my_xfp) not in ln) and ('DONE' not in ln)] + assert unsigned + + # find another signer + for idx, (xfp, *_) in enumerate(keys): + if xfp2str(xfp) in unsigned: + break + else: + assert 0, 'missing unsigned' + + # check XFP changes + next_xfp = keys[idx][0] + assert next_xfp != my_xfp + last_xfp = my_xfp + + # pick other xfp to send to + nm, = [mi for mi in m if xfp2str(next_xfp) in mi] + pick_menu_item(nm) + + # grab the payload and pw + pw, data, qr_raw = grab_payload('E') + assert len(pw) == 8 + + nn = xfp2str(next_xfp) + with open(f'{sim_root_dir}/debug/next_qr_{nn}.txt', 'wt') as f: + f.write(f'{nn}\n\n{pw}\n\n{data}') + + time.sleep(.1) + title, story = cap_story() + assert title == 'Sent by Teleport' + # s, aux = ("", "is") if num_sigs_needed == 1 else ("s", "are") + # msg = "%d more signature%s %s still required." % (num_sigs_needed, s, aux) + # assert msg in story + + # switch personalities, and try to read that QR + new_xfp = select_wallet(idx, no_import=hobbled) + assert new_xfp == next_xfp + my_xfp = next_xfp + assert settings_get('xfp') == my_xfp + + # import and sign + rx_complete(('E', qr_raw), pw, expect_xfp=last_xfp) + + title, body = cap_story() + assert title == 'OK TO SEND?' + + press_select() + time.sleep(.25) + + title, body = cap_story() + if 'Finalized TX' in body: + break + + assert '(T) to use Key Teleport to send PSBT to other co-signers' in body + num_sigs_needed -= 1 + + txid = txid_from_export_prompt() + press_select() # exit QR + + if nfc_is_enabled(): + # share signed txn via low-level NFC + press_nfc() + time.sleep(.1) + contents = nfc_read() + + got_psbt, got_txn, _ = ndef_parse_txn_psbt(contents, txid, expect_finalized=True) + else: + # NFC disabled. use other means .. bbqr + need_keypress(KEY_QR) + tcode, contents = readback_bbqr() + got_txn = (tcode == 'T') + got_psbt = (tcode == 'P') + + assert not got_psbt + assert got_txn + + +def test_teleport_big_ms(clear_miniscript, fake_ms_txn, try_sign, cap_story, + need_keypress, cap_menu, pick_menu_item, grab_payload, rx_complete, + press_select, ndef_parse_txn_psbt, set_master_key, goto_home, press_nfc, + nfc_read, open_microsd, import_ms_wallet, press_cancel): + + # define lots of wallets and do teleport from SD disk + + clear_miniscript() + M, N = 2, 15 + for i in range(5): + keys = import_ms_wallet(M, N, name=f'ms{i}-test', unique=(i*73), accept=True, bip67=True) + + # just use last wallet + psbt = fake_ms_txn(1, 1, M, keys) + + fname = 'ms-example.psbt' + open_microsd(fname, 'wb').write(psbt) + + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('File Management') + pick_menu_item('Teleport Multisig/Miniscript PSBT') + + need_keypress('1') # top slot + + try: + pick_menu_item(fname) + except KeyError: + # maybe just one file that is suitable --> str8 to xfp picking + pass + + # on Co-signer list menu + m = cap_menu() + assert len(m) == N + + myself, = [i for i in m if 'YOU' in i] + pick_menu_item(myself) + + title, body = cap_story() + assert title == 'OK TO SEND?' + press_select() + + time.sleep(.25) + + # have 1 sigs now, need one more via teleport + title, body = cap_story() + assert '(T) to use Key Teleport to send PSBT to other co-signers' in body + need_keypress('t') + + # pick another one randomly + m = cap_menu() + assert len(m) == N + + target = m[-1] if 'YOU' not in m[0] else m[-2] + pick_menu_item(target) + target_xfp = str2xfp(target[1:9]) + + # capture QR+pw to go there + pw, data, qr_raw = grab_payload('E') + + # switch to that key, receive it + node, = [n for x,n,_ in keys if x == target_xfp] + set_master_key(node.hwif(as_private=True)) + + # copy over the one MS wallet this xfp was involved in + import_ms_wallet(M, N, name=f'www', keys=keys, accept=True, bip67=True) + + # import and sign + rx_complete(('E', qr_raw), pw, expect_xfp=simulator_fixed_xfp) + + title, body = cap_story() + assert title == 'OK TO SEND?' + + press_select() + time.sleep(.25) + + title, body = cap_story() + assert 'Finalized TX' in body + press_cancel() + + +@pytest.mark.manual +def test_teleport_real_ms(dev, fake_ms_txn, sim_root_dir): + # + # Do a 2-of-2 w/ USB-attached REAL Q and simulator + # - build ms wallet beforehand, both devices (QR); default air-gap settings + # - this makes fake txn, sents to (real) device via USB + # - do your signature, press (T) to teleport to next + # - observe BBQr, but press NFC and capture URL text via keyteleport.com + # - get that BBQr string into clipboard, and paste into simulator + # - observe working signature on sim side + # + # py.test test_teleport.py --dev --manual -k test_teleport_real_ms + # + M = N = 2 + + #p2wsh + deriv = "m/48h/1h/0h/2h" + + # simulator key + n = BIP32Node.from_hwif(simulator_fixed_xprv).subkey_for_path(deriv) + keys = [ (simulator_fixed_xfp, None, n) ] + + # add device + xfp = dev.master_fingerprint + xpk = dev.send_recv(CCProtocolPacker.get_xpub(deriv)) + node = BIP32Node.from_wallet_key(xpk) + keys.append((xfp, None, node)) + + def p2wsh_mapper(cosigner_idx): + # match the default paths created by CC in airgapped MS wallet creation. + return str_to_path(deriv) + + psbt = fake_ms_txn(3, 2, M, keys, fee=10000, outvals=None, inp_addr_fmt="p2wsh", + outstyles=['p2pkh'], change_outputs=[], + hack_change_out=False, input_amount=1E8, path_mapper=p2wsh_mapper) + + with open(f'{sim_root_dir}/debug/teleport_real_ms.psbt', 'wb') as f: + f.write(psbt) + + ll, sha = dev.upload_file(psbt) + dev.send_recv(CCProtocolPacker.sign_transaction(ll, sha)) + + print("Follow signing prompts on device, and then do teleport back " + "to Simulator via NFC => website => clipboard") + + +@pytest.mark.parametrize('testcase', [ 'weak', 'partial', 'strong']) +def test_send_backup(testcase, rx_start, tx_start, cap_menu, enter_complex, pick_menu_item, + grab_payload, rx_complete, cap_story, press_cancel, press_select, settings_get, + settings_set, restore_backup_unpacked, main_do_over, set_encoded_secret, + reset_seed_words, make_big_notes): + # Send complete backup file. + code, rx_pubkey = rx_start() + pw = tx_start(rx_pubkey, code) + + if testcase == 'strong': + notes = make_big_notes() + + # other contents require other features to be enabled + pick_menu_item('Full COLDCARD Backup') + + title, body = cap_story() + + assert 'Sending complete backup' in body + + press_select() + + time.sleep(.150) # required? + pw, data, qr_raw = grab_payload('S') + + if testcase == 'partial': + # be on a different master, so backup is restored into seed vault/tmp seed + kp = settings_get('ktrx') + set_encoded_secret(b'\x20' + prandom(32)) + settings_set('ktrx', kp) + + if testcase == 'strong': + # wipe everything; except we need the keypair + main_do_over() + + # now, send that back + rx_complete(('S', qr_raw), pw) + + title, body = cap_story() + + if testcase == 'weak': + assert title == 'FAILED' + assert 'Cannot use master seed as temp' in body + assert 'successfully tested' in body + press_cancel() + + elif testcase == 'partial': + # should be in a tmp seed now + assert title == '[0F056943]' + assert 'temporary master key is in effect' in body + + reset_seed_words() + + elif testcase == 'strong': + restore_backup_unpacked() + assert settings_get('notes') == notes + settings_set('notes', []) + + +@pytest.mark.bitcoind +@pytest.mark.parametrize("taproot", [True, False]) +@pytest.mark.parametrize("keys", [True, False, None]) +@pytest.mark.parametrize("policy", [ + "thresh(4,pk(@0),s:pk(@1),s:pk(@2),s:pk(@3),sln:older(12960))", +]) +def test_teleport_miniscript_sign(dev, taproot, policy, get_cc_key, bitcoind, use_regtest, + clear_miniscript, set_bip39_pw, press_select, pick_menu_item, + need_keypress, offer_minsc_import, load_export, reset_seed_words, + cap_story, cap_menu, grab_payload, sim_root_dir, rx_complete, + settings_set, try_sign, settings_get, press_cancel, keys, + cap_screen): + + reset_seed_words() + use_regtest() + clear_miniscript() + + # bitcoin core is PSBT provider + name = "msc_tele" + wo = bitcoind.create_wallet(name, disable_private_keys=True, blank=True) + + deriv = "86h/1h/%dh" if taproot else "48h/1h/%dh/2h" + if keys is True: + # actually just 2 signers - both with 2 keys with different subderivation (change based) + deriv = deriv % 0 + keys = [get_cc_key(deriv)] + keys.append(get_cc_key(deriv, subderiv="/<2;3>/*")) + + seed = Mnemonic.to_seed(simulator_fixed_words, passphrase="11") + master = BIP32Node.from_master_secret(seed, netcode="XTN") + master_xfp = master.fingerprint().hex() + account_key = master.subkey_for_path(deriv) + keys.append(f"[{master_xfp}/{deriv}]{account_key.hwif()}/<0;1>/*") + keys.append(f"[{master_xfp}/{deriv}]{account_key.hwif()}/<2;3>/*") + + signers = [keys[0], keys[2]] + elif keys is False: + # 3 signers, 1 signer has two keys with different account derivation index + keys = [get_cc_key(deriv % 0)] + for i in range(1, 3): + seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=str(i)+str(i)) + master = BIP32Node.from_master_secret(seed, netcode="XTN") + master_xfp = master.fingerprint().hex() + dd = deriv % 0 + account_key = master.subkey_for_path(dd) + keys.append(f"[{master_xfp}/{dd}]{account_key.hwif()}/<0;1>/*") + if i == 1: + dd = deriv % 1 + account_key = master.subkey_for_path(dd) + keys.append(f"[{master_xfp}/{dd}]{account_key.hwif()}/<0;1>/*") + + signers = [keys[0], keys[1], keys[3]] + + else: + # all keys different + # default simulator key always on index 0 + deriv = deriv % 0 + keys = [get_cc_key(deriv)] + + # 4 more keys for other co-signers + for i in range(1, 4): + seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=str(i)*2) + master = BIP32Node.from_master_secret(seed, netcode="XTN") + master_xfp = master.fingerprint().hex() + account_key = master.subkey_for_path(deriv) + keys.append(f"[{master_xfp}/{deriv}]{account_key.hwif()}/<0;1>/*") + + signers = keys + + for i, key in enumerate(keys): + policy = policy.replace(f"@{i}", key) + + if taproot: + from test_miniscript import ranged_unspendable_internal_key + desc = f"tr(%s,%s)" % (ranged_unspendable_internal_key(), policy) + else: + desc = f"wsh(%s)" % policy + + title, story = offer_minsc_import(json.dumps({"name": name, "desc": desc})) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + + pick_menu_item("Settings") + pick_menu_item("Multisig/Miniscript") + pick_menu_item(name) + pick_menu_item("Descriptors") + pick_menu_item("Bitcoin Core") + text = load_export("sd", label="Bitcoin Core Miniscript", is_json=False) + text = text.replace("importdescriptors ", "").strip() + # remove junk + r1 = text.find("[") + r2 = text.find("]", -1, 0) + text = text[r1: r2] + core_desc_object = json.loads(text) + res = wo.importdescriptors(core_desc_object) + for obj in res: + assert obj["success"] + + af = "bech32m" if taproot else "bech32" + addr = wo.getnewaddress("", af) + assert bitcoind.supply_wallet.sendtoaddress(addr, 20) + bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress()) + psbt_resp = wo.walletcreatefundedpsbt( + [], + [{bitcoind.supply_wallet.getnewaddress(): 2.5}], + 0, + {"fee_rate": 2, "change_type": af}, + ) + psbt = psbt_resp.get("psbt") + + _, psbt = try_sign(base64.b64decode(psbt), accept=True, exit_export_loop=False) + title, body = cap_story() + assert title == "PSBT Signed" + assert '(T) to use Key Teleport to send PSBT to other co-signers' in body + + my_xfp = xfp2str(simulator_fixed_xfp) + for i in range(len(signers)): + # expect: a menu of other signers to pick from + if i == (len(signers) - 1): + done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) + resp_len, chk = done + psbt_out = dev.download_file(resp_len, chk) + res = wo.finalizepsbt(base64.b64encode(psbt_out).decode()) + assert res["complete"] + tx_hex = res["hex"] + res = wo.testmempoolaccept([tx_hex]) + assert res[0]["allowed"] + res = wo.sendrawtransaction(tx_hex) + assert len(res) == 64 # tx id + press_cancel() + # done + break + + need_keypress('t') + time.sleep(.1) + + m = cap_menu() + assert len(m) == len(signers) + assert 'YOU' in [ln for ln in m if my_xfp in ln][0] + + unsigned = [ln[1:9] for ln in m if (my_xfp not in ln) and ('DONE' not in ln)] + assert unsigned + + # make sure we have checkmark after YOU because self has signed + assert "YOU \x14\x00" in cap_screen() + + # find another signer + for idx, k in enumerate(signers): + if k[1:9].upper() in unsigned: + next_xfp = k[1:9].upper() + break + else: + assert 0, 'missing unsigned' + + # check XFP changes + assert next_xfp != my_xfp + last_xfp = my_xfp + + # pick other xfp to send to + nm, = [mi for mi in m if next_xfp in mi] + pick_menu_item(nm) + + # grab the payload and pw + pw, data, qr_raw = grab_payload('E') + assert len(pw) == 8 + + with open(f'{sim_root_dir}/debug/next_qr_{next_xfp}.txt', 'wt') as f: + f.write(f'{next_xfp}\n\n{pw}\n\n{data}') + + time.sleep(.1) + title, story = cap_story() + assert title == 'Sent by Teleport' + + # try to enter the menu again & make sure "No more signers?" is not shown + need_keypress('t') + time.sleep(.1) + m = cap_menu() + assert len(m) == len(signers) + assert 'YOU' in [ln for ln in m if my_xfp in ln][0] + press_cancel() + + # switch personalities, and try to read that QR + new_xfp = set_bip39_pw(str(idx) + str(idx)) + use_regtest() + clear_miniscript() + dev.start_encryption() # + assert xfp2str(new_xfp) == next_xfp + assert settings_get('xfp') == new_xfp + my_xfp = xfp2str(new_xfp) + + # need miniscript wallet + title, story = offer_minsc_import(json.dumps({"name": name, "desc": desc})) + assert "Create new miniscript wallet?" in story + press_select() + time.sleep(.2) + + # import and sign + rx_complete(('E', qr_raw), pw, expect_xfp=str2xfp(last_xfp)) + + title, body = cap_story() + assert title == 'OK TO SEND?' + + press_select() + time.sleep(.25) + + title, body = cap_story() + + assert '(T) to use Key Teleport to send PSBT to other co-signers' in body + + +def test_hobble_limited(set_hobble, scan_a_qr, cap_menu, cap_screen, pick_menu_item, grab_payload, + rx_complete, cap_story, press_cancel, press_select, settings_get, + settings_set, restore_backup_unpacked, main_do_over, set_encoded_secret, + reset_seed_words, make_big_notes): + # verify: in hobbled mode, KT is blocked for everything except multisig cases + + set_hobble(True) + + from bbqr import split_qrs + + _, parts = split_qrs(b's'*33, 'R') + rx_complete(parts[0], '12345678', expect_fail=True) + time.sleep(.1) + last = cap_screen().split('\n')[-1] + assert last == 'KT Blocked' + + _, parts = split_qrs(b's'*33, 'S') + rx_complete(parts[0], 'abcdefgh', expect_fail=True) + time.sleep(.1) + last = cap_screen().split('\n')[-1] + assert last == 'KT Blocked' + +# EOF diff --git a/testing/test_unit.py b/testing/test_unit.py index 4fca5baf1..44c84da49 100644 --- a/testing/test_unit.py +++ b/testing/test_unit.py @@ -4,7 +4,9 @@ # import pytest, os, shutil -from helpers import B2A +from helpers import B2A, taptweak +from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH +from charcodes import * def test_remote_exec(sim_exec): @@ -71,9 +73,13 @@ def test_public(sim_execfile): assert sk.hwif() == result elif result[0] in '1mn': assert result == sk.address() - elif result[0:3] in { 'bc1', 'tb1' }: + elif result[0:4] in {'bc1q', 'tb1q'}: h20 = sk.hash160() assert result == bech32.encode(result[0:2], 0, h20) + elif result[0:4] in {'bc1p', 'tb1p'}: + from bech32 import encode + tweked_xonly = taptweak(sk.sec()[1:]) + assert result == encode(result[:2], 1, tweked_xonly) elif result[0] in '23': h20 = hash160(b'\x00\x14' + sk.hash160()) assert h20 == decode_base58_checksum(result)[1:] @@ -131,10 +137,6 @@ def test_slip132(unit_test): # slip132 ?pub stuff unit_test('devtest/unit_slip132.py') -def test_multisig(unit_test): - # scripts/multisig unit tests - unit_test('devtest/unit_multisig.py') - def test_decoding(unit_test): # utils.py Hex/Base64 streaming decoders unit_test('devtest/unit_decoding.py') @@ -274,28 +276,55 @@ def test_is_dir(microsd_path, sim_exec): assert rv == "False" shutil.rmtree(microsd_path("my_dir")) -@pytest.mark.parametrize('txt, x_line2', [ - ('Disk, press \x0e to share via NFC, \x11 to share', '\x11 to share'), +DOUBLE_W = ['⋯', '✔', '✓', '→', '←', '↦', '◉', '◯', '◌', '※', '—', '\x0e', '\x11', '\t', '\x0f', '\x12', '\x13', '\x14', '\x16', '\x17'] + +@pytest.mark.parametrize('txt, target', [ + ('Disk, press \x0e to share via NFC, \x11 to share', ['Disk, press \x0e to share via NFC,', '\x11 to share']), + ((KEY_NFC * 17)+".", [KEY_NFC * 16, KEY_NFC + '.']), + ((KEY_NFC * 17)+(17*KEY_QR), [KEY_NFC * 16, KEY_NFC +(KEY_QR * 15), 2 * KEY_QR]), + ((KEY_NFC * 17)+" "+(17*KEY_QR), [KEY_NFC * 16, KEY_NFC, KEY_QR * 16, KEY_QR]), + ((KEY_NFC * 16)+".", [(KEY_NFC * 16)+'.']), + (f"Use {KEY_NFC}, or {KEY_F1}, {KEY_F2}, {KEY_F3}, or or or {KEY_F4}", [f"Use {KEY_NFC}, or {KEY_F1}, {KEY_F2}, {KEY_F3}, or or or", f"{KEY_F4}"]), + ("".join(DOUBLE_W), ["".join(DOUBLE_W[:16]), "".join(DOUBLE_W[16:])]), + ("".join(6*DOUBLE_W), ["".join(6*DOUBLE_W)[i:i + 16] for i in range(0, len(6*DOUBLE_W), 16)]), ]) -def test_word_wrap(txt, x_line2, sim_exec, only_q1, width=34): - # one tricky double-wide char word-wrapping case .. but add others - assert '\n' not in txt - +def test_word_wrap_double_wide(only_q1, txt, target, sim_exec): + width = 33 # check shared/ux.py CHAR_PER_W cmd = f'from utils import word_wrap; RV.write("\\n".join(word_wrap({txt!r}, {width})))' got = sim_exec(cmd) assert 'Traceback' not in got lines = got.split('\n') - assert width*2//3 <= len(lines[0]) <= width - assert lines[1] == x_line2 + assert lines == target + +@pytest.mark.parametrize('txt, target, width', [ + ((17*'a')+". ccc", [(17*'a')+".", "ccc"], 17), + ((17*'a')+".", [(17*'a')+"."], 17), + ((17*'-')+". ccc", [(17*'-')+".", "ccc"], 17), + ((34 * 'A'), [33 * "A", "A"], 33), + ((33 * 'A')+". ccc", [(33 * "A")+".", "ccc"], 33), + ('Coldcard is ready to sign spending transactions!', ['Coldcard is ready to sign', 'spending transactions!'], 33), + ('Coldcard is ready to sign spending transactions!', ['Coldcard is ready', 'to sign spending', 'transactions!'], 17), + ((16*"B")+ " AAAA", [16*"B", "AAAA"], 17), + ((16*"B")+ " AAAA", [(16*"B")+" ", "AAAA"], 17), + ((17*"B")+ " AAAA", [17*"B", "AAAA"], 17), + ((17*"B")+ " AAAA", [17*"B", " AAAA"], 17), + ("(recommended), or by typing numbers.", ["(recommended), or", "by typing numbers."], 17), + ("difficult to recover your funds.", ["difficult to", "recover your", "funds."], 17), + ("USB Serial Number:", ["USB Serial Number:"], 17), + ("USB Serial Number;", ["USB Serial Number;"], 17), + ("USB Serial Number/", ["USB Serial", "Number/"], 17), +]) +def test_word_wrap(txt, target, width, sim_exec): + cmd = f'from utils import word_wrap; RV.write("\\n".join(word_wrap({txt!r}, {width})))' + got = sim_exec(cmd) + assert 'Traceback' not in got - want_words = [i.strip() for i in txt.split()] - got_words = [i.strip() for i in got.split()] + lines = got.split('\n') - assert want_words == got_words + assert lines == target -from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH @pytest.mark.parametrize('addr,net,fmt', [ ( 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', 'BTC', AF_P2WPKH ), @@ -356,4 +385,17 @@ def test_aes_compatibility(sim_execfile): res = sim_execfile('devtest/unit_aes_compat.py') assert res == "" + +def test_script(sim_execfile): + res = sim_execfile('devtest/unit_script.py') + assert res == "" + +def test_bip388(sim_execfile): + res = sim_execfile('devtest/unit_bip388.py') + assert res == "" + +def test_bip32(sim_execfile): + res = sim_execfile('devtest/unit_bip32.py') + assert res == "" + # EOF diff --git a/testing/test_upgrades.py b/testing/test_upgrades.py index b52353f4a..b89eac03f 100644 --- a/testing/test_upgrades.py +++ b/testing/test_upgrades.py @@ -14,7 +14,7 @@ def parse_hdr(hdr): return Header(**dict(zip(FWH_PY_VALUES.split(), struct.unpack(FWH_PY_FORMAT, hdr)))) -@pytest.fixture() +@pytest.fixture def upload_file(dev): def doit(data, pkt_len=2048): for pos in range(0, len(data), pkt_len): @@ -25,13 +25,13 @@ def doit(data, pkt_len=2048): return doit @pytest.fixture -def make_firmware(): - def doit(hw_compat, fname='../stm32/firmware-signed.bin', outname='tmp-firmware.bin'): +def make_firmware(src_root_dir): + def doit(hw_compat, fname=f'{src_root_dir}/stm32/firmware-signed.bin', outname='tmp-firmware.bin'): # os.system(f'signit sign 3.0.99 --keydir ../stm32/keys -r {fname} -o {outname} --hw-compat=0x{hw_compat:02x}') p = subprocess.run( [ 'signit', 'sign', '3.0.99', - '--keydir', '../stm32/keys', + '--keydir', f'{src_root_dir}/stm32/keys', '-r', f'{fname}', '-o', f'{outname}', f'--hw-compat={hw_compat}' @@ -50,7 +50,7 @@ def doit(hw_compat, fname='../stm32/firmware-signed.bin', outname='tmp-firmware. return doit @pytest.fixture -def upgrade_by_sd(open_microsd, cap_story, pick_menu_item, goto_home, press_select, microsd_path, sim_exec): +def upgrade_by_sd(open_microsd, cap_story, pick_menu_item, goto_home, press_select, microsd_path, sim_exec, src_root_dir): # send a firmware file over the microSD card @@ -64,7 +64,7 @@ def doit(data, expect_fail=None): # create DFU file (wrapper) open(f'{fname}.bin', 'wb').write(data) dfu = microsd_path('tmp-firmware.dfu') - cmd = f'../external/micropython/tools/dfu.py -b 0x08008000:{fname}.bin {dfu}' + cmd = f'{src_root_dir}/external/micropython/tools/dfu.py -b 0x08008000:{fname}.bin {dfu}' print(cmd) os.system(cmd) @@ -91,28 +91,19 @@ def doit(data, expect_fail=None): @pytest.mark.parametrize('mode', ['compat', 'incompat']) @pytest.mark.parametrize('transport', ['sd', 'usb']) -def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, upload_file, sim_eval, upgrade_by_sd): - - # manually: run this test on all Mark1 thru 3 simulators - hw_label = eval(sim_eval('version.hw_label')) - assert hw_label[0:2] in ['mk', 'q1'] - try: - mkn = int(hw_label[2]) - except IndexError: - mkn = "q1" # q1 - - print(f"Simulator is {hw_label}") +def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, upload_file, + upgrade_by_sd, press_cancel, is_q1): if mode == 'compat': - data = make_firmware(mkn) + data = make_firmware("q1" if is_q1 else 4) elif mode == 'incompat': - with pytest.raises(RuntimeError) as err: - if mkn == "q1": - mkn = 4 - - make_firmware(mkn-1) - assert "too big for our USB upgrades" in str(err) - return + if is_q1: + data = make_firmware(4) + else: + with pytest.raises(RuntimeError) as err: + make_firmware(3) + assert "too big for our USB upgrades" in str(err) + return hdr = data[FW_HEADER_OFFSET:FW_HEADER_OFFSET+FW_HEADER_SIZE] @@ -139,6 +130,7 @@ def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, _, story = cap_story() assert "Install this new firmware?" in story + press_cancel() # check data was uploaded verbatim (VERY SLOW) # for pos in range(0, cooked.firmware_length + 128, 128): # to_eval = f'from sflash import SF;SF.array[{pos}:{pos+128}]' @@ -148,6 +140,6 @@ def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, # assert a == hdr, f"wrong @ {pos}" # else: # assert a == data[pos:pos+128], repr(pos) - + # EOF diff --git a/testing/test_ux.py b/testing/test_ux.py index 6f534c6bb..dc85336a0 100644 --- a/testing/test_ux.py +++ b/testing/test_ux.py @@ -2,7 +2,7 @@ # import pytest, time, os, re, hashlib, shutil from helpers import xfp2str, prandom -from charcodes import KEY_DOWN, KEY_QR, KEY_NFC, KEY_DELETE +from charcodes import KEY_DOWN, KEY_QR, KEY_NFC, KEY_DELETE, KEY_CANCEL from constants import AF_CLASSIC, simulator_fixed_words, simulator_fixed_xfp from mnemonic import Mnemonic from bip32 import BIP32Node @@ -10,7 +10,7 @@ @pytest.fixture def enable_hw_ux(pick_menu_item, cap_story, press_select, goto_home): - def doit(way): + def doit(way, disable=False): pick_menu_item("Settings") pick_menu_item("Hardware On/Off") if way == "vdisk": @@ -18,13 +18,19 @@ def doit(way): _, story = cap_story() if "emulate a virtual disk drive" in story: press_select() - pick_menu_item("Enable") + if disable: + pick_menu_item("Default Off") + else: + pick_menu_item("Enable") elif way == "nfc": pick_menu_item("NFC Sharing") _, story = cap_story() if "(Near Field Communications)" in story: press_select() - pick_menu_item("Enable NFC") + if disable: + pick_menu_item("Default Off") + else: + pick_menu_item("Enable NFC") else: raise RuntimeError("TODO") @@ -39,8 +45,9 @@ def test_get_secrets(get_secrets, master_xpub): assert v['xpub'] == master_xpub def test_home_menu(cap_menu, cap_story, cap_screen, need_keypress, reset_seed_words, - press_select, press_cancel, press_down, is_q1): + press_select, press_cancel, press_down, is_q1, microsd_wipe): reset_seed_words() + microsd_wipe() # get to top, force a redraw press_cancel() press_cancel() @@ -80,7 +87,7 @@ def test_home_menu(cap_menu, cap_story, cap_screen, need_keypress, reset_seed_wo need_keypress('0') press_select() - time.sleep(.01) # required + time.sleep(.1) # required title, body = cap_story() assert title == 'NO-TITLE' @@ -95,33 +102,34 @@ def doit(words, has_checksum=True, q_accept=True): # easier for us on Q, but have to anticipate the autocomplete for n, w in enumerate(words, start=1): do_keypresses(w[0:2]) - time.sleep(0.50) + time.sleep(0.05) if 'Next key' in cap_screen(): do_keypresses(w[2]) - time.sleep(.1) + time.sleep(.01) if 'Next key' in cap_screen(): if len(w) > 3: do_keypresses(w[3]) else: do_keypresses(KEY_DOWN) - time.sleep(.1) + time.sleep(.01) pat = rf'{n}:\s?{w}' for x in range(10): if re.search(pat, cap_screen()): break - time.sleep(0.20) + time.sleep(0.02) else: raise RuntimeError('timeout') if len(words) == 23: do_keypresses(KEY_DOWN) - time.sleep(.3) + time.sleep(.03) cap_scr = cap_screen() while 'Next key' in cap_scr: target = cap_scr.split("\n")[-1].replace("Next key: ", "") + # picks first choice!? do_keypresses(target[0]) - time.sleep(.3) + time.sleep(.03) cap_scr = cap_screen() else: cap_scr = cap_screen() @@ -338,12 +346,13 @@ def test_import_from_dice(count, nwords, goto_home, pick_menu_item, cap_story, n time.sleep(0.1) title, body = cap_story() - assert f'Record these {nwords}' in body - - assert f'{KEY_QR if is_q1 else "(1)"} to view as QR Code' in body + target = f'Record these {nwords}' if is_q1: + assert target in title words = [i[:4].upper() for i in seed_story_to_words(body)] else: + assert target in body + assert "(1) to view as QR Code" in body words = [i[4:4+4].upper() for i in re.findall(r'[ 0-9][0-9]: \w*', body)] if not is_headless: @@ -389,8 +398,12 @@ def test_new_wallet(nwords, goto_home, pick_menu_item, cap_story, expect_ftux, pick_menu_item(f'{nwords} Words') title, body = cap_story() - assert title == 'NO-TITLE' - assert f'Record these {nwords} secret words!' in body + target = f'Record these {nwords} secret words!' + if is_q1: + assert target in title + else: + assert title == 'NO-TITLE' + assert target in body if is_q1: words = seed_story_to_words(body) @@ -584,10 +597,11 @@ def test_show_seed(mode, b39_word, goto_home, pick_menu_item, cap_story, need_ke time.sleep(0.01) title, body = cap_story() - assert title == 'NO-TITLE' + if not is_q1: + assert title == 'NO-TITLE' if mode == 'words': - assert '24' in body + assert '24' in (title if is_q1 else body) lines = body.split('\n') if is_q1: @@ -597,10 +611,10 @@ def test_show_seed(mode, b39_word, goto_home, pick_menu_item, cap_story, need_ke if b39_word: if is_q1: - assert lines[11] == 'BIP-39 Passphrase:' - assert "*" in lines[12] - assert "Seed+Passphrase" in lines[14] - ek = lines[15] + assert lines[9] == 'BIP-39 Passphrase:' + assert "*" in lines[10] + assert "Seed+Passphrase" in lines[12] + ek = lines[13] else: assert lines[26] == 'BIP-39 Passphrase:' assert "*" in lines[27] @@ -702,11 +716,14 @@ def test_destroy_seed(goto_home, pick_menu_item, cap_story, press_select, def test_menu_wrapping(goto_home, pick_menu_item, cap_story, cap_menu, press_select, press_up, press_down, press_cancel, - is_q1): + is_q1, settings_remove): + settings_remove("wa") # disable goto_home() # first try that infinite scroll is turned off # home - for i in range(10): # settings on 5th in home (10 is way past that) + assert len(cap_menu()) < 10 + + for i in range(10): press_down() # sitting at Logout @@ -717,7 +734,7 @@ def test_menu_wrapping(goto_home, pick_menu_item, cap_story, cap_menu, press_select() pick_menu_item("Menu Wrapping") press_select() - pick_menu_item("Enable") + pick_menu_item("Always Wrap") time.sleep(1) press_cancel() # back to home menu press_cancel() # at Ready To Sign @@ -728,7 +745,7 @@ def test_menu_wrapping(goto_home, pick_menu_item, cap_story, cap_menu, press_select() pick_menu_item("Menu Wrapping") - pick_menu_item("Default Off") + pick_menu_item("Default") time.sleep(1) press_cancel() # back in home menu press_cancel() # at Ready To Sign @@ -802,7 +819,6 @@ def test_sign_file_from_list_files(f_len, goto_home, cap_story, pick_menu_item, verify_detached_signature_file([fname], signame, "sd", AF_CLASSIC) time.sleep(0.1) _, story = cap_story() - assert "(4) to sign file digest and export detached signature" not in story assert "(6) to delete" in story @@ -812,8 +828,71 @@ def test_sign_file_from_list_files(f_len, goto_home, cap_story, pick_menu_item, assert "List Files" in menu +def test_rename_from_list_files(goto_home, cap_story, pick_menu_item, need_keypress, is_q1, + microsd_path, press_select, cap_screen, enter_complex): + def clear(fname): + for i in range(len(fname)): + if not is_q1 and not i: + # Mk4 different menu entry UX + continue + need_keypress(KEY_DELETE if is_q1 else "x") + time.sleep(0.01) + + fname = "file_to_rename.pdf" + fpath = microsd_path(fname) + contents = os.urandom(64) + digest = hashlib.sha256(contents).digest().hex() + with open(fpath, "wb") as f: + f.write(contents) + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item('File Management') + pick_menu_item('List Files') + time.sleep(0.1) + pick_menu_item(fname) + time.sleep(0.1) + _, story = cap_story() + assert f"SHA256({fname})" in story + assert digest in story + assert "Press (1) to rename file" in story + need_keypress("1") + time.sleep(0.1) + if is_q1: + scr = cap_screen() + assert fname in scr + + clear(fname) + + bad_fnames = ["renamed file.txt", "/sd/renamed_file.txt", "renamed\\file.txt"] + for bad in bad_fnames: + enter_complex(bad, b39pass=False) + time.sleep(.1) + title, story = cap_story() + assert title == "Failure" + assert "Failed to rename the file" in story + assert "illegal char" in story + press_select() + time.sleep(.1) + need_keypress("1") # rename again + time.sleep(.1) + clear(fname) + if not is_q1: + need_keypress("1") # toggle case back to upper (enter complex expect to start in that state) + + new_fname = "renamed_file.txt" + enter_complex(new_fname, b39pass=False) + time.sleep(.1) + _, story = cap_story() + assert f"SHA256({new_fname})" in story + assert digest in story + assert not os.path.exists(fpath) + assert os.path.exists(microsd_path(new_fname)) + + def test_bip39_pw_signing_xfp_ux(pick_menu_item, press_select, cap_story, enter_complex, - reset_seed_words, cap_menu, go_to_passphrase): + reset_seed_words, cap_menu, go_to_passphrase, microsd_wipe): + microsd_wipe() # need to wipe all PSBT on SD card so we do not proceed to signing go_to_passphrase() enter_complex("21coinkite21", apply=True) time.sleep(0.3) @@ -821,6 +900,7 @@ def test_bip39_pw_signing_xfp_ux(pick_menu_item, press_select, cap_story, enter_ assert title == "[0C9DC99D]" assert 'Above is the master key fingerprint of the new wallet' in story press_select() # confirm passphrase + time.sleep(0.1) m = cap_menu() assert m[0] == "[0C9DC99D]" pick_menu_item("Ready To Sign") @@ -949,43 +1029,172 @@ def test_custom_pushtx_url(goto_home, pick_menu_item, press_select, enter_comple assert settings_get('ptxurl', None) is None -@pytest.mark.parametrize("fname,mode,ftype", [ - ("ccbk-start.json", "r", "J"), - ("ckcc-backup.txt", "r", "U"), - ("devils-txn.txn", "rb", "T"), - ("example-change.psbt", "rb", "P"), - ("sim_conso5.psbt", "rb", "P"), # binary psbt - ("payjoin.psbt", "r", "U"), # base64 string in file - ("worked-unsigned.psbt", "rb", "U"), # hex string psbt - ("coldcard-export.json", "rb", "J"), - ("coldcard-export.sig", "r", "U"), +@pytest.mark.parametrize("fname,ftype", [ + ("ccbk-start.json", "J"), + ("ckcc-backup.txt", "U"), + ("devils-txn.txn", "T"), + ("example-change.psbt", "P"), + ("sim_conso5.psbt", "P"), # binary psbt + ("payjoin.psbt", "U"), # base64 string in file + ("worked-unsigned.psbt", "U"), # hex string psbt + ("coldcard-export.json", "J"), + ("coldcard-export.sig", "U"), ]) -def test_qr_share_files(fname, mode, ftype, readback_bbqr, need_keypress, - goto_home, pick_menu_item, is_q1, cap_menu): +def test_bbqr_share_files(fname, ftype, readback_bbqr, need_keypress, src_root_dir, + goto_home, pick_menu_item, is_q1, cap_menu, sim_root_dir): goto_home() if not is_q1: pick_menu_item("Advanced/Tools") pick_menu_item("File Management") - assert "QR File Share" not in cap_menu() + assert "BBQr File Share" not in cap_menu() return - fpath = "data/" + fname - shutil.copy2(fpath, '../unix/work/MicroSD') + fpath = f"{src_root_dir}/testing/data/" + fname + shutil.copy2(fpath, f'{sim_root_dir}/MicroSD') pick_menu_item("Advanced/Tools") pick_menu_item("File Management") - pick_menu_item("QR File Share") + pick_menu_item("BBQr File Share") time.sleep(.1) pick_menu_item(fname) file_type, rb = readback_bbqr() assert file_type == ftype - with open(fpath, mode) as f: + with open(fpath, "rb") as f: res = f.read() - if fname.endswith(".txn"): - res = bytes.fromhex(res.decode()) - assert res == rb - os.remove('../unix/work/MicroSD/' + fname) + os.remove(f'{sim_root_dir}/MicroSD/' + fname) + +@pytest.mark.parametrize("fname", [ + "ccbk-start.json", + "devils-txn.txn", + "payjoin.psbt", # base64 string in file +]) +def test_qr_share_files(fname, pick_menu_item, goto_home, is_q1, cap_menu, cap_screen_qr, + src_root_dir, sim_root_dir): + goto_home() + if not is_q1: + pick_menu_item("Advanced/Tools") + pick_menu_item("File Management") + assert "QR File Share" not in cap_menu() + return + + fpath = f"{src_root_dir}/testing/data/" + fname + shutil.copy2(fpath, f'{sim_root_dir}/MicroSD') + pick_menu_item("Advanced/Tools") + pick_menu_item("File Management") + pick_menu_item("QR File Share") + time.sleep(.1) + pick_menu_item(fname) + qr = cap_screen_qr() + with open(fpath, "r") as f: + res = f.read() + + assert res == qr.decode() + os.remove(f'{sim_root_dir}/MicroSD/' + fname) + +@pytest.mark.parametrize("word,cs_word", [ + # few combos with all words with length 8 + their longest possible checksum word + ("acoustic", "decrease"), + ("electric", "witness"), + ("umbrella", "convince"), + ("universe", "hamster"), +]) +def test_q1_24_8char_words(set_seed_words, is_q1, goto_home, pick_menu_item, press_select, + cap_story, cap_screen, word, cs_word): + # /issues/965 + # vectors calculated with `coldcard-mpy`: + # + # w8 = [w for w in bip39.wordlist_en if len(w) >= 8] + # for w in w8: + # wl = ([w]*23) + # ds = list(bip39.a2b_words_guess(wl)) + # print(w, max(ds, key=len)) + if not is_q1: + raise pytest.skip("only Q") + + goto_home() + # longest words in wordlist_en have 8 chars + words = ([word] * 23) + [cs_word] + set_seed_words(" ".join(words)) + + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("Seed Functions") + pick_menu_item('View Seed Words') + time.sleep(.01) + press_select() # skip warning + time.sleep(0.01) + + title, body = cap_story() + assert '24' in title + scr = cap_screen().split("\n") + assert "Seed words (24)" in scr[0] + assert scr[1] == "" + # 8 rows + assert len(scr[2:]) == 8 + + x = 1 + y = 9 + z = 17 + for row in scr[2:]: + # each row contains 3 colons (aka 3 words) + srow = [r for r in row.split(" ") if r] # filter empty strings + assert len(srow) == 3 # three columns + + # 8 words for each column + (tx, w0), (ty, w1), (tz, w2) = [pr.split(":") for pr in srow] + assert x == int(tx) and y == int(ty) and z == int(tz) + x += 1 + y += 1 + z += 1 + + if int(tz) == 24: + # last line with checksum word + assert w2 == cs_word + assert w0 == w1 == word + else: + assert w0 == w1 == w2 == word + + +def test_file_picker_suffixes(pick_menu_item, goto_home, cap_story, microsd_wipe, press_select, + microsd_path): + # make sure no .txt, .7z & .pdf files are not on the SD card + microsd_wipe() + # create files that must not be recognized, because they're missing the dot + for fn in ["backup7z", "backuptxt", "template:pdf"]: + with open(microsd_path(fn), "w") as f: + f.write("dummy") + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("I Am Developer.") + pick_menu_item("Restore Bkup") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: .7z OR .txt" in story + press_select() + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Paper Wallets") + press_select() + pick_menu_item("Don't make PDF") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: .pdf" in story + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("File Management") + pick_menu_item("Sign Text File") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: .txt OR .json" in story + microsd_wipe() @pytest.mark.onetime @@ -994,7 +1203,7 @@ def test_dump_menutree(sim_execfile): sim_execfile('devtest/menu_dump.py') if 0: - # show what the final word can be (debug only) + # show what the final word can be (debug only) Mk4 only def test_23_words(goto_home, pick_menu_item, cap_story, need_keypress, unit_test, cap_menu, word_menu_entry, get_secrets, reset_seed_words, cap_screen_qr, qr_quality_check): unit_test('devtest/clear_seed.py') diff --git a/testing/test_vdisk.py b/testing/test_vdisk.py index 44ef91252..c33161c07 100644 --- a/testing/test_vdisk.py +++ b/testing/test_vdisk.py @@ -29,14 +29,16 @@ def test_vd_basics(dev, virtdisk_path, is_simulator): assert os.path.isfile(virtdisk_path(f'ident/ckcc-{sn}.txt')) @pytest.fixture -def try_sign_virtdisk(press_select, virtdisk_path, cap_story, virtdisk_wipe, press_cancel): +def try_sign_virtdisk(press_select, virtdisk_path, cap_story, virtdisk_wipe, press_cancel, + pick_menu_item, goto_home, sim_root_dir): # like "try_sign" but use Virtual Disk to send/receive PSBT/results # - on real dev, need user to manually say yes ... alot # - on simulator, start with "--eject" arg so no SDCard emulated - def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, complete=False, encoding='binary'): + def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, + encoding='binary'): assert not accept_ms_import, 'no support' assert accept, 'no support' @@ -48,7 +50,8 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, filename = 'memory' else: filename = f_or_data - ip = open(f_or_data, 'rb').read() + with open(f_or_data, 'rb') as f: + ip = f.read() if ip[0:10] == b'70736274ff': ip = a2b_hex(ip.strip()) assert ip[0:5] == b'psbt\xff' @@ -65,12 +68,16 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, virtdisk_wipe() xfn = virtdisk_path('testcase.psbt') - open(xfn, 'wb').write(ip) + with open(xfn, 'wb') as f: + f.write(ip) - press_select() # ready to sign (hopefully) + goto_home() + pick_menu_item("Ready To Sign") # CC scans drive, reads PSBT, verifies... time.sleep(1) + title, story = cap_story() + assert "OK TO SEND" in title # approve siging txn if accept: @@ -78,35 +85,35 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, else: press_cancel() - if accept == False: + if accept is False: time.sleep(0.050) # look for "Aborting..." ?? return ip, None, None # wait for it to finish signing + time.sleep(.1) title, story = cap_story() - if "OK TO SEND" in title or "PSBT Signed" in title: - press_select() - result_fn = xfn.replace('.psbt', '-*.psbt') - result_txn = xfn.replace('.psbt', '.txn') + split_story = story.split("\n\n") + result_fn = split_story[1] + result_txn = None + result_txid = None + if expect_finalize: + result_txn = split_story[3] + result_txid = split_story[4].split("\n")[-1] got_psbt = None got_txn = None txid, got_txid = None, None + for i in range(15): - try: - got_txn = open(result_txn, 'rb').read() - except FileNotFoundError as e: - print(e) - pass - - lst = glob.glob(result_fn) - if lst: - assert len(lst) == 1, "multi files: " + ', '.join(lst) - result_fn = lst[0] - got_psbt = open(result_fn, 'rb').read() + if result_txn: + with open(virtdisk_path(result_txn), 'rb') as f: + got_txn = f.read() + + with open(virtdisk_path(result_fn), 'rb') as f: + got_psbt = f.read() # for delete-psbt mode for ff in glob.glob(virtdisk_path('*.txn')): @@ -115,7 +122,9 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, got_txid = re.findall(r'[0-9a-f]{64}', ff)[0] except IndexError: got_txid = None - got_txn = a2b_hex(open(ff, 'rt').read().strip()) + + with open(ff, 'rt') as f: + got_txn = a2b_hex(f.read().strip()) if got_txn or got_psbt: break @@ -130,11 +139,11 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, txid = got_txid if got_txid: - assert got_txn - assert got_txid == txid assert expect_finalize - open("debug/vd-result.txn", 'wb').write(got_txid) - + assert got_txn + assert got_txid == txid == result_txid + with open(f"{sim_root_dir}/debug/vd-result.txn", 'wb') as f: + f.write(got_txid) # check output encoding matches input (for PSBT only) if got_psbt: @@ -160,7 +169,8 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, if got_psbt: assert got_psbt[0:5] == b'psbt\xff' - open("debug/vd-result.psbt", 'wb').write(got_psbt) + with open(f"{sim_root_dir}/debug/vd-result.psbt", 'wb') as f: + f.write(got_psbt) from psbt import BasicPSBT was = BasicPSBT().parse(ip) @@ -168,16 +178,18 @@ def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, assert was.txn == now.txn assert was != now - return ip, (got_psbt or got_txn), txid + return ip, (got_txn or got_psbt), txid return doit @pytest.mark.unfinalized # iff partial=1 +@pytest.mark.reexport @pytest.mark.parametrize('encoding', ['binary', 'hex', 'base64']) @pytest.mark.parametrize('num_outs', [1,2]) @pytest.mark.parametrize('partial', [1, 0]) -def test_virtdisk_signing(encoding, num_outs, partial, try_sign_virtdisk, fake_txn, dev, sd_cards_eject): +def test_virtdisk_signing(encoding, num_outs, partial, try_sign_virtdisk, fake_txn, dev, + sd_cards_eject, signing_artifacts_reexport): xp = dev.master_xpub sd_cards_eject() @@ -188,10 +200,18 @@ def hack(psbt): pp = psbt.inputs[0].bip32_paths[pk] psbt.inputs[0].bip32_paths[pk] = b'what' + pp[4:] - psbt = fake_txn(2, num_outs, xp, segwit_in=True, psbt_hacker=hack) + psbt = fake_txn(2, num_outs, xp, addr_fmt="p2wpkh", psbt_hacker=hack) _, txn, txid = try_sign_virtdisk(psbt, expect_finalize=not partial, encoding=encoding) + sd_cards_eject(slot_a=0) + _psbt, _txn = signing_artifacts_reexport("vdisk", tx_final=not partial, txid=txid, + encoding=encoding) + if partial: + assert _psbt == txn + else: + assert _txn == txn + if 0: @pytest.mark.parametrize('num_outs', [ 1, 20, 250]) def test_virtdisk_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story, only_mk4): @@ -237,11 +257,11 @@ def test_macos_detection(): # not a portable test... at all. import platform, subprocess, plistlib - if not platform.platform().startswith('macOS-11'): + if not platform.platform().startswith('macOS-'): raise pytest.xfail("requires MacOS") if not os.path.isdir('/Volumes/COLDCARD'): - raise pytest.xfail("needs COLDCARD mounted in usual spot") + raise pytest.xfail("needs COLDCARD connected & mounted") cmd = ['diskutil', 'info', '-plist', '/Volumes/COLDCARD'] pl = subprocess.check_output(cmd) @@ -260,7 +280,7 @@ def test_macos_detection(): def test_import_prv_virtdisk(testnet, pick_menu_item, cap_story, need_keypress, unit_test, cap_menu, get_secrets, multiple_runs, reset_seed_words, virtdisk_path, virtdisk_wipe, - settings_set, press_select): + settings_set, press_select, enable_hw_ux): # copied from test_ux as we need vdisk enabled and card ejected if testnet: netcode = "XTN" @@ -271,6 +291,8 @@ def test_import_prv_virtdisk(testnet, pick_menu_item, cap_story, need_keypress, unit_test('devtest/clear_seed.py') + enable_hw_ux("vdisk") + fname = 'test-%d.txt' % os.getpid() path = virtdisk_path(fname) diff --git a/testing/txn.py b/testing/txn.py index d1f2bd643..dad6e4e3f 100644 --- a/testing/txn.py +++ b/testing/txn.py @@ -2,32 +2,43 @@ # # Creating fake transactions. Not simple. # -import pytest, struct +import pytest, struct, os from ckcc_protocol.protocol import MAX_TXN_LEN from psbt import BasicPSBT, BasicPSBTInput, BasicPSBTOutput from io import BytesIO -from helpers import fake_dest_addr, make_change_addr, hash160 +from helpers import fake_dest_addr, make_change_addr, hash160, taptweak, str_to_path from base58 import decode_base58 from bip32 import BIP32Node -from constants import ADDR_STYLES, simulator_fixed_tprv +from constants import simulator_fixed_tprv from serialize import uint256_from_str from ctransaction import CTransaction, COutPoint, CTxIn, CTxOut -@pytest.fixture() +@pytest.fixture def fake_txn(dev, pytestconfig): # make various size txn's ... completely fake and pointless values # - but has UTXO's to match needs # - input total = num_inputs * 1BTC - def doit(num_ins, num_outs, master_xpub=None, subpath="0/%d", fee=10000, - invals=None, outvals=None, segwit_in=False, wrapped=False, - outstyles=['p2pkh'], psbt_hacker=None, change_outputs=[], - capture_scripts=None, add_xpub=None, op_return=None, - psbt_v2=None, input_amount=1E8): + def doit(inputs, outputs, master_xpub=None, psbt_hacker=None, add_xpub=None, psbt_v2=None, + fee=200, addr_fmt="p2wpkh", input_amount=100_000_000, capture_scripts=None, + force_full_tx_utxo=False, supply_num_ins=1, supply_num_outs=1): # input_amount in sats psbt = BasicPSBT() + # support old argument types + if isinstance(inputs, int): + num_ins = inputs + inputs = range(num_ins) + else: + num_ins = len(inputs) + + if isinstance(outputs, int): + num_outs = outputs + outputs = range(num_outs) + else: + num_outs = len(outputs) + if psbt_v2 is None: # anything passed directly to this function overrides # pytest flag --psbt2 - only care about pytest flag @@ -45,23 +56,75 @@ def doit(num_ins, num_outs, master_xpub=None, subpath="0/%d", fee=10000, master_xpub = master_xpub or dev.master_xpub or simulator_fixed_tprv # we have a key; use it to provide "plausible" value inputs - mk = BIP32Node.from_wallet_key(master_xpub) - xfp = mk.fingerprint() + my_mk = BIP32Node.from_wallet_key(master_xpub) + my_xfp = my_mk.fingerprint() + + foreign_mk = BIP32Node.from_master_secret(os.urandom(32)) + foreign_xfp = foreign_mk.fingerprint() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] - for i in range(num_ins): + inp_total = 0 + added_mine = False + added_foreign = False + for i, inp in enumerate(inputs): + sp = f"0/{i}" + af = addr_fmt + ia = input_amount + is_mine = True + try: + if inp[0] is not None: + af = inp[0] + if inp[1] is not None: + sp = inp[1] + if inp[2] is not None: + ia = inp[2] + is_mine = inp[3] + except: pass + # make a fake txn to supply each of the inputs - # - each input is 1BTC + # - each input is 1BTC if not specified otherwise + inp_total += ia + + # will this be my input that I cna sign + if is_mine: + mk = my_mk + mfp = my_xfp + added_mine = True + else: + mk = foreign_mk + mfp = foreign_xfp + added_foreign = True # addr where the fake money will be stored. - subkey = mk.subkey_for_path(subpath % i) + int_path = str_to_path(sp) + subkey = mk.subkey_for_path(sp) sec = subkey.sec() assert len(sec) == 33, "expect compressed" - assert subpath[0:2] == '0/' - psbt.inputs[i].bip32_paths[sec] = xfp + struct.pack(' go to the Derive Entropy menu inside settings, also loads XPRV from BIP - `--secret 01abababab...` => directly set contents of SE secret, see SecretStash.encode() - `--eject` => pretend no (simulated) SD Card is inserted -- `--eff` => (mk4) wipe setttings at startup, use simulator defaults +- `--eff` => wipe setttings at startup, use simulator defaults, save nothing. - `--seq 1234yx34` => after start, enter those keypresses to get you to some submenu - `--seq 2ENTER` => (Q) press 2 then ENTER, does QR at startup - `--bootup-movie` => begin a movie on startup, to capture boot sequence - `--scan` => (Q) use attached serial port connected to a QR scanner module (not simulation) - `--battery` => (Q) assume the USB cable is NOT connected (ie. on battery power) - `--early-usb` => start simulated USB interface even before user is login (useful for login testing) +- `--segregate` => scroll down to `Running simulators in parallel` section +- `--bricked` => simulate a system w/ bricked SE1: no more pin tries, etc. +- `--fails N` => simulate N wrong PIN attempts before login, where (1 <= N <= 13) See `variant/sim_settings.py` for the details of settings-related options. @@ -78,7 +81,7 @@ See `variant/sim_settings.py` for the details of settings-related options. ## Requirements -- uses good olde `xterm` for console input and output +- uses good old `xterm` for console input and output - this directory has additional `requirements.txt` (a superset of other requirements of the project) - run "brew install sdl2" before/after doing python requirements - run "make setup" then "make" @@ -99,4 +102,39 @@ See `variant/sim_settings.py` for the details of settings-related options. - linux supported (only tested on debian based Ubuntu 20.04), please check main README.md - Windows can work under WSL but is not supported by our team. Follow instructions on - +# Running simulators in parallel + +Normally, when simulator is spwned with `./simulator.py --eff --q1` (or similar) +the default socket file is produced (`/tmp/ckcc-simulator.sock`) for simulator to be able to emulate various types of comms. +Each time new simulator is spwned (while som older still running) the socket file gets claimed by the most recently opened simulator. +You can continue to use older simulator manually, but it is no longer possible to communicate via the socket file. +Besides shared socket file, all simulators share some working directories (`work` & previously `testing/debug`, currently `work/debug`). + +To enable full parallel operation on multiple simulators use `--segregate` simulator flag that: +* creates unique simulator socket file `/tmp/ckcc-simulator-.sock` for every simulator spwned with the flag +* creates separate simulator work directory in `/tmp/cc-simulators/` (every simulator has its own debug dir in work dir) + +Spawn two simulators: + +```shell +./simulator.py --eff --segregate # Mk4 +./simulator.py --eff --segregate --q1 # Q +``` + +Two directories were created inside `/tmp/cc-simulators` and two socket files in `/tmp` with same PID in names as directory names created. +To operate above simulators new `--socket`/`-c` flag needs to be used with client: `ckcc -c /tmp/ckcc-simulator-35156.sock ...` + +```shell +ckcc -c /tmp/ckcc-simulator-35156.sock addr -s +ckcc -c /tmp/ckcc-simulator-35291.sock addr -s +``` + +Simulator socket path is dumped to STDOUT after simulator is started: +```shell +Coldcard Simulator: Commands (over simulated window): + - Control-Q to quit + - ^Z to snapshot screen. + - ^S/^E to start/end movie recording + - ^N to capture NFC data (tap it) + - socket: /tmp/ckcc-simulator-35291.sock +``` diff --git a/unix/linux_addr.patch b/unix/linux_addr.patch deleted file mode 100644 index c7174eb5a..000000000 --- a/unix/linux_addr.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/unix/variant/pyb.py b/unix/variant/pyb.py -index d22bb1b..fe8e7ca 100644 ---- a/unix/variant/pyb.py -+++ b/unix/variant/pyb.py -@@ -36,10 +36,10 @@ class USB_HID: - import usocket as socket - self.pipe = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - # If on linux, try commenting the following line -- addr = bytes([len(self.fn)+2, socket.AF_UNIX] + list(self.fn)) -+ # addr = bytes([len(self.fn)+2, socket.AF_UNIX] + list(self.fn)) - # If on linux, try uncommenting the following two lines -- #import struct -- #addr = struct.pack('H108s', socket.AF_UNIX, self.fn) -+ import struct -+ addr = struct.pack('H108s', socket.AF_UNIX, self.fn) - while 1: - try: - self.pipe.bind(addr) diff --git a/unix/sim_boot.py b/unix/sim_boot.py index b39b6128e..2a28e6c08 100644 --- a/unix/sim_boot.py +++ b/unix/sim_boot.py @@ -6,6 +6,11 @@ import machine, pyb, sys +socket_path = sys.argv.pop() # last arg must be a socket path - remove +assert ("ckcc-simulator" in socket_path) and (".sock" in socket_path) +pyb.SOCKET_FILE_PATH = socket_path +print("socket:", pyb.SOCKET_FILE_PATH) + if '--metal' in sys.argv: # next in argv will be two open file descriptors to use for serial I/O to a real Coldcard import bare_metal @@ -24,11 +29,16 @@ if '--eff' in sys.argv: # ignore files ondisk from previous runs, and also dont write any - nvstore.SettingsObject.load = lambda *a:None - nvstore.SettingsObject.save = lambda *a:None - # limitation: pre-login values arent stored even during operation + # - but do track settings during this run + NVSTORE_FAKE = {bytes(32): dict(sim_defaults)} # prelogin values + + def _monkey_load(self, *a): + self.current = dict(NVSTORE_FAKE.get(self.nvram_key, False) or sim_defaults) + def _monkey_save(self, *a): + NVSTORE_FAKE[self.nvram_key] = dict(self.current) - #glob.settings.current = dict(sim_defaults) + nvstore.SettingsObject.load = _monkey_load + nvstore.SettingsObject.save = _monkey_save if '--early-usb' in sys.argv: from usb import enable_usb diff --git a/unix/simulator.py b/unix/simulator.py index 016bb3072..af7e94fa0 100755 --- a/unix/simulator.py +++ b/unix/simulator.py @@ -14,12 +14,10 @@ # Limitations: # - USB light not fully implemented, because happens at irq level on real product # -import os, sys, signal, time, pdb, tempfile, struct, zlib -import subprocess, asyncio +import os, sys, signal, time, pdb, tempfile, struct, zlib, subprocess, shutil from dataclasses import dataclass import sdl2.ext -import PIL -from PIL import Image, ImageSequence, ImageOps +from PIL import Image, ImageOps from select import select import fcntl from bare import BareMetal @@ -31,6 +29,7 @@ current_led_state = 0x0 + def activate_file(filename): # see if sys.platform == "win32": @@ -745,6 +744,14 @@ def handle_q1_key_events(event, numpad_tx, data_tx): def start(): is_q1 = ('--q1' in sys.argv) + segregate = ("--segregate" in sys.argv) + pid = os.getpid() + # for compatibility with old clients + # UNIX_SOCKET_PATH is always used if not segregate + socket_path = UNIX_SOCKET_PATH + if segregate: + socket_path = '/tmp/ckcc-simulator-%d.sock' % pid + if "--headless" in sys.argv: sys.argv.remove("--headless") is_headless = True @@ -760,6 +767,7 @@ def start(): - ^S/^E to start/end movie recording - ^N to capture NFC data (tap it)''' ) + print(" - socket: %s" % socket_path) if is_q1: print('''\ Q1 specials: @@ -818,10 +826,10 @@ def start(): # manage unix socket cleanup for client def sock_cleanup(): import os - fp = UNIX_SOCKET_PATH + fp = socket_path if os.path.exists(fp): os.remove(fp) - sock_cleanup() + import atexit atexit.register(sock_cleanup) @@ -849,11 +857,30 @@ def sock_cleanup(): scan_args = [ '--scan', str(port.fileno()) ] sys.argv.remove('--scan') - os.chdir('./work') - cc_cmd = ['../coldcard-mpy', - '-X', 'heapsize=9m', - '-i', '../sim_boot.py'] + [str(i) for i in pass_fds] \ - + metal_args + scan_args + sys.argv[1:] + # unix + cwd = os.getcwd() + # abs paths + cc_mpy = os.path.join(cwd, "coldcard-mpy") + sim_boot = os.path.join(cwd, "sim_boot.py") + + if segregate: + os.makedirs("/tmp/cc-simulators", exist_ok=True) + os.chdir("/tmp/cc-simulators") + # our new work /tmp/cc-simulators/ + os.mkdir(str(pid)) + os.chdir(str(pid)) + os.mkdir("MicroSD") + os.mkdir("settings") + os.mkdir("VirtDisk") + os.mkdir("debug") + # needed for VirtDisk test + shutil.copy(os.path.join(cwd, "work", "VirtDisk", "README.md"), + os.path.join(os.getcwd(), "VirtDisk", "README.md")) + else: + os.chdir('./work') + + cc_cmd = [cc_mpy, '-X', 'heapsize=9m', '-i', sim_boot] + [str(i) for i in pass_fds] \ + + metal_args + scan_args + sys.argv[1:] + [socket_path] if is_headless: pass_fds.remove("-1") diff --git a/unix/variant/ckcc.py b/unix/variant/ckcc.py index e32cb0b4a..d634a3c95 100644 --- a/unix/variant/ckcc.py +++ b/unix/variant/ckcc.py @@ -98,6 +98,9 @@ def gate(method, buf_io, arg2): if method == 5: # are we a brick? No. + if '--bricked' in sys.argv: + # if SE1 has pairing secret rotated; wont be able to do much + return 1 return 0 if method == 6: @@ -198,10 +201,9 @@ def is_simulator(): def is_debug_build(): return True - def get_sim_root_dirs(): # return a single path and list of files to pretend to find there - import ffilib, os + import ffilib libc = ffilib.libc() b = bytearray(500) diff --git a/unix/variant/pyb.py b/unix/variant/pyb.py index 4c06ad67e..b0b6b2867 100644 --- a/unix/variant/pyb.py +++ b/unix/variant/pyb.py @@ -2,9 +2,10 @@ # import utime as time import uerrno as errno -import sys +import usocket as socket +import sys, struct, os -from machine import Pin +SOCKET_FILE_PATH = None class USB_VCP: @staticmethod @@ -28,22 +29,16 @@ def usb_mode(nm=UNSET, **kws): return _umode class USB_HID: - fn = b'/tmp/ckcc-simulator.sock' - def __init__(self): self.pipe = None self.last_from = None self._open() def _open(self): - import sys - import usocket as socket + + assert SOCKET_FILE_PATH # has to be set in sim_boot.py by caller self.pipe = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - # If on linux, try commenting the following line - addr = bytes([len(self.fn)+2, socket.AF_UNIX] + list(self.fn)) - # If on linux, try uncommenting the following two lines - #import struct - #addr = struct.pack('H108s', socket.AF_UNIX, self.fn) + addr = struct.pack('H108s', socket.AF_UNIX, SOCKET_FILE_PATH.encode()) while 1: try: self.pipe.bind(addr) @@ -51,8 +46,7 @@ def _open(self): except OSError as exc: if exc.args[0] == errno.EADDRINUSE: # handle restart after first run - import os - os.remove(self.fn) + os.remove(SOCKET_FILE_PATH) continue def recv(self, buf, timeout=0): diff --git a/unix/variant/sim_quickstart.py b/unix/variant/sim_quickstart.py index 476d8ab98..414598397 100644 --- a/unix/variant/sim_quickstart.py +++ b/unix/variant/sim_quickstart.py @@ -155,6 +155,7 @@ # keep at end of file: extra enter to confirm something from above numpad.inject('y') + # not best place for this import hsm hsm.POLICY_FNAME = hsm.POLICY_FNAME.replace('/flash/', '') diff --git a/unix/variant/sim_se2.py b/unix/variant/sim_se2.py index a03e2b045..450ba94b1 100644 --- a/unix/variant/sim_se2.py +++ b/unix/variant/sim_se2.py @@ -15,30 +15,34 @@ def __init__(self): self.wallet = None self.load() - if not self.state: - # reconstruct based on user-space understanding of SE2 content - # - can't work with duress wallet cases here (no data) - # - mostly here so sim_settings works w/ non-empty defaults - print("SIM SE2: found no state, trying to reconstruct") - from glob import settings - from trick_pins import TC_FAKE_OUT, TC_WORD_WALLET, TC_XPRV_WALLET - from trick_pins import TC_DELTA_MODE, make_slot, TRICK_SLOT_LAYOUT - - for pin, (slot_num, tc_flags, tc_arg) in settings.get('tp', {}).items(): - if (tc_flags & (TC_DELTA_MODE | TC_WORD_WALLET | TC_XPRV_WALLET)): - print("cant do duress cases") - continue - #assert not (tc_flags & (TC_DELTA_MODE | TC_WORD_WALLET | TC_XPRV_WALLET)), \ - #'unhandled simulated case: 0x%x' % tc_flags - - b, s = make_slot() - s.pin_len = len(pin) - s.pin[:s.pin_len] = pin.encode() - s.tc_flags = tc_flags - s.tc_arg = tc_arg - s.slot_num = slot_num - - self.state[slot_num] = bytes(b) + def reconstruct(self, tp): + # reconstruct based on user-space understanding of SE2 content + # - can't work with duress wallet cases here (no data) + # - mostly here so sim_settings works w/ non-empty defaults + print("SIM SE2: found no state, trying to reconstruct") + from glob import settings + from trick_pins import TC_FAKE_OUT, TC_WORD_WALLET, TC_XPRV_WALLET + from trick_pins import TC_DELTA_MODE, make_slot, TRICK_SLOT_LAYOUT + + print(" .. tp = %r" % tp) + if not tp: return + + for pin, (slot_num, tc_flags, tc_arg) in tp.items(): + if (tc_flags & (TC_DELTA_MODE | TC_WORD_WALLET | TC_XPRV_WALLET)): + print("cant do duress cases") + continue + #assert not (tc_flags & (TC_DELTA_MODE | TC_WORD_WALLET | TC_XPRV_WALLET)), \ + #'unhandled simulated case: 0x%x' % tc_flags + + b, s = make_slot() + s.pin_len = len(pin) + s.pin[:s.pin_len] = pin.encode() + s.tc_flags = tc_flags + s.tc_arg = tc_arg + s.slot_num = slot_num + + self.state[slot_num] = bytes(b) + print("slot[%d] <= flags=0x%x arg=0x%x" % (slot_num, tc_flags, tc_arg)) # Storage: base64 encoded binary for all the slot numbers in a dict @@ -64,16 +68,18 @@ def load(self): # merging default values as they contain useful nfc,vidsk info dv = obj.default_values() obj.current.update(dv) - s = obj.get('_se2', None) - if not s: - print("no SE2 data") - return + s = obj.get('_se2', None) or [] for record in s: b = a2b_base64(record) slot = uctypes.struct(uctypes.addressof(b), TRICK_SLOT_LAYOUT) self.state[slot.slot_num] = b print("SE2 slot %d is populated" % slot.slot_num) + else: + print("no SE2 data") + + if not self.state: + self.reconstruct(obj.get('tp')) def callgate(self, buf_io, arg2): # ckcc.callgate(22, ...) @@ -149,11 +155,12 @@ def try_trick_login(self, pin, num_fails): # similar to stm32/mk4-bootloader/se2.c se2_test_trick_pin(safety_mode=False) xs = self.get_by_pin(pin.encode(), num_fails) if not xs: + self.wallet = None # bugfix: normal login after trick login (SP unlock case) return None - print("PIN %s is a TRICK!" % pin) tc_flags = xs.tc_flags tc_arg = xs.tc_arg + print("PIN %s is a TRICK! flags=0x%x arg=%d" % (pin, tc_flags, tc_arg)) from trick_pins import TC_WIPE, TC_BRICK, TC_REBOOT, TC_FAKE_OUT from trick_pins import TC_WORD_WALLET, TC_XPRV_WALLET, TC_DELTA_MODE diff --git a/unix/variant/sim_settings.py b/unix/variant/sim_settings.py index 2706fb4ae..9a4343609 100644 --- a/unix/variant/sim_settings.py +++ b/unix/variant/sim_settings.py @@ -65,15 +65,57 @@ # Include useful multisig wallet, and shortcut to MS menu if '--p2wsh' in sys.argv: - sim_defaults['multisig'] = [["P2WSH--2-of-4", [2, 4], [[1130956047, "tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP"], [3503269483, "tpubDFcrvj5n7gyaxWQkoX69k2Zij4vthiAwvN2uhYjDrE6wktKoQaE7gKVZRiTbYdrAYH1UFPGdzdtWJc6WfR2gFMq6XpxA12gCdQmoQNU9mgm"], [2389277556, "tpubDExj5FnaUnPAn7sHGUeBqD3buoNH5dqmjAT6884vbDpH1iDYWigb7kFo2cA97dc8EHb54u13TRcZxC4kgRS9gc3Ey2xc8c5urytEzTcp3ac"], [3190206587, "tpubDFiuHYSJhNbHcbLJoxWdbjtUcbKR6PvLq53qC1Xq6t93CrRx78W3wcng8vJyQnY3giMJZEgNCRVzTojLb8RqPFpW5Ms2dYpjcJYofN1joyu"]], {"pp": "48'/1'/0'/2'", "ch": "XTN", "ft": 14}]] + sim_defaults['miniscript'] = [['P2WSH--2-of-4', 'wsh(sortedmulti(2,@0/**,@1/**))', ['[0f056943/48h/1h/0h/2h]tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP', '[6ba6cfd0/48h/1h/0h/2h]tpubDFcrvj5n7gyaxWQkoX69k2Zij4vthiAwvN2uhYjDrE6wktKoQaE7gKVZRiTbYdrAYH1UFPGdzdtWJc6WfR2gFMq6XpxA12gCdQmoQNU9mgm', '[747b698e/48h/1h/0h/2h]tpubDExj5FnaUnPAn7sHGUeBqD3buoNH5dqmjAT6884vbDpH1iDYWigb7kFo2cA97dc8EHb54u13TRcZxC4kgRS9gc3Ey2xc8c5urytEzTcp3ac', '[7bb026be/48h/1h/0h/2h]tpubDFiuHYSJhNbHcbLJoxWdbjtUcbKR6PvLq53qC1Xq6t93CrRx78W3wcng8vJyQnY3giMJZEgNCRVzTojLb8RqPFpW5Ms2dYpjcJYofN1joyu'], {'af': 14, 'm_n': (2, 4), 'b67': 1, 'ct': 'XTN'}]] elif '--wrap' in sys.argv: # p2wsh-p2sh case - sim_defaults['multisig'] = [["CC-2-of-4", [2, 4], [[1130956047, "tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP"], [3503269483, "tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax"], [2389277556, "tpubDExj5FnaUnPAjjgzELoSiNRkuXJG8Cm1pbdiA4Hc5vkAZHphibeVcUp6mqH5LuNVKbtLVZxVSzyja5X26Cfmx6pzRH6gXBUJAH7MiqwNyuM"], [3190206587, "tpubDFiuHYSJhNbHaGtB5skiuDLg12tRboh2uVZ6KGXxr8WVr28pLcS7F3gv8SsHFa2tm1jtx3VAuw56YfgRkdo6DXyfp51oygTKY3nJFT5jBMt"]], {"pp": "48'/1'/0'/1'", "ch": "XTN", "ft": 26}]] + sim_defaults['miniscript'] = [['CC-2-of-4', 'sh(wsh(sortedmulti(2,@0/**,@1/**)))', ['[0f056943/48h/1h/0h/1h]tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP', '[6ba6cfd0/48h/1h/0h/1h]tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax', '[747b698e/48h/1h/0h/1h]tpubDExj5FnaUnPAjjgzELoSiNRkuXJG8Cm1pbdiA4Hc5vkAZHphibeVcUp6mqH5LuNVKbtLVZxVSzyja5X26Cfmx6pzRH6gXBUJAH7MiqwNyuM', '[7bb026be/48h/1h/0h/1h]tpubDFiuHYSJhNbHaGtB5skiuDLg12tRboh2uVZ6KGXxr8WVr28pLcS7F3gv8SsHFa2tm1jtx3VAuw56YfgRkdo6DXyfp51oygTKY3nJFT5jBMt'], {'af': 26, 'm_n': (2, 4), 'b67': 1, 'ct': 'XTN'}]] else: # P2SH: 2of4 using BIP39 passwords: "Me", "Myself", "and I", and (empty string) on simulator - sim_defaults['multisig'] = [['MeMyself', [2, 4], [[3503269483, 'tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9'], [2389277556, 'tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc'], [3190206587, 'tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa'], [1130956047, 'tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n']], {'ch': 'XTN', 'pp': "45'"}]] + sim_defaults['miniscript'] = [['MeMyself', 'sh(sortedmulti(2,@0/**,@1/**))', ['[6ba6cfd0/45h]tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9', '[747b698e/45h]tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc', '[7bb026be/45h]tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa', '[0f056943/45h]tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n'], {'af': 8, 'm_n': (2, 4), 'b67': 1, 'ct': 'XTN'}]] sim_defaults['fee_limit'] = -1 +ms_mig = [['ms0', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP'), (4118082990, 0, 'tpubDDX85PzueTZjod816TDBdJPk8vWhqyZkSAXJ5xUjvSd1PyuEKnjt5UxiinKJSZzTTFVGSsSEm57LtpxQGdmSjQJtBmz1KUKtA9H63EzZmbA')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/2h'], 'ch': 'XTN', 'ft': 14}, 0],['ms1', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP'), (642592534, 0, 'tpubDCRchFK4N5fkmpD19kfdVBTPcRbcG321XpZc9EF5y9uH2d6DZdiYsVWvuZ6mTQpfqNuTVjqgb4ye33bFGHdhdS1eNwqrdbVQAwSwsftTCGZ')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/2h'], 'ch': 'XTN', 'ft': 14}],['ms2', (2, 2), [(1130956047, 1, 'tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP'), (2783214288, 0, 'tpubDCqWSUR4xtNPhMrVjQ2h5rdN2BACCHfviVnUrAynei9WaqvuykcjGyvGcbY9hJfpeovM4xVy5E3jMPw1tUc19PeqpVT9LxiTvgS9bZT5ceE')], {'d': ['m/44h/1h/0h', 'm/48h/1h/0h/1h'], 'ch': 'XTN', 'ft': 26}],['ms3', (2, 2), [(1130956047, 1, 'tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r'), (2267113793, 0, 'tpubDCGx6bNmE4zRFgfeV2PbGfcuhg6aeqtLYgNEGZ2pghgFiarh8j2yVruetVWUd6ykfkxaGgB8GhEkaGva1jXvqJrLXC3LboxsQTHqqCZD5Jj')], {'ch': 'XTN', 'd': ['m', 'm/84h/1h/0h'], 'ft': 14}]] + +if '--multi-mig' in sys.argv: + sim_defaults['multisig'] = ms_mig + +if '--name-clash-mig' in sys.argv: + mscs = [['msc11', 'XTN', 14, None, ['[0f056943/84h/1h/0h]tpubDC7jGaaSE66Pn4dgtbAAstde4bCyhSUs4r3P8WhMVvPByvcRrzrwqSvpF9Ghx83Z1LfVugGRrSBko5UEKELCz9HoMv5qKmGq3fqnnbS5E9r/<0;1>/*', '[0f056943/84h/1h/9h]tpubDC7jGaaSE66QBAcX8TUD3JKWari1zmGH4gNyKZcrfq6NwCofKujNF2kyeVXgKshotxw5Yib8UxLrmmCmWd8NVPVTAL8rGfMdc7TsAKqsy6y/<0;1>/*'], 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<0;1>/*),older(5)))', False, True, False, False],['msc16', 'XTN', 14, None, ['[0f056943/99h/0h/0h]tpubDDjN26baDEVS3st3MXRhPod1jGchwFby8WKR84V3TVj1WhXEA6kVUPDWcbG65HTZhaxecuNZJZ7wP7mXZyFrZfnqGWKuuaTPc32g7Nuhf65/<0;1>/*'], 'and_v(v:pk(@0/<0;1>/*),older(10))', False, True, False, False],['msc6', 'XTN', 14, None, ['[22da0343/44h/1h/0h]tpubDDTfrYcgqkLoq79TNkVThZ9nqW4VWJRY3zCWkQnNkXzoraF5GMENKPJM1dMQascfTBNJstMLkmJLJ3k4b1k9rAjf3dgNMhYMfHJSUcM4hgL/<0;1>/*', '[0f056943/84h/0h/0h]tpubDCx8y86cKonoPyTtj3f9NZLpBYoBNkbAzUdafMHhggjxkhF8Dny2aekWfDafywEMZEQaQjkK9Gxn7aN7usLRUQdYbvDgcnmYRf72khPEouL/<0;1>/*'], 'or_d(pk(@0/<0;1>/*),and_v(v:pkh(@1/<0;1>/*),older(5)))', False, True, False, False]] + mscs[0][0] = "ms0" # same as ms_mig[0] + mscs[1][0] = 32 * "a" + mscs[2][0] = 29 * "a" + ms_mig[2][0] = 35 * "a" + sim_defaults['multisig'] = ms_mig + sim_defaults['miniscript'] = mscs + +if '--der-pth-mig' in sys.argv: + multisigs = [ + ['ms1', (2, 3), [(2718032886, 0, + 'tpubDGThxU1AibvJnWta5ghydVz3WDMAKFEe2mAP8vtoYfUXkgoYisuk5heGfrqgrE18RUPvEVhUWfZHCH3EVi2sBEQyLFMx9JVyNvWa7zQtRaC'), + (3913158354, 1, + 'tpubDGauoqnAp5SEYQHYrasWkfWNoh1SD3izdfPtHRXQXp2YWhnJ5pPQEFsxe696c6iuuqA9SfaJcenv4ZLmXFfRavQDAnKKky7QTPxznp3vUUQ'), + (1130956047, 2, + 'tpubDH8ECUKZYchtZF1RmJ3oBGWKtroMxyUyd6iQKJx2JWoezuethw6PHSewUgbC3vWkihaFuKUVmLAYMVdxq3iMo9AV7beRceQGQzHYq9UhgBR')], + {'d': ["m/48'/1'/0'/2'/0", "m/48'/1'/0'/2'/1", "m/48'/1'/0'/2'/2"], 'ch': 'XTN', 'ft': 14}], + ['ms2', (3, 5), [(2044885442, + 'tpubD9h2yEghZWRp4Mvi4MPhyP7ZN8GDqYVRMk6rNf5omds7WTjmRZiok8xgwEP3uXLVbpxVrqnjm4bNXL6tLwHtYF9J7uVSG9u95Yid38fX9dT'), + (3035660899, + 'tpubD8zYsexbkYEiCbTso12bUsE8Y1CUn3WHjLER3fWqc8mcP7FhDK1Rc6Tixr6v3SQ4XBi5d4bbTskUCxe4eZujkL2cQ3enCDENtBYJYzYuUaR'), + (3343279201, + 'tpubD8yeTfF4L8aCEaQbuPjjzNeyPs2WGJPNWcBMDuDP7NP2VjLBCB5afvfhAg3oTytxvnLXZbMBWyEhs2nt3wmduwSCMotB8RHcxxkvMRtZHrq'), + (1010565321, + 'tpubD9jpJX26AjUzTjCuZb9PfWmKjrSjFzXfNjBFwMY6ckt9qw3m9rpYw3NGD2yZut6UbFuQZm2xttchgchzGjJn26Fu1uZp1tveV1WcmUaXpay'), + (1130956047, + 'tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')], + {'pp': "m/45'", 'ch': 'XTN', 'ft': 26}], + ('ms', (2, 2), [(2285969762, 0, + 'tpubDEy2hd2VTrqbBS8cS2svq12UmjGM2j7FHmocjHzAXfVhmJdhBFVVbmAi13humi49esaAuSmz36NEJ6GL3u58RzNuUkExP9vL4d81PM3s8u6'), + (1130956047, 1, + 'tpubDEFX3QojMWh7x4vSAHN17wpsywpP78aSs2t6nyELHuq1k34gub9mQ7QiaHNCBAYjSQ4UCMMpfBkf5np1cTQaStrvvRCxwxZ7kZaGHqYxUv3')], + {'ch': 'XTN', 'ft': 14, 'd': ["m/48'/0'/99'/2'", "m/48'/0'/33'/2'"]}) + ] + sim_defaults['multisig'] = multisigs + if '--xfp' in sys.argv: # --xfp aabbccdd => pretend we know that key (won't be able to sign) from ustruct import unpack @@ -141,7 +183,14 @@ if '-g' in sys.argv: - # do login + # do login.. but does not work if _skip_pin got saved into settings already + sim_defaults.pop('_skip_pin', 0) + +if '--fails' in sys.argv: + # fast-forward as if N PIN failures have already happened. + count = int(sys.argv[sys.argv.index('--fails') + 1]) + import ckcc + ckcc.SE_STATE.force_fails(count) sim_defaults.pop('_skip_pin', 0) if '--nick' in sys.argv: diff --git a/unix/variant/sim_vdisk.py b/unix/variant/sim_vdisk.py index 321617f56..16097655a 100644 --- a/unix/variant/sim_vdisk.py +++ b/unix/variant/sim_vdisk.py @@ -75,9 +75,12 @@ def unmount(self, written_files, readonly=False): def import_file(self, filename, sz): # copy file into another area of PSRAM where rest of system can use it print("sim-virtdisk: read %s" % filename) - contents = open(SIMDIR_PATH+filename, 'rb').read(sz) + with open(filename, 'rb') as f: + contents = f.read(sz) from glob import PSRAM - PSRAM.write_at(0, sz)[:] = contents + runt = (4 - sz % 4) + sz = sz + runt + PSRAM.write_at(0, sz)[:] = contents + bytes(runt) return sz vdisk.VirtDisk = SimulatedVirtDisk diff --git a/unix/variant/version.py b/unix/variant/version.py index 62875beb4..81931fd20 100644 --- a/unix/variant/version.py +++ b/unix/variant/version.py @@ -44,7 +44,7 @@ def get_header_value(fld_name): num_sd_slots = 1 has_battery = False has_qwerty = False -is_edge = False +is_edge = True if '--mk1' in sys.argv: # doubt this works still diff --git a/unix/work/debug/.gitignore b/unix/work/debug/.gitignore new file mode 100644 index 000000000..5146bd3b0 --- /dev/null +++ b/unix/work/debug/.gitignore @@ -0,0 +1,11 @@ +*.7z +*.txt +*.json +*.psbt +*.csv +*.pdf +*.dfu +*.txn +*.sig +*.png +.tmp.tmp \ No newline at end of file diff --git a/testing/debug/README.md b/unix/work/debug/README.md similarity index 100% rename from testing/debug/README.md rename to unix/work/debug/README.md