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

fix: fix typescript wrapper according to mls transport api changes [WPB-15407] #860

3 changes: 1 addition & 2 deletions crypto-ffi/bindings/js/src/CoreCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,18 @@ export {
GroupInfoEncryptionType,
RatchetTreeType,
DeviceStatus,
WelcomeBundle,
} from "./CoreCryptoMLS.js";
export type {
ProposalRef,
MlsTransportResponse,
ConversationId,
ClientId,
WelcomeBundle,
ProposalBundle,
MlsTransport,
GroupInfoBundle,
BufferedDecryptedMessage,
CommitBundle,
ConversationInitBundle,
DecryptedMessage,
} from "./CoreCryptoMLS.js";

Expand Down
179 changes: 50 additions & 129 deletions crypto-ffi/bindings/js/src/CoreCryptoContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import { CoreCryptoError } from "./CoreCryptoError.js";
import {
Ciphersuite,
ClientId,
CommitBundle,
ConversationId,
ConversationInitBundle,
CredentialType,
DecryptedMessage,
WelcomeBundle,
Expand Down Expand Up @@ -216,28 +214,24 @@ export class CoreCryptoContext {
creatorCredentialType: CredentialType,
configuration: Partial<ConversationConfiguration> = {}
) {
try {
const {
ciphersuite,
externalSenders,
custom = {},
} = configuration || {};
const config = new ConversationConfiguration(
ciphersuite,
externalSenders,
(custom as CustomConfiguration)?.keyRotationSpan,
(custom as CustomConfiguration)?.wirePolicy
);
return await CoreCryptoError.asyncMapErr(
this.#ctx.create_conversation(
conversationId,
creatorCredentialType,
config
)
);
} catch (e) {
throw CoreCryptoError.fromStdError(e as Error);
}
const {
ciphersuite,
externalSenders,
custom = {},
} = configuration || {};
const config = new ConversationConfiguration(
ciphersuite,
externalSenders,
(custom as CustomConfiguration)?.keyRotationSpan,
(custom as CustomConfiguration)?.wirePolicy
);
return await CoreCryptoError.asyncMapErr(
this.#ctx.create_conversation(
conversationId,
creatorCredentialType,
config
)
);
}

