{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"4a69695e-f640-43bc-9022-e87026b354c3","kind":"audit","nodes":[{"acceptedSubmissionHash":null,"dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"failed"},{"acceptedSubmissionHash":null,"dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"ready"},{"acceptedSubmissionHash":null,"dependsOn":["audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"failed"},{"acceptedSubmissionHash":null,"dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"ready"}],"objective":"Audit BunkerVault, a shared post-quantum authorized vault for public ERC-20 and ETH balances (Foundry repo, solc 0.8.30, Cancun). Read README.md and docs/CONTRACT.md (specification, invariants, trust assumptions) first.\n\nSCOPE\nPrimary: src/BunkerVault.sol, src/AuthorizationMessage.sol, src/interfaces/. Secondary: how the vault relies on the vendored research ML-DSA-44 verifier test/vendor/fireblocks/MLDSA44Verifier.sol (return handling, code-hash and helper pinning, 20,545-byte key blob layout); report a verifier bug only if it lets a signature verify without the key. Out of scope: lib/ (vendored forge-std, OpenZeppelin 5.6.1), test/mocks/, script/, deployments/, gas optimizations without security impact.\n\nDESIGN\nOne contract holds many vaults. vaultId = keccak(domain, keccak(1312-byte ML-DSA public key)). createVault: the payer submits it themselves, an ML-DSA proof of possession over the full registration intent is verified BEFORE any fee-token call, then exactly 10**decimals fee tokens are pulled: 90% stay locked forever (totalLockedFees), 10% go to an immutable developer. The payer becomes controllerOf[vaultId], permanently. Every later operation (depositERC20, withdrawERC20, depositETH, withdrawETH, rotateKey) goes through _consumeAuthorization: msg.sender must be the controller AND a valid ML-DSA signature must cover chain id, vault address, vaultId, key hash, key code hash, operation constant, paramsHash of all arguments, exact next nonce, expiry (max 1 hour) and key epoch. The nonce is consumed before token/ETH calls, under nonReentrant. ETH is wrapped into one constructor-pinned WETH; receive() accepts ETH only from WETH during withdrawETH. A token's runtime code hash is pinned at its first deposit. rotateKey needs the old key's signature and the new key's possession signature; nonce stays monotonic, epoch increments, old keys stay burned. No owner, admin, upgrade, rescue, approval or ECDSA fallback exists.\n\nTHREAT MODEL\nThe vault's key file is secret. The attacker may control the controller wallet (e.g. an ECDSA break), any other EOA, contracts, malicious or reentrant tokens, the mempool and block ordering, and may register their own vaults and keys. Main question: can anyone WITHOUT a vault's ML-DSA key move, lock, freeze or misdirect that vault's assets, or take assets from other vaults or the locked fees?\n\nINVARIANTS TO BREAK\n1. No state change of a vault without a valid signature from its current key plus a transaction from its controller.\n2. A signature authorizes exactly one operation with exactly its signed parameters, once (no replay across nonces, epochs, operations, vaults, contracts or chains; no ambiguous encodings).\n3. For every token: sum of publicBalanceOf over vaults == totalPublicLiability <= token.balanceOf(vault). One vault, or a malicious token, cannot reduce another vault's withdrawable balance of a well-behaved token.\n4. totalLockedFees only grows; the fee token can never leave except the developer share at registration; the fee token is never a custody asset.\n5. The contract holds no ETH between calls except ETH forced in, which is never credited to anyone.\n6. Any failed operation reverts completely, including nonce consumption, asset admission and fee settlement.\n7. Registration and rotation reject wrong key sizes, blob size/prefix/code hash, reused keys, and run PoP before any external fee call.\n\nFOCUS PER SPECIALIST\n- Math: fee split for decimals 1..18, balance-delta checks in deposits/withdrawals/fee settlement, overflow/underflow paths, heldTokens paging bounds.\n- Permissions: _consumeAuthorization and every caller of it, controller binding, executor/feePayer checks in createVault and rotateKey, domain separation in AuthorizationMessage and paramsHash encodings, rotation logic, absence of privileged paths, abuse of open admission (registering someone else's key or blob) beyond the disclosed key-squatting risk.\n- Economics: fee collection and locking, griefing (blocking a payer slot, front-running registration or rotation, permanent freezes), donations, forced ETH, cost to an attacker vs harm to victims.\n- Control flow: reentrancy through tokens, WETH and ETH recipients, the unwrapping flag and receive()/fallback(), ordering of checks/effects/interactions, tokens whose code changes after pinning (metamorphic or proxy), tokens that lie in balanceOf/totalSupply, ERC-777 style hooks, revert and return-data handling of the verifier and SafeERC20.\n\nKNOWN AND ACCEPTED, see docs/CONTRACT.md section 9 (report only if the impact goes beyond what is stated)\nBalances and amounts are public. The contract cannot prove a blob equals prepare(pk) or that a key came from honest KeyGen (clients must check this off-chain); anyone can register a victim's public key first with a blob they control, which only denies that key. Losing or compromising the controller wallet cannot be fixed (no controller change by design). The controller wallet and Ethereum are not quantum safe. The fee token is trusted and reviewed separately. A malicious token can lie about itself; accepting a token is not certifying it, but harm must stay confined to that token. The ML-DSA verifier is unaudited research code. Each verification costs about 1.35M gas.\n\nHOW TO WORK\nforge build; forge test (103 tests). test/utils/VaultFixture.sol builds a vault with a mock verifier that accepts exactly the (key blob, message, signature) it was armed with: use authorize(...) and arm(...) to craft authorizations. test/utils/RealVerifierBase.sol deploys the real verifier. forge test --ffi adds real-key end-to-end tests (needs npm ci in test/ffi, 122 tests).\n\nDELIVERABLE\nEach finding: severity (Critical, High, Medium, Low, Informational), file:line, root cause, exact attack sequence and preconditions, impact on which vaults/assets, a Foundry test under test/ that fails on the current code (or a precise reason why one cannot be written), and a minimal fix. Mark any finding that only restates an accepted risk. Also list the invariants above you checked and could not break, with the reasoning.","parentJobId":null,"planHash":"956327e91e5d135073a6855ddd7fab4d0515143bc1b50abeb0539ab6b1643b9f","previousHash":"c308aa9f8e1741f98a3a7bce3daf8309732932ad8bd4130a1c033a41e57c5a4b","projectId":"4a69695e-f640-43bc-9022-e87026b354c3","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"f0e555d26dd2e2d03acc4d0a80c2259564ba8657c4bacfa1d115494927d463d2","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bdd9b74dce66953d","findings":[],"hash":"20068af7dfd0e7c1707aabdac91c346303e923b7e873fee9f5e26a4e4b585a34","nodeId":"8a2419da-dcc0-4294-a745-d1b30dac01d6","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":645}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"918f8261a6fd589c","findings":[],"hash":"279a4c85e117d2835b8880fa1c6a98212651731e50aaacd5567321d545ee8e31","nodeId":"f26bae53-e076-4721-820b-63286532c475","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":1386}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"af1843f371022d4a","findings":[],"hash":"7465ef319f1514428697f8d786d0fb5995750a2a6af6dc1b2136a90b340bd251","nodeId":"61adf776-c3b7-41bd-9b76-3f0de397e542","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":1473}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"a5c5e95a2ed07117","findings":[],"hash":"b56d2fdc0fe44ee78c7e3b741a9577e8e7823a7a2029d9ad2bb4029749762467","nodeId":"8a2419da-dcc0-4294-a745-d1b30dac01d6","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":792}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0e78515c4d95885a","findings":[],"hash":"d0ad91ef83d24e79c8a0db99c3692ac83055a3fa3c4893768c529de95948a178","nodeId":"4286aad2-a98d-40cb-84a5-4a0487c5c6d3","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":1013}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"9c6767b941fcfedc","findings":[],"hash":"ecc939c7407d9224d32ad916b0ee524434aa658e5458456b6d73308632ce1d24","nodeId":"4286aad2-a98d-40cb-84a5-4a0487c5c6d3","outcome":"failed","summary":"refused to run this repository's build, so the agent was not started: fs_permissions: filesystem access \"true\" on \"./cache/ffi\" reaches outside the build output","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":1513}}],"verification":[]}