Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TSS functions #2

Merged
merged 2 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion contracts/registration/Registration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.s
import {VerifierHelper} from "@solarity/solidity-lib/libs/zkp/snarkjs/VerifierHelper.sol";

import {PoseidonSMT} from "../utils/PoseidonSMT.sol";
import {TSSSigner} from "../utils/TSSSigner.sol";
import {RSAVerifier} from "../utils/RSAVerifier.sol";

contract Registration is PoseidonSMT, Initializable {
contract Registration is PoseidonSMT, TSSSigner, Initializable {
using VerifierHelper for address;
using RSAVerifier for bytes;

uint256 public constant E = 65537;
string public constant ICAO_PREFIX = "Rarimo CSCA root";
bytes32 public constant REVOKED = keccak256("REVOKED");

struct PassportInfo {
Expand All @@ -41,10 +43,12 @@ contract Registration is PoseidonSMT, Initializable {

function __Registration_init(
uint256 treeHeight_,
address signer_,
address verifier_,
bytes32 icaoMasterTreeMerkleRoot_
) external initializer {
__PoseidonSMT_init(treeHeight_);
__TSSSigner_init(signer_);

verifier = verifier_;
icaoMasterTreeMerkleRoot = icaoMasterTreeMerkleRoot_;
Expand Down Expand Up @@ -167,6 +171,25 @@ contract Registration is PoseidonSMT, Initializable {
emit ReissuedIdentity(bytes32(passportKey_), bytes32(identityKey_));
}

function changeICAOMasterTreeRoot(
bytes32 newRoot_,
uint64 timestamp,
bytes memory proof_
) external {
bytes32 leaf_ = keccak256(abi.encodePacked(ICAO_PREFIX, newRoot_, timestamp));

_useNonce(timestamp);
_checkMerkleSignature(leaf_, proof_);

icaoMasterTreeMerkleRoot = newRoot_;
}

function changeSigner(bytes memory newSignerPubKey_, bytes memory signature_) external {
_checkSignature(keccak256(newSignerPubKey_), signature_);

signer = _convertPubKeyToAddress(newSignerPubKey_);
}

function getPassportInfo(
bytes memory passportPublicKey_
)
Expand Down
2 changes: 2 additions & 0 deletions contracts/utils/PoseidonSMT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ contract PoseidonSMT {
)
);
}

uint256[46] private _gap;
}
60 changes: 60 additions & 0 deletions contracts/utils/TSSSigner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

abstract contract TSSSigner {
using ECDSA for bytes32;
using MerkleProof for bytes32[];

uint256 public constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;

address public signer;

mapping(uint64 => bool) internal _nonces;

function __TSSSigner_init(address signer_) internal {
signer = signer_;
}

function _useNonce(uint64 nonce_) internal {
require(!_nonces[nonce_], "TSSSigner: nonce used");

_nonces[nonce_] = true;
}

function _checkSignature(bytes32 signHash_, bytes memory signature_) internal view {
address signer_ = signHash_.recover(signature_);

require(signer == signer_, "TSSSigner: invalid signature");
}

function _checkMerkleSignature(bytes32 merkleLeaf_, bytes memory proof_) internal view {
(bytes32[] memory merklePath_, bytes memory signature_) = abi.decode(
proof_,
(bytes32[], bytes)
);

bytes32 merkleRoot_ = merklePath_.processProof(merkleLeaf_);

_checkSignature(merkleRoot_, signature_);
}

function _convertPubKeyToAddress(bytes memory pubKey_) internal pure returns (address) {
require(pubKey_.length == 64, "TSSSigner: wrong pubKey length");

(uint256 x_, uint256 y_) = abi.decode(pubKey_, (uint256, uint256));

// @dev y^2 = x^3 + 7, x != 0, y != 0 (mod P)
require(x_ != 0 && y_ != 0 && x_ != P && y_ != P, "TSSSigner: zero pubKey");
require(
mulmod(y_, y_, P) == addmod(mulmod(mulmod(x_, x_, P), x_, P), 7, P),
"TSSSigner: pubKey not on the curve"
);

return address(uint160(uint256(keccak256(pubKey_))));
}

uint256[48] private _gap;
}
38 changes: 38 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@
"chai": "^4.4.1",
"circomlibjs": "^0.1.7",
"dotenv": "16.4.5",
"hardhat": "2.20.1",
"typechain": "8.3.2",
"ethers": "^6.11.1",
"hardhat": "2.20.1",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-gas-reporter": "^2.0.2",
"husky": "^9.0.11",
"merkletreejs": "^0.3.11",
"mocha": "^10.3.0",
"prettier": "^3.2.5",
"prettier-plugin-solidity": "^1.3.1",
Expand All @@ -85,6 +85,7 @@
"solidity-coverage": "^0.8.11",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typechain": "8.3.2",
"typescript": "^5.4.3"
}
}
48 changes: 48 additions & 0 deletions test/helpers/TSSMerkleTree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ethers } from "hardhat";

import { MerkleTree } from "merkletreejs";

import { TSSSigner } from "./TSSSigner";

export class TSSMerkleTree {
public tree: MerkleTree;

constructor(public tssSigner: TSSSigner) {
const leaves = Array.from({ length: 10 }, () => ethers.randomBytes(32));

this.tree = new MerkleTree(
leaves,
(e: Buffer) => {
const hash = ethers.solidityPackedKeccak256(["bytes"], [e]);

return Buffer.from(hash.slice(2), "hex");
},
{ sortPairs: true },
);
}

public addLeaf(leaf: string) {
this.tree.addLeaf(Buffer.from(leaf.slice(2), "hex"));
}

public getPath(leaf: string): Array<string> {
return this.tree.getProof(leaf).map((el) => "0x" + el.data.toString("hex"));
}

public getProof(leaf: string, addLeaf: boolean = true): string {
if (addLeaf) {
this.addLeaf(leaf);
}

const root = this.getRoot();
const path = this.getPath(leaf);

const signature = this.tssSigner.sign(root);

return ethers.AbiCoder.defaultAbiCoder().encode(["bytes32[]", "bytes"], [path, signature]);
}

public getRoot(): string {
return "0x" + this.tree.getRoot().toString("hex");
}
}
25 changes: 25 additions & 0 deletions test/helpers/TSSSigner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ethers } from "hardhat";
import { BigNumberish, BytesLike, HDNodeWallet } from "ethers";

export class TSSSigner {
constructor(public signer: HDNodeWallet) {}

public changeICAOMasterTreeRoot(newRoot: string, timestamp: BigNumberish): string {
const hash = ethers.solidityPackedKeccak256(
["string", "bytes32", "uint64"],
["Rarimo CSCA root", newRoot, timestamp],
);

return this.sign(hash);
}

public signChangeSigner(newPubKey: string): string {
const hash = ethers.solidityPackedKeccak256(["bytes"], [newPubKey]);

return this.sign(hash);
}

public sign(hash: BytesLike) {
return ethers.Signature.from(this.signer.signingKey.sign(hash)).serialized;
}
}
2 changes: 2 additions & 0 deletions test/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./reverter";
export * from "./poseidon-hash";
export * from "./poseidon-deploy";
export * from "./TSSSigner";
export * from "./TSSMerkleTree";
Loading
Loading