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

Apply PR suggestions for #273 #286

Merged
merged 5 commits into from
Sep 8, 2023
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
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);
derekpierre marked this conversation as resolved.
Show resolved Hide resolved
}
}
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 @@ -75,6 +75,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
Loading