/**
Expand Down Expand Up @@ -324,9 +318,9 @@ export class CoreCryptoContext {
/**
* Ingest a TLS-serialized MLS welcome message to join an existing MLS group
*
* Important: you have to catch the error with this reason "Although this Welcome seems valid, the local KeyPackage
* it references has already been deleted locally. Join this group with an external commit", ignore it and then try
* to join this group with an external commit.
* You have to catch the error with this reason "Although this Welcome seems valid, the local KeyPackage
* it references has already been deleted locally. Join this group with an external commit", ignore it and then
* join this group via {@link CoreCryptoContext.joinByExternalCommit}.
*
* @param welcomeMessage - TLS-serialized MLS Welcome message
* @param configuration - configuration of the MLS group
Expand All @@ -336,21 +330,11 @@ export class CoreCryptoContext {
welcomeMessage: Uint8Array,
configuration: Partial<CustomConfiguration> = {}
): Promise<WelcomeBundle> {
try {
const { keyRotationSpan, wirePolicy } = configuration || {};
const config = new CustomConfiguration(keyRotationSpan, wirePolicy);
const ffiRet: CoreCryptoFfiTypes.WelcomeBundle =
await CoreCryptoError.asyncMapErr(
this.#ctx.process_welcome_message(welcomeMessage, config)
);

return {
id: ffiRet.id,
crlNewDistributionPoints: ffiRet.crl_new_distribution_points,
};
} catch (e) {
throw CoreCryptoError.fromStdError(e as Error);
}
const { keyRotationSpan, wirePolicy } = configuration || {};
const config = new CustomConfiguration(keyRotationSpan, wirePolicy);
return await CoreCryptoError.asyncMapErr(
this.#ctx.process_welcome_message(welcomeMessage, config)
);
}

/**
Expand Down Expand Up @@ -460,119 +444,56 @@ export class CoreCryptoContext {
}

/**
* Creates an update commit which forces every client to update their LeafNode in the conversation
* Update the keying material of the conversation.
*
* @param conversationId - The ID of the conversation
*
* @returns A {@link CommitBundle}
*/
async updateKeyingMaterial(
conversationId: ConversationId
): Promise<CommitBundle> {
try {
const ffiRet: CoreCryptoFfiTypes.CommitBundle =
await CoreCryptoError.asyncMapErr(
this.#ctx.update_keying_material(conversationId)
);

const gi = ffiRet.group_info;

return {
welcome: ffiRet.welcome,
commit: ffiRet.commit,
groupInfo: {
encryptionType: gi.encryption_type,
ratchetTreeType: gi.ratchet_tree_type,
payload: gi.payload,
},
};
} catch (e) {
throw CoreCryptoError.fromStdError(e as Error);
}
async updateKeyingMaterial(conversationId: ConversationId): Promise<void> {
return await CoreCryptoError.asyncMapErr(
this.#ctx.update_keying_material(conversationId)
);
}

/**
* Commits the local pending proposals and returns the {@link CommitBundle} object containing what can result from this operation.
* Commits the local pending proposals.
*
* @param conversationId - The ID of the conversation
* Sends the corresponding commit via {@link MlsTransport.sendCommitBundle}
* and merges it if the call is successful.
*
* @returns A {@link CommitBundle} or `undefined` when there was no pending proposal to commit
* @param conversationId - The ID of the conversation
*/
async commitPendingProposals(
conversationId: ConversationId
): Promise<CommitBundle | undefined> {
try {
const ffiCommitBundle: CoreCryptoFfiTypes.CommitBundle | undefined =
await CoreCryptoError.asyncMapErr(
this.#ctx.commit_pending_proposals(conversationId)
);

if (!ffiCommitBundle) {
return undefined;
}

const gi = ffiCommitBundle.group_info;

return {
welcome: ffiCommitBundle.welcome,
commit: ffiCommitBundle.commit,
groupInfo: {
encryptionType: gi.encryption_type,
ratchetTreeType: gi.ratchet_tree_type,
payload: gi.payload,
},
};
} catch (e) {
throw CoreCryptoError.fromStdError(e as Error);
}
): Promise<void> {
return await CoreCryptoError.asyncMapErr(
this.#ctx.commit_pending_proposals(conversationId)
);
}

/**
* Allows to create an external commit to "apply" to join a group through its GroupInfo.
* "Apply" to join a group through its GroupInfo.
*
* If the Delivery Service rejects it, you can retry by just
* calling again the function again.
* Sends the corresponding commit via {@link MlsTransport.sendCommitBundle}
* and creates the group if the call is successful.
*
* @param groupInfo - a TLS encoded GroupInfo fetched from the Delivery Service
* @param credentialType - kind of Credential to use for joining this group. If {@link CredentialType.Basic} is
* chosen and no Credential has been created yet for it, a new one will be generated.
* @param configuration - configuration of the MLS group
* When {@link CredentialType.X509} is chosen, it fails when no Credential has been created for the given {@link Ciphersuite}.
* @returns see {@link ConversationInitBundle}
*
* @return see {@link WelcomeBundle}
*/
async joinByExternalCommit(
groupInfo: Uint8Array,
credentialType: CredentialType,
configuration: Partial<CustomConfiguration> = {}
): Promise<ConversationInitBundle> {
try {
const { keyRotationSpan, wirePolicy } = configuration || {};
const config = new CustomConfiguration(keyRotationSpan, wirePolicy);
const ffiInitMessage: CoreCryptoFfiTypes.ConversationInitBundle =
await CoreCryptoError.asyncMapErr(
this.#ctx.join_by_external_commit(
groupInfo,
config,
credentialType
)
);

const gi = ffiInitMessage.group_info;

return {
conversationId: ffiInitMessage.conversation_id,
commit: ffiInitMessage.commit,
groupInfo: {
encryptionType: gi.encryption_type,
ratchetTreeType: gi.ratchet_tree_type,
payload: gi.payload,
},
crlNewDistributionPoints:
ffiInitMessage.crl_new_distribution_points,
};
} catch (e) {
throw CoreCryptoError.fromStdError(e as Error);
}
): Promise<WelcomeBundle> {
const { keyRotationSpan, wirePolicy } = configuration || {};
const config = new CustomConfiguration(keyRotationSpan, wirePolicy);
return await CoreCryptoError.asyncMapErr(
this.#ctx.join_by_external_commit(groupInfo, config, credentialType)
);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions crypto-ffi/bindings/js/src/CoreCryptoError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export class CoreCryptoError extends Error {
}

static fromStdError(e: Error): CoreCryptoError | Error {
if (e instanceof CoreCryptoError) {
return e;
}
const opts = {
cause: e.cause || undefined,
stack: e.stack || undefined,
Expand Down
12 changes: 4 additions & 8 deletions crypto-ffi/bindings/js/src/CoreCryptoInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
WireIdentity,
ConversationConfiguration,
CustomConfiguration,
WelcomeBundle,
} from "./core-crypto-ffi.js";

import { CoreCryptoError } from "./CoreCryptoError.js";
Expand All @@ -36,9 +37,6 @@ import {
ConversationId,
ClientId,
Ciphersuite,
ConversationInitBundle,
WelcomeBundle,
CommitBundle,
DecryptedMessage,
MlsTransport,
} from "./CoreCryptoMLS.js";
Expand Down Expand Up @@ -669,9 +667,7 @@ export class CoreCrypto {
* @deprecated Create a transaction with {@link CoreCrypto.transaction}
* and use {@link CoreCryptoContext.updateKeyingMaterial} instead.
*/
async updateKeyingMaterial(
conversationId: ConversationId
): Promise<CommitBundle> {
async updateKeyingMaterial(conversationId: ConversationId): Promise<void> {
return await this.transaction(
async (ctx) => await ctx.updateKeyingMaterial(conversationId)
);
Expand All @@ -697,7 +693,7 @@ export class CoreCrypto {
*/
async commitPendingProposals(
conversationId: ConversationId
): Promise<CommitBundle | undefined> {
): Promise<void> {
return await this.transaction(
async (ctx) => await ctx.commitPendingProposals(conversationId)
);
Expand All @@ -713,7 +709,7 @@ export class CoreCrypto {
groupInfo: Uint8Array,
credentialType: CredentialType,
configuration: Partial<CustomConfiguration> = {}
): Promise<ConversationInitBundle> {
): Promise<WelcomeBundle> {
return await this.transaction(
async (ctx) =>
await ctx.joinByExternalCommit(
Expand Down
43 changes: 2 additions & 41 deletions crypto-ffi/bindings/js/src/CoreCryptoMLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
WireIdentity,
} from "./core-crypto-ffi.js";

export { WelcomeBundle } from "./core-crypto-ffi.js";

/**
* see [core_crypto::prelude::CiphersuiteName]
*/
Expand Down Expand Up @@ -172,47 +174,6 @@ export enum RatchetTreeType {
ByRef = 0x03,
}

export interface ConversationInitBundle {
/**
* Conversation ID of the conversation created
*
* @readonly
*/
conversationId: ConversationId;
/**
* TLS-serialized MLS External Commit that needs to be fanned out
*
* @readonly
*/
commit: Uint8Array;
/**
* MLS Public Group State (aka Group Info) which becomes valid when the external commit
* is accepted by the Delivery Service
*
* @readonly
*/
groupInfo: GroupInfoBundle;
/**
* New CRL distribution points that appeared by the introduction of a new credential
*/
crlNewDistributionPoints?: string[];
}

export interface WelcomeBundle {
/**
* Conversation ID
*
* @readonly
*/
id: Uint8Array;
/**
* New CRL Distribution of members of this group
*
* @readonly
*/
crlNewDistributionPoints?: string[];
}

/**
* This is a wrapper for all the possible outcomes you can get after decrypting a message
*/
Expand Down
6 changes: 5 additions & 1 deletion crypto-ffi/bindings/js/test/CoreCrypto.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@ describe("Error type mapping", () => {
createConversation(ALICE_ID, CONV_ID)
// wdio wraps the error and prepends the original message with
// the error type as prefix
).rejects.toThrowError(new Error(`Error: ${expectedErrorMessage}`));
).rejects.toThrowError(
new Error(
`MlsErrorConversationAlreadyExists: ${expectedErrorMessage}`
)
);
});
});
Loading
Loading