diff --git a/contracts/certificate/signers/CRSAPSSSHA2Signer.sol b/contracts/certificate/signers/CRSAPSSSHA2Signer.sol new file mode 100644 index 0000000..27d2ce4 --- /dev/null +++ b/contracts/certificate/signers/CRSAPSSSHA2Signer.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +import {RSAPSS} from "../../utils/RSAPSS.sol"; + +contract CRSAPSSSHA2Signer is Initializable { + using RSAPSS for bytes; + + uint256 public exponent; // RSAPSS exponent + + function __CRSAPSSSHA2Signer_init(uint256 exponent_) external initializer { + exponent = exponent_; + } + + /** + * @notice Verifies ICAO member RSAPSS signature of the X509 certificate SA. + */ + function verifyICAOSignature( + bytes memory x509SignedAttributes_, + bytes memory icaoMemberSignature_, + bytes memory icaoMemberKey_ + ) external view returns (bool) { + return + x509SignedAttributes_.verify( + icaoMemberSignature_, + abi.encodePacked(exponent), + icaoMemberKey_ + ); + } +} diff --git a/contracts/registration/types.sol b/contracts/registration/types.sol index 96029cb..c835f6e 100644 --- a/contracts/registration/types.sol +++ b/contracts/registration/types.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.16; bytes32 constant C_RSA_4096 = keccak256("C_RSA_4096"); bytes32 constant C_RSA_2048 = keccak256("C_RSA_2048"); +bytes32 constant C_RSAPSS_4096 = keccak256("C_RSAPSS_4096"); bytes32 constant P_NO_AA = keccak256("P_NO_AA"); bytes32 constant P_RSA_SHA1_2688 = keccak256("P_RSA_SHA1_2688"); diff --git a/contracts/utils/RSAPSS.sol b/contracts/utils/RSAPSS.sol new file mode 100644 index 0000000..3f0e01a --- /dev/null +++ b/contracts/utils/RSAPSS.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import {RSA} from "./RSA.sol"; + +library RSAPSS { + uint256 constant HASH_LEN = 32; + uint256 constant SALT_LEN = 32; + uint256 constant MS_BITS = 4095 & 7; + uint256 constant MS_BYTES = 512; + + /** + * @notice RSAPSS verification algorithm + */ + function verify( + bytes memory message_, + bytes memory s_, + bytes memory e_, + bytes memory n_ + ) internal view returns (bool) { + if (s_.length == 0 || e_.length == 0 || n_.length == 0) { + return false; + } + + bytes memory decipher_ = RSA.decrypt(s_, e_, n_); + + return pss(message_, decipher_); + } + + function pss(bytes memory message_, bytes memory signature_) private pure returns (bool) { + if (message_.length > 2 ** 61 - 1) { + return false; + } + + bytes32 messageHash_ = sha256(message_); + + if (MS_BYTES < HASH_LEN + SALT_LEN + 2) { + return false; + } + + if (signature_[MS_BYTES - 1] != hex"BC") { + return false; + } + + bytes memory db_ = new bytes(MS_BYTES - HASH_LEN - 1); + bytes memory h_ = new bytes(HASH_LEN); + + for (uint256 i = 0; i < db_.length; ++i) { + db_[i] = signature_[i]; + } + + for (uint256 i = 0; i < HASH_LEN; ++i) { + h_[i] = signature_[i + db_.length]; + } + + if (uint8(db_[0] & bytes1(uint8(((0xFF << (MS_BITS)))))) == 1) { + return false; + } + + bytes memory dbMask_ = mgf(h_, db_.length); + + for (uint256 i = 0; i < db_.length; ++i) { + db_[i] ^= dbMask_[i]; + } + + if (MS_BITS > 0) { + db_[0] &= bytes1(uint8(0xFF >> (8 - MS_BITS))); + } + + uint256 zeroBytes_; + + for ( + zeroBytes_ = 0; + db_[zeroBytes_] == 0 && zeroBytes_ < (db_.length - 1); + ++zeroBytes_ + ) {} + + if (db_[zeroBytes_++] != hex"01") { + return false; + } + + bytes memory salt_ = new bytes(SALT_LEN); + + for (uint256 i = 0; i < salt_.length; ++i) { + salt_[i] = db_[db_.length - salt_.length + i]; + } + + bytes32 hh_ = sha256(abi.encodePacked(hex"0000000000000000", messageHash_, salt_)); + + if (bytes32(h_) != hh_) { + return false; + } + + return true; + } + + function mgf( + bytes memory message_, + uint256 maskLen_ + ) private pure returns (bytes memory res_) { + bytes memory cnt_ = new bytes(4); + + require(maskLen_ <= (2 ** 32) * HASH_LEN, "RSAPSS: mask too lengthy"); + + for (uint256 i = 0; i < (maskLen_ + HASH_LEN - 1) / HASH_LEN; ++i) { + cnt_[0] = bytes1(uint8((i >> 24) & 255)); + cnt_[1] = bytes1(uint8((i >> 16) & 255)); + cnt_[2] = bytes1(uint8((i >> 8) & 255)); + cnt_[3] = bytes1(uint8(i & 255)); + + bytes32 hashedResInter_ = sha256(abi.encodePacked(message_, cnt_)); + + res_ = abi.encodePacked(res_, hashedResInter_); + } + + assembly { + mstore(res_, maskLen_) + } + } +} diff --git a/deploy/10_setup.migration.ts b/deploy/10_setup.migration.ts index bd7bc02..3feea6b 100644 --- a/deploy/10_setup.migration.ts +++ b/deploy/10_setup.migration.ts @@ -1,44 +1,42 @@ import { Deployer } from "@solarity/hardhat-migrate"; import { - StateKeeperMock__factory, - Registration2Mock__factory, CRSASHA2Dispatcher__factory, + PECDSASHA1Dispatcher__factory, + PInternalVerifier2__factory, PNOAADispatcher__factory, PRSASHA1Dispatcher__factory, - PECDSASHA1Dispatcher__factory, PUniversal2048Verifier2__factory, PUniversal4096Verifier2__factory, - PInternalVerifier2__factory, + Registration2Mock__factory, + StateKeeperMock__factory, } from "@ethers-v6"; import { - Z_UNIVERSAL_2048, - Z_UNIVERSAL_4096, - Z_INTERNAL, - C_RSA_4096, C_RSA_2048, - P_NO_AA, + C_RSA_4096, + C_RSAPSS_4096, P_ECDSA_SHA1_2704, + P_NO_AA, P_RSA_SHA1_2688, P_RSA_SHA1_2688_3, + Z_INTERNAL, + Z_UNIVERSAL_2048, + Z_UNIVERSAL_4096, } from "@/scripts/utils/types"; import { getConfig } from "./config/config"; export = async (deployer: Deployer) => { const config = (await getConfig())!; - const stateKeeper = await deployer.deployed(StateKeeperMock__factory, "StateKeeper Proxy"); - const registration = await deployer.deployed(Registration2Mock__factory, "Registration Proxy"); - const cRsa4096Dispatcher = await deployer.deployed(CRSASHA2Dispatcher__factory, "CRSASHA2Dispatcher 65537 512"); - const cRsa2048Dispatcher = await deployer.deployed(CRSASHA2Dispatcher__factory, "CRSASHA2Dispatcher 65537 256"); - + const cRsa4096Dispatcher = await deployer.deployed(CRSASHA2Dispatcher__factory, "CRSASHA2Dispatcher 512"); + const cRsa2048Dispatcher = await deployer.deployed(CRSASHA2Dispatcher__factory, "CRSASHA2Dispatcher 256"); + const cRsaPss4096Dispatcher = await deployer.deployed(CRSASHA2Dispatcher__factory, "CRSAPSSSHA2Dispatcher 512"); const pRsaSha12688Dispatcher = await deployer.deployed(PRSASHA1Dispatcher__factory, "PRSASHA1Dispatcher 65537"); const pRsaSha126883Dispatcher = await deployer.deployed(PRSASHA1Dispatcher__factory, "PRSASHA1Dispatcher 3"); - const pNoAaDispatcher = await deployer.deployed(PNOAADispatcher__factory); const pEcdsaSha12704Dispatcher = await deployer.deployed(PECDSASHA1Dispatcher__factory); @@ -48,6 +46,7 @@ export = async (deployer: Deployer) => { await registration.mockAddCertificateDispatcher(C_RSA_4096, await cRsa4096Dispatcher.getAddress()); await registration.mockAddCertificateDispatcher(C_RSA_2048, await cRsa2048Dispatcher.getAddress()); + await registration.mockAddCertificateDispatcher(C_RSAPSS_4096, await cRsaPss4096Dispatcher.getAddress()); await registration.mockAddPassportDispatcher(P_NO_AA, await pNoAaDispatcher.getAddress()); await registration.mockAddPassportDispatcher(P_RSA_SHA1_2688, await pRsaSha12688Dispatcher.getAddress()); diff --git a/deploy/11_downgrade.migration.ts b/deploy/11_downgrade.migration.ts index 24e425e..122a895 100644 --- a/deploy/11_downgrade.migration.ts +++ b/deploy/11_downgrade.migration.ts @@ -1,10 +1,10 @@ import { Deployer } from "@solarity/hardhat-migrate"; import { - StateKeeper__factory, - StateKeeperMock__factory, Registration2__factory, Registration2Mock__factory, + StateKeeper__factory, + StateKeeperMock__factory, } from "@ethers-v6"; export = async (deployer: Deployer) => { diff --git a/deploy/1_state.migration.ts b/deploy/1_state.migration.ts index 535260d..0e20a78 100644 --- a/deploy/1_state.migration.ts +++ b/deploy/1_state.migration.ts @@ -1,45 +1,26 @@ import { Deployer, Reporter } from "@solarity/hardhat-migrate"; -import { deployPoseidons } from "./helpers/helper"; +import { deployPoseidons, deployProxy, deploySMTProxy } from "./helpers/helper"; -import { StateKeeperMock__factory, PoseidonSMT__factory, ERC1967Proxy__factory } from "@ethers-v6"; +import { PoseidonSMT, StateKeeperMock, StateKeeperMock__factory } from "@ethers-v6"; import { getConfig } from "./config/config"; +const smtInit = async (smt: PoseidonSMT, stateKeeper: StateKeeperMock, config: any) => { + await smt.__PoseidonSMT_init(config.tssSigner, config.chainName, await stateKeeper.getAddress(), config.treeSize); +}; + export = async (deployer: Deployer) => { const config = (await getConfig())!; await deployPoseidons(deployer, [1, 2, 3, 5]); - let registrationSmt = await deployer.deploy(PoseidonSMT__factory, { name: "RegistrationSMT" }); - await deployer.deploy(ERC1967Proxy__factory, [await registrationSmt.getAddress(), "0x"], { - name: "RegistrationSMT Proxy", - }); - registrationSmt = await deployer.deployed(PoseidonSMT__factory, "RegistrationSMT Proxy"); - - let certificatesSmt = await deployer.deploy(PoseidonSMT__factory, { name: "CertificatesSMT" }); - await deployer.deploy(ERC1967Proxy__factory, [await certificatesSmt.getAddress(), "0x"], { - name: "CertificatesSMT Proxy", - }); - certificatesSmt = await deployer.deployed(PoseidonSMT__factory, "CertificatesSMT Proxy"); - - let stateKeeper = await deployer.deploy(StateKeeperMock__factory); - await deployer.deploy(ERC1967Proxy__factory, [await stateKeeper.getAddress(), "0x"], { - name: "StateKeeper Proxy", - }); - stateKeeper = await deployer.deployed(StateKeeperMock__factory, "StateKeeper Proxy"); - - await registrationSmt.__PoseidonSMT_init( - config.tssSigner, - config.chainName, - await stateKeeper.getAddress(), - config.treeSize, - ); - await certificatesSmt.__PoseidonSMT_init( - config.tssSigner, - config.chainName, - await stateKeeper.getAddress(), - config.treeSize, - ); + const registrationSmt = await deploySMTProxy(deployer, "RegistrationSMT"); + const certificatesSmt = await deploySMTProxy(deployer, "CertificatesSMT"); + + const stateKeeper = await deployProxy(deployer, StateKeeperMock__factory, "StateKeeper"); + + await smtInit(registrationSmt, stateKeeper, config); + await smtInit(certificatesSmt, stateKeeper, config); await stateKeeper.__StateKeeper_init( config.tssSigner, diff --git a/deploy/2_registration.migration.ts b/deploy/2_registration.migration.ts index 6d18674..998c39e 100644 --- a/deploy/2_registration.migration.ts +++ b/deploy/2_registration.migration.ts @@ -1,29 +1,55 @@ import { Deployer, Reporter } from "@solarity/hardhat-migrate"; import { - StateKeeperMock__factory, - ERC1967Proxy__factory, - Registration2Mock__factory, + CRSAPSSSHA2Signer__factory, CRSASHA2Dispatcher__factory, CRSASHA2Signer__factory, - PNOAADispatcher__factory, - PRSASHA1Dispatcher__factory, + PECDSASHA1Authenticator__factory, PECDSASHA1Dispatcher__factory, + PInternalVerifier2__factory, + PNOAADispatcher__factory, PRSASHA1Authenticator__factory, - PECDSASHA1Authenticator__factory, + PRSASHA1Dispatcher__factory, PUniversal2048Verifier2__factory, PUniversal4096Verifier2__factory, - PInternalVerifier2__factory, + Registration2Mock__factory, + StateKeeperMock__factory, } from "@ethers-v6"; +import { BaseContract } from "ethers"; import { getConfig } from "./config/config"; +import { deployProxy } from "./helpers/helper"; + +const deployCDispatcher = async ( + deployer: Deployer, + signer: BaseContract, + keyLength: string, + keyPrefix: string, + name: string, +) => { + const dispatcher = await deployer.deploy(CRSASHA2Dispatcher__factory, { name: `${name} ${keyLength}` }); + await dispatcher.__CRSASHA2Dispatcher_init(await signer.getAddress(), keyLength, keyPrefix); +}; const deployCRSASHA2Dispatcher = async (deployer: Deployer, exponent: string, keyLength: string, keyPrefix: string) => { const signer = await deployer.deploy(CRSASHA2Signer__factory, { name: `CRSASHA2Signer ${exponent} ${keyLength}` }); - const dispatcher = await deployer.deploy(CRSASHA2Dispatcher__factory, { name: `CRSASHA2Dispatcher ${keyLength}` }); - await signer.__CRSASHA2Signer_init(exponent); - await dispatcher.__CRSASHA2Dispatcher_init(await signer.getAddress(), keyLength, keyPrefix); + + await deployCDispatcher(deployer, signer, keyLength, keyPrefix, "CRSASHA2Dispatcher"); +}; + +const deployCRSAPSSSHA2Dispatcher = async ( + deployer: Deployer, + exponent: string, + keyLength: string, + keyPrefix: string, +) => { + const signer = await deployer.deploy(CRSAPSSSHA2Signer__factory, { + name: `CRSAPSSSHA2Signer ${exponent} ${keyLength}`, + }); + await signer.__CRSAPSSSHA2Signer_init(exponent); + + await deployCDispatcher(deployer, signer, keyLength, keyPrefix, "CRSAPSSSHA2Dispatcher"); }; const deployPNOAADispatcher = async (deployer: Deployer) => { @@ -61,18 +87,14 @@ export = async (deployer: Deployer) => { const config = (await getConfig())!; const stateKeeper = await deployer.deployed(StateKeeperMock__factory, "StateKeeper Proxy"); - let registration = await deployer.deploy(Registration2Mock__factory); - await deployer.deploy(ERC1967Proxy__factory, [await registration.getAddress(), "0x"], { - name: "Registration Proxy", - }); - registration = await deployer.deployed(Registration2Mock__factory, "Registration Proxy"); - + const registration = await deployProxy(deployer, Registration2Mock__factory, "Registration"); await registration.__Registration_init(config.tssSigner, config.chainName, await stateKeeper.getAddress()); await deployPVerifiers(deployer); await deployCRSASHA2Dispatcher(deployer, "65537", "512", "0x0282020100"); await deployCRSASHA2Dispatcher(deployer, "65537", "256", "0x0282010100"); + await deployCRSAPSSSHA2Dispatcher(deployer, "65537", "512", "0x0282020100"); await deployPRSASHA12688Dispatcher(deployer, "65537"); await deployPRSASHA12688Dispatcher(deployer, "3"); diff --git a/deploy/helpers/helper.ts b/deploy/helpers/helper.ts index 8bceea7..7fc3755 100644 --- a/deploy/helpers/helper.ts +++ b/deploy/helpers/helper.ts @@ -1,4 +1,7 @@ -import { Deployer, Reporter } from "@solarity/hardhat-migrate"; +import { ERC1967Proxy__factory, PoseidonSMT, PoseidonSMT__factory } from "@/generated-types/ethers"; +import { Deployer } from "@solarity/hardhat-migrate"; +import { EthersContract } from "@solarity/hardhat-migrate/dist/src/types/adapter"; +import { BaseContract } from "ethers"; const { poseidonContract } = require("circomlibjs"); @@ -24,3 +27,21 @@ export async function deployPoseidons(deployer: Deployer, poseidonSizeParams: nu await deployPoseidon(size); } } + +export async function deploySMTProxy(deployer: Deployer, name: string) { + return (await deployProxy(deployer, PoseidonSMT__factory, name)) as PoseidonSMT; +} + +export async function deployProxy( + deployer: Deployer, + factory: EthersContract, + name: string, +) { + const implementation = (await deployer.deploy(factory, { name: name })) as BaseContract; + + await deployer.deploy(ERC1967Proxy__factory, [await implementation.getAddress(), "0x"], { + name: `${name} Proxy`, + }); + + return await deployer.deployed(factory, `${name} Proxy`); +} diff --git a/scripts/utils/types.ts b/scripts/utils/types.ts index ee489e7..23bc4ee 100644 --- a/scripts/utils/types.ts +++ b/scripts/utils/types.ts @@ -2,6 +2,7 @@ import { ethers } from "hardhat"; export const C_RSA_4096 = ethers.solidityPackedKeccak256(["string"], ["C_RSA_4096"]); export const C_RSA_2048 = ethers.solidityPackedKeccak256(["string"], ["C_RSA_2048"]); +export const C_RSAPSS_4096 = ethers.solidityPackedKeccak256(["string"], ["C_RSAPSS_4096"]); export const P_NO_AA = ethers.solidityPackedKeccak256(["string"], ["P_NO_AA"]); export const P_RSA_SHA1_2688 = ethers.solidityPackedKeccak256(["string"], ["P_RSA_SHA1_2688"]); diff --git a/test/certificate/signers/CRSAPSSSHA2Signer.test.ts b/test/certificate/signers/CRSAPSSSHA2Signer.test.ts new file mode 100644 index 0000000..c735861 --- /dev/null +++ b/test/certificate/signers/CRSAPSSSHA2Signer.test.ts @@ -0,0 +1,36 @@ +import { ethers } from "hardhat"; +import { expect } from "chai"; +import { Reverter } from "@/test/helpers/"; + +import { CRSAPSSSHA2Signer } from "@ethers-v6"; + +describe("CRSAPSSSHA2Signer", () => { + const reverter = new Reverter(); + + let signer: CRSAPSSSHA2Signer; + + before("setup", async () => { + const CRSAPSSSHA2Signer = await ethers.getContractFactory("CRSAPSSSHA2Signer"); + + signer = await CRSAPSSSHA2Signer.deploy(); + + await signer.__CRSAPSSSHA2Signer_init(65537); + + await reverter.snapshot(); + }); + + afterEach(reverter.revert); + + describe("#verifyICAOSignature", () => { + it("should verify icao signature", async () => { + const signedAttributes = + "0x308203c3a003020102020874442b6b708ef7a2304106092a864886f70d01010a3034a00f300d06096086480165030402010500a11c301a06092a864886f70d010108300d06096086480165030402010500a203020120302f310b3009060355040613025048310c300a060355040a0c034446413112301006035504030c09435343413031303037301e170d3232313231323136303030305a170d3333303430313135353935395a302d310b3009060355040613025048310c300a060355040a13034446413110300e060355040313074453303131313330820122300d06092a864886f70d01010105000382010f003082010a0282010100ab630b320a41ecf8886a904ab50fabcfad658f5af8a9f8aaecefe0dc5e2ea99eba3deccba3f58885f8574fe0ad5c889763afc2b68e66b5928403d508724ad1e7fd05c573c053e04660fd31128cff2e2f574ec92430202f5dafa6df66b46fb16ece1372424d3aa3b975428c59f18fe1f32e6c328b64f58f95e05684dfff2d21a85cb73bcb32ac172c8f782fa2ea942118379833bec37cab64de493ddae79014ed0e6fcaa2ca4cdc3bdb0442ba550cde8355194c3c3934b2d8bfa513fcf5788c0569e0527cd20daa5e8e114204661a3d1f21650d01703e7a112602cf8fbefbc329afc18d3d49a68b60e5c89c5152ad6e7f0480b0e4157b26640c569ae477e04f190203010001a38201c7308201c3300e0603551d0f0101ff040403020780301d0603551d0e04160414363257ff5b20debaa6e26c257d3ebb4fa1e7bcf5305e0603551d23045730558014a1436db84f1c134e49b387da56cee801102d4f73a133a431302f310b3009060355040613025048310c300a060355040a0c034446413112301006035504030c0943534341303130303782086b8a5f2f46fa934b302b0603551d1004243022800f32303232313231323136303030305a810f32303233303430313135353935395a30390603551d1104323030811c70617373706f72742e6469726563746f72406466612e676f762e7068a410300e310c300a06035504070c0350484c30390603551d1204323030811c70617373706f72742e6469726563746f72406466612e676f762e7068a410300e310c300a06035504070c0350484c306d0603551d1f046630643030a02ea02c862a68747470733a2f2f706b64646f776e6c6f6164312e6963616f2e696e742f43524c732f50484c2e63726c3030a02ea02c862a68747470733a2f2f706b64646f776e6c6f6164322e6963616f2e696e742f43524c732f50484c2e63726c3020060767810801010602041530131301501302504f130250441302505213025053"; + const icaoMemberSignature = + "0x875ef62f6832599f41b50ca51a478c92ff47b61f2090157f64b425b1e1ad5612e6abb7d5808d9be5f0eaaa16d2b516ef161534c78d542ffd659107535c2bab643163fb9af27a50389792508d1cdbda347a103404c5e08d2d97c7935994631d42fe7e0caa892dc3ec39d3ac94dbccb3cd0870b21b9c836feed5bc32e9ec6830392bdade1fc9b5280fbaa2ceaa78d9524af3d015cbaf07eebc84a9caec81a4407452573a101b79772056193d207a8398690ed0dd0cc5a6410fd844d313c50934d6e1d556f8e7b39b12525f3cd766c9342fbd892e40408b0c232d888da11fc64d0f09db70971d395d7a1d2aacfe9da78e3c46ce43b3ce5b9fc1e6a90c065cdafa2e8a117d63c00cf9f54e3a3313789f03dd7efc76641c2cf5068ca4512c82fa6c62f6bd36b12523dc46f444b8312d2f6e6ec22cd10eddb19220d9b8ba4cc442dd836335482c6309d56e87492d2fdaefdb7b5ede566ed43eb87955451225846ce2535b803a9ca79034cc3aa41307cc57f0962cb8b2c3b99a5c87150387f7d8de6a18f6a838404c4aa5bb279378fb285c096d4c2664c700ac4e3c0cb44f920928e764dd4b10f22d3cb5bdfac78066b1b0a5ae75528e447b262510d41150a94ab0f645cc61ae99a3719bd29cf3901dde6de7cc162051f34c642a0f7854ef00d4143d755ad72bc71371c3a8dedb94118272f37f853bd171743b0c7a9a8cb96095476c9c"; + const icaoMemberKey = + "0xd24081be6cc14fe3fb4b35ab6df1a7f28f373017ef15a26b67ff2dd04773a3ef8942b7ba5f2f91aea469fe757e2e3362a907610b441f3610f528b1f39739a132c4bebc26c37d25b6d12481336fecddfc6bdcd011be4f2912ff0663cb70d9938280813dd3f32f2e6fef184881f784bbd2fd2d165b169d8594c45d832dbaebfdcb532d6542b57413825df5164b577dcdc248dfc4a8b071eb0bef021128e9172b77a18a5a6b00ebc0e07af0a9df6592684805a4ba0db00dddaabf793641ec0da51aecc6160acd56a0194d1161271b8feaaf0ae851ae65f1464c79607bbb237de3dbd0a299e9cda846c362976108d10555b57866a56c87c6a5d92d1888d6260fa90459afb14688b8a53921d2d477d1677518956412e01eb2592b27ad62a3d3a50777c4bee3f348b4788bb7bbd38d6bd902968a7b3640d75f98b78824ee9462e1b2d405b8c1ce7d7dbef479c2979553790b7d7fa8a6f05dad4cf95b92a218b410eb5b9df712d099b6952a07f122d21e95b934e9a765e758397b191fd01c0c4ae669039a0d7003308ab78d03809752bb7b676c3f3bbd9ac4b8cf0162efb50e01c52e3a97fed31d1e73f12b3da7b4df87fd7e93f70f92dc154d0bcef5a39c9631b2b50c7c7b91f4a63d4e3d487c37fc63bbdf87b71cad5581409661d15f77ff738a4d53d23424d999490607ebfd8293b2fb5c269cd8a19477ca88f6f7cb1abe00fb16c9"; + + expect(await signer.verifyICAOSignature(signedAttributes, icaoMemberSignature, icaoMemberKey)).to.be.true; + }); + }); +});