# Quantum Core Post-Quantum Readiness Report

**Target:** `/Users/israelbergenstein/Desktop/Desktop/Functor-Fund/.`
**Generated:** 2026-10-07 21:23:41 UTC
**Files scanned:** 62
**Files skipped:** 306

| Measure | Value | Meaning |
|---|---|---|
| **Weakness Score** | **0/100 (None detected)** | Cryptography that is broken today. Gate CI on this. |
| **Migration Exposure** | **Extensive** | 86 asymmetric site(s) across 13 file(s), 20% of files scanned. Work to schedule before 2035. |

**Weaknesses (act now):** 0
**Inventory signals (migration):** 99
**Likely noise (suppressed):** 324
**Files ignored (.quantumignore):** 165
**Total matches:** 423

## Executive Summary

This report separates two different kinds of result, and scores them separately, because they are not the same question.

**Weaknesses (0)** are cryptography that is wrong today on a classical computer. They have nothing to do with quantum computing and should be scheduled as ordinary security work.

**Inventory signals (99)** are cryptography that is correct today but sits on a migration clock. Nothing here is a bug. Under NIST IR 8547, RSA and elliptic-curve cryptography are deprecated in 2030 and disallowed in 2035, so this half of the report is a planning input, not a defect list.

**This codebase has no detected cryptographic weaknesses.** It does have an asymmetric-cryptography surface that will need to migrate, which is normal and expected for working cryptographic software. A Migration Exposure figure is a measure of scope, not a finding against the code.

A further 324 matches were classified as likely noise and held back from the sections below. They are summarised in the Likely Noise section at the end of this report.

## Quantum Reality Note

This report is grounded in conservative post-quantum assumptions. RSA and elliptic-curve public-key systems are the main public-key migration concern because Shor's algorithm would break their underlying hardness assumptions on a sufficiently large fault-tolerant quantum computer. Hash functions and symmetric primitives are not affected in the same way; Grover's algorithm gives a quadratic search speedup, so they are generally handled through larger security margins rather than treated as immediately broken. Hardcoded secrets are an immediate operational security problem, not specifically a quantum problem. This tool is a source-code signal scanner and inventory aid, not a proof of vulnerability, not a proof of quantum safety, and not a formal security audit.

## Weaknesses — Act Now

_No classical cryptographic weaknesses were detected by the current rules._

## Priority Review Queue

These are the first findings to inspect manually. They are not automatically vulnerabilities.

| Priority | Confidence | Category | Rule | File | Line | Match |
|---|---|---|---|---|---:|---|
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/functor-sign.js` | 69 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/functor-sign.js` | 119 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/functor-sign.js` | 124 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/functor-sign.js` | 133 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/trade.js` | 14 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/trade.js` | 17 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/wallet.js` | 12 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-ECC-001` | `site/js/wallet.js` | 15 | `secp256k1` |
| `high` | `high` | `Blockchain signatures` | `PQC-SIGN-001` | `site/js/functor-sign.js` | 119 | `.sign(` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `chain/crates/functor-node/src/eip712.rs` | 166 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `chain/crates/functor-node/src/eip712.rs` | 167 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `chain/crates/functor-node/src/node.rs` | 600 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `chain/crates/functor-node/src/node.rs` | 611 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/app.js` | 34 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/app.js` | 38 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/app.js` | 41 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/site.js` | 38 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/site.js` | 39 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 36 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 82 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 112 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 114 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 139 | `Wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 145 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 159 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 185 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 234 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 279 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 511 | `wallet` |
| `critical` | `medium` | `Wallet infrastructure` | `PQC-WALLET-001` | `site/js/trade.js` | 546 | `wallet` |

## Confidence Model

Every match is assigned a confidence level before it reaches this report.

- **high** — a specific pattern in a code context.
- **medium** — a real signal that still needs manual confirmation.
- **low** — a generic term, or a match inside a comment. Held back as likely noise.

Only `high` and `medium` matches contribute to the risk score, the severity, category and priority tables, and the detailed findings.

## Findings by Confidence

| Confidence | Count | Counted in report |
|---|---:|---|
| high | 10 | yes |
| medium | 89 | yes |
| low | 324 | no |

## Findings by Severity

| Severity | Count |
|---|---:|
| critical | 34 |
| high | 21 |
| medium | 32 |
| low | 0 |
| info | 12 |

## Findings by Crypto-Native Category

| Category | Count |
|---|---:|
| General cryptography | 13 |
| Wallet infrastructure | 34 |
| Blockchain signatures | 42 |
| Validator / governance keys | 0 |
| Exchange / custody infrastructure | 10 |
| Network / TLS dependencies | 0 |
| Secret hygiene | 0 |

