ts-mls
Version:
[](https://github.com/LukaJCB/ts-mls/actions/workflows/ci.yml) [](https://badge.fury.io/js/ts-mls) [ => {
for (const cs of Object.keys(ciphersuites)) {
test(`Out of order generation ${cs}`, async () => {
await generationOutOfOrder(cs);
});
}
for (const cs of Object.keys(ciphersuites)) {
test(`Out of order generation random ${cs}`, async () => {
await generationOutOfOrderRandom(cs, defaultKeyRetentionConfig.retainKeysForGenerations);
});
}
for (const cs of Object.keys(ciphersuites)) {
test(`Out of order generation limit reached fails ${cs}`, async () => {
await generationOutOfOrderLimitFails(cs, 10);
});
}
});
async function setupTestParticipants(cipherSuite, retainConfig) {
const impl = await getCiphersuiteImpl(getCiphersuiteFromName(cipherSuite));
const aliceCredential = { credentialType: "basic", identity: new TextEncoder().encode("alice") };
const alice = await generateKeyPackage(aliceCredential, defaultCapabilities(), defaultLifetime, [], impl);
const groupId = new TextEncoder().encode("group1");
let aliceGroup = await createGroup(groupId, alice.publicPackage, alice.privatePackage, [], impl);
const bobCredential = { credentialType: "basic", identity: new TextEncoder().encode("bob") };
const bob = await generateKeyPackage(bobCredential, defaultCapabilities(), defaultLifetime, [], impl);
const addBobProposal = {
proposalType: "add",
add: {
keyPackage: bob.publicPackage,
},
};
const addBobCommitResult = await createCommit(aliceGroup, emptyPskIndex, false, [addBobProposal], impl, true);
aliceGroup = addBobCommitResult.newState;
const bobGroup = await joinGroup(addBobCommitResult.welcome, bob.publicPackage, bob.privatePackage, emptyPskIndex, impl, undefined, undefined, { ...defaultClientConfig, keyRetentionConfig: retainConfig ?? defaultClientConfig.keyRetentionConfig });
return { aliceGroup, bobGroup, impl };
}
async function generationOutOfOrder(cipherSuite) {
const { aliceGroup: initialAliceGroup, bobGroup: initialBobGroup, impl } = await setupTestParticipants(cipherSuite);
let aliceGroup = initialAliceGroup;
let bobGroup = initialBobGroup;
const firstMessage = new TextEncoder().encode("Hello bob!");
const secondMessage = new TextEncoder().encode("How are ya?");
const thirdMessage = new TextEncoder().encode("Have you heard the news?");
// alice sends the first message
const aliceCreateFirstMessageResult = await createApplicationMessage(aliceGroup, firstMessage, impl);
aliceGroup = aliceCreateFirstMessageResult.newState;
const aliceCreateSecondMessageResult = await createApplicationMessage(aliceGroup, secondMessage, impl);
aliceGroup = aliceCreateSecondMessageResult.newState;
const aliceCreateThirdMessageResult = await createApplicationMessage(aliceGroup, thirdMessage, impl);
aliceGroup = aliceCreateThirdMessageResult.newState;
// bob receives 3rd message first
const bobProcessThirdMessageResult = await processPrivateMessage(bobGroup, aliceCreateThirdMessageResult.privateMessage, makePskIndex(bobGroup, {}), impl);
bobGroup = bobProcessThirdMessageResult.newState;
// then bob receives the first message
const bobProcessFirstMessageResult = await processPrivateMessage(bobGroup, aliceCreateFirstMessageResult.privateMessage, makePskIndex(bobGroup, {}), impl);
bobGroup = bobProcessFirstMessageResult.newState;
// bob receives 2nd message last
const bobProcessSecondMessageResult = await processPrivateMessage(bobGroup, aliceCreateSecondMessageResult.privateMessage, makePskIndex(bobGroup, {}), impl);
bobGroup = bobProcessSecondMessageResult.newState;
await testEveryoneCanMessageEveryone([aliceGroup, bobGroup], impl);
}
async function generationOutOfOrderRandom(cipherSuite, totalMessages) {
const { aliceGroup: initialAliceGroup, bobGroup: initialBobGroup, impl } = await setupTestParticipants(cipherSuite);
let aliceGroup = initialAliceGroup;
let bobGroup = initialBobGroup;
const message = new TextEncoder().encode("Hi!");
let messages = [];
for (let i = 0; i < totalMessages; i++) {
const createMessageResult = await createApplicationMessage(aliceGroup, message, impl);
aliceGroup = createMessageResult.newState;
messages.push(createMessageResult.privateMessage);
}
const shuffledMessages = shuffledIndices(messages).map((i) => messages[i]);
for (const msg of shuffledMessages) {
const bobProcessMessageResult = await processPrivateMessage(bobGroup, msg, makePskIndex(bobGroup, {}), impl);
bobGroup = bobProcessMessageResult.newState;
}
await testEveryoneCanMessageEveryone([aliceGroup, bobGroup], impl);
}
async function generationOutOfOrderLimitFails(cipherSuite, totalMessages) {
const retainConfig = { ...defaultKeyRetentionConfig, retainKeysForGenerations: totalMessages - 1 };
const { aliceGroup: initialAliceGroup, bobGroup: initialBobGroup, impl, } = await setupTestParticipants(cipherSuite, retainConfig);
let aliceGroup = initialAliceGroup;
let bobGroup = initialBobGroup;
const message = new TextEncoder().encode("Hi!");
let messages = [];
for (let i = 0; i < totalMessages + 1; i++) {
const createMessageResult = await createApplicationMessage(aliceGroup, message, impl);
aliceGroup = createMessageResult.newState;
messages.push(createMessageResult.privateMessage);
}
// read the last message first
const processResult = await processPrivateMessage(bobGroup, messages.at(-1), emptyPskIndex, impl);
bobGroup = processResult.newState;
// should fail reading the first message
await expect(processPrivateMessage(bobGroup, messages.at(0), emptyPskIndex, impl)).rejects.toThrow(ValidationError);
}
//# sourceMappingURL=generationOutOfOrder.test.js.map