# Bitcoin Armory Crypto Audit Release Memo

Date: 2026-06-30

Scope: release-oriented summary of the current crypto audit state, with emphasis on fund-safety risk for BIP84, BIP86, and BIP352 Silent Payments.

Related documents:

- [crypto-audit-plan.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-audit-plan.md:1)
- [crypto-derivation-matrix.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-derivation-matrix.md:1)
- [crypto-test-gap-analysis.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-test-gap-analysis.md:1)
- [crypto-audit-findings-initial.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-audit-findings-initial.md:1)

## Executive summary

The current working tree is materially safer than the starting point of the audit.

Most importantly:

1. Silent Payment compatibility is now checked against all 28 official BIP-352 cases in the checked-in fixture.
2. The major fund-loss bug in Silent Payment address handling was found and fixed.
3. Standard BIP86 signing now has direct end-to-end coverage.
4. The online/offline sender boundary is now stricter:
   - wallet ownership is checked by exact script, not address alone
   - SP sends only admit supported input types
   - RBF applies the same spendability boundary as normal sends

My current judgment:

1. standard BIP84 and BIP86 receive/sign paths look reasonably close to release-ready from a cryptographic correctness perspective
2. Silent Payments are much closer, but I would still treat them as release-blocked until the remaining operational and migration conditions below are satisfied

## What was proven in this audit pass

### Standard derivation and signing

Evidence now exists for:

1. BIP84 receive/change derivation and watch-only parity
2. BIP86 descriptor-origin correctness
3. BIP86 end-to-end Taproot signing
4. PSBT change-output derivation metadata for both BIP84 and BIP86

Primary evidence:

- [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:1)
- [tests/test_descriptor_and_psbt_metadata.py](/Users/juthicamallela/bitcoinarmory/tests/test_descriptor_and_psbt_metadata.py:1)

### Silent Payments

Evidence now exists for:

1. official BIP-352 vector coverage across all 28 checked-in cases
2. labeled and unlabeled recipient handling
3. `K_max` enforcement
4. receiver scan behavior across supported and unsupported input types
5. offline signer resolution of SP placeholder outputs
6. spending of detected SP UTXOs
7. compatibility handling for legacy 64-byte draft SP addresses

Primary evidence:

- [tests/test_silent_payment_official_vectors.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment_official_vectors.py:1)
- [tests/test_scanner.py](/Users/juthicamallela/bitcoinarmory/tests/test_scanner.py:1)
- [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:1)
- [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:1)

### Spend-construction boundary

Evidence now exists for:

1. rejection of foreign UTXOs even if an upstream source reports them
2. rejection of wallet-owned addresses paired with mismatched scripts
3. rejection of non-Taproot fake SP inputs
4. equivalent ownership/SP-policy checks in both:
   - `create_psbt()`
   - `create_rbf_psbt()`

Primary evidence:

- [online/setup.py](/Users/juthicamallela/bitcoinarmory/online/setup.py:2073)
- [online/setup.py](/Users/juthicamallela/bitcoinarmory/online/setup.py:2914)
- [tests/test_descriptor_and_psbt_metadata.py](/Users/juthicamallela/bitcoinarmory/tests/test_descriptor_and_psbt_metadata.py:1)
- [tests/test_online_app.py](/Users/juthicamallela/bitcoinarmory/tests/test_online_app.py:1)
- [tests/test_utxo_filter.py](/Users/juthicamallela/bitcoinarmory/tests/test_utxo_filter.py:1)

## Highest-severity issues found during the audit

### Fixed before release

1. Silent Payment addresses previously dropped pubkey parity by encoding x-only keys instead of the BIP-352 compressed-pubkey payload.
   Impact: real coin-loss risk.
   Status: fixed.

2. Receiver-side SP scanning still assumed the old x-only/even-Y behavior after the sender path was corrected.
   Impact: missed detections or derivation mismatch.
   Status: fixed.

3. Receiver-side input extraction was too narrow and could miss valid SP detections in mixed-input transactions.
   Impact: false negatives during scan.
   Status: fixed.

4. Online spend construction trusted address ownership more than exact script ownership.
   Impact: unsafe boundary if an indexer or future feature supplied inconsistent UTXO data.
   Status: fixed.

## Residual risks

These are the remaining risks I would still consider real.

### 1. Sender policy still lives partly above the low-level SP primitive