## Findings by Migration Priority

| Migration Priority | Count |
|---|---:|
| critical | 34 |
| high | 52 |
| medium | 0 |
| low | 13 |
| none | 0 |

## Findings by Rule

| Rule | Count |
|---|---:|
| `PQC-ECC-001` | 11 |
| `PQC-EXCHANGE-001` | 10 |
| `PQC-HASH-001` | 12 |
| `PQC-RNG-001` | 1 |
| `PQC-SIGN-001` | 31 |
| `PQC-WALLET-001` | 34 |

## Category-Specific Recommendations

| Category | Recommendation |
|---|---|
| `General cryptography` | Inventory the primitive and its role. RSA/ECC/signatures are migration-sensitive. Hashes and symmetric primitives are usually affected differently and should not be treated as automatically broken. |
| `Wallet infrastructure` | Review key generation, seed handling, signing, storage, backup, recovery, export, rotation, custody boundaries, and whether public keys are exposed before spend/use. |
| `Blockchain signatures` | Identify the signature scheme and what it authorizes. ECDSA, Ed25519, Schnorr-style flows, and secp256k1 signing are post-quantum migration priorities because large fault-tolerant quantum computers would threaten their classical hardness assumptions. |
| `Exchange / custody infrastructure` | Separate API credentials, HMAC signing, trading authentication, withdrawal authority, custody keys, HSM/MPC paths, and operational secrets. Immediate secret exposure is different from long-term PQ migration risk. |

## Crypto-Native Inventory Summary

This section groups findings by the type of crypto infrastructure they may affect.

- **General cryptography:** RSA, hashing, randomness, and general cryptographic primitives.
- **Wallet infrastructure:** private keys, seed phrases, HD wallets, keystores, and signing flows.
- **Blockchain signatures:** ECDSA, Ed25519, secp256k1, transaction signatures, and verification paths.
- **Validator / governance keys:** validator, bridge, treasury, deployer, admin, and governance keys.
- **Exchange / custody infrastructure:** API keys, HMAC, withdrawal keys, HSM, custody, and trading authentication.
- **Network / TLS dependencies:** HTTPS, TLS, SSL, RPC clients, and transport-layer dependencies.
- **Secret hygiene:** hardcoded secrets, tokens, passwords, and private key material.

## Top Files by Finding Count

| File | Findings |
|---|---:|
| `site/js/trade.js` | 26 |
| `chain/crates/functor-node/src/eip712.rs` | 21 |
| `site/js/vaults.js` | 11 |
| `site/js/wallet.js` | 11 |
| `chain/crates/functor-node/src/node.rs` | 8 |
| `site/js/functor-sign.js` | 7 |
| `site/js/app.js` | 3 |
| `site/js/site.js` | 3 |
| `chain/crates/functor-core/src/state_hash.rs` | 2 |
| `testnet/worker/src/index.js` | 2 |
| `chain/crates/functor-core/Cargo.toml` | 1 |
| `chain/crates/functor-node/Cargo.toml` | 1 |
| `deploy/all.sh` | 1 |
| `testnet/worker/src/limits.js` | 1 |
| `testnet/worker/src/prices.js` | 1 |

## Top Matched Patterns

| Pattern | Findings |
|---|---:|
| `wallet` | 31 |
| `signature` | 25 |
| `secp256k1` | 11 |
| `exchange` | 10 |
| `keccak` | 8 |
| `Signature` | 5 |
| `Wallet` | 3 |
| `Keccak256` | 2 |
| `Sha256` | 2 |
| `.sign(` | 1 |
| `secrets.` | 1 |

## Detailed Findings

### Elliptic-curve cryptography usage detected

**Rule:** `PQC-ECC-001`
**Severity:** `high`
**Category:** `Blockchain signatures`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `ecc, ecdsa, ed25519, secp256k1, asset-control-risk`

**References:** NIST IR 8547 (ECDSA/EdDSA deprecated 2030, disallowed 2035), FIPS 204 ML-DSA, FIPS 205 SLH-DSA

**Description:** Elliptic-curve cryptography rests on the hardness of the elliptic-curve discrete logarithm problem, which Shor's algorithm also solves. ECC is the load-bearing primitive for wallet signing, transaction authorisation, validator identity, and TLS key agreement, so it usually represents the largest single migration surface in a crypto codebase.

