Skip to content

Commit

Permalink
Apply PR suggestions for #273 (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec authored Sep 8, 2023
2 parents 8f1dcf1 + 363718f commit c18f996
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 160 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"prebuild": "yarn typechain"
},
"dependencies": {
"@nucypher/nucypher-core": "^0.12.0",
"@nucypher/nucypher-core": "^0.13.0",
"axios": "^1.5.0",
"deep-equal": "^2.2.1",
"ethers": "^5.7.2",
Expand Down
98 changes: 30 additions & 68 deletions src/characters/cbd-recipient.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import {
AccessControlPolicy,
AuthenticatedData,
Ciphertext,
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';

import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator';
import { ConditionExpression } from '../conditions';
import { DkgClient, DkgRitual } from '../dkg';
import { ConditionContext } from '../conditions';
import { DkgRitual } from '../dkg';
import { PorterClient } from '../porter';
import { fromJSON, toBytes, toJSON } from '../utils';
import { fromJSON, objectEquals, toJSON } from '../utils';

export type ThresholdDecrypterJSON = {
porterUri: string;
Expand All @@ -46,71 +42,40 @@ export class ThresholdDecrypter {
// Retrieve and decrypt ciphertext using provider and condition expression
public async retrieveAndDecrypt(
provider: ethers.providers.Provider,
signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext
thresholdMessageKit: ThresholdMessageKit,
signer?: ethers.Signer
): Promise<Uint8Array> {
const acp = await this.makeAcp(provider, signer, conditionExpr, ciphertext);

const decryptionShares = await this.retrieve(
provider,
conditionExpr,
ciphertext,
acp,
thresholdMessageKit,
signer
);

const sharedSecret = combineDecryptionSharesSimple(decryptionShares);
return decryptWithSharedSecret(
ciphertext,
conditionExpr.asAad(),
sharedSecret
);
}

private async makeAcp(
provider: ethers.providers.Provider,
signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext
) {
const dkgRitual = await DkgClient.getExistingRitual(
provider,
this.ritualId
);
const authData = new AuthenticatedData(
dkgRitual.dkgPublicKey,
conditionExpr.toWASMConditions()
);

const headerHash = keccak256(ciphertext.header.toBytes());
const authorization = await signer.signMessage(headerHash);

return new AccessControlPolicy(authData, toBytes(authorization));
return thresholdMessageKit.decryptWithSharedSecret(sharedSecret);
}

// Retrieve decryption shares
public async retrieve(
provider: ethers.providers.Provider,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext,
acp: AccessControlPolicy,
thresholdMessageKit: ThresholdMessageKit,
signer?: ethers.Signer
): Promise<DecryptionShareSimple[]> {
const dkgParticipants = await DkgCoordinatorAgent.getParticipants(
provider,
this.ritualId
);
const contextStr = await conditionExpr
.buildContext(provider, {}, signer)
.toJson();
const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests(
this.ritualId,
ciphertext,
contextStr,
dkgParticipants,
acp
);
const wasmContext = await ConditionContext.fromConditions(
provider,
thresholdMessageKit.acp.conditions,
signer
).toWASMContext();
const { sharedSecrets, encryptedRequests } =
await this.makeDecryptionRequests(
this.ritualId,
wasmContext,
dkgParticipants,
thresholdMessageKit
);

const { encryptedResponses, errors } = await this.porter.cbdDecrypt(
encryptedRequests,
Expand Down Expand Up @@ -152,22 +117,21 @@ export class ThresholdDecrypter {
);
}

private makeDecryptionRequests(
private async makeDecryptionRequests(
ritualId: number,
ciphertext: Ciphertext,
contextStr: string,
wasmContext: Context,
dkgParticipants: Array<DkgParticipant>,
acp: AccessControlPolicy
): {
thresholdMessageKit: ThresholdMessageKit
): Promise<{
sharedSecrets: Record<string, SessionSharedSecret>;
encryptedRequests: Record<string, EncryptedThresholdDecryptionRequest>;
} {
}> {
const decryptionRequest = new ThresholdDecryptionRequest(
ritualId,
FerveoVariant.simple,
ciphertext.header,
acp,
new Context(contextStr)
thresholdMessageKit.ciphertextHeader,
thresholdMessageKit.acp,
wasmContext
);

const ephemeralSessionKey = this.makeSessionKey();
Expand Down Expand Up @@ -234,8 +198,6 @@ export class ThresholdDecrypter {
}

public equals(other: ThresholdDecrypter): boolean {
return (
this.porter.porterUrl.toString() === other.porter.porterUrl.toString()
);
return objectEquals(this.toObj(), other.toObj());
}
}
33 changes: 21 additions & 12 deletions src/characters/enrico.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
Ciphertext,
AccessControlPolicy,
DkgPublicKey,
ferveoEncrypt,
encryptForDkg,
MessageKit,
PublicKey,
SecretKey,
ThresholdMessageKit,
} from '@nucypher/nucypher-core';
import { arrayify, keccak256 } from 'ethers/lib/utils';

import { ConditionExpression } from '../conditions';
import { Keyring } from '../keyring';
Expand Down Expand Up @@ -51,26 +53,33 @@ export class Enrico {

public encryptMessageCbd(
plaintext: Uint8Array | string,
withConditions?: ConditionExpression
): { ciphertext: Ciphertext; aad: Uint8Array } {
if (!withConditions) {
withConditions = this.conditions;
conditions?: ConditionExpression
): ThresholdMessageKit {
if (!conditions) {
conditions = this.conditions;
}

if (!withConditions) {
if (!conditions) {
throw new Error('Conditions are required for CBD encryption.');
}

if (!(this.encryptingKey instanceof DkgPublicKey)) {
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,
conditions.toWASMConditions()
);

const headerHash = keccak256(ciphertext.header.toBytes());
const authorization = this.keyring.signer.sign(arrayify(headerHash));
const acp = new AccessControlPolicy(
authenticatedData,
authorization.toBEBytes()
);
return { ciphertext, aad };

return new ThresholdMessageKit(ciphertext, acp);
}
}
12 changes: 6 additions & 6 deletions src/conditions/condition-expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core';
import { ethers } from 'ethers';
import { SemVer } from 'semver';

import { toBytes, toJSON } from '../utils';
import { toJSON } from '../utils';

import { Condition } from './condition';
import { ConditionContext, CustomContextParam } from './context';
Expand All @@ -13,7 +13,7 @@ export type ConditionExpressionJSON = {
};

export class ConditionExpression {
static VERSION = '1.0.0';
public static VERSION = '1.0.0';

constructor(
public readonly condition: Condition,
Expand Down Expand Up @@ -61,6 +61,10 @@ export class ConditionExpression {
return new WASMConditions(toJSON(this.toObj()));
}

public static fromWASMConditions(conditions: WASMConditions) {
return ConditionExpression.fromJSON(conditions.toString());
}

public buildContext(
provider: ethers.providers.Provider,
customParameters: Record<string, CustomContextParam> = {},
Expand All @@ -78,10 +82,6 @@ export class ConditionExpression {
return this.condition.requiresSigner();
}

public asAad(): Uint8Array {
return toBytes(this.toJson());
}

public equals(other: ConditionExpression): boolean {
return [
this.version === other.version,
Expand Down
2 changes: 1 addition & 1 deletion src/conditions/condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ export class Condition {
}

public equals(other: Condition) {
return objectEquals(this, other);
return objectEquals(this.toObj(), other.toObj());
}
}
42 changes: 29 additions & 13 deletions src/conditions/context/context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Conditions as WASMConditions } from '@nucypher/nucypher-core';
import { Context, Conditions as WASMConditions } from '@nucypher/nucypher-core';
import { ethers } from 'ethers';

import { fromJSON, toJSON } from '../../utils';
import { Condition } from '../condition';
import { ConditionExpression } from '../condition-expr';
import { USER_ADDRESS_PARAM } from '../const';

import { TypedSignature, WalletAuthenticationProvider } from './providers';
Expand Down Expand Up @@ -31,10 +32,6 @@ export class ConditionContext {
this.validate();
}

public requiresSigner(): boolean {
return this.conditions.some((cond) => cond.requiresSigner());
}

private validate() {
Object.keys(this.customParameters).forEach((key) => {
if (RESERVED_CONTEXT_PARAMS.includes(key)) {
Expand All @@ -49,9 +46,12 @@ export class ConditionContext {
}
});

if (this.requiresSigner() && !this.signer) {
const conditionRequiresSigner = this.conditions.some((c) =>
c.requiresSigner()
);
if (conditionRequiresSigner && !this.signer) {
throw new Error(
`Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
`Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate`
);
}

Expand Down Expand Up @@ -92,7 +92,7 @@ export class ConditionContext {
if (requestedParameters.has(USER_ADDRESS_PARAM)) {
if (!this.walletAuthProvider) {
throw new Error(
`Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
`Condition contains ${USER_ADDRESS_PARAM} context variable and requires a signer to populate`
);
}
parameters[USER_ADDRESS_PARAM] =
Expand Down Expand Up @@ -120,19 +120,35 @@ export class ConditionContext {
return parameters;
};

public toJson = async (): Promise<string> => {
public async toJson(): Promise<string> {
const parameters = await this.toObj();
return toJSON(parameters);
};
}

public withCustomParams = (
public withCustomParams(
params: Record<string, CustomContextParam>
): ConditionContext => {
): ConditionContext {
return new ConditionContext(
this.provider,
this.conditions,
params,
this.signer
);
};
}

public async toWASMContext(): Promise<Context> {
const asJson = await this.toJson();
return new Context(asJson);
}

public static fromConditions(
provider: ethers.providers.Provider,
conditions: WASMConditions,
signer?: ethers.Signer
): ConditionContext {
const innerConditions = [
ConditionExpression.fromWASMConditions(conditions).condition,
];
return new ConditionContext(provider, innerConditions, {}, signer);
}
}
2 changes: 1 addition & 1 deletion test/integration/dkg-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('DkgCoordinatorAgent', () => {

it('fetches participants from the coordinator', async () => {
const provider = fakeProvider(SecretKey.random().toBEBytes());
const fakeParticipants = fakeDkgParticipants(fakeRitualId);
const fakeParticipants = await fakeDkgParticipants(fakeRitualId);
const getParticipantsSpy = mockGetParticipants(
fakeParticipants.participants
);
Expand Down
Loading

0 comments on commit c18f996

Please sign in to comment.