N-07 Missing Relation Between ownPublicKey and Its Associated Private Key
Severity: Note
Source: Midnight Foundation #2 — OpenZeppelin Compact Contracts Audit, Release 0.3.0-alpha.1 (2026-08-18)
assertOnlyOwner derives an owner identifier from the bytes returned by ownPublicKey together with the witness wit_secretNonce, hashes the pair through _computeOwnerId, and compares the resulting commitment against the stored one. Nothing in that path establishes that the caller controls the private key belonging to the public key it supplies: there is no signature, no spend authorization, and no other in-circuit proof of key ownership.
The consequence is that the effective credential is the nonce alone. A public key contributes to the identifier but not to authentication, so an attacker who learns the nonce and presents the owner's public key bytes satisfies the gate and reaches every owner-restricted circuit, including transferOwnership and renounceOwnership. The module is consistent with itself here, its security considerations already treating nonce secrecy as load-bearing, so this is recorded as an informational observation rather than a defect. What is not stated is that the public key adds no security of its own, which a reader may not infer from a module named for public-key ownership, nor is any entropy requirement placed on the nonce that the credential now rests upon entirely.
Consider stating explicitly that the secret nonce is the sole authenticating secret, and specifying the entropy it must carry, so that an integrator does not treat the public key as a second factor. Where a stronger guarantee is wanted, consider binding the check to a primitive that proves control of the key rather than merely quoting its bytes.
N-07 Missing Relation Between
ownPublicKeyand Its Associated Private KeySeverity: Note
Source: Midnight Foundation #2 — OpenZeppelin Compact Contracts Audit, Release 0.3.0-alpha.1 (2026-08-18)
assertOnlyOwnerderives an owner identifier from the bytes returned byownPublicKeytogether with the witnesswit_secretNonce, hashes the pair through_computeOwnerId, and compares the resulting commitment against the stored one. Nothing in that path establishes that the caller controls the private key belonging to the public key it supplies: there is no signature, no spend authorization, and no other in-circuit proof of key ownership.The consequence is that the effective credential is the nonce alone. A public key contributes to the identifier but not to authentication, so an attacker who learns the nonce and presents the owner's public key bytes satisfies the gate and reaches every owner-restricted circuit, including
transferOwnershipandrenounceOwnership. The module is consistent with itself here, its security considerations already treating nonce secrecy as load-bearing, so this is recorded as an informational observation rather than a defect. What is not stated is that the public key adds no security of its own, which a reader may not infer from a module named for public-key ownership, nor is any entropy requirement placed on the nonce that the credential now rests upon entirely.Consider stating explicitly that the secret nonce is the sole authenticating secret, and specifying the entropy it must carry, so that an integrator does not treat the public key as a second factor. Where a stronger guarantee is wanted, consider binding the check to a primitive that proves control of the key rather than merely quoting its bytes.