**Recommendation:** Inventory each ECC use and rank by how long the key lives and how much authority it holds. Long-lived asset-control keys migrate first; ephemeral session keys migrate with the transport layer. For chains, note that on-chain public keys are already published, so 'harvest now, forge later' applies the moment a key is exposed rather than at migration time.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `site/js/functor-sign.js` | 69 | `high` | `secp256k1` | `export function makeSigner({ keccak_256, secp256k1 }) {` |
| `site/js/functor-sign.js` | 119 | `high` | `secp256k1` | `const s = secp256k1.sign(d, priv);` |
| `site/js/functor-sign.js` | 124 | `high` | `secp256k1` | `return hex(keccak_256(secp256k1.getPublicKey(priv, false).slice(1)).slice(12));` |
| `site/js/functor-sign.js` | 133 | `high` | `secp256k1` | `return { digest, typedData, signDigest, addressOf, agentRequest, hex, fromHex, randomKey: () => secp256k1.utils.randomPrivateKey() };` |
| `site/js/trade.js` | 14 | `high` | `secp256k1` | `import { secp256k1 } from "https://cdn.jsdelivr.net/npm/@noble/curves@1.9.1/secp256k1/+esm";` |
| `site/js/trade.js` | 17 | `high` | `secp256k1` | `const S = makeSigner({ keccak_256, secp256k1 });` |
| `site/js/wallet.js` | 12 | `high` | `secp256k1` | `import { secp256k1 } from "https://cdn.jsdelivr.net/npm/@noble/curves@1.9.1/secp256k1/+esm";` |
| `site/js/wallet.js` | 15 | `high` | `secp256k1` | `export const S = makeSigner({ keccak_256, secp256k1 });` |

### Digital signature flow detected

**Rule:** `PQC-SIGN-001`
**Severity:** `medium`
**Category:** `Blockchain signatures`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `signature, transaction-signing, authorization, migration-inventory`

**References:** NIST IR 8547, FIPS 204 ML-DSA, FIPS 205 SLH-DSA

**Description:** Signature verification paths are where a post-quantum migration actually becomes hard: a verifier must usually accept both the old and the new scheme for a long transition window, and consensus systems must agree on when that window closes.

**Recommendation:** Classify each signature flow by what it authorises: transaction value, identity, certificates, governance, deployment, validator operation, or API authentication. For each, decide now whether the verifier can be made algorithm-agnostic, because that refactor is the long pole in migration.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `site/js/functor-sign.js` | 119 | `high` | `.sign(` | `const s = secp256k1.sign(d, priv);` |

### Randomness source detected

**Rule:** `PQC-RNG-001`
**Severity:** `medium`
**Category:** `General cryptography`
**Migration priority:** `low`
**Class:** `inventory`
**Tags:** `rng, entropy, key-generation`

**References:** CWE-330: Use of Insufficiently Random Values, NIST SP 800-90A/B/C

**Description:** Every key, nonce, IV, and salt in the system traces back to a randomness source, which makes the RNG inventory a prerequisite for trusting any other finding in this report. Quantum computing does not change this; weak randomness is a classical, present-tense failure.

**Recommendation:** Confirm each source is a platform CSPRNG and that entropy is available at the time it is first used. Early-boot and container-start key generation is a recurring real-world failure mode. See CRYPTO-RNG-INSECURE-001 for sources that are actively unsafe.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `deploy/all.sh` | 23 | `high` | `secrets.` | `bash "$ROOT/tools/check-secrets.sh" --tracked \|\| { echo "credential check failed: nothing deployed"; exit 1; }` |

### Wallet or private-key handling detected

**Rule:** `PQC-WALLET-001`
**Severity:** `critical`
**Category:** `Wallet infrastructure`
**Migration priority:** `critical`
**Class:** `inventory`
**Tags:** `wallet, private-key, seed, asset-control-key, custody-risk`

**References:** NIST IR 8547, BIP-32 / BIP-39 / BIP-44, CWE-320: Key Management Errors

**Description:** Wallet keys are the longest-lived and highest-authority secrets in a crypto system. A hierarchical deterministic wallet compounds this: a single compromised master seed derives every child key, past and future, so the blast radius of one leak is the entire wallet tree.

