diff --git a/contracts/contracts/IdentityVerificationHubImplV1.sol b/contracts/contracts/IdentityVerificationHubImplV1.sol index b90832df4..5e4361b5e 100644 --- a/contracts/contracts/IdentityVerificationHubImplV1.sol +++ b/contracts/contracts/IdentityVerificationHubImplV1.sol @@ -53,6 +53,10 @@ contract IdentityVerificationHubImplV1 is IdentityVerificationHubStorageV1, IIdentityVerificationHubV1 { + using Formatter for uint256; + + uint256 constant MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH = 10; + // Events event HubInitialized( address registry, @@ -82,7 +86,7 @@ contract IdentityVerificationHubImplV1 is error INVALID_DSC_PROOF(); error INVALID_VC_AND_DISCLOSE_PROOF(); - error INVALID_IDENTITY_COMMITMENT_ROOT(); + error INVALID_COMMITMENT_ROOT(); error INVALID_OFAC_ROOT(); error INVALID_CSCA_ROOT(); @@ -213,10 +217,20 @@ contract IdentityVerificationHubImplV1 is return attrs; } + function getReadableForbiddenCountries( + uint256 forbiddenCountriesListPacked + ) + external + view + returns (bytes3[MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH] memory) + { + return Formatter.extractForbiddenCountriesFromPacked(forbiddenCountriesListPacked); + } + // verify and view function verifyVcAndDisclose( - IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof + VcAndDiscloseHubProof memory proof ) external view @@ -227,13 +241,13 @@ contract IdentityVerificationHubImplV1 is VcAndDiscloseVerificationResult memory result; for (uint256 i = 0; i < 3; i++) { - result.revealedDataPacked[i] = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i]; + result.revealedDataPacked[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i]; } - result.forbiddenCountriesListPacked = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX]; - result.nullifier = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX]; - result.attestationId = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]; - result.userIdentifier = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX]; - result.scope = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX]; + result.forbiddenCountriesListPacked = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX]; + result.nullifier = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX]; + result.attestationId = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]; + result.userIdentifier = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_USER_IDENTIFIER_INDEX]; + result.scope = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX]; return result; } @@ -353,27 +367,25 @@ contract IdentityVerificationHubImplV1 is // Functions for vc and disclose circuit function verifyVcAndDiscloseProof( - bool checkOlderThan, - uint256 expectedOlderThan, - bool checkOfac, - bool checkForbiddenCountries, - uint256 expectedForbiddenCountriesListPacked, - IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof + VcAndDiscloseHubProof memory proof ) internal view { - if (!IIdentityRegistryV1(_registry).checkIdentityCommitmentRoot(proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX])) { - revert INVALID_IDENTITY_COMMITMENT_ROOT(); + // verify identity commitment root + if (!IIdentityRegistryV1(_registry).checkIdentityCommitmentRoot(proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_MERKLE_ROOT_INDEX])) { + revert INVALID_COMMITMENT_ROOT(); } - if (!IIdentityRegistryV1(_registry).checkOfacRoot(proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SMT_ROOT_INDEX])) { + // verify ofac root + if (!IIdentityRegistryV1(_registry).checkOfacRoot(proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SMT_ROOT_INDEX])) { revert INVALID_OFAC_ROOT(); } + // verify current date uint[6] memory dateNum; for (uint256 i = 0; i < 6; i++) { - dateNum[i] = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_CURRENT_DATE_INDEX + i]; + dateNum[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_CURRENT_DATE_INDEX + i]; } uint currentTimestamp = Formatter.proofDateToUnixTimestamp(dateNum); if( @@ -383,33 +395,29 @@ contract IdentityVerificationHubImplV1 is revert CURRENT_DATE_NOT_IN_VALID_RANGE(); } + // verify attributes uint256[3] memory revealedDataPacked; for (uint256 i = 0; i < 3; i++) { - revealedDataPacked[i] = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i]; + revealedDataPacked[i] = proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX + i]; } - - if (checkOlderThan) { - uint256 olderThanInProof = CircuitAttributeHandler.getOlderThan(Formatter.fieldElementsToBytes(revealedDataPacked)); - if (olderThanInProof != expectedOlderThan) { + if (proof.olderThanEnabled) { + if (!CircuitAttributeHandler.compareOlderThan(Formatter.fieldElementsToBytes(revealedDataPacked), proof.olderThan)) { revert INVALID_OLDER_THAN(); } } - - if (checkOfac) { - uint256 ofacInProof = CircuitAttributeHandler.getOfac(Formatter.fieldElementsToBytes(proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_REVEALED_DATA_PACKED_INDEX])); - if (ofacInProof != 1) { + if (proof.ofacEnabled) { + if (!CircuitAttributeHandler.compareOfac(Formatter.fieldElementsToBytes(revealedDataPacked))) { revert INVALID_OFAC(); } } - - if (checkForbiddenCountries) { - uint256 forbiddenCountriesListPackedInProof = proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX]; - if (forbiddenCountriesListPackedInProof != expectedForbiddenCountriesListPacked) { + if (proof.forbiddenCountriesEnabled) { + if (proof.forbiddenCountriesListPacked != proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_FORBIDDEN_COUNTRIES_LIST_PACKED_INDEX]) { revert INVALID_FORBIDDEN_COUNTRIES(); } } - if (!IVcAndDiscloseCircuitVerifier(_vcAndDiscloseCircuitVerifier).verifyProof(proof.a, proof.b, proof.c, proof.pubSignals)) { + // verify the proof + if (!IVcAndDiscloseCircuitVerifier(_vcAndDiscloseCircuitVerifier).verifyProof(proof.vcAndDiscloseProof.a, proof.vcAndDiscloseProof.b, proof.vcAndDiscloseProof.c, proof.vcAndDiscloseProof.pubSignals)) { revert INVALID_VC_AND_DISCLOSE_PROOF(); } } @@ -428,6 +436,10 @@ contract IdentityVerificationHubImplV1 is revert NO_VERIFIER_SET(); } + if (!IIdentityRegistryV1(_registry).checkIdentityCommitmentRoot(registerCircuitProof.pubSignals[CircuitConstants.REGISTER_MERKLE_ROOT_INDEX])) { + revert INVALID_COMMITMENT_ROOT(); + } + result = IRegisterCircuitVerifier(verifier).verifyProof( registerCircuitProof.a, registerCircuitProof.b, diff --git a/contracts/contracts/abstract/passportAirdropRoot.sol b/contracts/contracts/abstract/passportAirdropRoot.sol index d19992c92..0889f6b55 100644 --- a/contracts/contracts/abstract/passportAirdropRoot.sol +++ b/contracts/contracts/abstract/passportAirdropRoot.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.28; import {IIdentityVerificationHubV1} from "../interfaces/IIdentityVerificationHubV1.sol"; -import {IVcAndDiscloseCircuitVerifier} from "../interfaces/IVcAndDiscloseCircuitVerifier.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {CircuitConstants} from "../constants/CircuitConstants.sol"; @@ -36,7 +35,7 @@ abstract contract PassportAirdropRoot is Ownable { function _registerAddress( address addressToRegister, - IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof + IIdentityVerificationHubV1.VcAndDiscloseHubProof memory proof ) internal returns (address registeredAddress) @@ -45,15 +44,15 @@ abstract contract PassportAirdropRoot is Ownable { revert RegistrationNotOpen(); } - if (scope != proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX]) { + if (scope != proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_SCOPE_INDEX]) { revert InvalidScope(); } - if (nullifiers[proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX]] != address(0)) { + if (nullifiers[proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_NULLIFIER_INDEX]] != address(0)) { revert AlreadyRegistered(); } - if(attestationId != proof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]) { + if(attestationId != proof.vcAndDiscloseProof.pubSignals[CircuitConstants.VC_AND_DISCLOSE_ATTESTATION_ID_INDEX]) { revert InvalidAttestationId(); } diff --git a/contracts/contracts/constants/CircuitConstants.sol b/contracts/contracts/constants/CircuitConstants.sol index 58861c918..942495a82 100644 --- a/contracts/contracts/constants/CircuitConstants.sol +++ b/contracts/contracts/constants/CircuitConstants.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.28; library CircuitConstants { uint256 constant REGISTER_NULLIFIER_INDEX = 0; uint256 constant REGISTER_COMMITMENT_INDEX = 1; + uint256 constant REGISTER_MERKLE_ROOT_INDEX = 2; uint256 constant DSC_TREE_LEAF_INDEX = 0; uint256 constant DSC_CSCA_ROOT_INDEX = 1; diff --git a/contracts/contracts/example/airdrop.sol b/contracts/contracts/example/airdrop.sol index ee38f161e..823f66d6e 100644 --- a/contracts/contracts/example/airdrop.sol +++ b/contracts/contracts/example/airdrop.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.28; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; -import {PassportAirdropRoot} from "../abstract/airdropRoot.sol"; +import {PassportAirdropRoot} from "../abstract/passportAirdropRoot.sol"; import {IIdentityVerificationHubV1} from "../interfaces/IIdentityVerificationHubV1.sol"; import {IVcAndDiscloseCircuitVerifier} from "../interfaces/IVcAndDiscloseCircuitVerifier.sol"; @@ -47,7 +47,7 @@ contract Airdrop is PassportAirdropRoot { } function registeredAddress( - IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof + IIdentityVerificationHubV1.VcAndDiscloseHubProof memory proof ) external { diff --git a/contracts/contracts/interfaces/IIdentityRegistryV1.sol b/contracts/contracts/interfaces/IIdentityRegistryV1.sol index 6e763daed..46463bc44 100644 --- a/contracts/contracts/interfaces/IIdentityRegistryV1.sol +++ b/contracts/contracts/interfaces/IIdentityRegistryV1.sol @@ -8,6 +8,8 @@ interface IIdentityRegistryV1 { function getIdentityCommitmentMerkleTreeSize() external view returns (uint256); function getIdentityCommitmentMerkleRoot() external view returns (uint256); function getIdentityCommitmentIndex(uint256 commitment) external view returns (uint256); + function getDscKeyCommitmentTreeRoot() external view returns (uint256); + function checkDscKeyCommitmentTreeRoot(uint256 root) external view returns (bool); function getOfacRoot() external view returns (uint256); function getCscaRoot() external view returns (uint256); function checkOfacRoot(uint256 root) external view returns (bool); diff --git a/contracts/contracts/interfaces/IIdentityVerificationHubV1.sol b/contracts/contracts/interfaces/IIdentityVerificationHubV1.sol index 316fa2492..76eaf7f96 100644 --- a/contracts/contracts/interfaces/IIdentityVerificationHubV1.sol +++ b/contracts/contracts/interfaces/IIdentityVerificationHubV1.sol @@ -39,8 +39,18 @@ interface IIdentityVerificationHubV1 { uint256 ofac; } + + struct VcAndDiscloseHubProof { + bool olderThanEnabled; + uint256 olderThan; + bool forbiddenCountriesEnabled; + uint256 forbiddenCountriesListPacked; + bool ofacEnabled; + IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof vcAndDiscloseProof; + } + function verifyVcAndDisclose( - IVcAndDiscloseCircuitVerifier.VcAndDiscloseProof memory proof + VcAndDiscloseHubProof memory proof ) external view diff --git a/contracts/contracts/libraries/CircuitAttributeHandler.sol b/contracts/contracts/libraries/CircuitAttributeHandler.sol index 3ab2005f4..f23298bc2 100644 --- a/contracts/contracts/libraries/CircuitAttributeHandler.sol +++ b/contracts/contracts/libraries/CircuitAttributeHandler.sol @@ -66,24 +66,25 @@ library CircuitAttributeHandler { } function getOlderThan(bytes memory charcodes) internal pure returns (uint256) { - return extractOlderThan(charcodes); + return Formatter.numAsciiToUint(uint8(charcodes[OLDER_THAN_START]))*10 + + Formatter.numAsciiToUint(uint8(charcodes[OLDER_THAN_START + 1])); } function getOfac(bytes memory charcodes) internal pure returns (uint256) { - return extractOfac(charcodes); + return uint8(charcodes[OFAC_START]); } function compareOlderThan( bytes memory charcodes, uint256 olderThan ) internal pure returns (bool) { - return extractOlderThan(charcodes) >= olderThan; + return getOlderThan(charcodes) >= olderThan; } function compareOfac( bytes memory charcodes ) internal pure returns (bool) { - return extractOfac(charcodes) == 1; + return getOfac(charcodes) == 1; } function extractStringAttribute(bytes memory charcodes, uint256 start, uint256 end) internal pure returns (string memory) { @@ -97,16 +98,4 @@ library CircuitAttributeHandler { return string(attributeBytes); } - function extractOlderThan( - bytes memory charcodes - ) internal pure returns (uint256) { - return Formatter.numAsciiToUint(uint8(charcodes[OLDER_THAN_START]))*10 - + Formatter.numAsciiToUint(uint8(charcodes[OLDER_THAN_START + 1])); - } - - function extractOfac( - bytes memory charcodes - ) internal pure returns (uint256) { - return uint8(charcodes[OFAC_START]); - } } \ No newline at end of file diff --git a/contracts/contracts/libraries/Formatter.sol b/contracts/contracts/libraries/Formatter.sol index d1a97f63c..528cabb03 100644 --- a/contracts/contracts/libraries/Formatter.sol +++ b/contracts/contracts/libraries/Formatter.sol @@ -5,7 +5,7 @@ library Formatter { error InvalidDateLength(); error InvalidAsciiCode(); - uint256 constant MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH = 20; + uint256 constant MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH = 10; function formatName( string memory input @@ -86,7 +86,7 @@ library Formatter { } function extractForbiddenCountriesFromPacked( - uint256[2] memory publicSignals + uint256 publicSignal ) internal pure @@ -98,32 +98,10 @@ library Formatter { for (uint256 j = 0; j < MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH; j++) { uint256 byteIndex = j * 3; - if (byteIndex + 2 < 32) { - uint256 shift = byteIndex * 8; - uint256 mask = 0xFFFFFF; - uint256 packedData = (publicSignals[0] >> shift) & mask; - forbiddenCountries[j] = bytes3(uint24(packedData)); - } else if (byteIndex < 32) { - uint256 bytesFrom0 = 32 - byteIndex; - uint256 bytesTo1 = 3 - bytesFrom0; - - uint256 shift0 = byteIndex * 8; - uint256 mask0 = (1 << (bytesFrom0 * 8)) - 1; - uint256 part0 = (publicSignals[0] >> shift0) & mask0; - - uint256 shift1 = 0; - uint256 mask1 = (1 << (bytesTo1 * 8)) - 1; - uint256 part1 = (publicSignals[1] >> shift1) & mask1; - - uint256 combined = (part1 << (bytesFrom0 * 8)) | part0; - forbiddenCountries[j] = bytes3(uint24(combined)); - } else { - uint256 byteIndexIn1 = byteIndex - 32; - uint256 shift = byteIndexIn1 * 8; - uint256 mask = 0xFFFFFF; - uint256 packedData = (publicSignals[1] >> shift) & mask; - forbiddenCountries[j] = bytes3(uint24(packedData)); - } + uint256 shift = byteIndex * 8; + uint256 mask = 0xFFFFFF; + uint256 packedData = (publicSignal >> shift) & mask; + forbiddenCountries[j] = bytes3(uint24(packedData)); } return forbiddenCountries;