diff --git a/ts/components/leftpane/ActionsPanel.tsx b/ts/components/leftpane/ActionsPanel.tsx index 9ea9c8d51..652607217 100644 --- a/ts/components/leftpane/ActionsPanel.tsx +++ b/ts/components/leftpane/ActionsPanel.tsx @@ -46,6 +46,7 @@ import { switchThemeTo } from '../../themes/switchTheme'; import { getOppositeTheme } from '../../util/theme'; import { ReleasedFeatures } from '../../util/releaseFeature'; +import { MultiEncryptWrapperActions } from '../../webworker/workers/browser/libsession_worker_interface'; const Section = (props: { type: SectionType }) => { const ourNumber = useSelector(getOurNumber); @@ -82,6 +83,7 @@ const Section = (props: { type: SectionType }) => { }; if (type === SectionType.Profile) { + void MultiEncryptWrapperActions.multiEncrypt({}); return ( { + // let us: TestUserKeyPairs; + // let groupX25519SecretKey: Uint8Array; + + beforeEach(async () => { + // us = await TestUtils.generateUserKeyPairs(); + // const group = await TestUtils.generateGroupV2(us.ed25519KeyPair.privKeyBytes); + // if (!group.secretKey) { + // throw new Error('failed to create grou[p'); + // } + // groupX25519SecretKey = group.secretKey; + }); + afterEach(() => { + Sinon.restore(); + }); + + describe('encrypt/decrypt multi encrypt/decrypt message', () => { + it('can encrypt/decrypt message one message to one recipient', async () => { + const toEncrypt = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + const plaintext = new Uint8Array(toEncrypt); + const domain = 'domain'; + + const us = await TestUtils.generateUserKeyPairs(); + const userXPk = us.x25519KeyPair.pubKey.slice(1); // remove 05 prefix + const userSk = us.ed25519KeyPair.privKeyBytes; + + const groupWrapper = new UserGroupsWrapperNode(us.ed25519KeyPair.privKeyBytes, null); + const group = await groupWrapper.createGroup(); + if (!group.secretKey) { + throw new Error('failed to create group'); + } + const groupEd25519SecretKey = group.secretKey; + const groupEd25519Pubkey = fromHexToArray(group.pubkeyHex).slice(1); // remove 03 prefix + + const encrypted = MultiEncryptWrapperNode.multiEncrypt({ + messages: [plaintext], + recipients: [userXPk], + ed25519SecretKey: groupEd25519SecretKey, + domain, + }); + const decrypted = MultiEncryptWrapperNode.multiDecryptEd25519({ + domain, + encoded: encrypted, + ed25519SecretKey: userSk, + senderEd25519Pubkey: groupEd25519Pubkey, + }); + console.warn('decrypted', decrypted); + expect(decrypted).to.be.deep.eq(Buffer.from(toEncrypt)); + }); + }); +}); diff --git a/ts/types/sqlSharedTypes.ts b/ts/types/sqlSharedTypes.ts index 1b37cf467..4d9dc6181 100644 --- a/ts/types/sqlSharedTypes.ts +++ b/ts/types/sqlSharedTypes.ts @@ -364,6 +364,10 @@ export function toFixedUint8ArrayOfLength( export function stringify(obj: unknown) { return JSON.stringify(obj, (_key, value) => { - return value instanceof Uint8Array ? `Uint8Array(${value.length}): ${toHex(value)}` : value; + return value instanceof Uint8Array + ? `Uint8Array(${value.length}): ${toHex(value)}` + : value.type === 'Buffer' && value.data + ? `Buffer: ${toHex(value.data)}` + : value; }); } diff --git a/ts/webworker/workers/browser/libsession_worker_functions.ts b/ts/webworker/workers/browser/libsession_worker_functions.ts index ab1346cc9..53e0d4deb 100644 --- a/ts/webworker/workers/browser/libsession_worker_functions.ts +++ b/ts/webworker/workers/browser/libsession_worker_functions.ts @@ -4,6 +4,7 @@ import { ConvoInfoVolatileConfigActionsType, GroupPubkeyType, MetaGroupActionsType, + MultiEncryptActionsType, UserConfigActionsType, UserGroupsConfigActionsType, } from 'libsession_util_nodejs'; @@ -15,8 +16,10 @@ export type UserGroupsConfig = 'UserGroupsConfig'; export type ConvoInfoVolatileConfig = 'ConvoInfoVolatileConfig'; export const MetaGroupConfigValue = 'MetaGroupConfig-'; +export const MultiEncryptConfigValue = 'MultiEncrypt'; type MetaGroupConfigType = typeof MetaGroupConfigValue; export type MetaGroupConfig = `${MetaGroupConfigType}${GroupPubkeyType}`; +export type MultiEncryptConfig = typeof MultiEncryptConfigValue; export type ConfigWrapperUser = | UserConfig @@ -26,7 +29,10 @@ export type ConfigWrapperUser = export type ConfigWrapperGroup = MetaGroupConfig; -export type ConfigWrapperObjectTypesMeta = ConfigWrapperUser | ConfigWrapperGroup; +export type ConfigWrapperObjectTypesMeta = + | ConfigWrapperUser + | ConfigWrapperGroup + | MultiEncryptConfig; export type ConfigWrapperGroupDetailed = 'GroupInfo' | 'GroupMember' | 'GroupKeys'; @@ -48,12 +54,15 @@ type ConvoInfoVolatileConfigFunctions = // Group-related calls type MetaGroupFunctions = [MetaGroupConfig, ...MetaGroupActionsType]; +type MultiEncryptFunctions = [MultiEncryptConfig, ...MultiEncryptActionsType]; + export type LibSessionWorkerFunctions = | UserConfigFunctions | ContactsConfigFunctions | UserGroupsConfigFunctions | ConvoInfoVolatileConfigFunctions - | MetaGroupFunctions; + | MetaGroupFunctions + | MultiEncryptFunctions; export function isUserConfigWrapperType( config: ConfigWrapperObjectTypesMeta @@ -70,6 +79,12 @@ export function isMetaWrapperType(config: ConfigWrapperObjectTypesMeta): config return config.startsWith(MetaGroupConfigValue); } +export function isMultiEncryptWrapperType( + config: ConfigWrapperObjectTypesMeta +): config is MultiEncryptConfig { + return config === 'MultiEncrypt'; +} + export function getGroupPubkeyFromWrapperType(type: ConfigWrapperGroup): GroupPubkeyType { if (!type.startsWith(`${MetaGroupConfigValue}03`)) { throw new Error(`not a metagroup variant: ${type}`); diff --git a/ts/webworker/workers/browser/libsession_worker_interface.ts b/ts/webworker/workers/browser/libsession_worker_interface.ts index 91620a556..ad82f1ec2 100644 --- a/ts/webworker/workers/browser/libsession_worker_interface.ts +++ b/ts/webworker/workers/browser/libsession_worker_interface.ts @@ -11,6 +11,7 @@ import { LegacyGroupInfo, MergeSingle, MetaGroupWrapperActionsCalls, + MultiEncryptActionsCalls, ProfilePicture, PubkeyType, Uint8ArrayLen100, @@ -638,6 +639,21 @@ export const MetaGroupWrapperActions: MetaGroupWrapperActionsCalls = { }, }; +export const MultiEncryptWrapperActions: MultiEncryptActionsCalls = { + /* Reuse the GenericWrapperActions with the UserConfig argument */ + ...createBaseActionsFor('UserConfig'), + + /** UserConfig wrapper specific actions */ + multiEncrypt: async args => + callLibSessionWorker(['MultiEncrypt', 'multiEncrypt', args]) as Promise< + ReturnType + >, + multiDecryptEd25519: async args => + callLibSessionWorker(['MultiEncrypt', 'multiDecryptEd25519', args]) as Promise< + ReturnType + >, +}; + export const callLibSessionWorker = async ( callToMake: LibSessionWorkerFunctions ): Promise => { diff --git a/ts/webworker/workers/node/libsession/libsession.worker.ts b/ts/webworker/workers/node/libsession/libsession.worker.ts index 214b3d4ce..b6e1d0390 100644 --- a/ts/webworker/workers/node/libsession/libsession.worker.ts +++ b/ts/webworker/workers/node/libsession/libsession.worker.ts @@ -7,6 +7,7 @@ import { GroupPubkeyType, GroupWrapperConstructor, MetaGroupWrapperNode, + MultiEncryptWrapperNode, UserConfigWrapperNode, UserGroupsWrapperNode, } from 'libsession_util_nodejs'; @@ -17,7 +18,9 @@ import { ConfigWrapperObjectTypesMeta, ConfigWrapperUser, MetaGroupConfig, + MultiEncryptConfig, isMetaWrapperType, + isMultiEncryptWrapperType, isUserConfigWrapperType, } from '../../browser/libsession_worker_functions'; @@ -112,6 +115,13 @@ function getCorrespondingGroupWrapper(wrapperType: MetaGroupConfig): MetaGroupWr ); } +function getMultiEncryptWrapper(wrapperType: MultiEncryptConfig): MultiEncryptWrapperNode { + if (isMultiEncryptWrapperType(wrapperType)) { + return MultiEncryptWrapperNode; + } + assertUnreachable(wrapperType, `getMultiEncrypt missing global handling for "${wrapperType}"`); +} + function isUInt8Array(value: any) { return value.constructor === Uint8Array; } @@ -227,11 +237,17 @@ onmessage = async (e: { data: [number, ConfigWrapperObjectTypesMeta, string, ... throw new Error(`Unhandled init wrapper type: ${config}`); } + if (action === 'multiEncrypt') { + await MultiEncryptWrapperNode.multiEncrypt(args[0]); + } + const wrapper = isUserConfigWrapperType(config) ? getCorrespondingUserWrapper(config) : isMetaWrapperType(config) ? getCorrespondingGroupWrapper(config) - : undefined; + : isMultiEncryptWrapperType(config) + ? getMultiEncryptWrapper(config) + : undefined; if (!wrapper) { throw new Error(`did not find an already built wrapper for config: "${config}"`); }