From e46a6a62c1b7375ca83793b74213d427494657fb Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 30 Aug 2023 10:21:59 +0200 Subject: [PATCH] apply pr suggestions for #273 --- src/characters/cbd-recipient.ts | 30 +++++------ src/characters/enrico.ts | 14 ++--- test/integration/dkg-client.test.ts | 2 +- test/unit/cbd-strategy.test.ts | 14 ++--- test/utils.ts | 79 ++++++++++++++++++++--------- 5 files changed, 82 insertions(+), 57 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 9a977665d..7c10fba50 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -2,16 +2,17 @@ import { AccessControlPolicy, AuthenticatedData, Ciphertext, + CiphertextHeader, combineDecryptionSharesSimple, Context, DecryptionShareSimple, - decryptWithSharedSecret, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, FerveoVariant, SessionSharedSecret, SessionStaticSecret, ThresholdDecryptionRequest, + ThresholdMessageKit, } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; @@ -49,27 +50,23 @@ export class ThresholdDecrypter { conditionExpr: ConditionExpression, ciphertext: Ciphertext ): Promise { - const acp = await this.makeAcp(provider, conditionExpr, ciphertext); + const acp = await this.makeAcp(provider, conditionExpr, ciphertext.header); + const messageKit = new ThresholdMessageKit(ciphertext, acp); const decryptionShares = await this.retrieve( provider, conditionExpr, - ciphertext, - acp + messageKit ); const sharedSecret = combineDecryptionSharesSimple(decryptionShares); - return decryptWithSharedSecret( - ciphertext, - conditionExpr.asAad(), - sharedSecret - ); + return messageKit.decryptWithSharedSecret(sharedSecret); } private async makeAcp( provider: ethers.providers.Web3Provider, conditionExpr: ConditionExpression, - ciphertext: Ciphertext + ciphertextHeader: CiphertextHeader ) { const dkgRitual = await DkgClient.getExistingRitual( provider, @@ -80,7 +77,7 @@ export class ThresholdDecrypter { conditionExpr.toWASMConditions() ); - const headerHash = keccak256(ciphertext.header.toBytes()); + const headerHash = keccak256(ciphertextHeader.toBytes()); const authorization = await provider.getSigner().signMessage(headerHash); return new AccessControlPolicy(authData, toBytes(authorization)); @@ -90,8 +87,7 @@ export class ThresholdDecrypter { public async retrieve( provider: ethers.providers.Web3Provider, conditionExpr: ConditionExpression, - ciphertext: Ciphertext, - acp: AccessControlPolicy + messageKit: ThresholdMessageKit ): Promise { const dkgParticipants = await DkgCoordinatorAgent.getParticipants( provider, @@ -100,10 +96,10 @@ export class ThresholdDecrypter { const contextStr = await conditionExpr.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( this.ritualId, - ciphertext, + messageKit.ciphertextHeader, contextStr, dkgParticipants, - acp + messageKit.acp ); const { encryptedResponses, errors } = await this.porter.cbdDecrypt( @@ -148,7 +144,7 @@ export class ThresholdDecrypter { private makeDecryptionRequests( ritualId: number, - ciphertext: Ciphertext, + ciphertextHeader: CiphertextHeader, contextStr: string, dkgParticipants: Array, acp: AccessControlPolicy @@ -159,7 +155,7 @@ export class ThresholdDecrypter { const decryptionRequest = new ThresholdDecryptionRequest( ritualId, FerveoVariant.simple, - ciphertext.header, + ciphertextHeader, acp, new Context(contextStr) ); diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index 17c6ff358..2475020f1 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -1,7 +1,8 @@ import { + AuthenticatedData, Ciphertext, DkgPublicKey, - ferveoEncrypt, + encryptForDkg, MessageKit, PublicKey, SecretKey, @@ -52,7 +53,7 @@ export class Enrico { public encryptMessageCbd( plaintext: Uint8Array | string, withConditions?: ConditionExpression - ): { ciphertext: Ciphertext; aad: Uint8Array } { + ): { ciphertext: Ciphertext; authenticatedData: AuthenticatedData } { if (!withConditions) { withConditions = this.conditions; } @@ -65,12 +66,11 @@ export class Enrico { throw new Error('Wrong key type. Use encryptMessagePre instead.'); } - const aad = withConditions.asAad(); - const ciphertext = ferveoEncrypt( + const [ciphertext, authenticatedData] = encryptForDkg( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), - aad, - this.encryptingKey + this.encryptingKey, + withConditions.toWASMConditions() ); - return { ciphertext, aad }; + return { ciphertext, authenticatedData }; } } diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index acb8337b6..283dc54f6 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -29,7 +29,7 @@ describe('DkgCoordinatorAgent', () => { it('fetches participants from the coordinator', async () => { const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const fakeParticipants = fakeDkgParticipants(fakeRitualId); + const fakeParticipants = await fakeDkgParticipants(fakeRitualId); const getParticipantsSpy = mockGetParticipants( fakeParticipants.participants ); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index d38aeeb45..65cd8f949 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -39,6 +39,7 @@ const conditionExpr = new ConditionExpression(ownsNFT); const ursulas = fakeUrsulas(); const variant = FerveoVariant.precomputed; const ritualId = 0; +const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const makeCbdStrategy = async () => { const cohort = await makeCohort(ursulas); @@ -52,7 +53,6 @@ async function makeDeployedCbdStrategy() { const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg); - const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const getUrsulasSpy = mockGetUrsulas(ursulas); const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual); const deployedStrategy = await strategy.deploy(web3Provider, ritualId); @@ -102,20 +102,20 @@ describe('CbdDeployedStrategy', () => { const { mockedDkg, deployedStrategy } = await makeDeployedCbdStrategy(); const message = 'this is a secret'; - const { ciphertext, aad } = deployedStrategy + const { ciphertext, authenticatedData } = deployedStrategy .makeEncrypter(conditionExpr) .encryptMessageCbd(message); // Setup mocks for `retrieveAndDecrypt` - const { decryptionShares } = fakeTDecFlow({ + const { decryptionShares } = await fakeTDecFlow({ ...mockedDkg, message: toBytes(message), - aad, ciphertext, + authenticatedData, + web3Provider, }); - const { participantSecrets, participants } = fakeDkgParticipants( - mockedDkg.ritualId, - variant + const { participantSecrets, participants } = await fakeDkgParticipants( + mockedDkg.ritualId ); const requesterSessionKey = SessionStaticSecret.random(); const decryptSpy = mockCbdDecrypt( diff --git a/test/utils.ts b/test/utils.ts index eec92f4cb..2afff4ba9 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -4,19 +4,20 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Block } from '@ethersproject/providers'; import { + AccessControlPolicy, AggregatedTranscript, + AuthenticatedData, Capsule, CapsuleFrag, Ciphertext, combineDecryptionSharesSimple, DecryptionSharePrecomputed, DecryptionShareSimple, - decryptWithSharedSecret, Dkg, EncryptedThresholdDecryptionResponse, EncryptedTreasureMap, + encryptForDkg, EthereumAddress, - ferveoEncrypt, FerveoVariant, Keypair, PublicKey, @@ -26,6 +27,7 @@ import { SessionStaticKey, SessionStaticSecret, ThresholdDecryptionResponse, + ThresholdMessageKit, Transcript, Validator, ValidatorMessage, @@ -43,6 +45,8 @@ import { DkgRitualState, } from '../src/agents/coordinator'; import { ThresholdDecrypter } from '../src/characters/cbd-recipient'; +import { ConditionExpression } from '../src/conditions'; +import { ERC721Balance } from '../src/conditions/predefined'; import { DkgClient, DkgRitual } from '../src/dkg'; import { BlockchainPolicy, PreEnactedPolicy } from '../src/policies/policy'; import { @@ -55,6 +59,8 @@ import { import { ChecksumAddress } from '../src/types'; import { toBytes, toHexString, zip } from '../src/utils'; +import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from './unit/testVariables'; + export const bytesEqual = (first: Uint8Array, second: Uint8Array): boolean => first.length === second.length && first.every((value, index) => value === second[index]); @@ -288,12 +294,13 @@ interface FakeDkgRitualFlow { threshold: number; receivedMessages: ValidatorMessage[]; ciphertext: Ciphertext; - aad: Uint8Array; + authenticatedData: AuthenticatedData; dkg: Dkg; message: Uint8Array; + web3Provider: ethers.providers.Web3Provider; } -export const fakeTDecFlow = ({ +export const fakeTDecFlow = async ({ validators, validatorKeypairs, ritualId, @@ -301,8 +308,9 @@ export const fakeTDecFlow = ({ threshold, receivedMessages, ciphertext, - aad, + authenticatedData, message, + web3Provider, }: FakeDkgRitualFlow) => { // Having aggregated the transcripts, the validators can now create decryption shares const decryptionShares: ( @@ -320,7 +328,7 @@ export const fakeTDecFlow = ({ const decryptionShare = aggregate.createDecryptionShareSimple( dkg, ciphertext.header, - aad, + authenticatedData.toBytes(), keypair ); decryptionShares.push(decryptionShare); @@ -331,44 +339,66 @@ export const fakeTDecFlow = ({ const sharedSecret = combineDecryptionSharesSimple(decryptionShares); // The client should have access to the public parameters of the DKG - const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret); + const headerHash = keccak256(ciphertext.header.toBytes()); + const authorization = await web3Provider.getSigner().signMessage(headerHash); + const acp = new AccessControlPolicy( + authenticatedData, + toBytes(authorization) + ); + const messageKit = new ThresholdMessageKit(ciphertext, acp); + + const plaintext = messageKit.decryptWithSharedSecret(sharedSecret); if (!bytesEqual(plaintext, message)) { throw new Error('Decryption failed'); } return { decryptionShares, sharedSecret, plaintext }; }; -export const fakeDkgTDecFlowE2e = ( - variant: FerveoVariant, - message = toBytes('fake-message'), - aad = toBytes('fake-aad'), +const fakeConditionExpr = () => { + const erc721Balance = new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }); + return new ConditionExpression(erc721Balance); +}; + +export const fakeDkgTDecFlowE2e = async ( ritualId = 0, + variant: FerveoVariant = FerveoVariant.precomputed, + conditions: ConditionExpression = fakeConditionExpr(), + message = toBytes('fake-message'), + web3Provider = fakeWeb3Provider(), sharesNum = 4, threshold = 4 ) => { const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); // In the meantime, the client creates a ciphertext and decryption request - const ciphertext = ferveoEncrypt(message, aad, ritual.dkg.publicKey()); - const { decryptionShares } = fakeTDecFlow({ + const [ciphertext, authenticatedData] = encryptForDkg( + message, + ritual.dkg.publicKey(), + conditions.toWASMConditions() + ); + const { decryptionShares } = await fakeTDecFlow({ ...ritual, ciphertext, - aad, + authenticatedData, message, + web3Provider, }); return { ...ritual, message, - aad, + authenticatedData, ciphertext, decryptionShares, }; }; -export const fakeCoordinatorRitual = ( +export const fakeCoordinatorRitual = async ( ritualId: number -): { +): Promise<{ aggregationMismatch: boolean; initTimestamp: number; aggregatedTranscriptHash: string; @@ -380,8 +410,8 @@ export const fakeCoordinatorRitual = ( aggregatedTranscript: string; publicKeyHash: string; totalAggregations: number; -} => { - const ritual = fakeDkgTDecFlowE2e(FerveoVariant.precomputed); +}> => { + const ritual = await fakeDkgTDecFlowE2e(); const dkgPkBytes = ritual.dkg.publicKey().toBytes(); return { id: ritualId, @@ -401,14 +431,13 @@ export const fakeCoordinatorRitual = ( }; }; -export const fakeDkgParticipants = ( - ritualId: number, - variant = FerveoVariant.precomputed -): { +export const fakeDkgParticipants = async ( + ritualId: number +): Promise<{ participants: DkgParticipant[]; participantSecrets: Record; -} => { - const ritual = fakeDkgTDecFlowE2e(variant); +}> => { + const ritual = await fakeDkgTDecFlowE2e(ritualId); const label = toBytes(`${ritualId}`); const participantSecrets: Record =