**Recommendation:** Review generation, storage, backup, recovery, export, rotation, and destruction separately; most incidents come from backup and export paths rather than from the signing code. Separate hot, warm, cold, custodial, and non-custodial flows, and record which keys can be rotated at all, because keys that cannot be rotated cannot be migrated later either.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `chain/crates/functor-node/src/eip712.rs` | 166 | `medium` | `wallet` | `let person = "Person(string name,address wallet)";` |
| `chain/crates/functor-node/src/eip712.rs` | 167 | `medium` | `wallet` | `let mail = "Mail(Person from,Person to,string contents)Person(string name,address wallet)";` |
| `chain/crates/functor-node/src/node.rs` | 600 | `medium` | `wallet` | `(true, None) => return Err("signatureChainId is required for wallet-signed actions".into()),` |
| `chain/crates/functor-node/src/node.rs` | 611 | `medium` | `wallet` | `\| ApiAction::SpotSend { .. } => signer, // the wallet itself, never an agent` |
| `site/js/app.js` | 34 | `medium` | `wallet` | `var btn = document.querySelector("main.tr, main[data-own-wallet]") ? null : $("#connect");` |
| `site/js/app.js` | 38 | `medium` | `wallet` | `btn.textContent = addr ? short(addr) : "Connect wallet";` |
| `site/js/app.js` | 41 | `medium` | `wallet` | `document.body.classList.toggle("has-wallet", !!addr);` |
| `site/js/site.js` | 38 | `medium` | `wallet` | `css/site.css. Injecting the Buy button and the wallet/exit modals into it` |
| `site/js/site.js` | 39 | `medium` | `wallet` | `dumped their raw markup — "Connect wallet", the MetaMask row, "live at` |
| `site/js/trade.js` | 36 | `medium` | `wallet` | `user: null, wallet: null /* "injected" \| "demo" */, demoKey: null, agentKey: null, agentOk: false,` |
| `site/js/trade.js` | 82 | `medium` | `wallet` | `[/InsufficientUsdc/, "Not enough USDC in your spot wallet. Use Perps ⇄ Spot to move some."],` |
| `site/js/trade.js` | 112 | `medium` | `wallet` | `st.wallet = addr ? kind : null;` |
| `site/js/trade.js` | 114 | `medium` | `wallet` | `btn.textContent = st.user ? short(st.user) + (kind === "demo" ? " (demo)" : "") : "Connect wallet";` |
| `site/js/trade.js` | 139 | `medium` | `Wallet` | `} catch (e) { return toast("Wallet connection was declined.", "err"); }` |
| `site/js/trade.js` | 145 | `medium` | `wallet` | `if (!confirm("No browser wallet found (or you chose a demo wallet).\n\nCreate a demo wallet stored in this browser? Testnet only: it holds mock USDC with no value.")) return;` |
| `site/js/trade.js` | 159 | `medium` | `wallet` | `if (st.wallet === "injected") {` |
| `site/js/trade.js` | 185 | `medium` | `wallet` | `toast("Trading enabled for this browser. Orders no longer need a wallet popup.", "ok");` |
| `site/js/trade.js` | 234 | `medium` | `wallet` | `$("#x-hint").textContent = \`Available: ${usd(avail)}. Your wallet signs the transfer.\`;` |
| `site/js/trade.js` | 279 | `medium` | `wallet` | `if (!confirm(\`This order holds $${fmt(need, 2)} in your spot wallet, which has $${fmt(free, 2)}.\n\nMove $${fmt(top, 2)} from perps to spot first? (one wallet signature)\`)) return;` |
| `site/js/trade.js` | 511 | `medium` | `wallet` | `const conn = st.user ? null : "Connect a wallet to see your account.";` |
| `site/js/trade.js` | 546 | `medium` | `wallet` | `if (!st.user) { go.textContent = "Connect wallet"; $("#o-hint").innerHTML = 'No wallet? <a href="#" id="demo-link">Use a demo wallet</a> (testnet only).'; }` |
| `site/js/trade.js` | 547 | `medium` | `wallet` | `else if (!st.agentOk) { go.textContent = "Enable trading (one signature)"; $("#o-hint").textContent = "Your wallet signs once to approve a session key in this browser. It can trade, never withdraw."; }` |
| `site/js/trade.js` | 553 | `medium` | `wallet` | `: st.spotBal && st.spotBal.balances[0].total === 0 && st.side === "buy" ? "Buying moves USDC from perps to spot first (one wallet signature)." : "";` |
| `site/js/trade.js` | 840 | `medium` | `wallet` | `window.addEventListener("beforeunload", () => store.set("fctr_demo_active", st.wallet === "demo" ? "1" : "0"));` |
| `site/js/vaults.js` | 7 | `medium` | `wallet` | `import { API, W, info, connect, onChange, agentSend, faucet, restore } from "./wallet.js";` |
| `site/js/vaults.js` | 37 | `medium` | `wallet` | `if (/neither the account nor its approved agent/.test(s)) return "Enable trading first (one wallet signature).";` |
| `site/js/vaults.js` | 113 | `medium` | `wallet` | `if (!W.user) { go.textContent = "Connect wallet"; $("#d-hint").textContent = "No wallet? A demo wallet is offered (testnet only)."; return; }` |
| `site/js/vaults.js` | 114 | `medium` | `wallet` | `if (!W.agentOk) { go.textContent = "Enable trading (one signature)"; $("#d-hint").textContent = "Your wallet approves a session key in this browser once. It can trade and move money into and out of vaults for you, never withdraw from the ex...` |
| `site/js/vaults.js` | 155 | `medium` | `Wallet` | `if (!W.user) { const r = await connect(); if (r === "declined") toast("Wallet connection was declined.", "err"); return; }` |
| `site/js/vaults.js` | 156 | `medium` | `wallet` | `if (!W.agentOk) { const { enableTrading } = await import("./wallet.js"); const r = await enableTrading(); if (!r.ok) toast(human(r.error), "err"); else toast("Trading enabled for this browser.", "ok"); return; }` |

