Move to protobufjs in ts/groups.ts
This commit is contained in:
parent
972a4cba0c
commit
9f0c630574
|
@ -13,6 +13,10 @@ const {
|
||||||
parseEnvironment,
|
parseEnvironment,
|
||||||
} = require('./ts/environment');
|
} = require('./ts/environment');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('./ts/context');
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
const config = url.parse(window.location.toString(), true).query;
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
const { locale } = config;
|
const { locale } = config;
|
||||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
|
|
@ -19,11 +19,15 @@ const {
|
||||||
|
|
||||||
const { nativeTheme } = remote.require('electron');
|
const { nativeTheme } = remote.require('electron');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('./ts/context');
|
||||||
|
|
||||||
const config = url.parse(window.location.toString(), true).query;
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
const { locale } = config;
|
const { locale } = config;
|
||||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
setEnvironment(parseEnvironment(config.environment));
|
setEnvironment(parseEnvironment(config.environment));
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
window.getEnvironment = getEnvironment;
|
window.getEnvironment = getEnvironment;
|
||||||
window.getVersion = () => config.version;
|
window.getVersion = () => config.version;
|
||||||
window.theme = config.theme;
|
window.theme = config.theme;
|
||||||
|
|
10
preload.js
10
preload.js
|
@ -24,6 +24,10 @@ try {
|
||||||
const { app } = remote;
|
const { app } = remote;
|
||||||
const { nativeTheme } = remote.require('electron');
|
const { nativeTheme } = remote.require('electron');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('./ts/context');
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
window.sqlInitializer = require('./ts/sql/initialize');
|
window.sqlInitializer = require('./ts/sql/initialize');
|
||||||
|
|
||||||
window.PROTO_ROOT = 'protos';
|
window.PROTO_ROOT = 'protos';
|
||||||
|
@ -483,6 +487,7 @@ try {
|
||||||
const { autoOrientImage } = require('./js/modules/auto_orient_image');
|
const { autoOrientImage } = require('./js/modules/auto_orient_image');
|
||||||
const { imageToBlurHash } = require('./ts/util/imageToBlurHash');
|
const { imageToBlurHash } = require('./ts/util/imageToBlurHash');
|
||||||
const { isGroupCallingEnabled } = require('./ts/util/isGroupCallingEnabled');
|
const { isGroupCallingEnabled } = require('./ts/util/isGroupCallingEnabled');
|
||||||
|
const { isValidGuid } = require('./ts/util/isValidGuid');
|
||||||
const { ActiveWindowService } = require('./ts/services/ActiveWindowService');
|
const { ActiveWindowService } = require('./ts/services/ActiveWindowService');
|
||||||
|
|
||||||
window.autoOrientImage = autoOrientImage;
|
window.autoOrientImage = autoOrientImage;
|
||||||
|
@ -509,10 +514,7 @@ try {
|
||||||
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
||||||
};
|
};
|
||||||
|
|
||||||
window.isValidGuid = maybeGuid =>
|
window.isValidGuid = isValidGuid;
|
||||||
/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(
|
|
||||||
maybeGuid
|
|
||||||
);
|
|
||||||
// https://stackoverflow.com/a/23299989
|
// https://stackoverflow.com/a/23299989
|
||||||
window.isValidE164 = maybeE164 => /^\+?[1-9]\d{1,14}$/.test(maybeE164);
|
window.isValidE164 = maybeE164 => /^\+?[1-9]\d{1,14}$/.test(maybeE164);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,10 @@ const {
|
||||||
CallingScreenSharingController,
|
CallingScreenSharingController,
|
||||||
} = require('./ts/components/CallingScreenSharingController');
|
} = require('./ts/components/CallingScreenSharingController');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('./ts/context');
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
const config = url.parse(window.location.toString(), true).query;
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
const { locale } = config;
|
const { locale } = config;
|
||||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
|
|
@ -20,6 +20,10 @@ setEnvironment(parseEnvironment(config.environment));
|
||||||
|
|
||||||
const { nativeTheme } = remote.require('electron');
|
const { nativeTheme } = remote.require('electron');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('./ts/context');
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
window.platform = process.platform;
|
window.platform = process.platform;
|
||||||
window.theme = config.theme;
|
window.theme = config.theme;
|
||||||
window.i18n = i18n.setup(locale, localeMessages);
|
window.i18n = i18n.setup(locale, localeMessages);
|
||||||
|
|
|
@ -21,12 +21,16 @@ const { makeGetter } = require('../preload_utils');
|
||||||
const { dialog } = remote;
|
const { dialog } = remote;
|
||||||
const { nativeTheme } = remote.require('electron');
|
const { nativeTheme } = remote.require('electron');
|
||||||
|
|
||||||
|
const { Context: SignalContext } = require('../ts/context');
|
||||||
|
|
||||||
const STICKER_SIZE = 512;
|
const STICKER_SIZE = 512;
|
||||||
const MIN_STICKER_DIMENSION = 10;
|
const MIN_STICKER_DIMENSION = 10;
|
||||||
const MAX_STICKER_DIMENSION = STICKER_SIZE;
|
const MAX_STICKER_DIMENSION = STICKER_SIZE;
|
||||||
const MAX_WEBP_STICKER_BYTE_LENGTH = 100 * 1024;
|
const MAX_WEBP_STICKER_BYTE_LENGTH = 100 * 1024;
|
||||||
const MAX_ANIMATED_STICKER_BYTE_LENGTH = 300 * 1024;
|
const MAX_ANIMATED_STICKER_BYTE_LENGTH = 300 * 1024;
|
||||||
|
|
||||||
|
window.SignalContext = new SignalContext();
|
||||||
|
|
||||||
setEnvironment(parseEnvironment(config.environment));
|
setEnvironment(parseEnvironment(config.environment));
|
||||||
|
|
||||||
window.sqlInitializer = require('../ts/sql/initialize');
|
window.sqlInitializer = require('../ts/sql/initialize');
|
||||||
|
|
|
@ -9,6 +9,8 @@ const chaiAsPromised = require('chai-as-promised');
|
||||||
const ByteBuffer = require('../components/bytebuffer/dist/ByteBufferAB.js');
|
const ByteBuffer = require('../components/bytebuffer/dist/ByteBufferAB.js');
|
||||||
const Long = require('../components/long/dist/Long.js');
|
const Long = require('../components/long/dist/Long.js');
|
||||||
const { setEnvironment, Environment } = require('../ts/environment');
|
const { setEnvironment, Environment } = require('../ts/environment');
|
||||||
|
const { Context: SignalContext } = require('../ts/context');
|
||||||
|
const { isValidGuid } = require('../ts/util/isValidGuid');
|
||||||
|
|
||||||
chai.use(chaiAsPromised);
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@ const storageMap = new Map();
|
||||||
|
|
||||||
// To replicate logic we have on the client side
|
// To replicate logic we have on the client side
|
||||||
global.window = {
|
global.window = {
|
||||||
|
SignalContext: new SignalContext(),
|
||||||
log: {
|
log: {
|
||||||
info: (...args) => console.log(...args),
|
info: (...args) => console.log(...args),
|
||||||
warn: (...args) => console.warn(...args),
|
warn: (...args) => console.warn(...args),
|
||||||
|
@ -32,6 +35,7 @@ global.window = {
|
||||||
get: key => storageMap.get(key),
|
get: key => storageMap.get(key),
|
||||||
put: async (key, value) => storageMap.set(key, value),
|
put: async (key, value) => storageMap.set(key, value),
|
||||||
},
|
},
|
||||||
|
isValidGuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For ducks/network.getEmptyState()
|
// For ducks/network.getEmptyState()
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
const { bytes } = window.SignalContext;
|
||||||
|
|
||||||
|
export function fromBase64(value: string): Uint8Array {
|
||||||
|
return bytes.fromBase64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromHex(value: string): Uint8Array {
|
||||||
|
return bytes.fromHex(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(indutny): deprecate it
|
||||||
|
export function fromBinary(value: string): Uint8Array {
|
||||||
|
return bytes.fromBinary(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromString(value: string): Uint8Array {
|
||||||
|
return bytes.fromString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toBase64(data: Uint8Array): string {
|
||||||
|
return bytes.toBase64(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toHex(data: Uint8Array): string {
|
||||||
|
return bytes.toHex(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(indutny): deprecate it
|
||||||
|
export function toBinary(data: Uint8Array): string {
|
||||||
|
return bytes.toBinary(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toString(data: Uint8Array): string {
|
||||||
|
return bytes.toString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function concatenate(list: Array<Uint8Array>): Uint8Array {
|
||||||
|
return bytes.concatenate(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmpty(data: Uint8Array | null | undefined): boolean {
|
||||||
|
return bytes.isEmpty(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNotEmpty(
|
||||||
|
data: Uint8Array | null | undefined
|
||||||
|
): data is Uint8Array {
|
||||||
|
return !bytes.isEmpty(data);
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
/* eslint-disable class-methods-use-this */
|
||||||
|
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
|
export class Bytes {
|
||||||
|
public fromBase64(value: string): Uint8Array {
|
||||||
|
return Buffer.from(value, 'base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
public fromHex(value: string): Uint8Array {
|
||||||
|
return Buffer.from(value, 'hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(indutny): deprecate it
|
||||||
|
public fromBinary(value: string): Uint8Array {
|
||||||
|
return Buffer.from(value, 'binary');
|
||||||
|
}
|
||||||
|
|
||||||
|
public fromString(value: string): Uint8Array {
|
||||||
|
return Buffer.from(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toBase64(data: Uint8Array): string {
|
||||||
|
return Buffer.from(data).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
public toHex(data: Uint8Array): string {
|
||||||
|
return Buffer.from(data).toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(indutny): deprecate it
|
||||||
|
public toBinary(data: Uint8Array): string {
|
||||||
|
return Buffer.from(data).toString('binary');
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(data: Uint8Array): string {
|
||||||
|
return Buffer.from(data).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public concatenate(list: ReadonlyArray<Uint8Array>): Uint8Array {
|
||||||
|
return Buffer.concat(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEmpty(data: Uint8Array | null | undefined): boolean {
|
||||||
|
if (!data) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return data.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNotEmpty(data: Uint8Array | null | undefined): data is Uint8Array {
|
||||||
|
return !this.isEmpty(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { Bytes } from './Bytes';
|
||||||
|
|
||||||
|
export class Context {
|
||||||
|
public readonly bytes = new Bytes();
|
||||||
|
}
|
1720
ts/groups.ts
1720
ts/groups.ts
File diff suppressed because it is too large
Load Diff
|
@ -11,14 +11,14 @@ import {
|
||||||
LINK_VERSION_ERROR,
|
LINK_VERSION_ERROR,
|
||||||
parseGroupLink,
|
parseGroupLink,
|
||||||
} from '../groups';
|
} from '../groups';
|
||||||
import { arrayBufferToBase64, base64ToArrayBuffer } from '../Crypto';
|
import * as Bytes from '../Bytes';
|
||||||
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
|
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
|
||||||
import { isGroupV1 } from '../util/whatTypeOfConversation';
|
import { isGroupV1 } from '../util/whatTypeOfConversation';
|
||||||
|
|
||||||
import type { GroupJoinInfoClass } from '../textsecure.d';
|
|
||||||
import type { ConversationAttributesType } from '../model-types.d';
|
import type { ConversationAttributesType } from '../model-types.d';
|
||||||
import type { ConversationModel } from '../models/conversations';
|
import type { ConversationModel } from '../models/conversations';
|
||||||
import type { PreJoinConversationType } from '../state/ducks/conversations';
|
import type { PreJoinConversationType } from '../state/ducks/conversations';
|
||||||
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
|
||||||
export async function joinViaLink(hash: string): Promise<void> {
|
export async function joinViaLink(hash: string): Promise<void> {
|
||||||
let inviteLinkPassword: string;
|
let inviteLinkPassword: string;
|
||||||
|
@ -42,11 +42,11 @@ export async function joinViaLink(hash: string): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = deriveGroupFields(base64ToArrayBuffer(masterKey));
|
const data = deriveGroupFields(Bytes.fromBase64(masterKey));
|
||||||
const id = arrayBufferToBase64(data.id);
|
const id = Bytes.toBase64(data.id);
|
||||||
const logId = `groupv2(${id})`;
|
const logId = `groupv2(${id})`;
|
||||||
const secretParams = arrayBufferToBase64(data.secretParams);
|
const secretParams = Bytes.toBase64(data.secretParams);
|
||||||
const publicParams = arrayBufferToBase64(data.publicParams);
|
const publicParams = Bytes.toBase64(data.publicParams);
|
||||||
|
|
||||||
const existingConversation =
|
const existingConversation =
|
||||||
window.ConversationController.get(id) ||
|
window.ConversationController.get(id) ||
|
||||||
|
@ -70,7 +70,7 @@ export async function joinViaLink(hash: string): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: GroupJoinInfoClass;
|
let result: Proto.GroupJoinInfo;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await longRunningTaskWrapper({
|
result = await longRunningTaskWrapper({
|
||||||
|
|
|
@ -39,7 +39,8 @@ import {
|
||||||
trimForDisplay,
|
trimForDisplay,
|
||||||
verifyAccessKey,
|
verifyAccessKey,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
import { GroupChangeClass, DataMessageClass } from '../textsecure.d';
|
import * as Bytes from '../Bytes';
|
||||||
|
import { DataMessageClass } from '../textsecure.d';
|
||||||
import { BodyRangesType } from '../types/Util';
|
import { BodyRangesType } from '../types/Util';
|
||||||
import { getTextWithMentions } from '../util';
|
import { getTextWithMentions } from '../util';
|
||||||
import { migrateColor } from '../util/migrateColor';
|
import { migrateColor } from '../util/migrateColor';
|
||||||
|
@ -62,6 +63,7 @@ import {
|
||||||
isMe,
|
isMe,
|
||||||
} from '../util/whatTypeOfConversation';
|
} from '../util/whatTypeOfConversation';
|
||||||
import { deprecated } from '../util/deprecated';
|
import { deprecated } from '../util/deprecated';
|
||||||
|
import { SignalService as Proto } from '../protobuf';
|
||||||
import {
|
import {
|
||||||
hasErrors,
|
hasErrors,
|
||||||
isIncoming,
|
isIncoming,
|
||||||
|
@ -71,6 +73,9 @@ import {
|
||||||
import { Deletes } from '../messageModifiers/Deletes';
|
import { Deletes } from '../messageModifiers/Deletes';
|
||||||
import { Reactions } from '../messageModifiers/Reactions';
|
import { Reactions } from '../messageModifiers/Reactions';
|
||||||
|
|
||||||
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
|
const FIXMEU8 = Uint8Array;
|
||||||
|
|
||||||
/* eslint-disable more/no-then */
|
/* eslint-disable more/no-then */
|
||||||
window.Whisper = window.Whisper || {};
|
window.Whisper = window.Whisper || {};
|
||||||
|
|
||||||
|
@ -385,7 +390,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async updateExpirationTimerInGroupV2(
|
async updateExpirationTimerInGroupV2(
|
||||||
seconds?: number
|
seconds?: number
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
const current = this.get('expireTimer');
|
const current = this.get('expireTimer');
|
||||||
const bothFalsey = Boolean(current) === false && Boolean(seconds) === false;
|
const bothFalsey = Boolean(current) === false && Boolean(seconds) === false;
|
||||||
|
@ -405,7 +410,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async promotePendingMember(
|
async promotePendingMember(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
// This user's pending state may have changed in the time between the user's
|
// This user's pending state may have changed in the time between the user's
|
||||||
|
@ -449,7 +454,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async approvePendingApprovalRequest(
|
async approvePendingApprovalRequest(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
// This user's pending state may have changed in the time between the user's
|
// This user's pending state may have changed in the time between the user's
|
||||||
|
@ -484,7 +489,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async denyPendingApprovalRequest(
|
async denyPendingApprovalRequest(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
// This user's pending state may have changed in the time between the user's
|
// This user's pending state may have changed in the time between the user's
|
||||||
|
@ -518,7 +523,7 @@ export class ConversationModel extends window.Backbone
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPendingApprovalRequest(): Promise<
|
async addPendingApprovalRequest(): Promise<
|
||||||
GroupChangeClass.Actions | undefined
|
Proto.GroupChange.Actions | undefined
|
||||||
> {
|
> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
|
@ -566,7 +571,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async addMember(
|
async addMember(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
const toRequest = window.ConversationController.get(conversationId);
|
const toRequest = window.ConversationController.get(conversationId);
|
||||||
|
@ -610,7 +615,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async removePendingMember(
|
async removePendingMember(
|
||||||
conversationIds: Array<string>
|
conversationIds: Array<string>
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
const uuids = conversationIds
|
const uuids = conversationIds
|
||||||
|
@ -656,7 +661,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async removeMember(
|
async removeMember(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
const idLog = this.idForLogging();
|
const idLog = this.idForLogging();
|
||||||
|
|
||||||
// This user's pending state may have changed in the time between the user's
|
// This user's pending state may have changed in the time between the user's
|
||||||
|
@ -691,7 +696,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async toggleAdminChange(
|
async toggleAdminChange(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): Promise<GroupChangeClass.Actions | undefined> {
|
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||||
if (!isGroupV2(this.attributes)) {
|
if (!isGroupV2(this.attributes)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -738,7 +743,7 @@ export class ConversationModel extends window.Backbone
|
||||||
inviteLinkPassword,
|
inviteLinkPassword,
|
||||||
name,
|
name,
|
||||||
}: {
|
}: {
|
||||||
createGroupChange: () => Promise<GroupChangeClass.Actions | undefined>;
|
createGroupChange: () => Promise<Proto.GroupChange.Actions | undefined>;
|
||||||
extraConversationsForSend?: Array<string>;
|
extraConversationsForSend?: Array<string>;
|
||||||
inviteLinkPassword?: string;
|
inviteLinkPassword?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -1099,7 +1104,7 @@ export class ConversationModel extends window.Backbone
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
masterKey: window.Signal.Crypto.base64ToArrayBuffer(
|
masterKey: Bytes.fromBase64(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
this.get('masterKey')!
|
this.get('masterKey')!
|
||||||
),
|
),
|
||||||
|
@ -1109,7 +1114,7 @@ export class ConversationModel extends window.Backbone
|
||||||
includePendingMembers,
|
includePendingMembers,
|
||||||
extraConversationsForSend,
|
extraConversationsForSend,
|
||||||
}),
|
}),
|
||||||
groupChange,
|
groupChange: groupChange ? new FIXMEU8(groupChange) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2832,8 +2837,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
validateUuid(): string | null {
|
validateUuid(): string | null {
|
||||||
if (isDirectConversation(this.attributes) && this.get('uuid')) {
|
if (isDirectConversation(this.attributes) && this.get('uuid')) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
if (window.isValidGuid(this.get('uuid'))) {
|
||||||
if (window.isValidGuid(this.get('uuid')!)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ import {
|
||||||
base64ToArrayBuffer,
|
base64ToArrayBuffer,
|
||||||
uuidToArrayBuffer,
|
uuidToArrayBuffer,
|
||||||
arrayBufferToUuid,
|
arrayBufferToUuid,
|
||||||
|
typedArrayToArrayBuffer,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
import { assert } from '../util/assert';
|
import { assert } from '../util/assert';
|
||||||
import { getOwn } from '../util/getOwn';
|
import { getOwn } from '../util/getOwn';
|
||||||
|
@ -384,7 +385,7 @@ export class CallingClass {
|
||||||
member =>
|
member =>
|
||||||
new GroupMemberInfo(
|
new GroupMemberInfo(
|
||||||
uuidToArrayBuffer(member.uuid),
|
uuidToArrayBuffer(member.uuid),
|
||||||
member.uuidCiphertext
|
typedArrayToArrayBuffer(member.uuidCiphertext)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
deriveMasterKeyFromGroupV1,
|
deriveMasterKeyFromGroupV1,
|
||||||
fromEncodedBinaryToArrayBuffer,
|
fromEncodedBinaryToArrayBuffer,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
|
import * as Bytes from '../Bytes';
|
||||||
import dataInterface from '../sql/Client';
|
import dataInterface from '../sql/Client';
|
||||||
import {
|
import {
|
||||||
AccountRecordClass,
|
AccountRecordClass,
|
||||||
|
@ -47,6 +48,9 @@ import { isGroupV1, isGroupV2 } from '../util/whatTypeOfConversation';
|
||||||
|
|
||||||
const { updateConversation } = dataInterface;
|
const { updateConversation } = dataInterface;
|
||||||
|
|
||||||
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
|
const FIXMEU8 = Uint8Array;
|
||||||
|
|
||||||
type RecordClass =
|
type RecordClass =
|
||||||
| AccountRecordClass
|
| AccountRecordClass
|
||||||
| ContactRecordClass
|
| ContactRecordClass
|
||||||
|
@ -520,8 +524,8 @@ export async function mergeGroupV1Record(
|
||||||
// retrieve the master key and find the conversation locally. If we
|
// retrieve the master key and find the conversation locally. If we
|
||||||
// are successful then we continue setting and applying state.
|
// are successful then we continue setting and applying state.
|
||||||
const masterKeyBuffer = await deriveMasterKeyFromGroupV1(groupId);
|
const masterKeyBuffer = await deriveMasterKeyFromGroupV1(groupId);
|
||||||
const fields = deriveGroupFields(masterKeyBuffer);
|
const fields = deriveGroupFields(new FIXMEU8(masterKeyBuffer));
|
||||||
const derivedGroupV2Id = arrayBufferToBase64(fields.id);
|
const derivedGroupV2Id = Bytes.toBase64(fields.id);
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'storageService.mergeGroupV1Record: failed to find group by v1 id ' +
|
'storageService.mergeGroupV1Record: failed to find group by v1 id ' +
|
||||||
|
@ -596,12 +600,12 @@ export async function mergeGroupV1Record(
|
||||||
async function getGroupV2Conversation(
|
async function getGroupV2Conversation(
|
||||||
masterKeyBuffer: ArrayBuffer
|
masterKeyBuffer: ArrayBuffer
|
||||||
): Promise<ConversationModel> {
|
): Promise<ConversationModel> {
|
||||||
const groupFields = deriveGroupFields(masterKeyBuffer);
|
const groupFields = deriveGroupFields(new FIXMEU8(masterKeyBuffer));
|
||||||
|
|
||||||
const groupId = arrayBufferToBase64(groupFields.id);
|
const groupId = Bytes.toBase64(groupFields.id);
|
||||||
const masterKey = arrayBufferToBase64(masterKeyBuffer);
|
const masterKey = arrayBufferToBase64(masterKeyBuffer);
|
||||||
const secretParams = arrayBufferToBase64(groupFields.secretParams);
|
const secretParams = Bytes.toBase64(groupFields.secretParams);
|
||||||
const publicParams = arrayBufferToBase64(groupFields.publicParams);
|
const publicParams = Bytes.toBase64(groupFields.publicParams);
|
||||||
|
|
||||||
// First we check for an existing GroupV2 group
|
// First we check for an existing GroupV2 group
|
||||||
const groupV2 = window.ConversationController.get(groupId);
|
const groupV2 = window.ConversationController.get(groupId);
|
||||||
|
@ -944,7 +948,7 @@ export async function mergeAccountRecord(
|
||||||
}
|
}
|
||||||
const masterKeyBuffer = pinnedConversation.groupMasterKey.toArrayBuffer();
|
const masterKeyBuffer = pinnedConversation.groupMasterKey.toArrayBuffer();
|
||||||
const groupFields = deriveGroupFields(masterKeyBuffer);
|
const groupFields = deriveGroupFields(masterKeyBuffer);
|
||||||
const groupId = arrayBufferToBase64(groupFields.id);
|
const groupId = Bytes.toBase64(groupFields.id);
|
||||||
|
|
||||||
conversationId = groupId;
|
conversationId = groupId;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import * as Bytes from '../../Bytes';
|
||||||
|
|
||||||
|
describe('Bytes', () => {
|
||||||
|
it('converts to base64 and back', () => {
|
||||||
|
const bytes = new Uint8Array([1, 2, 3]);
|
||||||
|
|
||||||
|
const base64 = Bytes.toBase64(bytes);
|
||||||
|
assert.strictEqual(base64, 'AQID');
|
||||||
|
|
||||||
|
assert.deepEqual(Bytes.fromBase64(base64), bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts to hex and back', () => {
|
||||||
|
const bytes = new Uint8Array([1, 2, 3]);
|
||||||
|
|
||||||
|
const hex = Bytes.toHex(bytes);
|
||||||
|
assert.strictEqual(hex, '010203');
|
||||||
|
|
||||||
|
assert.deepEqual(Bytes.fromHex(hex), bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts to string and back', () => {
|
||||||
|
const bytes = new Uint8Array([0x61, 0x62, 0x63]);
|
||||||
|
|
||||||
|
const binary = Bytes.toString(bytes);
|
||||||
|
assert.strictEqual(binary, 'abc');
|
||||||
|
|
||||||
|
assert.deepEqual(Bytes.fromString(binary), bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts to binary and back', () => {
|
||||||
|
const bytes = new Uint8Array([0xff, 0x01]);
|
||||||
|
|
||||||
|
const binary = Bytes.toBinary(bytes);
|
||||||
|
assert.strictEqual(binary, '\xff\x01');
|
||||||
|
|
||||||
|
assert.deepEqual(Bytes.fromBinary(binary), bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('concatenates bytes', () => {
|
||||||
|
const result = Bytes.concatenate([
|
||||||
|
Bytes.fromString('hello'),
|
||||||
|
Bytes.fromString(' '),
|
||||||
|
Bytes.fromString('world'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.strictEqual(Bytes.toString(result), 'hello world');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isEmpty', () => {
|
||||||
|
it('returns true for `undefined`', () => {
|
||||||
|
assert.strictEqual(Bytes.isEmpty(undefined), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for `null`', () => {
|
||||||
|
assert.strictEqual(Bytes.isEmpty(null), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for an empty Uint8Array', () => {
|
||||||
|
assert.strictEqual(Bytes.isEmpty(new Uint8Array(0)), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for not empty Uint8Array', () => {
|
||||||
|
assert.strictEqual(Bytes.isEmpty(new Uint8Array(123)), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isNotEmpty', () => {
|
||||||
|
it('returns false for `undefined`', () => {
|
||||||
|
assert.strictEqual(Bytes.isNotEmpty(undefined), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for `null`', () => {
|
||||||
|
assert.strictEqual(Bytes.isNotEmpty(null), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for an empty Uint8Array', () => {
|
||||||
|
assert.strictEqual(Bytes.isNotEmpty(new Uint8Array(0)), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for not empty Uint8Array', () => {
|
||||||
|
assert.strictEqual(Bytes.isNotEmpty(new Uint8Array(123)), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,16 +3,30 @@
|
||||||
|
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
|
|
||||||
import { assert } from '../../util/assert';
|
import { assert, strictAssert } from '../../util/assert';
|
||||||
|
|
||||||
describe('assert', () => {
|
describe('assert utilities', () => {
|
||||||
it('does nothing if the assertion passes', () => {
|
describe('assert', () => {
|
||||||
assert(true, 'foo bar');
|
it('does nothing if the assertion passes', () => {
|
||||||
|
assert(true, 'foo bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws if the assertion fails, because we're in a test environment", () => {
|
||||||
|
chai.assert.throws(() => {
|
||||||
|
assert(false, 'foo bar');
|
||||||
|
}, 'foo bar');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws because we're in a test environment", () => {
|
describe('strictAssert', () => {
|
||||||
chai.assert.throws(() => {
|
it('does nothing if the assertion passes', () => {
|
||||||
assert(false, 'foo bar');
|
strictAssert(true, 'foo bar');
|
||||||
}, 'foo bar');
|
});
|
||||||
|
|
||||||
|
it('throws if the assertion fails', () => {
|
||||||
|
chai.assert.throws(() => {
|
||||||
|
strictAssert(false, 'foo bar');
|
||||||
|
}, 'foo bar');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import { dropNull } from '../../util/dropNull';
|
||||||
|
|
||||||
|
describe('dropNull', () => {
|
||||||
|
it('swaps null with undefined', () => {
|
||||||
|
assert.strictEqual(dropNull(null), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves undefined be', () => {
|
||||||
|
assert.strictEqual(dropNull(undefined), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('non-null values undefined be', () => {
|
||||||
|
assert.strictEqual(dropNull('test'), 'test');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import { isValidGuid } from '../../util/isValidGuid';
|
||||||
|
|
||||||
|
describe('isValidGuid', () => {
|
||||||
|
const LOWERCASE_V4_UUID = '9cb737ce-2bb3-4c21-9fe0-d286caa0ca68';
|
||||||
|
|
||||||
|
it('returns false for non-strings', () => {
|
||||||
|
assert.isFalse(isValidGuid(undefined));
|
||||||
|
assert.isFalse(isValidGuid(null));
|
||||||
|
assert.isFalse(isValidGuid(1234));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for non-UUID strings', () => {
|
||||||
|
assert.isFalse(isValidGuid(''));
|
||||||
|
assert.isFalse(isValidGuid('hello world'));
|
||||||
|
assert.isFalse(isValidGuid(` ${LOWERCASE_V4_UUID}`));
|
||||||
|
assert.isFalse(isValidGuid(`${LOWERCASE_V4_UUID} `));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for UUIDs that aren't version 4", () => {
|
||||||
|
assert.isFalse(isValidGuid('a200a6e0-d2d9-11eb-bda7-dd5936a30ddf'));
|
||||||
|
assert.isFalse(isValidGuid('2adb8b83-4f2c-55ca-a481-7f98b716e615'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for v4 UUIDs', () => {
|
||||||
|
assert.isTrue(isValidGuid(LOWERCASE_V4_UUID));
|
||||||
|
assert.isTrue(isValidGuid(LOWERCASE_V4_UUID.toUpperCase()));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import { v4 as generateUuid } from 'uuid';
|
||||||
|
|
||||||
|
import { normalizeUuid } from '../../util/normalizeUuid';
|
||||||
|
|
||||||
|
describe('normalizeUuid', () => {
|
||||||
|
it('converts uuid to lower case', () => {
|
||||||
|
const uuid = generateUuid();
|
||||||
|
assert.strictEqual(normalizeUuid(uuid, 'context 1'), uuid);
|
||||||
|
assert.strictEqual(normalizeUuid(uuid.toUpperCase(), 'context 2'), uuid);
|
||||||
|
});
|
||||||
|
});
|
|
@ -51,6 +51,7 @@ import utils from './Helpers';
|
||||||
import WebSocketResource, {
|
import WebSocketResource, {
|
||||||
IncomingWebSocketRequest,
|
IncomingWebSocketRequest,
|
||||||
} from './WebsocketResources';
|
} from './WebsocketResources';
|
||||||
|
import * as Bytes from '../Bytes';
|
||||||
import Crypto from './Crypto';
|
import Crypto from './Crypto';
|
||||||
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
||||||
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
||||||
|
@ -73,6 +74,9 @@ import { ByteBufferClass } from '../window.d';
|
||||||
|
|
||||||
import { deriveGroupFields, MASTER_KEY_LENGTH } from '../groups';
|
import { deriveGroupFields, MASTER_KEY_LENGTH } from '../groups';
|
||||||
|
|
||||||
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
|
const FIXMEU8 = Uint8Array;
|
||||||
|
|
||||||
const GROUPV1_ID_LENGTH = 16;
|
const GROUPV1_ID_LENGTH = 16;
|
||||||
const GROUPV2_ID_LENGTH = 32;
|
const GROUPV2_ID_LENGTH = 32;
|
||||||
const RETRY_TIMEOUT = 2 * 60 * 1000;
|
const RETRY_TIMEOUT = 2 * 60 * 1000;
|
||||||
|
@ -1991,10 +1995,9 @@ class MessageReceiverInner extends EventTarget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const masterKey = await deriveMasterKeyFromGroupV1(groupId);
|
const masterKey = await deriveMasterKeyFromGroupV1(groupId);
|
||||||
const data = deriveGroupFields(masterKey);
|
const data = deriveGroupFields(new FIXMEU8(masterKey));
|
||||||
|
|
||||||
const toBase64 = MessageReceiverInner.arrayBufferToStringBase64;
|
return Bytes.toBase64(data.id);
|
||||||
return toBase64(data.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deriveGroupV1Data(message: DataMessageClass) {
|
async deriveGroupV1Data(message: DataMessageClass) {
|
||||||
|
@ -2040,11 +2043,11 @@ class MessageReceiverInner extends EventTarget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = deriveGroupFields(masterKey);
|
const fields = deriveGroupFields(new FIXMEU8(masterKey));
|
||||||
groupV2.masterKey = toBase64(masterKey);
|
groupV2.masterKey = toBase64(masterKey);
|
||||||
groupV2.secretParams = toBase64(fields.secretParams);
|
groupV2.secretParams = Bytes.toBase64(fields.secretParams);
|
||||||
groupV2.publicParams = toBase64(fields.publicParams);
|
groupV2.publicParams = Bytes.toBase64(fields.publicParams);
|
||||||
groupV2.id = toBase64(fields.id);
|
groupV2.id = Bytes.toBase64(fields.id);
|
||||||
|
|
||||||
if (groupV2.groupChange) {
|
if (groupV2.groupChange) {
|
||||||
groupV2.groupChange = groupV2.groupChange.toString('base64');
|
groupV2.groupChange = groupV2.groupChange.toString('base64');
|
||||||
|
|
|
@ -43,10 +43,6 @@ import {
|
||||||
CallingMessageClass,
|
CallingMessageClass,
|
||||||
ContentClass,
|
ContentClass,
|
||||||
DataMessageClass,
|
DataMessageClass,
|
||||||
GroupChangeClass,
|
|
||||||
GroupClass,
|
|
||||||
GroupExternalCredentialClass,
|
|
||||||
GroupJoinInfoClass,
|
|
||||||
StorageServiceCallOptionsType,
|
StorageServiceCallOptionsType,
|
||||||
StorageServiceCredentials,
|
StorageServiceCredentials,
|
||||||
SyncMessageClass,
|
SyncMessageClass,
|
||||||
|
@ -58,6 +54,7 @@ import {
|
||||||
LinkPreviewMetadata,
|
LinkPreviewMetadata,
|
||||||
} from '../linkPreviews/linkPreviewFetch';
|
} from '../linkPreviews/linkPreviewFetch';
|
||||||
import { concat } from '../util/iterables';
|
import { concat } from '../util/iterables';
|
||||||
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
|
||||||
function stringToArrayBuffer(str: string): ArrayBuffer {
|
function stringToArrayBuffer(str: string): ArrayBuffer {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
|
@ -108,8 +105,8 @@ type QuoteAttachmentType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GroupV2InfoType = {
|
export type GroupV2InfoType = {
|
||||||
groupChange?: ArrayBuffer;
|
groupChange?: Uint8Array;
|
||||||
masterKey: ArrayBuffer;
|
masterKey: Uint8Array;
|
||||||
revision: number;
|
revision: number;
|
||||||
members: Array<string>;
|
members: Array<string>;
|
||||||
};
|
};
|
||||||
|
@ -1961,27 +1958,27 @@ export default class MessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createGroup(
|
async createGroup(
|
||||||
group: GroupClass,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return this.server.createGroup(group, options);
|
return this.server.createGroup(group, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadGroupAvatar(
|
async uploadGroupAvatar(
|
||||||
avatar: ArrayBuffer,
|
avatar: Uint8Array,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return this.server.uploadGroupAvatar(avatar, options);
|
return this.server.uploadGroupAvatar(avatar, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroup(options: GroupCredentialsType): Promise<GroupClass> {
|
async getGroup(options: GroupCredentialsType): Promise<Proto.Group> {
|
||||||
return this.server.getGroup(options);
|
return this.server.getGroup(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupFromLink(
|
async getGroupFromLink(
|
||||||
groupInviteLink: string,
|
groupInviteLink: string,
|
||||||
auth: GroupCredentialsType
|
auth: GroupCredentialsType
|
||||||
): Promise<GroupJoinInfoClass> {
|
): Promise<Proto.GroupJoinInfo> {
|
||||||
return this.server.getGroupFromLink(groupInviteLink, auth);
|
return this.server.getGroupFromLink(groupInviteLink, auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1997,10 +1994,10 @@ export default class MessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
async modifyGroup(
|
async modifyGroup(
|
||||||
changes: GroupChangeClass.Actions,
|
changes: Proto.GroupChange.IActions,
|
||||||
options: GroupCredentialsType,
|
options: GroupCredentialsType,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
): Promise<GroupChangeClass> {
|
): Promise<Proto.IGroupChange> {
|
||||||
return this.server.modifyGroup(changes, options, inviteLinkBase64);
|
return this.server.modifyGroup(changes, options, inviteLinkBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2060,7 +2057,7 @@ export default class MessageSender {
|
||||||
|
|
||||||
async getGroupMembershipToken(
|
async getGroupMembershipToken(
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<GroupExternalCredentialClass> {
|
): Promise<Proto.GroupExternalCredential> {
|
||||||
return this.server.getGroupExternalCredential(options);
|
return this.server.getGroupExternalCredential(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,23 +47,23 @@ import {
|
||||||
getBytes,
|
getBytes,
|
||||||
getRandomValue,
|
getRandomValue,
|
||||||
splitUuids,
|
splitUuids,
|
||||||
|
typedArrayToArrayBuffer,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
import { calculateAgreement, generateKeyPair } from '../Curve';
|
import { calculateAgreement, generateKeyPair } from '../Curve';
|
||||||
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AvatarUploadAttributesClass,
|
AvatarUploadAttributesClass,
|
||||||
GroupChangeClass,
|
|
||||||
GroupChangesClass,
|
|
||||||
GroupClass,
|
|
||||||
GroupJoinInfoClass,
|
|
||||||
GroupExternalCredentialClass,
|
|
||||||
StorageServiceCallOptionsType,
|
StorageServiceCallOptionsType,
|
||||||
StorageServiceCredentials,
|
StorageServiceCredentials,
|
||||||
} from '../textsecure.d';
|
} from '../textsecure.d';
|
||||||
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
|
||||||
import MessageSender from './SendMessage';
|
import MessageSender from './SendMessage';
|
||||||
|
|
||||||
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
|
const FIXMEU8 = Uint8Array;
|
||||||
|
|
||||||
// Note: this will break some code that expects to be able to use err.response when a
|
// Note: this will break some code that expects to be able to use err.response when a
|
||||||
// web request fails, because it will force it to text. But it is very useful for
|
// web request fails, because it will force it to text. But it is very useful for
|
||||||
// debugging failed requests.
|
// debugging failed requests.
|
||||||
|
@ -881,7 +881,7 @@ type AjaxOptionsType = {
|
||||||
basicAuth?: string;
|
basicAuth?: string;
|
||||||
call: keyof typeof URL_CALLS;
|
call: keyof typeof URL_CALLS;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
data?: ArrayBuffer | Buffer | string;
|
data?: ArrayBuffer | Buffer | Uint8Array | string;
|
||||||
headers?: HeaderListType;
|
headers?: HeaderListType;
|
||||||
host?: string;
|
host?: string;
|
||||||
httpType: HTTPCodeType;
|
httpType: HTTPCodeType;
|
||||||
|
@ -926,7 +926,7 @@ export type GroupLogResponseType = {
|
||||||
currentRevision?: number;
|
currentRevision?: number;
|
||||||
start?: number;
|
start?: number;
|
||||||
end?: number;
|
end?: number;
|
||||||
changes: GroupChangesClass;
|
changes: Proto.GroupChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WebAPIType = {
|
export type WebAPIType = {
|
||||||
|
@ -939,17 +939,17 @@ export type WebAPIType = {
|
||||||
options?: { accessKey?: ArrayBuffer }
|
options?: { accessKey?: ArrayBuffer }
|
||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
createGroup: (
|
createGroup: (
|
||||||
group: GroupClass,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getAttachment: (cdnKey: string, cdnNumber?: number) => Promise<any>;
|
getAttachment: (cdnKey: string, cdnNumber?: number) => Promise<any>;
|
||||||
getAvatar: (path: string) => Promise<any>;
|
getAvatar: (path: string) => Promise<any>;
|
||||||
getDevices: () => Promise<any>;
|
getDevices: () => Promise<any>;
|
||||||
getGroup: (options: GroupCredentialsType) => Promise<GroupClass>;
|
getGroup: (options: GroupCredentialsType) => Promise<Proto.Group>;
|
||||||
getGroupFromLink: (
|
getGroupFromLink: (
|
||||||
inviteLinkPassword: string,
|
inviteLinkPassword: string,
|
||||||
auth: GroupCredentialsType
|
auth: GroupCredentialsType
|
||||||
) => Promise<GroupJoinInfoClass>;
|
) => Promise<Proto.GroupJoinInfo>;
|
||||||
getGroupAvatar: (key: string) => Promise<ArrayBuffer>;
|
getGroupAvatar: (key: string) => Promise<ArrayBuffer>;
|
||||||
getGroupCredentials: (
|
getGroupCredentials: (
|
||||||
startDay: number,
|
startDay: number,
|
||||||
|
@ -957,7 +957,7 @@ export type WebAPIType = {
|
||||||
) => Promise<Array<GroupCredentialType>>;
|
) => Promise<Array<GroupCredentialType>>;
|
||||||
getGroupExternalCredential: (
|
getGroupExternalCredential: (
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<GroupExternalCredentialClass>;
|
) => Promise<Proto.GroupExternalCredential>;
|
||||||
getGroupLog: (
|
getGroupLog: (
|
||||||
startVersion: number,
|
startVersion: number,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
|
@ -1020,10 +1020,10 @@ export type WebAPIType = {
|
||||||
body: ArrayBuffer | undefined
|
body: ArrayBuffer | undefined
|
||||||
) => Promise<ArrayBufferWithDetailsType>;
|
) => Promise<ArrayBufferWithDetailsType>;
|
||||||
modifyGroup: (
|
modifyGroup: (
|
||||||
changes: GroupChangeClass.Actions,
|
changes: Proto.GroupChange.IActions,
|
||||||
options: GroupCredentialsType,
|
options: GroupCredentialsType,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
) => Promise<GroupChangeClass>;
|
) => Promise<Proto.IGroupChange>;
|
||||||
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
||||||
putAttachment: (encryptedBin: ArrayBuffer) => Promise<any>;
|
putAttachment: (encryptedBin: ArrayBuffer) => Promise<any>;
|
||||||
registerCapabilities: (capabilities: CapabilitiesUploadType) => Promise<void>;
|
registerCapabilities: (capabilities: CapabilitiesUploadType) => Promise<void>;
|
||||||
|
@ -1060,7 +1060,7 @@ export type WebAPIType = {
|
||||||
setSignedPreKey: (signedPreKey: SignedPreKeyType) => Promise<void>;
|
setSignedPreKey: (signedPreKey: SignedPreKeyType) => Promise<void>;
|
||||||
updateDeviceName: (deviceName: string) => Promise<void>;
|
updateDeviceName: (deviceName: string) => Promise<void>;
|
||||||
uploadGroupAvatar: (
|
uploadGroupAvatar: (
|
||||||
avatarData: ArrayBuffer,
|
avatarData: Uint8Array,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<string>;
|
) => Promise<string>;
|
||||||
whoami: () => Promise<any>;
|
whoami: () => Promise<any>;
|
||||||
|
@ -2150,7 +2150,7 @@ export function initialize({
|
||||||
|
|
||||||
async function getGroupExternalCredential(
|
async function getGroupExternalCredential(
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<GroupExternalCredentialClass> {
|
): Promise<Proto.GroupExternalCredential> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
|
@ -2165,9 +2165,7 @@ export function initialize({
|
||||||
host: storageUrl,
|
host: storageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
return window.textsecure.protobuf.GroupExternalCredential.decode(
|
return Proto.GroupExternalCredential.decode(new FIXMEU8(response));
|
||||||
response
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyAttributes(attributes: AvatarUploadAttributesClass) {
|
function verifyAttributes(attributes: AvatarUploadAttributesClass) {
|
||||||
|
@ -2207,7 +2205,7 @@ export function initialize({
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadGroupAvatar(
|
async function uploadGroupAvatar(
|
||||||
avatarData: ArrayBuffer,
|
avatarData: Uint8Array,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
|
@ -2229,7 +2227,10 @@ export function initialize({
|
||||||
const verified = verifyAttributes(attributes);
|
const verified = verifyAttributes(attributes);
|
||||||
const { key } = verified;
|
const { key } = verified;
|
||||||
|
|
||||||
const manifestParams = makePutParams(verified, avatarData);
|
const manifestParams = makePutParams(
|
||||||
|
verified,
|
||||||
|
typedArrayToArrayBuffer(avatarData)
|
||||||
|
);
|
||||||
|
|
||||||
await _outerAjax(`${cdnUrlObject['0']}/`, {
|
await _outerAjax(`${cdnUrlObject['0']}/`, {
|
||||||
...manifestParams,
|
...manifestParams,
|
||||||
|
@ -2255,14 +2256,14 @@ export function initialize({
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createGroup(
|
async function createGroup(
|
||||||
group: GroupClass,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
);
|
);
|
||||||
const data = group.toArrayBuffer();
|
const data = Proto.Group.encode(group).finish();
|
||||||
|
|
||||||
await _ajax({
|
await _ajax({
|
||||||
basicAuth,
|
basicAuth,
|
||||||
|
@ -2276,7 +2277,7 @@ export function initialize({
|
||||||
|
|
||||||
async function getGroup(
|
async function getGroup(
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
): Promise<GroupClass> {
|
): Promise<Proto.Group> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
|
@ -2291,13 +2292,13 @@ export function initialize({
|
||||||
responseType: 'arraybuffer',
|
responseType: 'arraybuffer',
|
||||||
});
|
});
|
||||||
|
|
||||||
return window.textsecure.protobuf.Group.decode(response);
|
return Proto.Group.decode(new FIXMEU8(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroupFromLink(
|
async function getGroupFromLink(
|
||||||
inviteLinkPassword: string,
|
inviteLinkPassword: string,
|
||||||
auth: GroupCredentialsType
|
auth: GroupCredentialsType
|
||||||
): Promise<GroupJoinInfoClass> {
|
): Promise<Proto.GroupJoinInfo> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
auth.groupPublicParamsHex,
|
auth.groupPublicParamsHex,
|
||||||
auth.authCredentialPresentationHex
|
auth.authCredentialPresentationHex
|
||||||
|
@ -2315,19 +2316,19 @@ export function initialize({
|
||||||
redactUrl: _createRedactor(safeInviteLinkPassword),
|
redactUrl: _createRedactor(safeInviteLinkPassword),
|
||||||
});
|
});
|
||||||
|
|
||||||
return window.textsecure.protobuf.GroupJoinInfo.decode(response);
|
return Proto.GroupJoinInfo.decode(new FIXMEU8(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function modifyGroup(
|
async function modifyGroup(
|
||||||
changes: GroupChangeClass.Actions,
|
changes: Proto.GroupChange.IActions,
|
||||||
options: GroupCredentialsType,
|
options: GroupCredentialsType,
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
): Promise<GroupChangeClass> {
|
): Promise<Proto.IGroupChange> {
|
||||||
const basicAuth = generateGroupAuth(
|
const basicAuth = generateGroupAuth(
|
||||||
options.groupPublicParamsHex,
|
options.groupPublicParamsHex,
|
||||||
options.authCredentialPresentationHex
|
options.authCredentialPresentationHex
|
||||||
);
|
);
|
||||||
const data = changes.toArrayBuffer();
|
const data = Proto.GroupChange.Actions.encode(changes).finish();
|
||||||
const safeInviteLinkPassword = inviteLinkBase64
|
const safeInviteLinkPassword = inviteLinkBase64
|
||||||
? toWebSafeBase64(inviteLinkBase64)
|
? toWebSafeBase64(inviteLinkBase64)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -2348,7 +2349,7 @@ export function initialize({
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
return window.textsecure.protobuf.GroupChange.decode(response);
|
return Proto.GroupChange.decode(new FIXMEU8(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGroupLog(
|
async function getGroupLog(
|
||||||
|
@ -2370,7 +2371,7 @@ export function initialize({
|
||||||
urlParameters: `/${startVersion}`,
|
urlParameters: `/${startVersion}`,
|
||||||
});
|
});
|
||||||
const { data, response } = withDetails;
|
const { data, response } = withDetails;
|
||||||
const changes = window.textsecure.protobuf.GroupChanges.decode(data);
|
const changes = Proto.GroupChanges.decode(new FIXMEU8(data));
|
||||||
|
|
||||||
if (response && response.status === 206) {
|
if (response && response.status === 206) {
|
||||||
const range = response.headers.get('Content-Range');
|
const range = response.headers.get('Content-Range');
|
||||||
|
|
|
@ -19,3 +19,15 @@ export function assert(condition: unknown, message: string): asserts condition {
|
||||||
log.error('assert failure:', err && err.stack ? err.stack : err);
|
log.error('assert failure:', err && err.stack ? err.stack : err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error if the condition is falsy, regardless of environment.
|
||||||
|
*/
|
||||||
|
export function strictAssert(
|
||||||
|
condition: unknown,
|
||||||
|
message: string
|
||||||
|
): asserts condition {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
export function dropNull<T>(
|
||||||
|
value: NonNullable<T> | null | undefined
|
||||||
|
): T | undefined {
|
||||||
|
if (value === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2017-2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
export const isValidGuid = (value: unknown): value is string =>
|
||||||
|
typeof value === 'string' &&
|
||||||
|
/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(
|
||||||
|
value
|
||||||
|
);
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { isValidGuid } from './isValidGuid';
|
||||||
|
|
||||||
|
export function normalizeUuid(uuid: string, context: string): string {
|
||||||
|
if (!isValidGuid(uuid)) {
|
||||||
|
window.log.warn(
|
||||||
|
`Normalizing invalid uuid: ${uuid} in context "${context}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuid.toLowerCase();
|
||||||
|
}
|
|
@ -19,58 +19,51 @@ import {
|
||||||
ServerPublicParams,
|
ServerPublicParams,
|
||||||
UuidCiphertext,
|
UuidCiphertext,
|
||||||
} from 'zkgroup';
|
} from 'zkgroup';
|
||||||
import {
|
import * as Bytes from '../Bytes';
|
||||||
arrayBufferToBase64,
|
|
||||||
arrayBufferToHex,
|
|
||||||
base64ToArrayBuffer,
|
|
||||||
typedArrayToArrayBuffer,
|
|
||||||
} from '../Crypto';
|
|
||||||
|
|
||||||
export * from 'zkgroup';
|
export * from 'zkgroup';
|
||||||
|
|
||||||
export function arrayBufferToCompatArray(
|
export function uint8ArrayToCompatArray(
|
||||||
arrayBuffer: ArrayBuffer
|
buffer: Uint8Array
|
||||||
): FFICompatArrayType {
|
): FFICompatArrayType {
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
return new FFICompatArray(Buffer.from(buffer));
|
||||||
|
|
||||||
return new FFICompatArray(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compatArrayToArrayBuffer(
|
export function compatArrayToUint8Array(
|
||||||
compatArray: FFICompatArrayType
|
compatArray: FFICompatArrayType
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
return typedArrayToArrayBuffer(compatArray.buffer);
|
return compatArray.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64ToCompatArray(base64: string): FFICompatArrayType {
|
export function base64ToCompatArray(base64: string): FFICompatArrayType {
|
||||||
return arrayBufferToCompatArray(base64ToArrayBuffer(base64));
|
return uint8ArrayToCompatArray(Bytes.fromBase64(base64));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compatArrayToBase64(compatArray: FFICompatArrayType): string {
|
export function compatArrayToBase64(compatArray: FFICompatArrayType): string {
|
||||||
return arrayBufferToBase64(compatArrayToArrayBuffer(compatArray));
|
return Bytes.toBase64(compatArrayToUint8Array(compatArray));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compatArrayToHex(compatArray: FFICompatArrayType): string {
|
export function compatArrayToHex(compatArray: FFICompatArrayType): string {
|
||||||
return arrayBufferToHex(compatArrayToArrayBuffer(compatArray));
|
return Bytes.toHex(compatArrayToUint8Array(compatArray));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenarios
|
// Scenarios
|
||||||
|
|
||||||
export function decryptGroupBlob(
|
export function decryptGroupBlob(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
ciphertext: ArrayBuffer
|
ciphertext: Uint8Array
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
return compatArrayToArrayBuffer(
|
return compatArrayToUint8Array(
|
||||||
clientZkGroupCipher.decryptBlob(arrayBufferToCompatArray(ciphertext))
|
clientZkGroupCipher.decryptBlob(uint8ArrayToCompatArray(ciphertext))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decryptProfileKeyCredentialPresentation(
|
export function decryptProfileKeyCredentialPresentation(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
presentationBuffer: ArrayBuffer
|
presentationBuffer: Uint8Array
|
||||||
): { profileKey: ArrayBuffer; uuid: string } {
|
): { profileKey: Uint8Array; uuid: string } {
|
||||||
const presentation = new ProfileKeyCredentialPresentation(
|
const presentation = new ProfileKeyCredentialPresentation(
|
||||||
arrayBufferToCompatArray(presentationBuffer)
|
uint8ArrayToCompatArray(presentationBuffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
const uuidCiphertext = presentation.getUuidCiphertext();
|
const uuidCiphertext = presentation.getUuidCiphertext();
|
||||||
|
@ -83,18 +76,18 @@ export function decryptProfileKeyCredentialPresentation(
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
profileKey: compatArrayToArrayBuffer(profileKey.serialize()),
|
profileKey: compatArrayToUint8Array(profileKey.serialize()),
|
||||||
uuid,
|
uuid,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decryptProfileKey(
|
export function decryptProfileKey(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
profileKeyCiphertextBuffer: ArrayBuffer,
|
profileKeyCiphertextBuffer: Uint8Array,
|
||||||
uuid: string
|
uuid: string
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const profileKeyCiphertext = new ProfileKeyCiphertext(
|
const profileKeyCiphertext = new ProfileKeyCiphertext(
|
||||||
arrayBufferToCompatArray(profileKeyCiphertextBuffer)
|
uint8ArrayToCompatArray(profileKeyCiphertextBuffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
const profileKey = clientZkGroupCipher.decryptProfileKey(
|
const profileKey = clientZkGroupCipher.decryptProfileKey(
|
||||||
|
@ -102,15 +95,15 @@ export function decryptProfileKey(
|
||||||
uuid
|
uuid
|
||||||
);
|
);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(profileKey.serialize());
|
return compatArrayToUint8Array(profileKey.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decryptUuid(
|
export function decryptUuid(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
uuidCiphertextBuffer: ArrayBuffer
|
uuidCiphertextBuffer: Uint8Array
|
||||||
): string {
|
): string {
|
||||||
const uuidCiphertext = new UuidCiphertext(
|
const uuidCiphertext = new UuidCiphertext(
|
||||||
arrayBufferToCompatArray(uuidCiphertextBuffer)
|
uint8ArrayToCompatArray(uuidCiphertextBuffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
return clientZkGroupCipher.decryptUuid(uuidCiphertext);
|
return clientZkGroupCipher.decryptUuid(uuidCiphertext);
|
||||||
|
@ -129,56 +122,54 @@ export function deriveProfileKeyVersion(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deriveGroupPublicParams(
|
export function deriveGroupPublicParams(
|
||||||
groupSecretParamsBuffer: ArrayBuffer
|
groupSecretParamsBuffer: Uint8Array
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const groupSecretParams = new GroupSecretParams(
|
const groupSecretParams = new GroupSecretParams(
|
||||||
arrayBufferToCompatArray(groupSecretParamsBuffer)
|
uint8ArrayToCompatArray(groupSecretParamsBuffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(
|
return compatArrayToUint8Array(
|
||||||
groupSecretParams.getPublicParams().serialize()
|
groupSecretParams.getPublicParams().serialize()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deriveGroupID(
|
export function deriveGroupID(groupSecretParamsBuffer: Uint8Array): Uint8Array {
|
||||||
groupSecretParamsBuffer: ArrayBuffer
|
|
||||||
): ArrayBuffer {
|
|
||||||
const groupSecretParams = new GroupSecretParams(
|
const groupSecretParams = new GroupSecretParams(
|
||||||
arrayBufferToCompatArray(groupSecretParamsBuffer)
|
uint8ArrayToCompatArray(groupSecretParamsBuffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(
|
return compatArrayToUint8Array(
|
||||||
groupSecretParams.getPublicParams().getGroupIdentifier().serialize()
|
groupSecretParams.getPublicParams().getGroupIdentifier().serialize()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deriveGroupSecretParams(
|
export function deriveGroupSecretParams(
|
||||||
masterKeyBuffer: ArrayBuffer
|
masterKeyBuffer: Uint8Array
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const masterKey = new GroupMasterKey(
|
const masterKey = new GroupMasterKey(
|
||||||
arrayBufferToCompatArray(masterKeyBuffer)
|
uint8ArrayToCompatArray(masterKeyBuffer)
|
||||||
);
|
);
|
||||||
const groupSecretParams = GroupSecretParams.deriveFromMasterKey(masterKey);
|
const groupSecretParams = GroupSecretParams.deriveFromMasterKey(masterKey);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(groupSecretParams.serialize());
|
return compatArrayToUint8Array(groupSecretParams.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encryptGroupBlob(
|
export function encryptGroupBlob(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
plaintext: ArrayBuffer
|
plaintext: Uint8Array
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
return compatArrayToArrayBuffer(
|
return compatArrayToUint8Array(
|
||||||
clientZkGroupCipher.encryptBlob(arrayBufferToCompatArray(plaintext))
|
clientZkGroupCipher.encryptBlob(uint8ArrayToCompatArray(plaintext))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encryptUuid(
|
export function encryptUuid(
|
||||||
clientZkGroupCipher: ClientZkGroupCipher,
|
clientZkGroupCipher: ClientZkGroupCipher,
|
||||||
uuidPlaintext: string
|
uuidPlaintext: string
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const uuidCiphertext = clientZkGroupCipher.encryptUuid(uuidPlaintext);
|
const uuidCiphertext = clientZkGroupCipher.encryptUuid(uuidPlaintext);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(uuidCiphertext.serialize());
|
return compatArrayToUint8Array(uuidCiphertext.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateProfileKeyCredentialRequest(
|
export function generateProfileKeyCredentialRequest(
|
||||||
|
@ -206,7 +197,7 @@ export function getAuthCredentialPresentation(
|
||||||
clientZkAuthOperations: ClientZkAuthOperations,
|
clientZkAuthOperations: ClientZkAuthOperations,
|
||||||
authCredentialBase64: string,
|
authCredentialBase64: string,
|
||||||
groupSecretParamsBase64: string
|
groupSecretParamsBase64: string
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const authCredential = new AuthCredential(
|
const authCredential = new AuthCredential(
|
||||||
base64ToCompatArray(authCredentialBase64)
|
base64ToCompatArray(authCredentialBase64)
|
||||||
);
|
);
|
||||||
|
@ -218,14 +209,14 @@ export function getAuthCredentialPresentation(
|
||||||
secretParams,
|
secretParams,
|
||||||
authCredential
|
authCredential
|
||||||
);
|
);
|
||||||
return compatArrayToArrayBuffer(presentation.serialize());
|
return compatArrayToUint8Array(presentation.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createProfileKeyCredentialPresentation(
|
export function createProfileKeyCredentialPresentation(
|
||||||
clientZkProfileCipher: ClientZkProfileOperations,
|
clientZkProfileCipher: ClientZkProfileOperations,
|
||||||
profileKeyCredentialBase64: string,
|
profileKeyCredentialBase64: string,
|
||||||
groupSecretParamsBase64: string
|
groupSecretParamsBase64: string
|
||||||
): ArrayBuffer {
|
): Uint8Array {
|
||||||
const profileKeyCredentialArray = base64ToCompatArray(
|
const profileKeyCredentialArray = base64ToCompatArray(
|
||||||
profileKeyCredentialBase64
|
profileKeyCredentialBase64
|
||||||
);
|
);
|
||||||
|
@ -241,7 +232,7 @@ export function createProfileKeyCredentialPresentation(
|
||||||
profileKeyCredential
|
profileKeyCredential
|
||||||
);
|
);
|
||||||
|
|
||||||
return compatArrayToArrayBuffer(presentation.serialize());
|
return compatArrayToUint8Array(presentation.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getClientZkAuthOperations(
|
export function getClientZkAuthOperations(
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
isGroupV1,
|
isGroupV1,
|
||||||
isMe,
|
isMe,
|
||||||
} from '../util/whatTypeOfConversation';
|
} from '../util/whatTypeOfConversation';
|
||||||
|
import * as Bytes from '../Bytes';
|
||||||
import {
|
import {
|
||||||
canReply,
|
canReply,
|
||||||
getAttachmentsForMessage,
|
getAttachmentsForMessage,
|
||||||
|
@ -4157,13 +4158,11 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
} = window.Signal.Groups.parseGroupLink(groupData);
|
} = window.Signal.Groups.parseGroupLink(groupData);
|
||||||
|
|
||||||
const fields = window.Signal.Groups.deriveGroupFields(
|
const fields = window.Signal.Groups.deriveGroupFields(
|
||||||
window.Signal.Crypto.base64ToArrayBuffer(masterKey)
|
Bytes.fromBase64(masterKey)
|
||||||
);
|
);
|
||||||
const id = window.Signal.Crypto.arrayBufferToBase64(fields.id);
|
const id = Bytes.toBase64(fields.id);
|
||||||
const logId = `groupv2(${id})`;
|
const logId = `groupv2(${id})`;
|
||||||
const secretParams = window.Signal.Crypto.arrayBufferToBase64(
|
const secretParams = Bytes.toBase64(fields.secretParams);
|
||||||
fields.secretParams
|
|
||||||
);
|
|
||||||
|
|
||||||
window.log.info(`getGroupPreview/${logId}: Fetching pre-join state`);
|
window.log.info(`getGroupPreview/${logId}: Fetching pre-join state`);
|
||||||
const result = await window.Signal.Groups.getPreJoinGroupInfo(
|
const result = await window.Signal.Groups.getPreJoinGroupInfo(
|
||||||
|
|
|
@ -112,12 +112,14 @@ import { MIMEType } from './types/MIME';
|
||||||
import { AttachmentType } from './types/Attachment';
|
import { AttachmentType } from './types/Attachment';
|
||||||
import { ElectronLocaleType } from './util/mapToSupportLocale';
|
import { ElectronLocaleType } from './util/mapToSupportLocale';
|
||||||
import { SignalProtocolStore } from './SignalProtocolStore';
|
import { SignalProtocolStore } from './SignalProtocolStore';
|
||||||
|
import { Context as SignalContext } from './context';
|
||||||
import { StartupQueue } from './util/StartupQueue';
|
import { StartupQueue } from './util/StartupQueue';
|
||||||
import * as synchronousCrypto from './util/synchronousCrypto';
|
import * as synchronousCrypto from './util/synchronousCrypto';
|
||||||
import { SocketStatus } from './types/SocketStatus';
|
import { SocketStatus } from './types/SocketStatus';
|
||||||
import SyncRequest from './textsecure/SyncRequest';
|
import SyncRequest from './textsecure/SyncRequest';
|
||||||
import { ConversationColorType, CustomColorType } from './types/Colors';
|
import { ConversationColorType, CustomColorType } from './types/Colors';
|
||||||
import { MessageController } from './util/MessageController';
|
import { MessageController } from './util/MessageController';
|
||||||
|
import { isValidGuid } from './util/isValidGuid';
|
||||||
import { StateType } from './state/reducer';
|
import { StateType } from './state/reducer';
|
||||||
|
|
||||||
export { Long } from 'long';
|
export { Long } from 'long';
|
||||||
|
@ -211,7 +213,7 @@ declare global {
|
||||||
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isFullScreen: () => boolean;
|
isFullScreen: () => boolean;
|
||||||
isValidGuid: (maybeGuid: string | null) => boolean;
|
isValidGuid: typeof isValidGuid;
|
||||||
isValidE164: (maybeE164: unknown) => boolean;
|
isValidE164: (maybeE164: unknown) => boolean;
|
||||||
libphonenumber: {
|
libphonenumber: {
|
||||||
util: {
|
util: {
|
||||||
|
@ -524,6 +526,7 @@ declare global {
|
||||||
};
|
};
|
||||||
challengeHandler: ChallengeHandler;
|
challengeHandler: ChallengeHandler;
|
||||||
};
|
};
|
||||||
|
SignalContext: SignalContext;
|
||||||
|
|
||||||
ConversationController: ConversationController;
|
ConversationController: ConversationController;
|
||||||
Events: WhatIsThis;
|
Events: WhatIsThis;
|
||||||
|
|
Loading…
Reference in New Issue