-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: finalized InputVerifier for native version
- Loading branch information
Showing
6 changed files
with
273 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause-Clear | ||
pragma solidity ^0.8.24; | ||
|
||
import "./KMSVerifier.sol"; | ||
import "./TFHEExecutor.sol"; | ||
import "./KMSVerifierAddress.sol"; | ||
import "./CoprocessorAddress.sol"; | ||
|
||
// Importing OpenZeppelin contracts for cryptographic signature verification and access control. | ||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; | ||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
|
||
/// @title InputVerifier for signature verification of users encrypted inputs | ||
/// @notice This version is only for the Native version of fhEVM | ||
/// @notice This contract is called by the TFHEExecutor inside verifyCiphertext function, and calls the KMSVerifier to fetch KMS signers addresses | ||
/// @dev The contract uses OpenZeppelin's EIP712Upgradeable for cryptographic operations | ||
contract InputVerifier is UUPSUpgradeable, Ownable2StepUpgradeable { | ||
/// @notice Handle version | ||
uint8 public constant HANDLE_VERSION = 0; | ||
|
||
KMSVerifier public constant kmsVerifier = KMSVerifier(kmsVerifierAdd); | ||
|
||
/// @notice Name of the contract | ||
string private constant CONTRACT_NAME = "InputVerifier"; | ||
|
||
/// @notice Version of the contract | ||
uint256 private constant MAJOR_VERSION = 0; | ||
uint256 private constant MINOR_VERSION = 1; | ||
uint256 private constant PATCH_VERSION = 0; | ||
|
||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {} | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
/// @notice Initializes the contract setting `initialOwner` as the initial owner | ||
function initialize(address initialOwner) external initializer { | ||
__Ownable_init(initialOwner); | ||
} | ||
|
||
function typeOf(uint256 handle) internal pure virtual returns (uint8) { | ||
uint8 typeCt = uint8(handle >> 8); | ||
return typeCt; | ||
} | ||
|
||
function checkProofCache( | ||
bytes memory inputProof, | ||
address userAddress, | ||
address contractAddress, | ||
address aclAddress | ||
) internal view virtual returns (bool, bytes32) { | ||
bool isProofCached; | ||
bytes32 key = keccak256(abi.encodePacked(contractAddress, aclAddress, userAddress, inputProof)); | ||
assembly { | ||
isProofCached := tload(key) | ||
} | ||
return (isProofCached, key); | ||
} | ||
|
||
function cacheProof(bytes32 proofKey) internal virtual { | ||
assembly { | ||
tstore(proofKey, 1) | ||
} | ||
} | ||
|
||
function verifyCiphertext( | ||
TFHEExecutor.ContextUserInputs memory context, | ||
bytes32 inputHandle, | ||
bytes memory inputProof | ||
) external virtual returns (uint256) { | ||
(bool isProofCached, bytes32 cacheKey) = checkProofCache( | ||
inputProof, | ||
context.userAddress, | ||
context.contractAddress, | ||
context.aclAddress | ||
); | ||
uint256 result = uint256(inputHandle); | ||
uint256 indexHandle = (result & 0x0000000000000000000000000000000000000000000000000000000000ff0000) >> 16; | ||
|
||
if (!isProofCached) { | ||
// bundleCiphertext is compressedPackedCT+ZKPOK | ||
// inputHandle is keccak256(keccak256(bundleCiphertext)+index)[0:29]+index+type+version | ||
// and inputProof is len(list_handles) + numSignersKMS + list_handles + signatureKMSSigners + bundleCiphertext (1+1+NUM_HANDLES*32+65*numSignersKMS+bundleCiphertext.length) | ||
|
||
uint256 inputProofLen = inputProof.length; | ||
require(inputProofLen > 0, "Empty inputProof"); | ||
uint256 numHandles = uint256(uint8(inputProof[0])); | ||
uint256 numSignersKMS = uint256(uint8(inputProof[1])); | ||
|
||
require(numHandles > indexHandle, "Invalid index"); // @note: this checks in particular that the list is non-empty | ||
// @note: on native if an invalid indexHandle above the "real" numHandles is passed, it will be mapped to a trivialEncrypt(0) by backend | ||
|
||
require(inputProofLen > 2 + 32 * numHandles + 65 * numSignersKMS, "Error deserializing inputProof"); | ||
|
||
bytes32 hashCT; | ||
{ | ||
uint256 prefixLength = 2 + 32 * numHandles + 65 * numSignersKMS; | ||
uint256 bundleCiphertextLength = inputProofLen - prefixLength; | ||
bytes memory bundleCiphertext = new bytes(bundleCiphertextLength); | ||
for (uint256 i = 0; i < bundleCiphertextLength; i++) { | ||
bundleCiphertext[i] = inputProof[prefixLength + i]; | ||
} | ||
hashCT = keccak256(bundleCiphertext); | ||
} | ||
|
||
{ | ||
bytes[] memory signaturesKMS = new bytes[](numSignersKMS); | ||
for (uint256 j = 0; j < numSignersKMS; j++) { | ||
signaturesKMS[j] = new bytes(65); | ||
for (uint256 i = 0; i < 65; i++) { | ||
signaturesKMS[j][i] = inputProof[2 + 32 * numHandles + 65 * j + i]; | ||
} | ||
} | ||
KMSVerifier.CiphertextVerificationForKMS memory cvKMS; | ||
cvKMS.aclAddress = context.aclAddress; | ||
cvKMS.hashOfCiphertext = hashCT; | ||
cvKMS.userAddress = context.userAddress; | ||
cvKMS.contractAddress = context.contractAddress; | ||
bool kmsCheck = kmsVerifier.verifyInputEIP712KMSSignatures(cvKMS, signaturesKMS); | ||
require(kmsCheck, "Not enough unique KMS input signatures"); | ||
} | ||
|
||
// deseralize handle and check they are from correct version and correct values (handles are recomputed onchain in native case) | ||
for (uint256 i = 0; i < numHandles; i++) { | ||
uint256 element; | ||
assembly { | ||
element := mload(add(inputProof, add(34, mul(i, 32)))) | ||
} | ||
// check all handles are from correct version | ||
require(uint8(element) == HANDLE_VERSION, "Wrong handle version"); | ||
uint256 indexElement = (element & 0x0000000000000000000000000000000000000000000000000000000000ff0000) >> | ||
16; | ||
require(indexElement == i, "Wrong index for serialized handle"); | ||
|
||
uint256 recomputedHandle = uint256(keccak256(abi.encodePacked(hashCT, uint8(i)))); | ||
require( | ||
(recomputedHandle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000) == | ||
(element & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000), | ||
"Wrong handle in inputProof" | ||
); // @note only the before last byte corresponding to type, ie element[30] could not be checked, i.e on native type is malleable, this means it will be casted accordingly by the backend (or trivialEncrypt(0) if index is invalid) | ||
if (i==indexHandle) require(result == element, "Wrong inputHandle"); | ||
} | ||
|
||
cacheProof(cacheKey); | ||
|
||
} else { | ||
uint8 numHandles = uint8(inputProof[0]); // @note: we know inputProof is non-empty since it has been previously cached | ||
require(numHandles > indexHandle, "Invalid index"); | ||
uint256 element; | ||
for (uint256 j = 0; j < 32; j++) { | ||
element |= uint256(uint8(inputProof[34 + indexHandle * 32 + j])) << (8 * (31 - j)); | ||
} | ||
require(element == result, "Wrong inputHandle"); | ||
} | ||
return result; | ||
} | ||
|
||
/// @notice Getter for the name and version of the contract | ||
/// @return string representing the name and the version of the contract | ||
function getVersion() external pure virtual returns (string memory) { | ||
return | ||
string( | ||
abi.encodePacked( | ||
CONTRACT_NAME, | ||
" v", | ||
Strings.toString(MAJOR_VERSION), | ||
".", | ||
Strings.toString(MINOR_VERSION), | ||
".", | ||
Strings.toString(PATCH_VERSION) | ||
) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.