_Suppressed 4 additional repeated rows for this rule to keep the report readable._

### Exchange, custody, or trading secret signal detected

**Rule:** `PQC-EXCHANGE-001`
**Severity:** `high`
**Category:** `Exchange / custody infrastructure`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `exchange, custody, api-key, hmac, withdrawal, hft`

**References:** NIST IR 8547, CWE-522: Insufficiently Protected Credentials

**Description:** Exchange and custody infrastructure mixes two risk classes that expire on very different clocks. HMAC-based API authentication is symmetric and survives the quantum transition with a larger key. Withdrawal signing and custody keys are asymmetric and do not.

**Recommendation:** Split the inventory along that line before doing anything else. HMAC and symmetric paths need key-size review only. Withdrawal authority, custody keys, and HSM-held signing keys need a full migration plan, including whether the HSM firmware will ever support ML-DSA.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `chain/crates/functor-core/Cargo.toml` | 3 | `medium` | `exchange` | `description = "FunctorCore: the deterministic exchange state machine of the Functor L1"` |

### Elliptic-curve cryptography usage detected

**Rule:** `PQC-ECC-001`
**Severity:** `high`
**Category:** `Blockchain signatures`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `ecc, ecdsa, ed25519, secp256k1, asset-control-risk`

**References:** NIST IR 8547 (ECDSA/EdDSA deprecated 2030, disallowed 2035), FIPS 204 ML-DSA, FIPS 205 SLH-DSA

**Description:** Elliptic-curve cryptography rests on the hardness of the elliptic-curve discrete logarithm problem, which Shor's algorithm also solves. ECC is the load-bearing primitive for wallet signing, transaction authorisation, validator identity, and TLS key agreement, so it usually represents the largest single migration surface in a crypto codebase.

**Recommendation:** Inventory each ECC use and rank by how long the key lives and how much authority it holds. Long-lived asset-control keys migrate first; ephemeral session keys migrate with the transport layer. For chains, note that on-chain public keys are already published, so 'harvest now, forge later' applies the moment a key is exposed rather than at migration time.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `chain/crates/functor-node/src/eip712.rs` | 5 | `medium` | `secp256k1` | `//! recovered from the 65-byte signature with secp256k1 public-key recovery.` |
| `chain/crates/functor-node/src/eip712.rs` | 105 | `medium` | `secp256k1` | `/// Ethereum address of an uncompressed secp256k1 public key.` |
| `site/js/functor-sign.js` | 14 | `medium` | `secp256k1` | `*   makeSigner({ keccak_256, secp256k1 })   // from @noble/hashes and @noble/curves` |

### Exchange, custody, or trading secret signal detected

**Rule:** `PQC-EXCHANGE-001`
**Severity:** `high`
**Category:** `Exchange / custody infrastructure`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `exchange, custody, api-key, hmac, withdrawal, hft`

**References:** NIST IR 8547, CWE-522: Insufficiently Protected Credentials

**Description:** Exchange and custody infrastructure mixes two risk classes that expire on very different clocks. HMAC-based API authentication is symmetric and survives the quantum transition with a larger key. Withdrawal signing and custody keys are asymmetric and do not.

