Skip to main content

ZK Passport smart contracts reference

info

We're in process of migrating from legacy Circom circuits to Noir Ultra Plonk for passport verification during registration. Until the migration is complete, both are supported resulting in existence of ...Noir methods

A set of contracts facilitating self-issued anonymous identities based on the ZK Passport tech.

Currently, there are seven contract sections:

  1. State — main StateKeeper contract and a set of utility contracts. StateKeeper acts as a singleton state instance with which registration contracts interact. StateKeeper does the following:
    • Stores the Sparse Merkle Tree with registered identities;
    • Stores the Sparse Merkle Tree with registered certificates of ICAO members;
    • Stores and manages some passport and identity information;
    • Manages the "Passport <> Identity" bonds.
  2. Registration — primary section with Registration2 contract, that:
    • Allow users registration (with binding of their identity to the passport);
    • Allow revocation of passport-identity binding;
    • Allow re-issuance of previously revoked identity;
    • Allow registration and revocation of ICAO member's certificates (calls StateKeeper);
    • Call one of the verifier contracts, based on the provided passport type, to verify ZKP;
    • Manage different dispatchers.
  3. Verifiers — auto-generated contracts, the main purpose of which is to verify the ZKP.
  4. Dispatchers — acts as a bridge between registration contracts and:
    • authenticators corresponding to different passport types, if it is a passport dispatcher;
    • signers, if it is a certificate dispatcher.
  5. Signers — responsible for verifying the signature of the ICAO member's certificate. It also stores information required for signature verification (e.g. RSA exponent).
  6. Authenticators — perform the passport Active Authentication (AA).
  7. Utilities — some utility contracts, such as X509, SHA1, RSA implementations in solidity language and other.

Deployments

NameAddress
StateKeeper0x61aa5b68D811884dA4FEC2De4a7AA0464df166E1
RegistrationSMT0x479F84502Db545FA8d2275372E0582425204A879
CertificatesSMT0xA8b350d699632569D5351B20ffC1b31202AcEDD8
Registration20x11BB4B14AA6e4b836580F3DBBa741dD89423B971

Registration2

Interface

contract Registration2 is Initializable, TSSUpgradeable {

StateKeeper public stateKeeper;
mapping(bytes32 => address) public certificateDispatchers;
mapping(bytes32 => address) public passportDispatchers;
mapping(bytes32 => address) public passportVerifiers;

function registerCertificate(
Certificate memory certificate_,
ICAOMember memory icaoMember_,
bytes32[] memory icaoMerkleProof_
) external virtual {
/* ... */
}


function revokeCertificate(bytes32 certificateKey_) external virtual {
stateKeeper.removeCertificate(certificateKey_);
}


function register(
bytes32 certificatesRoot_,
uint256 identityKey_,
uint256 dgCommit_,
Passport memory passport_,
VerifierHelper.ProofPoints memory zkPoints_
) external virtual {
/* ... */
}

function registerViaNoir(
bytes32 certificatesRoot_,
uint256 identityKey_,
uint256 dgCommit_,
Passport memory passport_,
bytes memory zkPoints_
) external virtual {
/* ... */
}

function revoke(uint256 identityKey_, Passport memory passport_) external virtual {
/* ... */
}

function reissueIdentity(
bytes32 certificatesRoot_,
uint256 identityKey_,
uint256 dgCommit_,
Passport memory passport_,
VerifierHelper.ProofPoints memory zkPoints_
) external virtual {
/* ... */
}

function reissueIdentityViaNoir(
bytes32 certificatesRoot_,
uint256 identityKey_,
uint256 dgCommit_,
Passport memory passport_,
bytes memory zkPoints_
) external virtual {
/* ... */
}

function updateDependency(
MethodId methodId_,
bytes calldata data_,
bytes calldata proof_
) external virtual {
/* ... */
}

}
  • stateKeeper — instance of StateKeeper contract that saves all data about registrations, certificates, etc.;
  • certificateDispatchers — mapping from ICAO member's certificate to its dispatcher;
  • passportDispatchers — mapping from passport type to its dispatcher contract;
  • passportVerifiers — mapping from passport type to its verifier contract;
  • registerCertificate(...) — registers an ICAO member's X509 certificate in the certificates SMT (calls StateKeeper contract);
  • revokeCertificate(...) — revokes an expired ICAO member's X509 certificate (calls StateKeeper contract);
  • register(...) — registers the user (identity) by its passport, technically — issues the identity, calls the dispatcher to verify ZKP, links the identity to its passport by calling StateKeeper;
  • registerViaNoir(...) - the same as register(...) but uses Noir proof instead of Circom;
  • revoke(...) — revokes the identity and its passport by the user's will, marks the passport (and identity) as revoked;
  • reissueIdentity(...) — re-issues previous passport <> identity bond by migration to a new identity;
  • reissueIdentityViaNoir(...) — the same as reissueIdentity(...) but uses Noir proof instead of Circom;
  • updateDependency(...) — adds or removes, depending on the method provided, a dispatcher via Rarimo TSS;