`create_sp_output_pubkeys()` is intentionally a low-level cryptographic primitive. It still assumes its callers already filtered to valid, supported inputs.

That is acceptable if the application boundary remains disciplined, but it means:

1. future callers can still misuse the primitive
2. a future imported-input or advanced-coin-control feature could bypass the current policy checks unless it explicitly reuses them

Risk level: medium

### 2. Legacy SP address migration is compatibility-safe in code, but not yet fully closed operationally

Current evidence suggests persisted wallet state is safe because wallets store SP keys, not rendered SP address strings.

What is still not proven from source control alone:

1. whether any historical release exposed legacy SP addresses to users
2. whether any operator documentation, screenshots, exports, or external integrations captured those legacy addresses

Risk level: medium

### 3. Future spend sources remain the main place regressions could re-enter

I did not find hidden spend-construction paths in the current app beyond `create_psbt()` and `create_rbf_psbt()`.

That reduces current risk, but future risk remains if the project adds:

1. imported external inputs
2. sweep tools
3. transaction editors
4. alternate PSBT constructors

Risk level: medium

### 4. This audit is still code-and-test based, not implementation-diversity based

The BIP-352 vector coverage is strong, but most higher-level proofs still come from this codebase plus tests written in the same repository.

What is still missing if the bar is very high:

1. second-implementation cross-checks for descriptors or signing
2. stronger fuzz/property testing around PSBT metadata corruption
3. adversarial review of non-happy-path wallet migration and operator UX

Risk level: low to medium

## Release blockers for Silent Payments

I would require these before calling Silent Payments production-ready for real funds.

### Blocker 1: migration stance must be explicit

The release should answer this clearly:

1. were legacy draft SP addresses ever shown or exported in any public or user-facing build?
2. if yes, what exact operator action is required?
3. if no, the release notes should still say legacy SP addresses remain compatibility-only and canonical addresses now use the BIP-352 compressed-key form

### Blocker 2: spend-policy invariants must be treated as permanent API rules

The following should be documented as non-negotiable invariants for future development:

1. wallet-owned means address plus exact script match
2. SP send inputs must be explicitly policy-filtered before cryptographic derivation
3. non-Taproot `sp_tweak` inputs are invalid
4. future spend builders must reuse the same checks as `create_psbt()` and `create_rbf_psbt()`

### Blocker 3: regression suite should be considered mandatory for release

At minimum, release confidence for SP should depend on the current suites staying green:

1. [tests/test_silent_payment_official_vectors.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment_official_vectors.py:1)
2. [tests/test_scanner.py](/Users/juthicamallela/bitcoinarmory/tests/test_scanner.py:1)
3. [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:1)
4. [tests/test_descriptor_and_psbt_metadata.py](/Users/juthicamallela/bitcoinarmory/tests/test_descriptor_and_psbt_metadata.py:1)

### Blocker 4: no unresolved product requirement should weaken the current SP boundary

If a pending feature requires:

1. arbitrary imported inputs
2. partial-wallet or third-party coin-control
3. alternate transaction builders

then that feature should not ship with SP support unless it first proves the same ownership and input-eligibility guarantees.

## Recommended go/no-go position

### BIP84 and BIP86

Current recommendation: tentatively `go`, assuming no unrelated high-severity findings appear elsewhere.

Reason:

1. derivation and descriptor paths now have direct proof coverage
2. offline signing has direct proof coverage
3. the spend-construction boundary is much tighter than before

### Silent Payments

Current recommendation: `conditional go`, not unconditional go.

Silent Payments should be considered releasable only if:

1. the migration question above is answered explicitly
2. the current SP regression suites remain required release gates
3. no new spend-construction path is introduced without inheriting the same ownership/SP-policy checks

If those conditions are not met, my recommendation remains `no-go for real funds`.

## Verification snapshot

Relevant verification completed in the current working tree:

1. `python3 -m pytest tests/test_online_app.py tests/test_utxo_filter.py -q` -> `31 passed`
2. `python3 -m pytest tests/test_descriptor_and_psbt_metadata.py tests/test_silent_payment.py tests/test_signer.py tests/test_silent_payment_official_vectors.py tests/test_scanner.py -q` -> `74 passed`

## Practical next step

The next useful release deliverable is not more crypto code. It is:

1. a short operator-facing migration note for SP addresses
2. a maintainer-facing checklist that marks the regression suites above as release gates
3. a developer note that any future spend-construction feature must reuse the current ownership and SP input-policy checks