**Recommendation:** Split the inventory along that line before doing anything else. HMAC and symmetric paths need key-size review only. Withdrawal authority, custody keys, and HSM-held signing keys need a full migration plan, including whether the HSM firmware will ever support ML-DSA.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `site/js/site.js` | 813 | `medium` | `exchange` | `var s = "  FUNCTOR — the exchange engineered by quants  ·  perps · options · indices · vaults  ·  fixed 50,000,000 $FCTR  ·  no VCs  ·  founder locked 1yr + 3yr vest  ·  built in public  ·  verify, don't trust  ";` |
| `site/js/trade.js` | 180 | `medium` | `exchange` | `const r = await post("/exchange", req);` |
| `site/js/vaults.js` | 114 | `medium` | `exchange` | `if (!W.agentOk) { go.textContent = "Enable trading (one signature)"; $("#d-hint").textContent = "Your wallet approves a session key in this browser once. It can trade and move money into and out of vaults for you, never withdraw from the ex...` |
| `site/js/wallet.js` | 102 | `medium` | `exchange` | `const r = await post("/exchange", req);` |
| `site/js/wallet.js` | 114 | `medium` | `exchange` | `return post("/exchange", S.agentRequest(W.agentKey, { ...action, account: W.user }, nextNonce()));` |
| `testnet/worker/src/index.js` | 53 | `medium` | `exchange` | `endpoints: ["POST /info", "POST /exchange", "POST /faucet", "GET /status", "GET /ws"],` |
| `testnet/worker/src/index.js` | 191 | `medium` | `exchange` | `if (url.pathname === "/exchange" \|\| url.pathname === "/faucet") {` |
| `testnet/worker/src/limits.js` | 33 | `medium` | `exchange` | `if (path === "/exchange") {` |
| `testnet/worker/src/prices.js` | 56 | `medium` | `exchange` | `const j = await get(\`https://api.exchange.coinbase.com/products/${m.coinbase}/ticker\`);` |

### Digital signature flow detected

**Rule:** `PQC-SIGN-001`
**Severity:** `medium`
**Category:** `Blockchain signatures`
**Migration priority:** `high`
**Class:** `inventory`
**Tags:** `signature, transaction-signing, authorization, migration-inventory`

**References:** NIST IR 8547, FIPS 204 ML-DSA, FIPS 205 SLH-DSA

**Description:** Signature verification paths are where a post-quantum migration actually becomes hard: a verifier must usually accept both the old and the new scheme for a long transition window, and consensus systems must agree on when that window closes.

**Recommendation:** Classify each signature flow by what it authorises: transaction value, identity, certificates, governance, deployment, validator operation, or API authentication. For each, decide now whether the verifier can be made algorithm-agnostic, because that refactor is the long pole in migration.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `chain/crates/functor-node/Cargo.toml` | 3 | `medium` | `signature` | `description = "Functor test node: signatures, mempool, block producer, faucet, market maker, queries (pure Rust, no I/O)"` |
| `chain/crates/functor-node/src/eip712.rs` | 8 | `medium` | `Signature` | `use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};` |
| `chain/crates/functor-node/src/eip712.rs` | 124 | `medium` | `Signature` | `let signature = Signature::from_slice(&sig[..64]).map_err(\|_\| SigError::NonCanonical)?;` |
| `chain/crates/functor-node/src/eip712.rs` | 124 | `medium` | `signature` | `let signature = Signature::from_slice(&sig[..64]).map_err(\|_\| SigError::NonCanonical)?;` |
| `chain/crates/functor-node/src/eip712.rs` | 126 | `medium` | `signature` | `if signature.normalize_s().is_some() {` |
| `chain/crates/functor-node/src/eip712.rs` | 130 | `medium` | `signature` | `let key = VerifyingKey::recover_from_prehash(digest, &signature, rid).map_err(\|_\| SigError::Unrecoverable)?;` |
| `chain/crates/functor-node/src/eip712.rs` | 218 | `medium` | `signature` | `fn malformed_and_non_canonical_signatures_are_rejected() {` |
| `chain/crates/functor-node/src/eip712.rs` | 228 | `medium` | `Signature` | `let high = Signature::from_scalars(r, -*s).unwrap();` |
| `chain/crates/functor-node/src/node.rs` | 264 | `medium` | `signature` | `pub signature: String,` |
| `chain/crates/functor-node/src/node.rs` | 267 | `medium` | `signature` | `pub signature_chain_id: Option<u64>,` |
| `chain/crates/functor-node/src/node.rs` | 588 | `medium` | `signature` | `let sig = eip712::from_hex(&req.signature).ok_or("signature is not hex")?;` |
| `chain/crates/functor-node/src/node.rs` | 598 | `medium` | `signature` | `let chain_id = match (user_signed, req.signature_chain_id) {` |
| `chain/crates/functor-node/src/node.rs` | 600 | `medium` | `signature` | `(true, None) => return Err("signatureChainId is required for wallet-signed actions".into()),` |
| `chain/crates/functor-node/src/node.rs` | 604 | `medium` | `signature` | `let signer = eip712::recover(&digest, &sig).map_err(\|e\| format!("bad signature: {e:?}"))?;` |
| `site/js/functor-sign.js` | 130 | `medium` | `signature` | `return { action, nonce, signature: signDigest(priv, digest(primary, msg, AGENT_CHAIN_ID)) };` |
| `site/js/trade.js` | 157 | `medium` | `signature` | `let signature, chainId;` |
| `site/js/trade.js` | 162 | `medium` | `signature` | `signature = await eth.request({ method: "eth_signTypedData_v4", params: [st.user, JSON.stringify(typed)] });` |
| `site/js/trade.js` | 165 | `medium` | `signature` | `signature = S.signDigest(st.demoKey, S.digest(primary, msg, chainId));` |
| `site/js/trade.js` | 168 | `medium` | `Signature` | `toast("Signature declined.", "err");` |
| `site/js/trade.js` | 171 | `medium` | `signature` | `return { action, nonce, signature, signatureChainId: chainId };` |
| `site/js/trade.js` | 279 | `medium` | `signature` | `if (!confirm(\`This order holds $${fmt(need, 2)} in your spot wallet, which has $${fmt(free, 2)}.\n\nMove $${fmt(top, 2)} from perps to spot first? (one wallet signature)\`)) return;` |
| `site/js/trade.js` | 547 | `medium` | `signature` | `else if (!st.agentOk) { go.textContent = "Enable trading (one signature)"; $("#o-hint").textContent = "Your wallet signs once to approve a session key in this browser. It can trade, never withdraw."; }` |
| `site/js/trade.js` | 553 | `medium` | `signature` | `: st.spotBal && st.spotBal.balances[0].total === 0 && st.side === "buy" ? "Buying moves USDC from perps to spot first (one wallet signature)." : "";` |
| `site/js/vaults.js` | 37 | `medium` | `signature` | `if (/neither the account nor its approved agent/.test(s)) return "Enable trading first (one wallet signature).";` |
| `site/js/vaults.js` | 114 | `medium` | `signature` | `if (!W.agentOk) { go.textContent = "Enable trading (one signature)"; $("#d-hint").textContent = "Your wallet approves a session key in this browser once. It can trade and move money into and out of vaults for you, never withdraw from the ex...` |
| `site/js/wallet.js` | 84 | `medium` | `signature` | `let signature, chainId;` |
| `site/js/wallet.js` | 88 | `medium` | `signature` | `signature = await eth.request({ method: "eth_signTypedData_v4", params: [W.user, JSON.stringify(S.typedData(primary, msg, chainId))] });` |
| `site/js/wallet.js` | 91 | `medium` | `signature` | `signature = S.signDigest(W.demoKey, S.digest(primary, msg, chainId));` |
| `site/js/wallet.js` | 94 | `medium` | `signature` | `return { action, nonce, signature, signatureChainId: chainId };` |
| `site/js/wallet.js` | 101 | `medium` | `Signature` | `if (!req) return { ok: false, error: "Signature declined." };` |