For the full implementation, see Registration2.sol at the GitHub.

Verifiers

Interface

contract ExampleVerifier {

/* some constants */

function verifyProof(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[4] calldata _pubSignals
) public view returns (bool) {
/* ... */
}
}

All verifiers are generated contracts that share the same interface. However, each verifier is designed to accept proofs generated by specific ZK schemas. Below is a list of the current verifiers:

  • Circom verifiers — the list of acceptable by Registration2 smart contracts to verify biometric passport;
  • Noir verifiers — the list of acceptable by Registration2 smart contracts to verify biometric passport;
  • PInternalOptVerifier2 — Georgian ID card verifier for Registration2;
  • PInternalVerifier2 — Georgian ID card verifier for Registration2;
  • PMNEOptVerifier2 — Montenegro ID card verifier for Registration2;
  • verifyProof(...) — verifies passport validity ZKP;

For the full implementation, see verifiers for Registration2 at the GitHub.

PECDSASHA1Dispatcher

Interface

contract PECDSASHA1Dispatcher is IPassportDispatcher, Initializable {

address public authenticator;

function authenticate(
bytes memory challenge_,
bytes memory passportSignature_,
bytes memory passportPublicKey_
) external view returns (bool) {
/* ... */
}

}
  • authenticator — address of the ECDSASHA1Authenticator contract for passports;
  • authenticate(...) — authenticates the ECDSASHA1 passport;

For the full implementation, see PECDSASHA1Dispatcher.sol at the GitHub.

PNOAADispatcher

Interface

contract PNOAADispatcher is IPassportDispatcher, Initializable {

function authenticate(
bytes memory,
bytes memory passportSignature_,
bytes memory passportPublicKey_
) external pure returns (bool) {
return passportSignature_.length == 0 && passportPublicKey_.length == 0;
}

}

This is a special dispatcher for passports without Active Authentication.

  • authenticate(...) — authenticates the passport without AA. Just return true;

For the full implementation, see PNOAADispatcher.sol at the GitHub.

PRSASHADispatcher

Interface

contract PRSASHADispatcher is IPassportDispatcher, Initializable {

address public authenticator;

function authenticate(
bytes memory challenge_,
bytes memory passportSignature_,
bytes memory passportPublicKey_
) external view returns (bool) {
return
PRSASHAAuthenticator(authenticator).authenticate(
challenge_,
passportSignature_,
passportPublicKey_
);
}

}
  • authenticator — address of the RSASHAAuthenticator contract for passports;
  • authenticate(...) — authenticates the RSASHA passport;

For the full implementation, see PRSASHADispatcher.sol at the GitHub.

CRSADispatcher

Interface

