ODP Security Model · v0.6 reference line

Author: Andrei Chernikov

Object Digital Passport is a registry of claims, not a guarantee of physical authenticity.

This document describes the threat model, known limitations, and recommendations for the reference line on main: ObjectDigitalPassport with packed CONTRACT_VERSION = 6 — the on-chain card, the anchors[] identification block committed as anchorsHash, and append-only passport events — together with the paired satellites (ODPPassportLib, ODPWalletDocumentAnchor, ODPCounterfeitConcern, ODPRegistryRelations, ODPPassportProofRegistry, ODPExtensionMintRouter) and the static web pages wired via NET.*. Earlier lines (v0.5 and before) are different registries (address + ABI) and do not migrate. Normative field names, rules, and the canonical registry addresses: SPEC.md.


What the protocol guarantees

Registry versions: v0.6 vs older 0.x and future v1

What the protocol does NOT guarantee

URLs vs hashes (important)


Reference line trust boundaries (v0.6)

Governance (single on-chain address)

Mint agent (delegated mint)

Publishing agent (URLs only)

Extensions (IODPExtension)

P- and M-type profiles (social / institutional trust)

What stays mutable in v0.6 (and what does not)

Optional satellite: ODPCounterfeitConcern


Known risks and mitigations (carried forward)

1. Social engineering and phishing

Risk: Fake verifier site; fake “official” P/M institution; malicious dataUrl / noteUrl; misleading interpretation of a concern flag as “certified fake”.

Mitigation:

2. Stolen creator or owner wallet

Risk: updatePassportUrls (with confirmedDataHash) does not stop an attacker who can read dataHash from chain (stolen key).

Mitigation: Hardware wallet; limit exposure; revokePassport if needed (creator or governance).

3. Stolen mint agent wallet

Risk: While delegation is active, an agent can mint on behalf of the principal (within caps and rules).

Mitigation: Principal revokeMintAgentRole immediately; short-lived delegation; separate agent hot wallet with minimal privileges elsewhere.

4. Multiple wallets (anti-spam, not anti-Sybil)

Risk: C / B tiers have per-calendar-month caps; P / M are unlimited in the reference contract. New wallets bypass per-wallet caps.

Accepted tradeoff: permissionless design; off-chain reputation if needed.

5–8. Frontend CDN, RPC privacy, canonical JSON, NFC, numbered seals

Unchanged from earlier guidance — see SPEC §9–11 for JSON/NFC levels.


Deployer key security

The deployer key can freeze() the registry and nothing else; it cannot alter historical records. Protect it offline, and keep it long-lived — the address is fixed at deploy time and cannot be rotated. Stable v1 is planned to omit registry-wide freeze().


Verifier checklist (for users)

When verifying an object:


Protocol options outside the main registry

Described in SPEC.md:

What an author attestation does and does not prove. It proves that whoever controls the attesting key signed those exact bytes for that passport on that registry — the EIP-712 domain binds the signature to this chain and this satellite address, so it cannot be replayed elsewhere. It does not prove authorship in any legal sense, and it does not vouch for the key holder's real-world identity: as everywhere else in ODP, an ID is only as trustworthy as the issuer's own published channels (§3).

Reading attestations safely: an attestation is meaningful only while its stored dataHash still equals the passport's current on-chain dataHash — verifiers MUST compare the two before showing it. Its absence is not a negative signal: attestation is optional and most passports will not carry one. Submission is restricted to the passport's creator or owner so a third party cannot squat the single, one-shot attestation slot with a key of their own.


Static analysis in CI (Slither) — findings and triage

Not a substitute for a professional audit. Every push and pull request runs Slither over each entry contract (.github/workflows/ci.yml, job Slither static analysis) with --fail-high --solc-args "--via-ir --optimize". The build fails on high severity.

Current status: no high or critical findings. The detectors that do fire are listed below with the reason each is accepted, so contributors and reviewers can see they were triaged rather than ignored. Re-check this table whenever the contracts change.

Slither detector Where Verdict
uninitialized-local ODPPassportLib.utcYearMonthFromTimestampbool found Accepted. Solidity zero-initializes the local to false; the loop sets it and if (!found) revert EC(83) immediately follows. The detector flags the declaration style, not a defect.
missing-zero-check setRelationsSatellite, setExtensionRouter By design. address(0) is the documented way to clear a satellite pointer; a zero-check would remove that capability.
incorrect-equality profile-type comparisons (t == TYPE_P …), year == cy && month == cm Accepted. These compare a bytes1 type prefix and integer year/month — strict equality is the only correct comparison.
timestamp several functions Mostly noise (the detector taints whole functions, flagging even bytes(x).length > 0). The genuine uses are delegation expiry and UTC month derivation, where a miner's few-second drift is immaterial at that granularity.
reentrancy-events ODPExtensionMintRouter.mint*ViaExtension Low. Only event ordering: the external call targets our own registry and no state is read back afterwards.
unused-return getCreator, getPassportClassification calls By design. These are existence checks — the callee reverts when the record is absent, so the return value is intentionally discarded.
solc-version ^0.8.20 across all files Known limitation — see below.

Known limitation: solc 0.8.20

The deployed v0.6 bytecode was compiled with solc 0.8.20 + --via-ir --optimize. That version carries three known compiler bugs. Two are not applicable to this codebase — verified by inspection: MissingSideEffectsOnSelectorAccess needs .selector access (not used anywhere in chain/contracts/) and VerbatimInvalidDeduplication needs verbatim/inline assembly (not used). The third, FullInlinerNonExpressionSplitArgumentEvaluationOrder, relates to the full inliner and is therefore relevant in principle under --via-ir; it is fixed in 0.8.21+.

The compiler will be raised to ≥0.8.21 when the next registry line (v0.7) is compiled. Recompiling now would change the bytecode without changing the already-deployed v0.6 registry, so the live deployment stays on 0.8.20 and its Polygonscan verification must use that version.

Earlier informal review (Remix)

A pass through Remix (or similar IDE/static tooling) on the reference ObjectDigitalPassport.sol reported no major on-chain issues immediately suggesting direct theft or classic scam patterns, and noted sensible patterns: custom errors, input validation, role checks (governance, creator, owner paths), notFrozen on writes, and limited reentrancy surface on core paths.

Scope note: operators who deploy ODPCounterfeitConcern should include that artifact in their review process; it is a separate contract with its own trust boundary (paired main registry address).

Limits of this class of tools: they do not model off-chain trust (dataUrl / hosted files), governance policy (malicious extensions, revocation power), economic abuse (e.g. a mint agent burning a principal’s C/B monthly quota), or deployment/configuration mistakes. mint*ViaExtension uses staticcall into governance-approved extension contracts — not classic reentrancy into this registry, but a trust boundary on the extension’s correctness.

Treat formal verification, timelocks for governance (off-chain multisig process if bytecode stays minimal), and event consistency as production hardening, not conclusions from a single static run.


Reporting security issues

Open an issue at: https://github.com/object-digital-passport/object-digital-passport/issues

For sensitive disclosures, use GitHub private security advisories for this repository.