### Hash function usage detected

**Rule:** `PQC-HASH-001`
**Severity:** `info`
**Category:** `General cryptography`
**Migration priority:** `low`
**Class:** `inventory`
**Tags:** `hash, integrity, grover-margin`

**References:** NIST IR 8547, FIPS 205 SLH-DSA (hash-based signatures)

**Description:** Modern hash functions are not broken by quantum computers. Grover's algorithm gives at most a quadratic speedup on preimage search, and collision search gains far less than early analyses suggested once realistic memory costs are included. SHA-256 remains adequate.

**Recommendation:** Record hash functions and their security role rather than treating them as migration items. Pay attention to address derivation, commitments, Merkle trees, and proof systems, where the hash choice is consensus-critical and cannot be changed without a fork. This section exists to complete the inventory, not to generate work.

| File | Line | Confidence | Match | Source Line |
|---|---:|---|---|---|
| `chain/crates/functor-core/src/state_hash.rs` | 16 | `medium` | `Sha256` | `use sha2::{Digest, Sha256};` |
| `chain/crates/functor-core/src/state_hash.rs` | 164 | `medium` | `Sha256` | `Sha256::digest(&bytes).into()` |
| `chain/crates/functor-node/src/eip712.rs` | 9 | `medium` | `Keccak256` | `use sha3::{Digest, Keccak256};` |
| `chain/crates/functor-node/src/eip712.rs` | 11 | `medium` | `keccak` | `pub fn keccak(data: &[u8]) -> [u8; 32] {` |
| `chain/crates/functor-node/src/eip712.rs` | 12 | `medium` | `Keccak256` | `Keccak256::digest(data).into()` |
| `chain/crates/functor-node/src/eip712.rs` | 33 | `medium` | `keccak` | `Value::Str(s) => w = keccak(s.as_bytes()),` |
| `chain/crates/functor-node/src/eip712.rs` | 45 | `medium` | `keccak` | `buf.extend_from_slice(&keccak(encode_type.as_bytes()));` |
| `chain/crates/functor-node/src/eip712.rs` | 49 | `medium` | `keccak` | `keccak(&buf)` |
| `chain/crates/functor-node/src/eip712.rs` | 108 | `medium` | `keccak` | `let h = keccak(&point.as_bytes()[1..]);` |
| `chain/crates/functor-node/src/eip712.rs` | 192 | `medium` | `keccak` | `let sk = SigningKey::from_bytes(&keccak(b"cow").into()).unwrap();` |
| `chain/crates/functor-node/src/eip712.rs` | 198 | `medium` | `keccak` | `let sk = SigningKey::from_bytes(&keccak(b"functor test key").into()).unwrap();` |
| `chain/crates/functor-node/src/eip712.rs` | 225 | `medium` | `keccak` | `let sk = SigningKey::from_bytes(&keccak(b"k").into()).unwrap();` |


