Security reviews
Before release, the source code of BitTool Signer 1.0.0 was examined on each platform by two AI-assisted security reviews, each run on its own. All four reports are published in full below. None found a Critical or High issue. The iPhone reviews each found one Low issue. The Android reviews found Medium issues; four Medium-rated findings are open in the released Android 1.0.0, and three of them sit in the signing engine that both apps share.
- None is an audit by a security firm, and no human auditor was involved.
- The Opus 5 and Opus 5.5 reviews come from the same model family that wrote or changed a material part of the code under review. The reports say they are not third-party attestation or certification.
- The GPT-6 Astra reviews describe themselves as AI-assisted engineering reviews prepared for the project owner, not an audit-firm certification or human sign-off.
- Both Android reports recommend fixing their open Medium findings before the app protects valuable wallets. BitTool Signer for Android 1.0.0 was released with them open; see every finding.
- An independent third-party audit has not been done yet. The reports name it, together with testing on real devices and round trips with third-party wallets, as work that remains before holding significant funds.
BitTool Signer for iPhone
The two reports
| Opus 5 | GPT-6 Astra | |
|---|---|---|
| Date | 19 October 2026 | 18 October 2026 (report BTS-SEC-2026-1018-R3) |
| Code reviewed | Tag v1.0.0, commit f621372 | Working tree of 18 October: commit 3d706b7 plus 59 changed files and one new test |
Engine reviewed (signer.js, SHA-256) | 6933d0d8…f21077 | 6933d0d8…f21077 |
| Method | Line-by-line source review (3,546 lines of JavaScript, 11,858 of Swift), 11 attack probes, 4,000-case mutation test of the QR input, test suites, clean-room rebuild | Source review by subsystem, comparison with the previous round, independent Bitcoin and Solana fixtures, 1,509-case mutation test, test suites, reproducible build, dependency advisories |
| Tests run | 210 engine, 168 native | 210 engine, 177 native and UI |
| Result | 0 Critical · 0 High · 0 Medium · 1 Low · 3 Informational | 0 Critical · 0 High · 0 Medium · 1 Low · 4 observations |
| Independence | Self-review by the author of much of the code | AI-assisted, prepared for the project owner |
Both reviews examined the same engine file that BitTool Signer 1.0.0 ships: every transaction parser, risk rule and signing plan is in it. The rest of the app (interface, Keychain, storage) was reviewed by reading the Swift source.
Every finding
| ID | Severity | Finding | Status in 1.0.0 |
|---|---|---|---|
| BTS-OP5-01 | Low | High-risk consent is enforced only in the interface. The engine requires an explicit acknowledgement for partially decoded and opaque requests, but not for fully decoded high-risk ones (unlimited approvals, setApprovalForAll, Safe permission changes, token burns). The app disables Sign until the box is ticked, so an attacker cannot reach this in 1.0.0; it matters because the engine is the part users can verify by hash. | Open. The report recommends adding the check to the engine in 1.0.1. |
| R3-01 | Low | Bitcoin summary for a burn with change. For a transaction with a nonzero OP_RETURN output and verified change, the short summary on the result screen and in Activity reads "Every output returns to your wallet". The signing screen itself shows the burn, its amount and the High risk correctly. | Open (the engine shipped in 1.0.0 is the one reviewed). |
| BTS-OP5-02 | Informational | A localization key in Activity is built from engine output, which SwiftUI renders as Markdown. Every value it can hold today is a fixed label, so it is latent. | Open, latent. |
| BTS-OP5-03 | Informational | The vault's password KDF (PBKDF2-HMAC-SHA256, 600,000 rounds) is not memory-hard. Mitigated by the second encryption layer under a device-bound Keychain key: a copied vault file alone cannot even be attacked. | Accepted; Argon2id is suggested for a future vault format. |
| BTS-OP5-04 | Informational | Intermediate Ed25519 key material in the Solana derivation is not explicitly wiped; disposing of the JavaScript context is the actual control. | Open, inherent limitation. |
| O-01 | Informational | During import, a slow "is this wallet already here?" lookup can finish after the phrase is edited and offer a password reset for the previous wallet. The reset itself re-derives the wallet identity and refuses the wrong wallet; no vault changes. | Open at the time of the report. |
| O-02 | Informational | Safe and Solana summaries come from bounded decoders, not from executing the transaction. Live contract state is not known offline. | By design; disclosed in the review screen. |
| O-03 | Informational | Complete memory zeroization cannot be guaranteed in Swift and JavaScript; the physical device, the OS random generator and the distributed binary were not tested. | Limitation. |
| O-04 | Informational | Six development-only dependency advisories (3 moderate, 3 low). The affected packages are not in the shipped engine. | Maintenance. |
Both reports also re-tested the findings of two earlier review rounds, including one High (the summary of Safe multisig transactions with DELEGATECALL and nested MultiSend) and several Low ones in Bitcoin fee and Solana burn summaries, and found them fixed. Opus 5 additionally notes, as a property of the TRON protocol rather than a defect, that TRON signatures carry no network identifier; the app discloses this.
Found later in the shared engine
BitTool Signer for iPhone 1.0.0 ships the engine with SHA-256 6933d0d8…f21077. The GPT-6 Astra review of the Android app reproduced its engine findings against exactly this file, and the Bitcoin, EVM and TRON code behind the Opus 5.5 findings is identical in it. These engine findings therefore also apply to iPhone 1.0.0:
| ID | Severity | What it means on iPhone 1.0.0 |
|---|---|---|
| BTS-02, BTS-07 | Medium | A Solana burn, authority change or delegation sent together with a dust transfer is summarized as that transfer. The warnings in the details and the risk acknowledgement remain. |
| BTS-08 | Medium | Bitcoin change is accepted at any address index of the wallet's single-sig profiles, including indexes a watch-only wallet will never scan. |
| BTS-09 | Low–Medium | The stored summary omits the native coin sent with an unknown EVM or TRON contract call. The review card shows it. |
R3-01 above is the same defect as Android's BTS-04, which the Android engine fixes. The Opus 5.5 report recommends fixing the others in the shared engine, which then reaches both apps.
What the reviews confirmed
| Area | Confirmed | Details |
|---|---|---|
| Signing | In Release builds the only way to sign is through a fixed plan. Six independent checks tie the signature to the reviewed request, the planned wallet and the approved key, and the engine re-derives everything from the decrypted seed before signing. Confirmed by execution. | Before signing |
| Randomness | Recovery phrases come from SecRandomCopyBytes; an error stops creation with no fallback. The engine's crypto.getRandomValues and Math.random are bridged to the same source. | Seed generation |
| Key storage | Two AES-GCM layers with authenticated headers, a device-only Keychain key, and failed-attempt throttling that survives restarts. | Key storage |
| Refusals | Every refusal these docs describe holds: SIGHASH_NONE/SINGLE/ANYONECANPAY, SegWit v0 inputs without the previous transaction, Solana lookup tables, EIP-7702, blob transactions, raw-hash eth_sign and unsupported chain IDs. | Limits and errors |
| Untrusted input | 5,509 mutated QR, CBOR, PSBT and JSON inputs across both reviews: none accepted when invalid, no uncontrolled exception. | UR and CBOR |
| Offline | Neither CFNetwork nor Network.framework is linked, and the JavaScript bridge exposes no network API. | Security model |
| Supply chain | The engine rebuilds byte for byte from the tag; runtime dependencies are exact-pinned; the production dependency audit reported no vulnerabilities. | Below |
What they did not cover
Testing on physical devices (fault injection, memory dumps, side channels), the OS random generator and Secure Enclave internals, provenance of the App Store binary beyond the engine file, transactions on live chains, round trips with third-party wallets, a full audit of the cryptographic dependencies, linguistic review of the 21 interface languages, and formal verification. A passing test suite covers the cases it tests, not every transaction or device.
Verify the engine yourself
The App Store executable cannot be reproduced byte for byte (Apple re-signs and encrypts it), but the engine is an unencrypted resource that can be compared. Build it from the tag:
git clone https://github.com/bitbill/bittool-signer-ios
cd bittool-signer-ios && git checkout v1.0.0
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../BitToolSigner/Resources/signer.js
# 6933d0d8a6f60faa8222506ac0bd7eef80eb884c9f73e31e9f354b4740f21077 (1,181,026 bytes)
Then compare it with the file inside the app you installed: download the app as an .ipa with Apple Configurator, unzip it, and hash Payload/BitToolSigner.app/signer.js. The full procedure, including where verification stops, is in the repository's Reproducible-Build.md.
BitTool Signer for Android
The two reports
| Opus 5.5 | GPT-6 Astra | |
|---|---|---|
| Date | 25 October 2026 | 24 October 2026 |
| Code reviewed | Release 1.0.0 (version code 1000001): code commit 71af693, release record 62996b8 | Commit e7e71fb, before the release: a re-audit of the changes since the previous baseline bb1e656 |
Engine reviewed (signer.js, SHA-256) | 4b371759…00fa19 | 6933d0d8…f21077, the engine iPhone 1.0.0 ships |
| Method | Source review of five areas, each traced end to end; every finding re-checked against the code; engine reproductions with synthetic data; dependency and artifact checks | Review of the changed code and its callers, verification of three fixes, engine reproductions against the committed bundle, automated regression checks |
| Tests run | 2,075 app tests, 217 engine tests, release verification script 25 of 25 | 1,616 Android tests; 210 engine tests, one failing and one passing without running (BTS-06) |
| Result | 0 Critical · 0 High · 4 Medium (one rated Low–Medium) · 9 Low · informational notes | 0 Critical · 0 High · open: 1 Medium, 1 Low, 1 validation finding · 3 closed |
| Recommendation | Fix BTS-07, BTS-08 and BTS-10 before a general release that protects valuable wallets | Hold a general release for valuable wallets at that revision |
| Independence | The same model family wrote much of the code under review | AI-assisted; production code was not modified |
Both reviews looked at the whole Android app: the Kotlin modules, the native host (JNI over QuickJS, and the native PBKDF2) and the signing engine shared with iPhone.
Every finding
Opus 5.5 reviewed the released code, so these are the findings of BitTool Signer for Android 1.0.0.
| ID | Severity | Finding | Where |
|---|---|---|---|
| BTS-07 | Medium | Solana authority changes hide behind a dust transfer. A request that changes an account's owner or authority, approves a delegate or carries an opaque instruction, together with a 1-lamport transfer, gets that transfer as its headline and stored summary. The high-risk warnings stay in the details and the risk acknowledgement is still required. | Shared engine, also iPhone |
| BTS-08 | Medium | Bitcoin change accepted at any index. An output counts as verified change if its script matches any of the wallet's single-sig profiles, on branch 0 or 1, at any index up to 2³¹−1. Funds sent there review as "All outputs return to your wallet" but may never be found by a wallet that scans a limited range of addresses. | Shared engine, also iPhone |
| BTS-09 | Low–Medium | Stored summary omits native value on contract calls. An unknown EVM or TRON contract call that also sends the native coin is stored as "Unknown contract call". The review card and its warning do show the value. | Shared engine, also iPhone |
| BTS-10 | Medium | Release signing key on the development machine. The key and its passwords were stored in plaintext in the working tree, excluded from Git. | Release process |
| BTS-11 | Low | The QR camera can keep scanning behind the App Lock screen. Signing still needs the wallet password. | Android app |
| BTS-12 | Low | The risk acknowledgement for fully decoded high-risk requests is enforced only in the interface. | Android app (iPhone: BTS-OP5-01) |
| BTS-13 | Low | After a Keystore error on Android 8–11, the key that authenticates public wallet data can be regenerated, and wallets then need Repair. No secret or funds are lost. | Android app |
| BTS-14 | Low | Two auxiliary keys do not check for a screen lock, and parts of the documentation overstate the protection. The vault itself still requires one. | Android app |
| BTS-15 | Low | The default task affinity allows task hijacking on older Android versions. | Android app |
| BTS-16 | Low | The backup check can be passed by trial and error. | Android app (same on iPhone, per the report) |
| BTS-17 | Low | One Recovery Card of a 12-word phrase leaves about 40 bits of entropy. | Android app |
| BTS-18 | Low | A cancelled wallet match can leave the review on "Finding the matching wallet…". | Android app |
| BTS-19 | Low | Android 8.0–9 cannot detect overlays that cover only part of a consent control. The password is still required. | Android app |
The report also lists informational notes, among them: no CPU-time limit on the engine thread (availability only), the Google Play build signed with the APK key until a separate upload key is set up, a retry delay based on the wall clock, and short-lived copies of the phrase that the JVM cannot wipe.
GPT-6 Astra reviewed an earlier commit. Its findings, and where they stand in the release:
| ID | Severity | Finding | At e7e71fb | In Android 1.0.0 |
|---|---|---|---|---|
| BTS-01 | Medium | Partly covered Sign and risk controls could be tapped through an overlay | Closed | Closed |
| BTS-02 | Medium | A Solana burn or authority change mixed with a dust transfer is summarized as the transfer | Open | Burns fixed; authority changes remain as BTS-07 |
| BTS-03 | Low | Secret buffers could outlive a cancelled operation | Closed | Closed |
| BTS-04 | Low | A Bitcoin burn with change is summarized as "Every output returns to your wallet" (iPhone R3-01) | Open | Fixed |
| BTS-05 | Functional | An interrupted QR scan did not resume | Closed | Closed |
| BTS-06 | Validation | Outdated file paths made one engine test fail and another pass without running | Open | Fixed (217 of 217 engine tests pass) |
What the reviews confirmed
| Area | Confirmed |
|---|---|
| Signing | No password bypass and no way to sign anything but the reviewed and matched request. The plan is bound to the request hash, the plan hash and the digest of the signing bytes; the engine re-resolves it against the decrypted seed and rejects any change to an unsigned Bitcoin transaction. Late results after a cancellation, App Lock or a new request are dropped. |
| Key storage | PBKDF2-HMAC-SHA256 with 600,000 iterations in native code, AES-256-GCM with a fresh nonce and salt for each encryption, then a per-wallet, non-exportable Android Keystore key (StrongBox when the phone has one). Every password attempt is counted before the key derivation, and a damaged counter fails closed. |
| Secrets in memory | Unlocked secrets live in holders that are closed after use and wiped even on cancellation. The recovery phrase is typed on the app's own keyboard, which no input method sees. |
| Engine host | The bundle is pinned by SHA-256 and length; QuickJS runs without eval or Function, in a fresh runtime for every operation that touches a secret or untrusted input, with memory and stack limits. |
| Offline and platform | No Internet permission and no networking code. Only the main screen is exported, with no deep links; backups and device transfer are excluded; every window is kept out of screenshots, recordings and Recents; other apps' overlays are hidden on Android 12 and later. App Lock never authorizes a signature, and the release build logs nothing. |
| Supply chain | The engine rebuilt from a clean npm ci --ignore-scripts is byte-identical to the committed file and to the copy inside the APK. The production dependency audit reports no advisories. |
What they did not cover
Testing on real devices (Keystore and StrongBox behaviour of different manufacturers, biometrics, overlays, screen capture), malicious-overlay exercises, side channels, execution of the native code on ARM, transactions on live chains, a CVE scan of the Maven and native dependencies, and the copy that Google Play builds. Both reports ask for the release to be checked on representative devices from Android 8 to the latest version, and Opus 5.5 recommends an independent human audit before the app protects significant funds.
Verify the APK and the engine
The APK published on bittool.com/signer can be checked completely: its hash, its signing certificate, the engine inside it, and the absence of the Internet permission (aapt2 dump badging lists no android.permission.INTERNET).
# 1. The APK from bittool.com/signer
shasum -a 256 BitToolSigner-1.0.0-1000001.apk
# 109dbf5c622ee77672904c34a10229df3c9b0eac45b07096c510b1e1e169c2a4
apksigner verify --print-certs BitToolSigner-1.0.0-1000001.apk
# Signer #1 certificate DN: CN=BITTOOL PTE. LTD., O=BITTOOL PTE. LTD., C=SG
# Signer #1 certificate SHA-256 digest: af41ed101765393ae89038eb4c6fc7fa96d930f6fcd76b881437a39b5a9e4631
# 2. The engine inside it
unzip -p BitToolSigner-1.0.0-1000001.apk assets/engine/signer.js | shasum -a 256
# 4b3717599e8ee3ed9d2480e640e7203731543cc8ab0f2c3cec3b78e6f400fa19 (1,181,432 bytes)
# 3. The same engine, rebuilt from source
git clone https://github.com/bitbill/bittool-signer-android
cd bittool-signer-android && git checkout 71af693
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../core/engine/src/main/assets/engine/signer.js
The certificate never changes between releases, Android installs an update only if it carries the same one, and the app shows it in Settings › Fully Open Source › This build. A copy installed from Google Play is a different file, because Google Play builds it from the uploaded bundle, so its hash cannot be compared; its assets/engine/signer.js is unchanged and can be compared the same way after adb pull. The full procedure is in the repository's Reproducible-Build.md. We checked the values above against the published APK on 25 October 2026.
Download the reports
| Report | Pages | SHA-256 |
|---|---|---|
| iPhone · Opus 5, 19 October 2026 (PDF, 118 KB) | 17 | ff7ddd17e5dffccd99db6aaa43b8f0041167b58bd9b2bd7cc11d185e779bca1a |
| iPhone · GPT-6 Astra, report R3, 18 October 2026 (PDF, 46 KB) | 16 | 02d7a358f8f35b2978eab7db1292add4365a94b736ff79e158dee3afb842b64a |
| Android · Opus 5.5, 25 October 2026 (PDF, 252 KB) | 8 | 8242d58aeec1aa88823eb0a938c9461b0681abfab3f37805e2f77a41b3bba34c |
| Android · GPT-6 Astra, re-audit, 24 October 2026 (PDF, 22 KB) | 7 | b37aaefd40d0d522384a86255accadeb976067175d76e38466a1b4c90b036f53 |
The summaries on this page are ours; where they differ in wording from a report, the report is authoritative.