contract CRSADispatcher is ICertificateDispatcher, Initializable {

address public signer;

function verifyICAOSignature(
bytes memory x509SignedAttributes_,
bytes memory icaoMemberSignature_,
bytes memory icaoMemberKey_
) external view override returns (bool) {
return
ICertificateRSASigner(signer).verifyICAOSignature(
x509SignedAttributes_,
icaoMemberSignature_,
icaoMemberKey_
);
}

}

As for passports, there are authenticators for certificates, and it's one of them.

  • signer — address of the CRSASigner or CRSAPSSSigner contract for certificates;
  • verifyICAOSignature(...) — verifies RSA signature of ICAO member's certificate;

For the full implementation, see CRSADispatcher.sol at the GitHub.

CRSAPSSSigner

Interface

contract CRSAPSSSigner is ICertificateRSASigner, Initializable {
uint256 public exponent;
bool public isSha2;

function verifyICAOSignature(
bytes memory x509SignedAttributes_,
bytes memory icaoMemberSignature_,
bytes memory icaoMemberKey_
) external view override returns (bool) {
return
RSAPSS.verify(
x509SignedAttributes_,
icaoMemberSignature_,
abi.encodePacked(exponent),
icaoMemberKey_,
isSha2
);
}
  • exponent — RSAPSS exponent;
  • isSha2 — hash function switcher, true - sha2, false - sha512;
  • verifyICAOSignature(...) — verifies ICAO member RSAPSS signature of the X509 certificate SA;

For the full implementation, see CRSAPSSSigner.sol at the GitHub.

CRSASigner

Interface

contract CRSASigner is ICertificateRSASigner, Initializable {
uint256 public exponent; // RSA exponent
bool public isSha1; // hash function switcher, true - sha1, false - sha2

function verifyICAOSignature(
bytes memory x509SignedAttributes_,
bytes memory icaoMemberSignature_,
bytes memory icaoMemberKey_
) external view override returns (bool) {
/* ... */
}
}
  • exponent — RSA exponent;
  • isSha2 — hash function switcher, true - sha2, false - sha512;
  • verifyICAOSignature(...) — verifies ICAO member RSA signature of the X509 certificate SA.;

For the full implementation, see CRSASigner.sol at the GitHub.

PECDSASHA1Authenticator

Interface

contract PECDSASHA1Authenticator {

/* brainpool256r1 parameters */

function authenticate(
bytes memory challenge,
uint256 r,
uint256 s,
uint256 x,
uint256 y
) external pure returns (bool) {
/* ... */
}

}
  • authenticate(...) — checks active authentication of a passport (ECDSA signature);

For the full implementation, see PECDSASHA1Authenticator.sol at the GitHub.

PRSASHAAuthenticator

Interface

contract PRSASHAAuthenticator is Initializable {

uint256 public exponent; // RSA exponent

function authenticate(
bytes memory challenge_,
bytes memory s_,
bytes memory n_
) external view returns (bool) {
/* ... */
}
}
  • exponent — RSA exponent;
  • authenticate(...) — checks active authentication of a passport (RSA signature);

For the full implementation, see PRSASHAAuthenticator.sol at the GitHub.

TD3QueryProofVerifier

Interface

contract TD3QueryProofVerifier {
function verifyProof(
uint256[2] calldata _pA,
uint256[2][2] calldata _pB,
uint256[2] calldata _pC,
uint256[] calldata _pubSignals
) external view returns (bool) {
/* ... */
}
}
  • Verifies TD3-query zero-knowledge proofs produced by the Query circuit (e.g., age, uniqueness, country masks) without revealing raw passport data.
  • Accepts Groth16 proof points and the public signals array (23 items for Query circuit) and returns true if valid.
  • Intended for SDK-level integrations where on-chain verification of query proofs is needed.

For the full implementation, see TD3QueryProofVerifier.sol at the GitHub.

PublicSignalsBuilder

Interface

library PublicSignalsBuilder {
uint256 public constant PROOF_SIGNALS_COUNT = 23;

function newPublicSignalsBuilder(
uint256 selector_,
uint256 nullifier_
) internal pure returns (uint256 dataPointer_);

function withName(uint256 dataPointer_, uint256 name_) internal pure;
function withNameResidual(uint256 dataPointer_, uint256 nameResidual_) internal pure;
function withNationality(uint256 dataPointer_, uint256 nationality_) internal pure;
function withCitizenship(uint256 dataPointer_, uint256 citizenship_) internal pure;
function withSex(uint256 dataPointer_, uint256 sex_) internal pure;
function withEventIdAndData(uint256 dataPointer_, uint256 eventId_, uint256 eventData_) internal pure;
function withIdStateRoot(uint256 dataPointer_, bytes32 idStateRoot_) internal view;
function withSelector(uint256 dataPointer_, uint256 selector_) internal pure;
function withCurrentDate(uint256 dataPointer_, uint256 currentDate_, uint256 timeBound_) internal view;
function withTimestampLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure;
function withIdentityCounterLowerbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure;
function withBirthDateLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure;
function withExpirationDateLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure;
function withCitizenshipMask(uint256 dataPointer_, uint256 citizenshipMask_) internal pure;

function buildAsUintArray(uint256 dataPointer_) internal pure returns (uint256[] memory);
function buildAsBytesArray(uint256 dataPointer_) internal pure returns (bytes32[] memory);
}
  • Helper library to construct the Query circuit public signals in the exact index layout expected by verifiers (see Query circuit public signals spec).
  • Validates constraints via AQueryProofExecutor storage; withIdStateRoot(...) checks IPoseidonSMT(registrationSMT).isRootValid(...) before writing the root at index 11.
  • Date handling: withCurrentDate(...) parses yyMMdd to a timestamp and enforces a ±timeBound window using Date2Time utilities.
  • Produces either uint256[] or bytes32[] views of the same in-memory array for downstream verifier calls.

For the full implementation, see PublicSignalsBuilder.sol at the GitHub.

RegistrationSMTReplicator

Interface

contract RegistrationSMTReplicator is IPoseidonSMT {
uint256 public constant ROOT_VALIDITY = 1 hours;
string public constant REGISTRATION_ROOT_PREFIX = "Rarimo root";

address public sourceSMT;
bytes32 public latestRoot;
uint256 public latestTimestamp;

event RootTransitioned(bytes32 newRoot, uint256 transitionTimestamp);

function __RegistrationSMTReplicator_init(
address[] memory oracles_,
address sourceSMT_
) external;

function addOracles(address[] memory oracles_) external;
function removeOracles(address[] memory oracles_) external;
function setSourceSMT(address newSourceSMT_) external;

function transitionRoot(bytes32 newRoot_, uint256 transitionTimestamp_) external;
function transitionRootWithSignature(
bytes32 newRoot_,
uint256 transitionTimestamp_,
bytes memory signature_
) external;

function isRootValid(bytes32 root_) external view returns (bool);
function isRootLatest(bytes32 root_) public view returns (bool);
function isOracle(address oracle_) public view returns (bool);
function getOracles() external view returns (address[] memory);
}
  • Deployed on target chains to replicate the Registration SMT root when the canonical StateKeeper is not directly available.
  • Root updates can be pushed by:
    • Authorized oracles via transitionRoot(...).
    • Anyone with an oracle-signed message via transitionRootWithSignature(...) using keccak256(abi.encodePacked(REGISTRATION_ROOT_PREFIX, sourceSMT, address(this), newRoot, timestamp)) recovered with ECDSA.
  • isRootValid(root) returns true if the root is the latest or if it was observed within ROOT_VALIDITY (1 hour), enabling lightweight freshness checks by verifiers depending on IPoseidonSMT.
  • Owners manage the oracle set and can update sourceSMT; contract follows UUPS upgradeability and multi-owner controls.

For the full implementation, see RegistrationSMTReplicator.sol at the GitHub.