## Likely Noise

These matches were suppressed from the report body. They are shown here so the suppression is auditable, and so genuinely interesting patterns are not hidden by the filter. A high count usually means the pattern is domain vocabulary in this repository rather than a specific cryptographic signal.

| Rule | Pattern | Reason | Matches | Example |
|---|---|---|---:|---|
| `PQC-TLS-001` | `https://` | generic term | 64 | `deploy/all.sh:34` |
| `PQC-NONCE-001` | `nonce` | generic term | 63 | `chain/crates/functor-node/src/eip712.rs:200` |
| `PQC-WALLET-001` | `wallet` | generic term, comment line | 39 | `chain/crates/functor-node/src/eip712.rs:4` |
| `PQC-TLS-001` | `fetch(` | generic term | 27 | `marketing/herald/src/index.js:25` |
| `PQC-HASH-001` | `sha512` | dependency metadata, not project code | 26 | `testnet/package-lock.json:17` |
| `PQC-TLS-001` | `https://` | dependency metadata, not project code | 26 | `testnet/package-lock.json:16` |
| `PQC-SIGN-001` | `signature` | generic term, comment line | 17 | `chain/crates/functor-core/src/block.rs:68` |
| `PQC-EXCHANGE-001` | `exchange` | generic term, comment line | 13 | `chain/crates/functor-core/src/block.rs:174` |
| `PQC-EXCHANGE-001` | `withdrawal` | generic term, comment line | 11 | `chain/crates/functor-core/src/block.rs:64` |
| `CRYPTO-RNG-INSECURE-001` | `Math.random` | generic term | 8 | `site/js/main.js:253` |
| `PQC-HASH-001` | `keccak256` | comment line | 7 | `chain/crates/functor-node/src/eip712.rs:3` |
| `PQC-NONCE-001` | `nonce` | generic term, comment line | 6 | `chain/crates/functor-node/src/eip712.rs:41` |
| `PQC-TLS-001` | `fetch(` | minified or generated line | 5 | `site/js/site.js:509` |
| `PQC-WALLET-001` | `Wallet` | generic term, comment line | 4 | `chain/crates/functor-node/src/node.rs:288` |
| `PQC-HASH-001` | `SHA-256` | comment line | 2 | `chain/crates/functor-core/src/block.rs:386` |
| `PQC-TLS-001` | `https://` | minified or generated line | 2 | `site/js/site.js:518` |
| `PQC-TLS-001` | `requests.` | generic term, comment line | 2 | `chain/crates/functor-node/src/node.rs:46` |
| `CRYPTO-RNG-INSECURE-001` | `mt19937` | generic term | 1 | `derivatives-cpp/src/main.cpp:174` |
| `PQC-SIGN-001` | `Signature` | generic term, comment line | 1 | `chain/crates/functor-core/src/block.rs:17` |

_Showing the top 19 noise groups by match count._

## Recommended Next Steps

1. Review the Priority Review Queue first.
2. Treat hardcoded secrets as immediate operational security issues, not merely post-quantum issues.
3. Separate public-key migration concerns from hash/symmetric-primitive inventory.
4. Identify wallet, validator, governance, bridge, treasury, custody, and exchange key flows.
5. Classify long-lived asset-control keys separately from short-lived transport/session keys.
6. Convert repeated source-code signals into a clean cryptographic inventory.
7. Add project-specific rules as the codebase becomes better understood.
8. Scan the Likely Noise section for any pattern that is meaningful in this specific repository, and promote it to a project-specific rule.

---

Generated by Quantum Core PQ Scanner. This is a heuristic readiness scanner, not a formal security audit.
