mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-05-14 11:50:22 +01:00
Replace typescript compiler with native tsgo compiler
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/** @type {import("eslint").Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
hasSuggestions: true,
|
||||
fixable: true,
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
TSTypeReference(node) {
|
||||
if (node.typeName.type !== 'Identifier') {
|
||||
return;
|
||||
}
|
||||
|
||||
let replacement;
|
||||
if (node.typeName.name === 'Uint8Array') {
|
||||
replacement = 'Uint8Array<ArrayBuffer>';
|
||||
} else if (node.typeName.name === 'Buffer') {
|
||||
replacement = 'Buffer<ArrayBuffer>';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.typeParameters != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
message: `Should be ${replacement}`,
|
||||
fix(fixer) {
|
||||
return [fixer.replaceTextRange(node.range, replacement)];
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
const rule = require('./enforce-array-buffer');
|
||||
const RuleTester = require('eslint').RuleTester;
|
||||
|
||||
// avoid triggering mocha's global leak detection
|
||||
require('@typescript-eslint/parser');
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
});
|
||||
|
||||
const EXPECTED_ARRAY_ERROR = {
|
||||
message: 'Should be Uint8Array<ArrayBuffer>',
|
||||
type: 'TSTypeReference',
|
||||
};
|
||||
|
||||
const EXPECTED_BUFFER_ERROR = {
|
||||
message: 'Should be Buffer<ArrayBuffer>',
|
||||
type: 'TSTypeReference',
|
||||
};
|
||||
|
||||
ruleTester.run('enforce-array-buffer', rule, {
|
||||
valid: [
|
||||
{ code: 'type T = number;' },
|
||||
{ code: 'type T = Uint16Array;' },
|
||||
{ code: 'type T = Uint8Array<ArrayBuffer>;' },
|
||||
{ code: 'type T = Uint8Array<SharedArrayBuffer>;' },
|
||||
{ code: 'type T = Uint8Array<ArrayBufferLike>;' },
|
||||
{ code: 'type T = Uint8Array<U>;' },
|
||||
{ code: 'function f(): Uint8Array<ArrayBuffer> {}' },
|
||||
{ code: 'function f(p: Uint8Array<ArrayBuffer>) {}' },
|
||||
{ code: 'let v: Uint8Array<ArrayBuffer>;' },
|
||||
{ code: 'let v = new Uint8Array();' },
|
||||
{ code: 'let v = new Uint8Array<ArrayBuffer>();' },
|
||||
{ code: 'let v = Uint8Array.of();' },
|
||||
{ code: 'let v = Uint8Array.from();' },
|
||||
{ code: 'let v: { p: Uint8Array<ArrayBuffer> };' },
|
||||
{ code: 'type T = Buffer<ArrayBuffer>;' },
|
||||
{ code: 'type T = Buffer<SharedArrayBuffer>;' },
|
||||
{ code: 'type T = Buffer<ArrayBufferLike>;' },
|
||||
{ code: 'type T = Buffer<U>;' },
|
||||
{ code: 'let v = new Buffer();' },
|
||||
{ code: 'let v = Buffer.from();' },
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `type T = Uint8Array`,
|
||||
output: `type T = Uint8Array<ArrayBuffer>`,
|
||||
errors: [EXPECTED_ARRAY_ERROR],
|
||||
},
|
||||
{
|
||||
code: `function f(): Uint8Array {}`,
|
||||
output: `function f(): Uint8Array<ArrayBuffer> {}`,
|
||||
errors: [EXPECTED_ARRAY_ERROR],
|
||||
},
|
||||
{
|
||||
code: `function f(p: Uint8Array) {}`,
|
||||
output: `function f(p: Uint8Array<ArrayBuffer>) {}`,
|
||||
errors: [EXPECTED_ARRAY_ERROR],
|
||||
},
|
||||
{
|
||||
code: `let v: Uint8Array;`,
|
||||
output: `let v: Uint8Array<ArrayBuffer>;`,
|
||||
errors: [EXPECTED_ARRAY_ERROR],
|
||||
},
|
||||
{
|
||||
code: `let v: { p: Uint8Array };`,
|
||||
output: `let v: { p: Uint8Array<ArrayBuffer> };`,
|
||||
errors: [EXPECTED_ARRAY_ERROR],
|
||||
},
|
||||
{
|
||||
code: `type T = Buffer`,
|
||||
output: `type T = Buffer<ArrayBuffer>`,
|
||||
errors: [EXPECTED_BUFFER_ERROR],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -274,6 +274,8 @@ const typescriptRules = {
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
'local-rules/enforce-array-buffer': 'error',
|
||||
};
|
||||
|
||||
const TAILWIND_REPLACEMENTS = [
|
||||
|
||||
@@ -3,6 +3,7 @@ public-hoist-pattern[]=*eslint-*
|
||||
minimum-release-age=14400
|
||||
minimum-release-age-exclude[]=@signalapp/*
|
||||
minimum-release-age-exclude[]=@indutny/*
|
||||
minimum-release-age-exclude[]=@types/*
|
||||
minimum-release-age-exclude[]=electron
|
||||
minimum-release-age-exclude[]=react
|
||||
minimum-release-age-exclude[]=react-dom
|
||||
|
||||
@@ -3185,6 +3185,64 @@ Signal Desktop makes use of the following open source projects.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
|
||||
## @typescript/native-preview
|
||||
|
||||
Apache License
|
||||
|
||||
Version 2.0, January 2004
|
||||
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
## blob-util
|
||||
|
||||
Apache License
|
||||
|
||||
@@ -23,7 +23,7 @@ type EmojiEntryType = Readonly<{
|
||||
sheet: string;
|
||||
}>;
|
||||
|
||||
type SheetCacheEntry = Map<string, Uint8Array>;
|
||||
type SheetCacheEntry = Map<string, Uint8Array<ArrayBuffer>>;
|
||||
|
||||
export class EmojiService {
|
||||
readonly #emojiMap = new Map<string, EmojiEntryType>();
|
||||
@@ -83,10 +83,11 @@ export class EmojiService {
|
||||
const pack = Proto.JumbomojiPack.decode(proto);
|
||||
|
||||
imageMap = new Map(
|
||||
pack.items.map(({ name, image }) => [
|
||||
name != null ? utf16ToEmoji(name) : '',
|
||||
image || new Uint8Array(0),
|
||||
])
|
||||
pack.items.map(({ name, image }) => {
|
||||
const key = name != null ? utf16ToEmoji(name) : '';
|
||||
const value: Uint8Array<ArrayBuffer> = image || new Uint8Array(0);
|
||||
return [key, value];
|
||||
})
|
||||
);
|
||||
this.#sheetCache.set(sheet, imageMap);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const MAX_CACHE_SIZE = 50 * 1024 * 1024;
|
||||
export class OptionalResourceService {
|
||||
#maybeDeclaration: OptionalResourcesDictType | undefined;
|
||||
|
||||
readonly #cache = new LRUCache<string, Buffer>({
|
||||
readonly #cache = new LRUCache<string, Buffer<ArrayBuffer>>({
|
||||
maxSize: MAX_CACHE_SIZE,
|
||||
|
||||
sizeCalculation: buf => buf.length,
|
||||
@@ -53,7 +53,7 @@ export class OptionalResourceService {
|
||||
return new OptionalResourceService(resourcesDir);
|
||||
}
|
||||
|
||||
public async getData(name: string): Promise<Buffer | undefined> {
|
||||
public async getData(name: string): Promise<Buffer<ArrayBuffer> | undefined> {
|
||||
await this.#lazyInit();
|
||||
|
||||
const decl = this.#declaration[name];
|
||||
@@ -172,8 +172,10 @@ export class OptionalResourceService {
|
||||
name: string,
|
||||
decl: OptionalResourceType,
|
||||
destPath: string
|
||||
): Promise<Buffer> {
|
||||
const result = await got(decl.url, await getGotOptions()).buffer();
|
||||
): Promise<Buffer<ArrayBuffer>> {
|
||||
const opts = await getGotOptions();
|
||||
// @ts-expect-error https://github.com/sindresorhus/got/issues/2418#issuecomment-4071277145
|
||||
const result: Buffer<ArrayBuffer> = await got(decl.url, opts).buffer();
|
||||
|
||||
const digest = createHash('sha512').update(result).digest();
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ type RangeFinderContextType = Readonly<
|
||||
}
|
||||
| {
|
||||
type: 'incremental';
|
||||
digest: Uint8Array;
|
||||
incrementalMac: Uint8Array;
|
||||
digest: Uint8Array<ArrayBuffer>;
|
||||
incrementalMac: Uint8Array<ArrayBuffer>;
|
||||
chunkSize: number;
|
||||
keysBase64: string;
|
||||
size: number;
|
||||
@@ -113,8 +113,8 @@ type RangeFinderContextType = Readonly<
|
||||
>;
|
||||
|
||||
type DigestLRUEntryType = Readonly<{
|
||||
key: Buffer;
|
||||
digest: Uint8Array;
|
||||
key: Buffer<ArrayBuffer>;
|
||||
digest: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
const digestLRU = new LRUCache<string, DigestLRUEntryType>({
|
||||
|
||||
@@ -294,10 +294,10 @@ export const readAndDecryptDataFromDisk = async ({
|
||||
absolutePath: string;
|
||||
keysBase64: string;
|
||||
size: number;
|
||||
}): Promise<Uint8Array> => {
|
||||
}): Promise<Uint8Array<ArrayBuffer>> => {
|
||||
const sink = new PassThrough();
|
||||
|
||||
const chunks = new Array<Buffer>();
|
||||
const chunks = new Array<Buffer<ArrayBuffer>>();
|
||||
|
||||
sink.on('data', chunk => chunks.push(chunk));
|
||||
sink.resume();
|
||||
@@ -321,7 +321,7 @@ export const writeNewAttachmentData = async ({
|
||||
data,
|
||||
getAbsoluteAttachmentPath,
|
||||
}: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
getAbsoluteAttachmentPath: (relativePath: string) => string;
|
||||
}): Promise<LocalAttachmentV2Type> => {
|
||||
const keys = generateKeys();
|
||||
|
||||
+3
-39
@@ -5,7 +5,6 @@ import { join } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import lodash from 'lodash';
|
||||
import * as LocaleMatcher from '@formatjs/intl-localematcher';
|
||||
import { z } from 'zod';
|
||||
import { setupI18n } from '../ts/util/setupI18nMain.std.js';
|
||||
import { shouldNeverBeCalled } from '../ts/util/shouldNeverBeCalled.std.js';
|
||||
|
||||
@@ -15,18 +14,12 @@ import type {
|
||||
LocaleMessagesType,
|
||||
} from '../ts/types/I18N.std.js';
|
||||
import type { LocalizerType } from '../ts/types/Util.std.js';
|
||||
import * as Errors from '../ts/types/errors.std.js';
|
||||
import { parseUnknown } from '../ts/util/schemas.std.js';
|
||||
|
||||
const { merge } = lodash;
|
||||
|
||||
type CompactLocaleMessagesType = ReadonlyArray<string | null>;
|
||||
type CompactLocaleKeysType = ReadonlyArray<string>;
|
||||
|
||||
const TextInfoSchema = z.object({
|
||||
direction: z.enum(['ltr', 'rtl']),
|
||||
});
|
||||
|
||||
function getLocaleMessages(locale: string): LocaleMessagesType {
|
||||
const targetFile = join(__dirname, '..', '_locales', locale, 'messages.json');
|
||||
|
||||
@@ -80,37 +73,8 @@ export type LocaleType = {
|
||||
countryDisplayNames: CountryDisplayNames;
|
||||
};
|
||||
|
||||
function getLocaleDirection(
|
||||
localeName: string,
|
||||
logger: LoggerType
|
||||
): LocaleDirection {
|
||||
const locale = new Intl.Locale(localeName);
|
||||
// TC39 proposal is now `locale.getTextInfo()` but in browsers its currently
|
||||
// `locale.textInfo`
|
||||
try {
|
||||
// @ts-expect-error -- TS doesn't know about this method
|
||||
if (typeof locale.getTextInfo === 'function') {
|
||||
return parseUnknown(
|
||||
TextInfoSchema,
|
||||
// @ts-expect-error -- TS doesn't know about this method
|
||||
locale.getTextInfo() as unknown
|
||||
).direction;
|
||||
}
|
||||
// @ts-expect-error -- TS doesn't know about this property
|
||||
if (typeof locale.textInfo === 'object') {
|
||||
return parseUnknown(
|
||||
TextInfoSchema,
|
||||
// @ts-expect-error -- TS doesn't know about this property
|
||||
locale.textInfo as unknown
|
||||
).direction;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'locale: Error getting text info for locale',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
return 'ltr';
|
||||
function getLocaleDirection(localeName: string): LocaleDirection {
|
||||
return new Intl.Locale(localeName).getTextInfo().direction ?? 'ltr';
|
||||
}
|
||||
|
||||
export function _getAvailableLocales(): Array<string> {
|
||||
@@ -202,7 +166,7 @@ export function load({
|
||||
getHourCyclePreference: shouldNeverBeCalled,
|
||||
});
|
||||
const direction =
|
||||
localeDirectionTestingOverride ?? getLocaleDirection(matchedLocale, logger);
|
||||
localeDirectionTestingOverride ?? getLocaleDirection(matchedLocale);
|
||||
logger.info(`locale: Text info direction for ${matchedLocale}: ${direction}`);
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,5 +6,6 @@ module.exports = {
|
||||
'license-comments': require('./.eslint/rules/license-comments'),
|
||||
'type-alias-readonlydeep': require('./.eslint/rules/type-alias-readonlydeep'),
|
||||
'enforce-tw': require('./.eslint/rules/enforce-tw'),
|
||||
'enforce-array-buffer': require('./.eslint/rules/enforce-array-buffer'),
|
||||
'file-suffix': require('./.eslint/rules/file-suffix'),
|
||||
};
|
||||
|
||||
+13
-12
@@ -64,7 +64,7 @@
|
||||
"format": "pprettier --write '**/*.{ts,tsx,d.ts,js,json,html,scss,md,yml,yaml}' '!node_modules/**'",
|
||||
"svgo": "svgo --multipass images/**/*.svg images/*.svg",
|
||||
"transpile": "run-p check:types build:esbuild",
|
||||
"check:types": "tsc --noEmit",
|
||||
"check:types": "tsgo --noEmit",
|
||||
"clean-transpile": "node ./scripts/clean-transpile.js",
|
||||
"ready": "npm-run-all --print-label clean-transpile generate --parallel lint lint-deps lint-intl test-node test-electron",
|
||||
"dev": "pnpm run build:protobuf && cross-env SIGNAL_ENV=storybook storybook dev --port 6006",
|
||||
@@ -104,8 +104,6 @@
|
||||
"build:emoji": "run-p build:emoji:32 build:emoji:64",
|
||||
"build:emoji:32": "cwebp -progress -mt -preset icon -alpha_filter best -alpha_q 20 -pass 10 -q 75 ./node_modules/emoji-datasource-apple/img/apple/sheets/32.png -o ./images/emoji-sheet-32.webp",
|
||||
"build:emoji:64": "cwebp -progress -mt -preset icon -alpha_filter best -alpha_q 20 -pass 10 -q 75 ./node_modules/emoji-datasource-apple/img/apple/sheets/64.png -o ./images/emoji-sheet-64.webp",
|
||||
"verify": "run-p --print-label verify:*",
|
||||
"verify:ts": "tsc --noEmit",
|
||||
"electron:install-app-deps": "electron-builder install-app-deps",
|
||||
"check-upgradeable-deps": "ts-node ts/scripts/check-upgradeable-deps.node.ts",
|
||||
"react-devtools": "react-devtools",
|
||||
@@ -121,7 +119,7 @@
|
||||
"@formatjs/intl-localematcher": "0.2.32",
|
||||
"@indutny/dicer": "0.3.2",
|
||||
"@indutny/mac-screen-share": "1.0.13",
|
||||
"@indutny/protopiler": "3.2.0",
|
||||
"@indutny/protopiler": "3.2.1",
|
||||
"@indutny/range-finder": "1.3.4",
|
||||
"@indutny/simple-windows-notifications": "2.0.16",
|
||||
"@indutny/sneequals": "4.0.0",
|
||||
@@ -133,16 +131,17 @@
|
||||
"@react-aria/utils": "3.25.3",
|
||||
"@react-spring/web": "10.0.3",
|
||||
"@react-types/shared": "3.27.0",
|
||||
"@signalapp/libsignal-client": "0.88.3",
|
||||
"@signalapp/libsignal-client": "0.89.0",
|
||||
"@signalapp/minimask": "1.0.1",
|
||||
"@signalapp/mute-state-change": "workspace:1.0.0",
|
||||
"@signalapp/quill-cjs": "2.1.2",
|
||||
"@signalapp/ringrtc": "2.67.0",
|
||||
"@signalapp/sqlcipher": "2.4.4",
|
||||
"@signalapp/sqlcipher": "3.2.1",
|
||||
"@signalapp/windows-ucv": "1.0.1",
|
||||
"@tanstack/react-virtual": "3.11.2",
|
||||
"@types/dom-mediacapture-transform": "0.1.11",
|
||||
"@types/fabric": "4.5.3",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260303.1",
|
||||
"blob-util": "2.0.2",
|
||||
"blueimp-load-image": "5.16.0",
|
||||
"blurhash": "2.0.5",
|
||||
@@ -237,7 +236,7 @@
|
||||
"@indutny/parallel-prettier": "3.0.0",
|
||||
"@indutny/rezip-electron": "3.0.2",
|
||||
"@napi-rs/canvas": "0.1.61",
|
||||
"@signalapp/mock-server": "18.2.0",
|
||||
"@signalapp/mock-server": "18.3.0",
|
||||
"@storybook/addon-a11y": "8.4.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
"@storybook/addon-controls": "8.4.4",
|
||||
@@ -273,7 +272,7 @@
|
||||
"@types/lodash": "4.14.106",
|
||||
"@types/memoizee": "0.4.11",
|
||||
"@types/mocha": "10.0.9",
|
||||
"@types/node": "20.17.6",
|
||||
"@types/node": "24.12.0",
|
||||
"@types/node-fetch": "2.6.12",
|
||||
"@types/normalize-path": "3.0.2",
|
||||
"@types/parsecurrency": "1.0.2",
|
||||
@@ -336,7 +335,7 @@
|
||||
"npm-run-all": "4.1.5",
|
||||
"p-limit": "3.1.0",
|
||||
"pixelmatch": "5.3.0",
|
||||
"playwright": "1.54.2",
|
||||
"playwright": "1.58.2",
|
||||
"pngjs": "7.0.0",
|
||||
"postcss": "8.5.3",
|
||||
"postcss-loader": "8.1.1",
|
||||
@@ -360,7 +359,7 @@
|
||||
"tailwindcss": "4.1.7",
|
||||
"terser-webpack-plugin": "5.3.10",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.6.3",
|
||||
"typescript": "5.9.3",
|
||||
"wait-on": "8.0.1",
|
||||
"webpack": "5.96.1",
|
||||
"webpack-cli": "5.1.4",
|
||||
@@ -382,7 +381,7 @@
|
||||
"@types/express@4.17.21": "patches/@types+express+4.17.21.patch",
|
||||
"@types/fabric@4.5.3": "patches/@types+fabric+4.5.3.patch",
|
||||
"qrcode-generator@1.4.4": "patches/qrcode-generator+1.4.4.patch",
|
||||
"@types/node-fetch@2.6.12": "patches/@types+node-fetch+2.6.12.patch",
|
||||
"@types/node-fetch@2.6.12": "patches/@types__node-fetch@2.6.12.patch",
|
||||
"fabric@4.6.0": "patches/fabric+4.6.0.patch",
|
||||
"@vitest/expect@2.0.5": "patches/@vitest+expect+2.0.5.patch",
|
||||
"got@11.8.5": "patches/got+11.8.5.patch",
|
||||
@@ -394,7 +393,9 @@
|
||||
"eslint-plugin-better-tailwindcss": "patches/eslint-plugin-better-tailwindcss.patch",
|
||||
"framer-motion@6.5.1": "patches/framer-motion@6.5.1.patch",
|
||||
"fs-xattr": "patches/fs-xattr.patch",
|
||||
"@types/chai": "patches/@types__chai.patch"
|
||||
"@types/chai": "patches/@types__chai.patch",
|
||||
"@napi-rs/canvas@0.1.61": "patches/@napi-rs__canvas@0.1.61.patch",
|
||||
"playwright-core@1.58.2": "patches/playwright-core@1.58.2.patch"
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"@indutny/mac-screen-share",
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
diff --git a/index.d.ts b/index.d.ts
|
||||
index 37f233281dac070b5b64ad810d766dce269e095e..3698e98b27ddf4849097811e1e8b25df955fb9b9 100644
|
||||
--- a/index.d.ts
|
||||
+++ b/index.d.ts
|
||||
@@ -195,7 +195,7 @@ export class Image {
|
||||
readonly naturalHeight: number
|
||||
readonly complete: boolean
|
||||
alt: string
|
||||
- src: Buffer
|
||||
+ src: Buffer<ArrayBuffer>
|
||||
onload?(): void
|
||||
onerror?(err: Error): void
|
||||
}
|
||||
@@ -296,7 +296,7 @@ export interface SvgCanvas {
|
||||
height: number
|
||||
getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D
|
||||
|
||||
- getContent(): Buffer
|
||||
+ getContent(): Buffer<ArrayBuffer>
|
||||
}
|
||||
|
||||
export interface AvifConfig {
|
||||
@@ -360,18 +360,18 @@ export class Canvas {
|
||||
width: number
|
||||
height: number
|
||||
getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D
|
||||
- encodeSync(format: 'webp' | 'jpeg', quality?: number): Buffer
|
||||
- encodeSync(format: 'png'): Buffer
|
||||
- encodeSync(format: 'avif', cfg?: AvifConfig): Buffer
|
||||
- encode(format: 'webp' | 'jpeg', quality?: number): Promise<Buffer>
|
||||
- encode(format: 'png'): Promise<Buffer>
|
||||
- encode(format: 'avif', cfg?: AvifConfig): Promise<Buffer>
|
||||
-
|
||||
- toBuffer(mime: 'image/png'): Buffer
|
||||
- toBuffer(mime: 'image/jpeg' | 'image/webp', quality?: number): Buffer
|
||||
- toBuffer(mime: 'image/avif', cfg?: AvifConfig): Buffer
|
||||
+ encodeSync(format: 'webp' | 'jpeg', quality?: number): Buffer<ArrayBuffer>
|
||||
+ encodeSync(format: 'png'): Buffer<ArrayBuffer>
|
||||
+ encodeSync(format: 'avif', cfg?: AvifConfig): Buffer<ArrayBuffer>
|
||||
+ encode(format: 'webp' | 'jpeg', quality?: number): Promise<Buffer<ArrayBuffer>>
|
||||
+ encode(format: 'png'): Promise<Buffer<ArrayBuffer>>
|
||||
+ encode(format: 'avif', cfg?: AvifConfig): Promise<Buffer<ArrayBuffer>>
|
||||
+
|
||||
+ toBuffer(mime: 'image/png'): Buffer<ArrayBuffer>
|
||||
+ toBuffer(mime: 'image/jpeg' | 'image/webp', quality?: number): Buffer<ArrayBuffer>
|
||||
+ toBuffer(mime: 'image/avif', cfg?: AvifConfig): Buffer<ArrayBuffer>
|
||||
// raw pixels
|
||||
- data(): Buffer
|
||||
+ data(): Buffer<ArrayBuffer>
|
||||
toDataURL(mime?: 'image/png'): string
|
||||
toDataURL(mime: 'image/jpeg' | 'image/webp', quality?: number): string
|
||||
toDataURL(mime?: 'image/jpeg' | 'image/webp' | 'image/png', quality?: number): string
|
||||
@@ -439,7 +439,7 @@ export enum SvgExportFlag {
|
||||
RelativePathEncoding = 0x04,
|
||||
}
|
||||
|
||||
-export function convertSVGTextToPath(svg: Buffer | string): Buffer
|
||||
+export function convertSVGTextToPath(svg: Buffer | string): Buffer<ArrayBuffer>
|
||||
|
||||
export interface LoadImageOptions {
|
||||
alt?: string
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/index.d.ts b/index.d.ts
|
||||
index f95f9c1..72b3814 100644
|
||||
index f95f9c1747a4cc6229b29ab450a859277ad3c16e..6650687fe938868555bb7ec2dbc67ea4a219a0e9 100644
|
||||
--- a/index.d.ts
|
||||
+++ b/index.d.ts
|
||||
@@ -3,7 +3,8 @@
|
||||
@@ -12,3 +12,12 @@ index f95f9c1..72b3814 100644
|
||||
|
||||
declare class Request extends Body {
|
||||
constructor(input: RequestInfo, init?: RequestInit);
|
||||
@@ -130,7 +131,7 @@ declare class Body {
|
||||
blob(): Promise<Blob>;
|
||||
body: NodeJS.ReadableStream;
|
||||
bodyUsed: boolean;
|
||||
- buffer(): Promise<Buffer>;
|
||||
+ buffer(): Promise<Buffer<ArrayBuffer>>;
|
||||
json(): Promise<any>;
|
||||
size: number;
|
||||
text(): Promise<string>;
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/types/types.d.ts b/types/types.d.ts
|
||||
index f6e6165885f0ae32b9d130797c77d5da04fba8cf..961c42536324ae50f48a8fbd0259228869a5487a 100644
|
||||
--- a/types/types.d.ts
|
||||
+++ b/types/types.d.ts
|
||||
@@ -4101,7 +4101,7 @@ export interface Page {
|
||||
* Returns the buffer with the captured screenshot.
|
||||
* @param options
|
||||
*/
|
||||
- screenshot(options?: PageScreenshotOptions): Promise<Buffer>;
|
||||
+ screenshot(options?: PageScreenshotOptions): Promise<Buffer<ArrayBuffer>>;
|
||||
|
||||
/**
|
||||
* **NOTE** Use locator-based
|
||||
Generated
+324
-284
File diff suppressed because it is too large
Load Diff
+32
-30
@@ -64,19 +64,19 @@ const log = createLogger('AttachmentCrypto');
|
||||
// This file was split from ts/Crypto.ts because it pulls things in from node, and
|
||||
// too many things pull in Crypto.ts, so it broke storybook.
|
||||
|
||||
export function _generateAttachmentIv(): Uint8Array {
|
||||
export function _generateAttachmentIv(): Uint8Array<ArrayBuffer> {
|
||||
return randomBytes(IV_LENGTH);
|
||||
}
|
||||
|
||||
export function generateAttachmentKeys(): Uint8Array {
|
||||
export function generateAttachmentKeys(): Uint8Array<ArrayBuffer> {
|
||||
return randomBytes(KEY_SET_LENGTH);
|
||||
}
|
||||
|
||||
export type EncryptedAttachmentV2 = {
|
||||
chunkSize: number | undefined;
|
||||
digest: Uint8Array;
|
||||
incrementalMac: Uint8Array | undefined;
|
||||
iv: Uint8Array;
|
||||
digest: Uint8Array<ArrayBuffer>;
|
||||
incrementalMac: Uint8Array<ArrayBuffer> | undefined;
|
||||
iv: Uint8Array<ArrayBuffer>;
|
||||
plaintextHash: string;
|
||||
ciphertextSize: number;
|
||||
};
|
||||
@@ -97,14 +97,14 @@ export type DecryptedAttachmentV2 = {
|
||||
};
|
||||
|
||||
export type PlaintextSourceType =
|
||||
| { data: Uint8Array }
|
||||
| { data: Uint8Array<ArrayBuffer> }
|
||||
| { stream: Readable; size?: number }
|
||||
| { absolutePath: string };
|
||||
|
||||
type EncryptAttachmentV2OptionsType = Readonly<{
|
||||
_testOnlyDangerousIv?: Uint8Array;
|
||||
_testOnlyDangerousIv?: Uint8Array<ArrayBuffer>;
|
||||
_testOnlyDangerousSkipPadding?: boolean;
|
||||
keys: Readonly<Uint8Array>;
|
||||
keys: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
needIncrementalMac: boolean;
|
||||
plaintext: PlaintextSourceType;
|
||||
}>;
|
||||
@@ -170,7 +170,7 @@ export async function encryptAttachmentV2({
|
||||
const digest = createHash(HashType.size256);
|
||||
|
||||
let ciphertextSize: number | undefined;
|
||||
let mac: Uint8Array | undefined;
|
||||
let mac: Uint8Array<ArrayBuffer> | undefined;
|
||||
let incrementalDigestCreator: DigestingPassThrough | undefined;
|
||||
let chunkSizeChoice: ChunkSizeChoice | undefined;
|
||||
|
||||
@@ -274,16 +274,16 @@ export async function encryptAttachmentV2({
|
||||
}
|
||||
|
||||
export type IntegrityCheckType =
|
||||
| { type: 'plaintext'; plaintextHash: Readonly<Uint8Array> }
|
||||
| { type: 'encrypted'; digest: Readonly<Uint8Array> };
|
||||
| { type: 'plaintext'; plaintextHash: Readonly<Uint8Array<ArrayBuffer>> }
|
||||
| { type: 'encrypted'; digest: Readonly<Uint8Array<ArrayBuffer>> };
|
||||
|
||||
export type DecryptAttachmentToSinkOptionsType = Readonly<
|
||||
{
|
||||
idForLogging: string;
|
||||
size: number;
|
||||
outerEncryption?: {
|
||||
aesKey: Readonly<Uint8Array>;
|
||||
macKey: Readonly<Uint8Array>;
|
||||
aesKey: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
macKey: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
};
|
||||
} & (
|
||||
| {
|
||||
@@ -296,7 +296,7 @@ export type DecryptAttachmentToSinkOptionsType = Readonly<
|
||||
(
|
||||
| {
|
||||
type: 'standard';
|
||||
theirIncrementalMac: Readonly<Uint8Array> | undefined;
|
||||
theirIncrementalMac: Readonly<Uint8Array<ArrayBuffer>> | undefined;
|
||||
theirChunkSize: number | undefined;
|
||||
integrityCheck: IntegrityCheckType;
|
||||
}
|
||||
@@ -308,8 +308,8 @@ export type DecryptAttachmentToSinkOptionsType = Readonly<
|
||||
) &
|
||||
(
|
||||
| {
|
||||
aesKey: Readonly<Uint8Array>;
|
||||
macKey: Readonly<Uint8Array>;
|
||||
aesKey: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
macKey: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
}
|
||||
| {
|
||||
// The format used by most stored attachments
|
||||
@@ -369,8 +369,8 @@ export async function decryptAttachmentV2ToSink(
|
||||
): Promise<Omit<DecryptedAttachmentV2, 'path'>> {
|
||||
const { idForLogging, outerEncryption } = options;
|
||||
|
||||
let aesKey: Uint8Array;
|
||||
let macKey: Uint8Array;
|
||||
let aesKey: Uint8Array<ArrayBuffer>;
|
||||
let macKey: Uint8Array<ArrayBuffer>;
|
||||
|
||||
if ('aesKey' in options) {
|
||||
({ aesKey, macKey } = options);
|
||||
@@ -387,11 +387,11 @@ export async function decryptAttachmentV2ToSink(
|
||||
const hmac = createHmac(HashType.size256, macKey);
|
||||
const plaintextHash = createHash(HashType.size256);
|
||||
|
||||
let theirMac: Uint8Array | undefined;
|
||||
let theirMac: Uint8Array<ArrayBuffer> | undefined;
|
||||
|
||||
// When downloading from backup there is an outer encryption layer; in that case we
|
||||
// need to decrypt the outer layer and check its MAC
|
||||
let theirOuterMac: Uint8Array | undefined;
|
||||
let theirOuterMac: Uint8Array<ArrayBuffer> | undefined;
|
||||
const outerHmac = outerEncryption
|
||||
? createHmac(HashType.size256, outerEncryption.macKey)
|
||||
: undefined;
|
||||
@@ -407,8 +407,8 @@ export async function decryptAttachmentV2ToSink(
|
||||
: undefined;
|
||||
|
||||
let readFd: FileHandle | undefined;
|
||||
let ourPlaintextHash: Uint8Array | undefined;
|
||||
let ourDigest: Uint8Array | undefined;
|
||||
let ourPlaintextHash: Uint8Array<ArrayBuffer> | undefined;
|
||||
let ourDigest: Uint8Array<ArrayBuffer> | undefined;
|
||||
let ciphertextStream: Readable;
|
||||
|
||||
try {
|
||||
@@ -616,10 +616,12 @@ export async function decryptAndReencryptLocally(
|
||||
*/
|
||||
|
||||
type AttachmentEncryptionKeysType = {
|
||||
aesKey: Uint8Array;
|
||||
macKey: Uint8Array;
|
||||
aesKey: Uint8Array<ArrayBuffer>;
|
||||
macKey: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
export function splitKeys(keys: Uint8Array): AttachmentEncryptionKeysType {
|
||||
export function splitKeys(
|
||||
keys: Uint8Array<ArrayBuffer>
|
||||
): AttachmentEncryptionKeysType {
|
||||
strictAssert(
|
||||
keys.byteLength === KEY_SET_LENGTH,
|
||||
`attachment keys must be ${KEY_SET_LENGTH} bytes, got ${keys.byteLength}`
|
||||
@@ -629,7 +631,7 @@ export function splitKeys(keys: Uint8Array): AttachmentEncryptionKeysType {
|
||||
return { aesKey, macKey };
|
||||
}
|
||||
|
||||
export function generateKeys(): Uint8Array {
|
||||
export function generateKeys(): Uint8Array<ArrayBuffer> {
|
||||
return randomBytes(KEY_SET_LENGTH);
|
||||
}
|
||||
|
||||
@@ -679,8 +681,8 @@ function checkIntegrity({
|
||||
integrityCheck,
|
||||
logId,
|
||||
}: {
|
||||
locallyCalculatedDigest: Uint8Array;
|
||||
locallyCalculatedPlaintextHash: Uint8Array;
|
||||
locallyCalculatedDigest: Uint8Array<ArrayBuffer>;
|
||||
locallyCalculatedPlaintextHash: Uint8Array<ArrayBuffer>;
|
||||
integrityCheck: IntegrityCheckType;
|
||||
logId: string;
|
||||
}): void {
|
||||
@@ -709,7 +711,7 @@ function checkIntegrity({
|
||||
/**
|
||||
* Prepends the iv to the stream.
|
||||
*/
|
||||
function prependIv(iv: Uint8Array) {
|
||||
function prependIv(iv: Uint8Array<ArrayBuffer>) {
|
||||
strictAssert(
|
||||
iv.byteLength === IV_LENGTH,
|
||||
`prependIv: iv should be ${IV_LENGTH} bytes, got ${iv.byteLength} bytes`
|
||||
@@ -718,7 +720,7 @@ function prependIv(iv: Uint8Array) {
|
||||
}
|
||||
|
||||
export function getPlaintextHashForInMemoryAttachment(
|
||||
data: Uint8Array
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): string {
|
||||
return createHash(HashType.size256).update(data).digest('hex');
|
||||
}
|
||||
|
||||
+27
-17
@@ -5,44 +5,44 @@ import { Bytes } from './context/Bytes.std.js';
|
||||
|
||||
const bytes = globalThis.window?.SignalContext?.bytes || new Bytes();
|
||||
|
||||
export function fromBase64(value: string): Uint8Array {
|
||||
export function fromBase64(value: string): Uint8Array<ArrayBuffer> {
|
||||
return bytes.fromBase64(value);
|
||||
}
|
||||
export function fromBase64url(value: string): Uint8Array {
|
||||
export function fromBase64url(value: string): Uint8Array<ArrayBuffer> {
|
||||
return bytes.fromBase64url(value);
|
||||
}
|
||||
|
||||
export function fromHex(value: string): Uint8Array {
|
||||
export function fromHex(value: string): Uint8Array<ArrayBuffer> {
|
||||
return bytes.fromHex(value);
|
||||
}
|
||||
|
||||
// TODO(indutny): deprecate it
|
||||
export function fromBinary(value: string): Uint8Array {
|
||||
export function fromBinary(value: string): Uint8Array<ArrayBuffer> {
|
||||
return bytes.fromBinary(value);
|
||||
}
|
||||
|
||||
export function fromString(value: string): Uint8Array {
|
||||
export function fromString(value: string): Uint8Array<ArrayBuffer> {
|
||||
return bytes.fromString(value);
|
||||
}
|
||||
|
||||
export function toBase64(data: Uint8Array): string {
|
||||
export function toBase64(data: Uint8Array<ArrayBuffer>): string {
|
||||
return bytes.toBase64(data);
|
||||
}
|
||||
|
||||
export function toBase64url(data: Uint8Array): string {
|
||||
export function toBase64url(data: Uint8Array<ArrayBuffer>): string {
|
||||
return bytes.toBase64url(data);
|
||||
}
|
||||
|
||||
export function toHex(data: Uint8Array): string {
|
||||
export function toHex(data: Uint8Array<ArrayBuffer>): string {
|
||||
return bytes.toHex(data);
|
||||
}
|
||||
|
||||
// TODO(indutny): deprecate it
|
||||
export function toBinary(data: Uint8Array): string {
|
||||
export function toBinary(data: Uint8Array<ArrayBuffer>): string {
|
||||
return bytes.toBinary(data);
|
||||
}
|
||||
|
||||
export function toString(data: Uint8Array): string {
|
||||
export function toString(data: Uint8Array<ArrayBuffer>): string {
|
||||
return bytes.toString(data);
|
||||
}
|
||||
|
||||
@@ -50,27 +50,37 @@ export function byteLength(value: string): number {
|
||||
return bytes.byteLength(value);
|
||||
}
|
||||
|
||||
export function concatenate(list: ReadonlyArray<Uint8Array>): Uint8Array {
|
||||
export function concatenate(
|
||||
list: ReadonlyArray<Uint8Array<ArrayBuffer>>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return bytes.concatenate(list);
|
||||
}
|
||||
|
||||
export function isEmpty(data: Uint8Array | null | undefined): boolean {
|
||||
export function isEmpty(
|
||||
data: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): boolean {
|
||||
return bytes.isEmpty(data);
|
||||
}
|
||||
|
||||
export function isNotEmpty(
|
||||
data: Uint8Array | null | undefined
|
||||
): data is Uint8Array {
|
||||
data: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): data is Uint8Array<ArrayBuffer> {
|
||||
return !bytes.isEmpty(data);
|
||||
}
|
||||
|
||||
export function areEqual(
|
||||
a: Uint8Array | null | undefined,
|
||||
b: Uint8Array | null | undefined
|
||||
a: Uint8Array<ArrayBuffer> | null | undefined,
|
||||
b: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): boolean {
|
||||
return bytes.areEqual(a, b);
|
||||
}
|
||||
|
||||
export function readBigUint64BE(value: Uint8Array): bigint {
|
||||
export function readBigUint64BE(value: Uint8Array<ArrayBuffer>): bigint {
|
||||
return bytes.readBigUint64BE(value);
|
||||
}
|
||||
|
||||
export function isNonSharedUint8Array(
|
||||
value: unknown
|
||||
): value is Uint8Array<ArrayBuffer> {
|
||||
return value instanceof Uint8Array && value.buffer instanceof ArrayBuffer;
|
||||
}
|
||||
|
||||
+123
-90
@@ -36,8 +36,8 @@ export const PaddedLengths = {
|
||||
};
|
||||
|
||||
export type EncryptedAttachment = {
|
||||
ciphertext: Uint8Array;
|
||||
digest: Uint8Array;
|
||||
ciphertext: Uint8Array<ArrayBuffer>;
|
||||
digest: Uint8Array<ArrayBuffer>;
|
||||
plaintextHash: string;
|
||||
};
|
||||
|
||||
@@ -45,7 +45,9 @@ export function generateRegistrationId(): number {
|
||||
return randomInt(1, 16383);
|
||||
}
|
||||
|
||||
export function deriveStickerPackKey(packKey: Uint8Array): Uint8Array {
|
||||
export function deriveStickerPackKey(
|
||||
packKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const salt = getZeroes(32);
|
||||
const info = Bytes.fromString('Sticker Pack');
|
||||
|
||||
@@ -55,10 +57,10 @@ export function deriveStickerPackKey(packKey: Uint8Array): Uint8Array {
|
||||
}
|
||||
|
||||
export function deriveSecrets(
|
||||
input: Uint8Array,
|
||||
salt: Uint8Array,
|
||||
info: Uint8Array
|
||||
): [Uint8Array, Uint8Array, Uint8Array] {
|
||||
input: Uint8Array<ArrayBuffer>,
|
||||
salt: Uint8Array<ArrayBuffer>,
|
||||
info: Uint8Array<ArrayBuffer>
|
||||
): [Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>] {
|
||||
const output = hkdf(3 * 32, input, info, salt);
|
||||
return [
|
||||
output.subarray(0, 32),
|
||||
@@ -67,7 +69,9 @@ export function deriveSecrets(
|
||||
];
|
||||
}
|
||||
|
||||
export function deriveMasterKeyFromGroupV1(groupV1Id: Uint8Array): Uint8Array {
|
||||
export function deriveMasterKeyFromGroupV1(
|
||||
groupV1Id: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const salt = getZeroes(32);
|
||||
const info = Bytes.fromString('GV2 Migration');
|
||||
|
||||
@@ -96,7 +100,7 @@ export function hashProfileKey(
|
||||
return webSafe.slice(-3);
|
||||
}
|
||||
|
||||
export function computeHash(data: Uint8Array): string {
|
||||
export function computeHash(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Bytes.toBase64(hash(HashType.size512, data));
|
||||
}
|
||||
|
||||
@@ -104,8 +108,8 @@ export function computeHash(data: Uint8Array): string {
|
||||
|
||||
export type EncryptedDeviceName = {
|
||||
ephemeralPublic: PublicKey;
|
||||
syntheticIv: Uint8Array;
|
||||
ciphertext: Uint8Array;
|
||||
syntheticIv: Uint8Array<ArrayBuffer>;
|
||||
ciphertext: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
|
||||
export function encryptDeviceName(
|
||||
@@ -163,7 +167,7 @@ export function encryptDeviceCreatedAt(
|
||||
deviceId: number,
|
||||
registrationId: number,
|
||||
identityPublic: PublicKey
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const createdAtBuffer = new ArrayBuffer(8);
|
||||
const dataView = new DataView(createdAtBuffer);
|
||||
dataView.setBigUint64(0, BigInt(createdAt), false);
|
||||
@@ -180,7 +184,7 @@ export function encryptDeviceCreatedAt(
|
||||
// createdAtCiphertext is an Int64, encrypted using the identity key
|
||||
// PrivateKey with 5 bytes of associated data (deviceId || registrationId).
|
||||
export function decryptDeviceCreatedAt(
|
||||
createdAtCiphertext: Uint8Array,
|
||||
createdAtCiphertext: Uint8Array<ArrayBuffer>,
|
||||
deviceId: number,
|
||||
registrationId: number,
|
||||
identityPrivate: PrivateKey
|
||||
@@ -200,7 +204,7 @@ export function decryptDeviceCreatedAt(
|
||||
function getAssociatedDataForDeviceCreatedAt(
|
||||
deviceId: number,
|
||||
registrationId: number
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
if (deviceId > 255) {
|
||||
throw new Error('deviceId above 255, must be 1 byte');
|
||||
}
|
||||
@@ -212,18 +216,22 @@ function getAssociatedDataForDeviceCreatedAt(
|
||||
return new Uint8Array(associatedDataBuffer);
|
||||
}
|
||||
|
||||
export function deriveMasterKey(accountEntropyPool: string): Uint8Array {
|
||||
export function deriveMasterKey(
|
||||
accountEntropyPool: string
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return AccountEntropyPool.deriveSvrKey(accountEntropyPool);
|
||||
}
|
||||
|
||||
export function deriveStorageServiceKey(masterKey: Uint8Array): Uint8Array {
|
||||
export function deriveStorageServiceKey(
|
||||
masterKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return hmacSha256(masterKey, Bytes.fromString('Storage Service Encryption'));
|
||||
}
|
||||
|
||||
export function deriveStorageManifestKey(
|
||||
storageServiceKey: Uint8Array,
|
||||
storageServiceKey: Uint8Array<ArrayBuffer>,
|
||||
version = 0n
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return hmacSha256(storageServiceKey, Bytes.fromString(`Manifest_${version}`));
|
||||
}
|
||||
|
||||
@@ -232,16 +240,16 @@ const STORAGE_SERVICE_ITEM_KEY_INFO_PREFIX =
|
||||
const STORAGE_SERVICE_ITEM_KEY_LEN = 32;
|
||||
|
||||
export type DeriveStorageItemKeyOptionsType = Readonly<{
|
||||
storageServiceKey: Uint8Array;
|
||||
recordIkm: Uint8Array | undefined;
|
||||
key: Uint8Array;
|
||||
storageServiceKey: Uint8Array<ArrayBuffer>;
|
||||
recordIkm: Uint8Array<ArrayBuffer> | undefined;
|
||||
key: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
export function deriveStorageItemKey({
|
||||
storageServiceKey,
|
||||
recordIkm,
|
||||
key,
|
||||
}: DeriveStorageItemKeyOptionsType): Uint8Array {
|
||||
}: DeriveStorageItemKeyOptionsType): Uint8Array<ArrayBuffer> {
|
||||
if (recordIkm == null) {
|
||||
const itemID = Bytes.toBase64(key);
|
||||
return hmacSha256(storageServiceKey, Bytes.fromString(`Item_${itemID}`));
|
||||
@@ -258,15 +266,17 @@ export function deriveStorageItemKey({
|
||||
);
|
||||
}
|
||||
|
||||
export function getAccessKeyVerifier(accessKey: Uint8Array): Uint8Array {
|
||||
export function getAccessKeyVerifier(
|
||||
accessKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const plaintext = getZeroes(32);
|
||||
|
||||
return hmacSha256(accessKey, plaintext);
|
||||
}
|
||||
|
||||
export function verifyAccessKey(
|
||||
accessKey: Uint8Array,
|
||||
theirVerifier: Uint8Array
|
||||
accessKey: Uint8Array<ArrayBuffer>,
|
||||
theirVerifier: Uint8Array<ArrayBuffer>
|
||||
): boolean {
|
||||
const ourVerifier = getAccessKeyVerifier(accessKey);
|
||||
|
||||
@@ -282,9 +292,9 @@ const NONCE_LENGTH = 16;
|
||||
const SYMMETRIC_MAC_LENGTH = 16;
|
||||
|
||||
export function encryptSymmetric(
|
||||
key: Uint8Array,
|
||||
plaintext: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
plaintext: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const iv = getZeroes(IV_LENGTH);
|
||||
const nonce = getRandomBytes(NONCE_LENGTH);
|
||||
|
||||
@@ -301,9 +311,9 @@ export function encryptSymmetric(
|
||||
}
|
||||
|
||||
export function decryptSymmetric(
|
||||
key: Uint8Array,
|
||||
data: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const iv = getZeroes(IV_LENGTH);
|
||||
|
||||
const nonce = getFirstBytes(data, NONCE_LENGTH);
|
||||
@@ -336,7 +346,10 @@ export function decryptSymmetric(
|
||||
|
||||
// Encryption
|
||||
|
||||
export function hmacSha256(key: Uint8Array, plaintext: Uint8Array): Uint8Array {
|
||||
export function hmacSha256(
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
plaintext: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return sign(key, plaintext);
|
||||
}
|
||||
|
||||
@@ -344,9 +357,9 @@ export function hmacSha256(key: Uint8Array, plaintext: Uint8Array): Uint8Array {
|
||||
// to be longer than the passed-in length. This allows easy comparisons against
|
||||
// arbitrary MAC lengths.
|
||||
export function verifyHmacSha256(
|
||||
plaintext: Uint8Array,
|
||||
key: Uint8Array,
|
||||
theirMac: Uint8Array,
|
||||
plaintext: Uint8Array<ArrayBuffer>,
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
theirMac: Uint8Array<ArrayBuffer>,
|
||||
length: number
|
||||
): void {
|
||||
const ourMac = hmacSha256(key, plaintext);
|
||||
@@ -366,10 +379,10 @@ export function verifyHmacSha256(
|
||||
}
|
||||
|
||||
export function encryptAes256CbcPkcsPadding(
|
||||
key: Uint8Array,
|
||||
plaintext: Uint8Array,
|
||||
iv: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
plaintext: Uint8Array<ArrayBuffer>,
|
||||
iv: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return encrypt(CipherType.AES256CBC, {
|
||||
key,
|
||||
plaintext,
|
||||
@@ -378,10 +391,10 @@ export function encryptAes256CbcPkcsPadding(
|
||||
}
|
||||
|
||||
export function decryptAes256CbcPkcsPadding(
|
||||
key: Uint8Array,
|
||||
ciphertext: Uint8Array,
|
||||
iv: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
ciphertext: Uint8Array<ArrayBuffer>,
|
||||
iv: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return decrypt(CipherType.AES256CBC, {
|
||||
key,
|
||||
ciphertext,
|
||||
@@ -390,10 +403,10 @@ export function decryptAes256CbcPkcsPadding(
|
||||
}
|
||||
|
||||
export function encryptAesCtr(
|
||||
key: Uint8Array,
|
||||
plaintext: Uint8Array,
|
||||
counter: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
plaintext: Uint8Array<ArrayBuffer>,
|
||||
counter: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return encrypt(CipherType.AES256CTR, {
|
||||
key,
|
||||
plaintext,
|
||||
@@ -402,10 +415,10 @@ export function encryptAesCtr(
|
||||
}
|
||||
|
||||
export function decryptAesCtr(
|
||||
key: Uint8Array,
|
||||
ciphertext: Uint8Array,
|
||||
counter: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
ciphertext: Uint8Array<ArrayBuffer>,
|
||||
counter: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return decrypt(CipherType.AES256CTR, {
|
||||
key,
|
||||
ciphertext,
|
||||
@@ -414,11 +427,11 @@ export function decryptAesCtr(
|
||||
}
|
||||
|
||||
export function encryptAesGcm(
|
||||
key: Uint8Array,
|
||||
iv: Uint8Array,
|
||||
plaintext: Uint8Array,
|
||||
aad?: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
iv: Uint8Array<ArrayBuffer>,
|
||||
plaintext: Uint8Array<ArrayBuffer>,
|
||||
aad?: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return encrypt(CipherType.AES256GCM, {
|
||||
key,
|
||||
plaintext,
|
||||
@@ -428,10 +441,10 @@ export function encryptAesGcm(
|
||||
}
|
||||
|
||||
export function decryptAesGcm(
|
||||
key: Uint8Array,
|
||||
iv: Uint8Array,
|
||||
ciphertext: Uint8Array
|
||||
): Uint8Array {
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
iv: Uint8Array<ArrayBuffer>,
|
||||
ciphertext: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return decrypt(CipherType.AES256GCM, {
|
||||
key,
|
||||
ciphertext,
|
||||
@@ -441,13 +454,13 @@ export function decryptAesGcm(
|
||||
|
||||
// Hashing
|
||||
|
||||
export function sha256(data: Uint8Array): Uint8Array {
|
||||
export function sha256(data: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
|
||||
return hash(HashType.size256, data);
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
||||
export function getZeroes(n: number): Uint8Array {
|
||||
export function getZeroes(n: number): Uint8Array<ArrayBuffer> {
|
||||
return new Uint8Array(n);
|
||||
}
|
||||
|
||||
@@ -464,11 +477,16 @@ export function intsToByteHighAndLow(
|
||||
return ((highValue << 4) | lowValue) & 0xff;
|
||||
}
|
||||
|
||||
export function getFirstBytes(data: Uint8Array, n: number): Uint8Array {
|
||||
export function getFirstBytes(
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
n: number
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return data.subarray(0, n);
|
||||
}
|
||||
|
||||
export function trimForDisplay(padded: Uint8Array): Uint8Array {
|
||||
export function trimForDisplay(
|
||||
padded: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
let paddingEnd = 0;
|
||||
for (paddingEnd; paddingEnd < padded.length; paddingEnd += 1) {
|
||||
if (padded[paddingEnd] === 0x00) {
|
||||
@@ -478,7 +496,10 @@ export function trimForDisplay(padded: Uint8Array): Uint8Array {
|
||||
return padded.subarray(0, paddingEnd);
|
||||
}
|
||||
|
||||
function verifyDigest(data: Uint8Array, theirDigest: Uint8Array): void {
|
||||
function verifyDigest(
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
theirDigest: Uint8Array<ArrayBuffer>
|
||||
): void {
|
||||
const ourDigest = sha256(data);
|
||||
let result = 0;
|
||||
for (let i = 0; i < theirDigest.byteLength; i += 1) {
|
||||
@@ -491,10 +512,10 @@ function verifyDigest(data: Uint8Array, theirDigest: Uint8Array): void {
|
||||
}
|
||||
|
||||
export function decryptAttachmentV1(
|
||||
encryptedBin: Uint8Array,
|
||||
keys: Uint8Array,
|
||||
theirDigest?: Uint8Array
|
||||
): Uint8Array {
|
||||
encryptedBin: Uint8Array<ArrayBuffer>,
|
||||
keys: Uint8Array<ArrayBuffer>,
|
||||
theirDigest?: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
if (keys.byteLength !== 64) {
|
||||
throw new Error('Got invalid length attachment keys');
|
||||
}
|
||||
@@ -530,9 +551,9 @@ export function encryptAttachment({
|
||||
keys,
|
||||
dangerousTestOnlyIv,
|
||||
}: {
|
||||
plaintext: Readonly<Uint8Array>;
|
||||
keys: Readonly<Uint8Array>;
|
||||
dangerousTestOnlyIv?: Readonly<Uint8Array>;
|
||||
plaintext: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
keys: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
dangerousTestOnlyIv?: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
}): Omit<EncryptedAttachment, 'plaintextHash'> {
|
||||
const logId = 'encryptAttachment';
|
||||
if (!(plaintext instanceof Uint8Array)) {
|
||||
@@ -573,9 +594,9 @@ export function padAndEncryptAttachment({
|
||||
keys,
|
||||
dangerousTestOnlyIv,
|
||||
}: {
|
||||
plaintext: Readonly<Uint8Array>;
|
||||
keys: Readonly<Uint8Array>;
|
||||
dangerousTestOnlyIv?: Readonly<Uint8Array>;
|
||||
plaintext: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
keys: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
dangerousTestOnlyIv?: Readonly<Uint8Array<ArrayBuffer>>;
|
||||
}): EncryptedAttachment {
|
||||
const size = plaintext.byteLength;
|
||||
const paddedSize = logPadSize(size);
|
||||
@@ -594,7 +615,10 @@ export function padAndEncryptAttachment({
|
||||
};
|
||||
}
|
||||
|
||||
export function encryptProfile(data: Uint8Array, key: Uint8Array): Uint8Array {
|
||||
export function encryptProfile(
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
key: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const iv = getRandomBytes(PROFILE_IV_LENGTH);
|
||||
if (key.byteLength !== PROFILE_KEY_LENGTH) {
|
||||
throw new Error('Got invalid length profile key');
|
||||
@@ -606,7 +630,10 @@ export function encryptProfile(data: Uint8Array, key: Uint8Array): Uint8Array {
|
||||
return Bytes.concatenate([iv, ciphertext]);
|
||||
}
|
||||
|
||||
export function decryptProfile(data: Uint8Array, key: Uint8Array): Uint8Array {
|
||||
export function decryptProfile(
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
key: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
if (data.byteLength < 12 + 16 + 1) {
|
||||
throw new Error(`Got too short input: ${data.byteLength}`);
|
||||
}
|
||||
@@ -630,10 +657,10 @@ export function decryptProfile(data: Uint8Array, key: Uint8Array): Uint8Array {
|
||||
}
|
||||
|
||||
export function encryptProfileItemWithPadding(
|
||||
item: Uint8Array,
|
||||
profileKey: Uint8Array,
|
||||
item: Uint8Array<ArrayBuffer>,
|
||||
profileKey: Uint8Array<ArrayBuffer>,
|
||||
paddedLengths: (typeof PaddedLengths)[keyof typeof PaddedLengths]
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const paddedLength = paddedLengths.find(
|
||||
(length: number) => item.byteLength <= length
|
||||
);
|
||||
@@ -647,8 +674,8 @@ export function encryptProfileItemWithPadding(
|
||||
|
||||
export function decryptProfileName(
|
||||
encryptedProfileName: string,
|
||||
key: Uint8Array
|
||||
): { given: Uint8Array; family: Uint8Array | null } {
|
||||
key: Uint8Array<ArrayBuffer>
|
||||
): { given: Uint8Array<ArrayBuffer>; family: Uint8Array<ArrayBuffer> | null } {
|
||||
const data = Bytes.fromBase64(encryptedProfileName);
|
||||
const padded = decryptProfile(data, key);
|
||||
|
||||
@@ -681,23 +708,29 @@ export function decryptProfileName(
|
||||
|
||||
const crypto = globalThis.window?.SignalContext.crypto || new Crypto();
|
||||
|
||||
export function sign(key: Uint8Array, data: Uint8Array): Uint8Array {
|
||||
export function sign(
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto.sign(key, data);
|
||||
}
|
||||
|
||||
export function hash(type: HashType, data: Uint8Array): Uint8Array {
|
||||
export function hash(
|
||||
type: HashType,
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto.hash(type, data);
|
||||
}
|
||||
|
||||
export function encrypt(
|
||||
...args: Parameters<typeof crypto.encrypt>
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto.encrypt(...args);
|
||||
}
|
||||
|
||||
export function decrypt(
|
||||
...args: Parameters<typeof crypto.decrypt>
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto.decrypt(...args);
|
||||
}
|
||||
|
||||
@@ -708,13 +741,13 @@ export function randomInt(min: number, max: number): number {
|
||||
return crypto.randomInt(min, max + 1);
|
||||
}
|
||||
|
||||
export function getRandomBytes(size: number): Uint8Array {
|
||||
export function getRandomBytes(size: number): Uint8Array<ArrayBuffer> {
|
||||
return crypto.getRandomBytes(size);
|
||||
}
|
||||
|
||||
export function constantTimeEqual(
|
||||
left: Uint8Array,
|
||||
right: Uint8Array
|
||||
left: Uint8Array<ArrayBuffer>,
|
||||
right: Uint8Array<ArrayBuffer>
|
||||
): boolean {
|
||||
return crypto.constantTimeEqual(left, right);
|
||||
}
|
||||
@@ -730,7 +763,7 @@ export function getIdentifierHash({
|
||||
pni: PniString | undefined;
|
||||
groupId: string | undefined;
|
||||
}): number | null {
|
||||
let identifier: Uint8Array;
|
||||
let identifier: Uint8Array<ArrayBuffer>;
|
||||
if (aci != null) {
|
||||
identifier = Aci.parseFromServiceIdString(aci).getServiceIdBinary();
|
||||
} else if (e164 != null) {
|
||||
|
||||
+16
-10
@@ -79,7 +79,9 @@ export function generateKeyPair(): KeyPairType {
|
||||
return new client.IdentityKeyPair(pubKey, privKey);
|
||||
}
|
||||
|
||||
export function createKeyPair(incomingKey: Uint8Array): KeyPairType {
|
||||
export function createKeyPair(
|
||||
incomingKey: Uint8Array<ArrayBuffer>
|
||||
): KeyPairType {
|
||||
const copy = new Uint8Array(incomingKey);
|
||||
clampPrivateKey(copy);
|
||||
if (!constantTimeEqual(copy, incomingKey)) {
|
||||
@@ -96,7 +98,9 @@ export function createKeyPair(incomingKey: Uint8Array): KeyPairType {
|
||||
return new client.IdentityKeyPair(pubKey, privKey);
|
||||
}
|
||||
|
||||
export function prefixPublicKey(pubKey: Uint8Array): Uint8Array {
|
||||
export function prefixPublicKey(
|
||||
pubKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return Bytes.concatenate([
|
||||
new Uint8Array([0x05]),
|
||||
validatePubKeyFormat(pubKey),
|
||||
@@ -106,26 +110,28 @@ export function prefixPublicKey(pubKey: Uint8Array): Uint8Array {
|
||||
export function calculateAgreement(
|
||||
pubKey: client.PublicKey,
|
||||
privKey: client.PrivateKey
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return privKey.agree(pubKey);
|
||||
}
|
||||
|
||||
export function verifySignature(
|
||||
pubKey: client.PublicKey,
|
||||
message: Uint8Array,
|
||||
signature: Uint8Array
|
||||
message: Uint8Array<ArrayBuffer>,
|
||||
signature: Uint8Array<ArrayBuffer>
|
||||
): boolean {
|
||||
return pubKey.verify(message, signature);
|
||||
}
|
||||
|
||||
export function calculateSignature(
|
||||
privKey: client.PrivateKey,
|
||||
plaintext: Uint8Array
|
||||
): Uint8Array {
|
||||
plaintext: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return privKey.sign(plaintext);
|
||||
}
|
||||
|
||||
function validatePubKeyFormat(pubKey: Uint8Array): Uint8Array {
|
||||
function validatePubKeyFormat(
|
||||
pubKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
if (
|
||||
pubKey === undefined ||
|
||||
((pubKey.byteLength !== 33 || pubKey[0] !== 5) && pubKey.byteLength !== 32)
|
||||
@@ -139,12 +145,12 @@ function validatePubKeyFormat(pubKey: Uint8Array): Uint8Array {
|
||||
return pubKey;
|
||||
}
|
||||
|
||||
export function setPublicKeyTypeByte(publicKey: Uint8Array): void {
|
||||
export function setPublicKeyTypeByte(publicKey: Uint8Array<ArrayBuffer>): void {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
publicKey[0] = 5;
|
||||
}
|
||||
|
||||
export function clampPrivateKey(privateKey: Uint8Array): void {
|
||||
export function clampPrivateKey(privateKey: Uint8Array<ArrayBuffer>): void {
|
||||
/* eslint-disable no-bitwise, no-param-reassign */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
privateKey[0]! &= 248;
|
||||
|
||||
@@ -332,21 +332,24 @@ export class SignedPreKeys extends SignedPreKeyStore {
|
||||
}
|
||||
|
||||
export class KeyTransparencyStore implements KeyTransparencyStoreInterface {
|
||||
async getLastDistinguishedTreeHead(): Promise<Uint8Array | null> {
|
||||
async getLastDistinguishedTreeHead(): Promise<Uint8Array<ArrayBuffer> | null> {
|
||||
return signalProtocolStore.getLastDistinguishedTreeHead();
|
||||
}
|
||||
|
||||
async setLastDistinguishedTreeHead(
|
||||
bytes: Readonly<Uint8Array> | null
|
||||
bytes: Readonly<Uint8Array<ArrayBuffer>> | null
|
||||
): Promise<void> {
|
||||
return signalProtocolStore.setLastDistinguishedTreeHead(bytes);
|
||||
}
|
||||
|
||||
async getAccountData(aci: Aci): Promise<Uint8Array | null> {
|
||||
async getAccountData(aci: Aci): Promise<Uint8Array<ArrayBuffer> | null> {
|
||||
return signalProtocolStore.getKTAccountData(aci);
|
||||
}
|
||||
|
||||
async setAccountData(aci: Aci, bytes: Readonly<Uint8Array>): Promise<void> {
|
||||
async setAccountData(
|
||||
aci: Aci,
|
||||
bytes: Readonly<Uint8Array<ArrayBuffer>>
|
||||
): Promise<void> {
|
||||
return signalProtocolStore.setKTAccountData(aci, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ export type SaveIdentityOptions = Readonly<{
|
||||
export type VerifyAlternateIdentityOptionsType = Readonly<{
|
||||
aci: AciString;
|
||||
pni: PniString;
|
||||
signature: Uint8Array;
|
||||
signature: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
export type SetVerifiedExtra = Readonly<{
|
||||
@@ -1933,7 +1933,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
// https://github.com/signalapp/Signal-Android/blob/fc3db538bcaa38dc149712a483d3032c9c1f3998/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalBaseIdentityKeyStore.java#L128
|
||||
async isTrustedIdentity(
|
||||
encodedAddress: Address,
|
||||
publicKey: Uint8Array,
|
||||
publicKey: Uint8Array<ArrayBuffer>,
|
||||
direction: number
|
||||
): Promise<boolean> {
|
||||
if (!this.identityKeys) {
|
||||
@@ -1978,7 +1978,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
// https://github.com/signalapp/Signal-Android/blob/fc3db538bcaa38dc149712a483d3032c9c1f3998/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalBaseIdentityKeyStore.java#L233
|
||||
isTrustedForSending(
|
||||
serviceId: ServiceIdString,
|
||||
publicKey: Uint8Array,
|
||||
publicKey: Uint8Array<ArrayBuffer>,
|
||||
identityRecord?: IdentityKeyType
|
||||
): boolean {
|
||||
if (!identityRecord) {
|
||||
@@ -2029,7 +2029,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
|
||||
async loadIdentityKey(
|
||||
serviceId: ServiceIdString
|
||||
): Promise<Uint8Array | undefined> {
|
||||
): Promise<Uint8Array<ArrayBuffer> | undefined> {
|
||||
if (serviceId == null) {
|
||||
throw new Error('loadIdentityKey: serviceId was undefined/null');
|
||||
}
|
||||
@@ -2078,7 +2078,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
// https://github.com/signalapp/Signal-Android/blob/fc3db538bcaa38dc149712a483d3032c9c1f3998/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalBaseIdentityKeyStore.java#L69
|
||||
async saveIdentity(
|
||||
encodedAddress: Address,
|
||||
publicKey: Uint8Array,
|
||||
publicKey: Uint8Array<ArrayBuffer>,
|
||||
nonblockingApproval = false,
|
||||
{ zone = GLOBAL_ZONE, noOverwrite = false }: SaveIdentityOptions = {}
|
||||
): Promise<IdentityChange> {
|
||||
@@ -2350,7 +2350,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
// the same!
|
||||
checkPreviousKey(
|
||||
serviceId: ServiceIdString,
|
||||
publicKey: Uint8Array,
|
||||
publicKey: Uint8Array<ArrayBuffer>,
|
||||
context: string
|
||||
): void {
|
||||
const conversation = window.ConversationController.get(serviceId);
|
||||
@@ -2383,7 +2383,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
async updateIdentityAfterSync(
|
||||
serviceId: ServiceIdString,
|
||||
verifiedStatus: number,
|
||||
publicKey: Uint8Array
|
||||
publicKey: Uint8Array<ArrayBuffer>
|
||||
): Promise<{ shouldAddVerifiedChangedMessage: boolean }> {
|
||||
strictAssert(
|
||||
validateVerifiedStatus(verifiedStatus),
|
||||
@@ -2798,12 +2798,12 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
|
||||
// Key Transparency
|
||||
|
||||
getLastDistinguishedTreeHead(): Uint8Array | null {
|
||||
getLastDistinguishedTreeHead(): Uint8Array<ArrayBuffer> | null {
|
||||
return itemStorage.get('lastDistinguishedTreeHead') ?? null;
|
||||
}
|
||||
|
||||
async setLastDistinguishedTreeHead(
|
||||
bytes: Readonly<Uint8Array> | null
|
||||
bytes: Readonly<Uint8Array<ArrayBuffer>> | null
|
||||
): Promise<void> {
|
||||
if (bytes == null) {
|
||||
await itemStorage.remove('lastDistinguishedTreeHead');
|
||||
@@ -2812,7 +2812,9 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async getKTAccountData(aciObject: Aci): Promise<Uint8Array | null> {
|
||||
async getKTAccountData(
|
||||
aciObject: Aci
|
||||
): Promise<Uint8Array<ArrayBuffer> | null> {
|
||||
const aci = fromAciObject(aciObject);
|
||||
const data = await DataReader.getKTAccountData(aci);
|
||||
return data ?? null;
|
||||
@@ -2820,7 +2822,7 @@ export class SignalProtocolStore extends EventEmitter {
|
||||
|
||||
async setKTAccountData(
|
||||
aciObject: Aci,
|
||||
bytes: Readonly<Uint8Array>
|
||||
bytes: Readonly<Uint8Array<ArrayBuffer>>
|
||||
): Promise<void> {
|
||||
const aci = fromAciObject(aciObject);
|
||||
return DataWriter.setKTAccountData(aci, bytes);
|
||||
|
||||
@@ -418,7 +418,7 @@ export const MAX_VIDEO_CAPTURE_BUFFER_SIZE = MAX_VIDEO_CAPTURE_AREA * 4;
|
||||
export class CanvasVideoRenderer {
|
||||
private canvas?: RefObject<HTMLCanvasElement | null>;
|
||||
private sizeCallback?: SizeCallbackType;
|
||||
private buffer: Uint8Array;
|
||||
private buffer: Uint8Array<ArrayBuffer>;
|
||||
private imageData?: ImageData;
|
||||
private source?: VideoFrameSource;
|
||||
private rafId?: ReturnType<typeof requestAnimationFrame>;
|
||||
|
||||
@@ -12,8 +12,8 @@ import { FRAME_BUFFER_SIZE } from './constants.std.js';
|
||||
* of allocating one per participant. Be careful when using this buffer elsewhere, as it
|
||||
* is not cleaned up and may hold stale data.
|
||||
*/
|
||||
export function useGetCallingFrameBuffer(): () => Uint8Array {
|
||||
const ref = useRef<Uint8Array | null>(null);
|
||||
export function useGetCallingFrameBuffer(): () => Uint8Array<ArrayBuffer> {
|
||||
const ref = useRef<Uint8Array<ArrayBuffer> | null>(null);
|
||||
|
||||
return useCallback(() => {
|
||||
if (!ref.current) {
|
||||
|
||||
@@ -29,14 +29,14 @@ const { isEqual } = lodash;
|
||||
export type PropsType = {
|
||||
avatarColor?: AvatarColorType;
|
||||
avatarUrl?: string;
|
||||
avatarValue?: Uint8Array;
|
||||
avatarValue?: Uint8Array<ArrayBuffer>;
|
||||
conversationId?: string;
|
||||
conversationTitle?: string;
|
||||
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
|
||||
i18n: LocalizerType;
|
||||
isGroup?: boolean;
|
||||
onCancel: () => unknown;
|
||||
onSave: (buffer: Uint8Array | undefined) => unknown;
|
||||
onSave: (buffer: Uint8Array<ArrayBuffer> | undefined) => unknown;
|
||||
userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||
replaceAvatar: ReplaceAvatarActionType;
|
||||
saveAvatarToDisk: SaveAvatarToDiskActionType;
|
||||
@@ -66,12 +66,12 @@ export function AvatarEditor({
|
||||
const [provisionalSelectedAvatar, setProvisionalSelectedAvatar] = useState<
|
||||
AvatarDataType | undefined
|
||||
>();
|
||||
const [avatarPreview, setAvatarPreview] = useState<Uint8Array | undefined>(
|
||||
avatarValue
|
||||
);
|
||||
const [initialAvatar, setInitialAvatar] = useState<Uint8Array | undefined>(
|
||||
avatarValue
|
||||
);
|
||||
const [avatarPreview, setAvatarPreview] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>(avatarValue);
|
||||
const [initialAvatar, setInitialAvatar] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>(avatarValue);
|
||||
const [localAvatarData, setLocalAvatarData] = useState<Array<AvatarDataType>>(
|
||||
userAvatarData.slice()
|
||||
);
|
||||
@@ -166,10 +166,13 @@ export function AvatarEditor({
|
||||
]
|
||||
);
|
||||
|
||||
const handleAvatarLoaded = useCallback((avatarBuffer: Uint8Array) => {
|
||||
setAvatarPreview(avatarBuffer);
|
||||
setInitialAvatar(avatarBuffer);
|
||||
}, []);
|
||||
const handleAvatarLoaded = useCallback(
|
||||
(avatarBuffer: Uint8Array<ArrayBuffer>) => {
|
||||
setAvatarPreview(avatarBuffer);
|
||||
setInitialAvatar(avatarBuffer);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
let content: React.JSX.Element | undefined;
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ export function AvatarIconEditor({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>();
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>();
|
||||
const [avatarData, setAvatarData] =
|
||||
useState<AvatarDataType>(initialAvatarData);
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ const log = createLogger('AvatarPreview');
|
||||
export type PropsType = {
|
||||
avatarColor?: AvatarColorType;
|
||||
avatarUrl?: string;
|
||||
avatarValue?: Uint8Array;
|
||||
avatarValue?: Uint8Array<ArrayBuffer>;
|
||||
conversationTitle?: string;
|
||||
i18n: LocalizerType;
|
||||
isEditable?: boolean;
|
||||
isGroup?: boolean;
|
||||
noteToSelf?: boolean;
|
||||
onAvatarLoaded?: (avatarBuffer: Uint8Array) => unknown;
|
||||
onAvatarLoaded?: (avatarBuffer: Uint8Array<ArrayBuffer>) => unknown;
|
||||
onClear?: () => unknown;
|
||||
onClick?: () => unknown;
|
||||
showUploadButton?: boolean;
|
||||
@@ -58,7 +58,9 @@ export function AvatarPreview({
|
||||
showUploadButton,
|
||||
style = {},
|
||||
}: PropsType): React.JSX.Element {
|
||||
const [avatarPreview, setAvatarPreview] = useState<Uint8Array | undefined>();
|
||||
const [avatarPreview, setAvatarPreview] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>();
|
||||
|
||||
// Loads the initial avatarUrl if one is provided, but only if we're in editable mode.
|
||||
// If we're not editable, we assume that we either have an avatarUrl or we show a
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
const { noop } = lodash;
|
||||
|
||||
type DoneHandleType = (
|
||||
avatarBuffer: Uint8Array,
|
||||
avatarBuffer: Uint8Array<ArrayBuffer>,
|
||||
avatarData: AvatarDataType
|
||||
) => unknown;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const { noop } = lodash;
|
||||
export type PropsType = {
|
||||
className: string;
|
||||
i18n: LocalizerType;
|
||||
onChange: (avatar: Uint8Array) => unknown;
|
||||
onChange: (avatar: Uint8Array<ArrayBuffer>) => unknown;
|
||||
};
|
||||
|
||||
export function AvatarUploadButton({
|
||||
@@ -33,7 +33,7 @@ export function AvatarUploadButton({
|
||||
let shouldCancel = false;
|
||||
|
||||
void (async () => {
|
||||
let newAvatar: Uint8Array;
|
||||
let newAvatar: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
newAvatar = await processImageFile(processingFile);
|
||||
} catch (err) {
|
||||
|
||||
@@ -18,7 +18,7 @@ export type PropsType = {
|
||||
avatarData: AvatarDataType;
|
||||
i18n: LocalizerType;
|
||||
isSelected?: boolean;
|
||||
onClick: (avatarBuffer: Uint8Array | undefined) => unknown;
|
||||
onClick: (avatarBuffer: Uint8Array<ArrayBuffer> | undefined) => unknown;
|
||||
onDelete: () => unknown;
|
||||
size?: AvatarSize;
|
||||
};
|
||||
@@ -31,9 +31,9 @@ export function BetterAvatar({
|
||||
onDelete,
|
||||
size = 48,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>(
|
||||
avatarData.buffer
|
||||
);
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>(avatarData.buffer);
|
||||
const [avatarURL, setAvatarURL] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -17,7 +17,7 @@ const OVERFLOW_SCROLL_BUTTON_RATIO = 0.75;
|
||||
export const OVERFLOW_PARTICIPANT_WIDTH = 107;
|
||||
|
||||
export type PropsType = {
|
||||
getFrameBuffer: () => Uint8Array;
|
||||
getFrameBuffer: () => Uint8Array<ArrayBuffer>;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
i18n: LocalizerType;
|
||||
imageDataCache: React.RefObject<CallingImageDataCache | null>;
|
||||
|
||||
@@ -42,7 +42,7 @@ const DELAY_TO_SHOW_MISSING_MEDIA_KEYS = 5000;
|
||||
const CONTAINER_TRANSITION_TIME = 200;
|
||||
|
||||
type BasePropsType = {
|
||||
getFrameBuffer: () => Uint8Array;
|
||||
getFrameBuffer: () => Uint8Array<ArrayBuffer>;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
i18n: LocalizerType;
|
||||
imageDataCache: React.RefObject<CallingImageDataCache | null>;
|
||||
@@ -235,7 +235,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
||||
imageData?.data.buffer !== frameBuffer.buffer ||
|
||||
imageData?.data.byteOffset !== frameBuffer.byteOffset
|
||||
) {
|
||||
const view = new Uint8ClampedArray(
|
||||
const view = new Uint8ClampedArray<ArrayBuffer>(
|
||||
frameBuffer.buffer,
|
||||
frameBuffer.byteOffset,
|
||||
frameWidth * frameHeight * 4
|
||||
|
||||
@@ -165,7 +165,7 @@ export type PropsType = {
|
||||
saveAlerts: (alerts: ServerAlertsType) => Promise<void>;
|
||||
savePreferredLeftPaneWidth: (_: number) => void;
|
||||
searchInConversation: (conversationId: string) => unknown;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array) => void;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array<ArrayBuffer>) => void;
|
||||
setComposeGroupExpireTimer: (_: DurationInSeconds) => void;
|
||||
setComposeGroupName: (_: string) => void;
|
||||
setComposeSearchTerm: (composeSearchTerm: string) => void;
|
||||
|
||||
@@ -72,7 +72,7 @@ const { get, has, noop } = lodash;
|
||||
const log = createLogger('MediaEditor');
|
||||
|
||||
export type MediaEditorResultType = Readonly<{
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
contentType: MIMEType;
|
||||
blurHash: string;
|
||||
caption?: string;
|
||||
@@ -1451,7 +1451,7 @@ export function MediaEditor({
|
||||
setEditMode(undefined);
|
||||
setIsSaving(true);
|
||||
|
||||
let data: Uint8Array;
|
||||
let data: Uint8Array<ArrayBuffer>;
|
||||
let blurHash: string;
|
||||
try {
|
||||
const renderFabricCanvas =
|
||||
|
||||
@@ -247,7 +247,7 @@ function renderDonationsPane(props: {
|
||||
me: typeof me;
|
||||
donationReceipts: ReadonlyArray<DonationReceipt>;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
name: string;
|
||||
baseDir?: string | undefined;
|
||||
}) => Promise<{ fullPath: string; name: string } | null>;
|
||||
|
||||
@@ -287,7 +287,7 @@ type PropsFunctionType = {
|
||||
|
||||
internalAddDonationReceipt: (receipt: DonationReceipt) => void;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
name: string;
|
||||
baseDir?: string | undefined;
|
||||
}) => Promise<{ fullPath: string; name: string } | null>;
|
||||
|
||||
@@ -85,7 +85,7 @@ export type PropsDataType = {
|
||||
donationReceipts: ReadonlyArray<DonationReceipt>;
|
||||
theme: ThemeType;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
name: string;
|
||||
baseDir?: string | undefined;
|
||||
}) => Promise<{ fullPath: string; name: string } | null>;
|
||||
@@ -345,7 +345,7 @@ function PreferencesReceiptList({
|
||||
i18n: LocalizerType;
|
||||
donationReceipts: ReadonlyArray<DonationReceipt>;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
name: string;
|
||||
baseDir?: string | undefined;
|
||||
}) => Promise<{ fullPath: string; name: string } | null>;
|
||||
|
||||
@@ -50,7 +50,7 @@ export function PreferencesInternal({
|
||||
donationReceipts: ReadonlyArray<DonationReceipt>;
|
||||
internalAddDonationReceipt: (receipt: DonationReceipt) => void;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
name: string;
|
||||
baseDir?: string | undefined;
|
||||
}) => Promise<{ fullPath: string; name: string } | null>;
|
||||
|
||||
@@ -230,11 +230,11 @@ export function ProfileEditor({
|
||||
const [startingAvatarUrl, setStartingAvatarUrl] = useState(profileAvatarUrl);
|
||||
|
||||
const [oldAvatarBuffer, setOldAvatarBuffer] = useState<
|
||||
Uint8Array | undefined
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>(undefined);
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<
|
||||
Uint8Array<ArrayBuffer> | undefined
|
||||
>(undefined);
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [stagedProfile, setStagedProfile] = useState<ProfileEditorData>({
|
||||
aboutEmoji,
|
||||
aboutText,
|
||||
@@ -285,7 +285,7 @@ export function ProfileEditor({
|
||||
|
||||
// To make AvatarEditor re-render less often
|
||||
const handleAvatarChanged = useCallback(
|
||||
(avatar: Uint8Array | undefined) => {
|
||||
(avatar: Uint8Array<ArrayBuffer> | undefined) => {
|
||||
// Do not display stale avatar from disk anymore.
|
||||
setStartingAvatarUrl(undefined);
|
||||
|
||||
@@ -325,7 +325,7 @@ export function ProfileEditor({
|
||||
|
||||
// To make AvatarEditor re-render less often
|
||||
const handleAvatarLoaded = useCallback(
|
||||
(avatar: Uint8Array) => {
|
||||
(avatar: Uint8Array<ArrayBuffer>) => {
|
||||
setAvatarBuffer(avatar);
|
||||
setOldAvatarBuffer(avatar);
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ const ERROR_CORRECTION_LEVEL = 'L';
|
||||
export type PropsType = Readonly<{
|
||||
alt: string;
|
||||
className?: string;
|
||||
data: string | Uint8Array;
|
||||
data: string | Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
export function QrCode(props: PropsType): ReactElement {
|
||||
|
||||
@@ -71,23 +71,26 @@ export default {
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const Template: StoryFn<PropsType> = args => {
|
||||
const [attachment, setAttachment] = useState<string | undefined>();
|
||||
const saveAttachment = useCallback(({ data }: { data?: Uint8Array }) => {
|
||||
if (!data) {
|
||||
setAttachment(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([data], {
|
||||
type: 'image/png',
|
||||
});
|
||||
|
||||
setAttachment(oldURL => {
|
||||
if (oldURL) {
|
||||
URL.revokeObjectURL(oldURL);
|
||||
const saveAttachment = useCallback(
|
||||
({ data }: { data?: Uint8Array<ArrayBuffer> }) => {
|
||||
if (!data) {
|
||||
setAttachment(undefined);
|
||||
return;
|
||||
}
|
||||
return URL.createObjectURL(blob);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const blob = new Blob([data], {
|
||||
type: 'image/png',
|
||||
});
|
||||
|
||||
setAttachment(oldURL => {
|
||||
if (oldURL) {
|
||||
URL.revokeObjectURL(oldURL);
|
||||
}
|
||||
return URL.createObjectURL(blob);
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -432,7 +432,7 @@ export function UsernameLinkEditor({
|
||||
|
||||
onBack,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const [pngData, setPngData] = useState<Uint8Array | undefined>();
|
||||
const [pngData, setPngData] = useState<Uint8Array<ArrayBuffer> | undefined>();
|
||||
const [showColors, setShowColors] = useState(false);
|
||||
const [confirmReset, setConfirmReset] = useState(false);
|
||||
const [recoveryModalVisibility, setRecoveryModalVisibility] = useState(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import LinkifyIt from 'linkify-it';
|
||||
import LinkifyIt, { type Match as LinkifyItMatch } from 'linkify-it';
|
||||
|
||||
import type { RenderTextCallbackType } from '../../types/Util.std.js';
|
||||
import {
|
||||
@@ -335,7 +335,7 @@ export function Linkify(props: Props): React.JSX.Element {
|
||||
|
||||
const chunkData: Array<{
|
||||
chunk: string;
|
||||
matchData: ReadonlyArray<LinkifyIt.Match>;
|
||||
matchData: ReadonlyArray<LinkifyItMatch>;
|
||||
}> = splitByEmoji(text).map(({ type, value: chunk }) => {
|
||||
if (type === 'text') {
|
||||
return { chunk, matchData: linkify.match(chunk) || [] };
|
||||
|
||||
@@ -273,7 +273,7 @@ export function ConversationDetails({
|
||||
}
|
||||
makeRequest={async (
|
||||
options: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
avatar?: undefined | Uint8Array<ArrayBuffer>;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ type PropsType = {
|
||||
initiallyFocusDescription: boolean;
|
||||
makeRequest: (
|
||||
_: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
avatar?: undefined | Uint8Array<ArrayBuffer>;
|
||||
description?: string;
|
||||
title?: undefined | string;
|
||||
}>
|
||||
@@ -70,7 +70,7 @@ export function EditConversationAttributesModal({
|
||||
const startingAvatarUrlRef = useRef<undefined | string>(externalAvatarUrl);
|
||||
|
||||
const [editingAvatar, setEditingAvatar] = useState(false);
|
||||
const [avatar, setAvatar] = useState<undefined | Uint8Array>();
|
||||
const [avatar, setAvatar] = useState<undefined | Uint8Array<ArrayBuffer>>();
|
||||
const [rawTitle, setRawTitle] = useState(externalTitle);
|
||||
const [rawGroupDescription, setRawGroupDescription] = useState(
|
||||
externalGroupDescription
|
||||
@@ -135,7 +135,7 @@ export function EditConversationAttributesModal({
|
||||
event.preventDefault();
|
||||
|
||||
const request: {
|
||||
avatar?: undefined | Uint8Array;
|
||||
avatar?: undefined | Uint8Array<ArrayBuffer>;
|
||||
description?: string;
|
||||
title?: string;
|
||||
} = {};
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ReactNode } from 'react';
|
||||
import React, { memo, useCallback, useEffect } from 'react';
|
||||
import type { Placement } from 'react-aria';
|
||||
import { DialogTrigger } from 'react-aria-components';
|
||||
// @ts-expect-error - Needs tinykeys to update package.json type resolution
|
||||
import { createKeybindingsHandler } from 'tinykeys';
|
||||
import { FunPickerTabKey } from './constants.dom.js';
|
||||
import { FunPopover } from './base/FunPopover.dom.js';
|
||||
|
||||
@@ -94,7 +94,7 @@ async function fetchSegment(
|
||||
contentLength: number,
|
||||
doFetchBytesViaProxy: typeof fetchBytesViaProxy,
|
||||
signal?: AbortSignal
|
||||
): Promise<ArrayBufferView> {
|
||||
): Promise<Uint8Array<ArrayBuffer>> {
|
||||
const { data, response } = await doFetchBytesViaProxy({
|
||||
method: 'GET',
|
||||
url,
|
||||
@@ -122,7 +122,7 @@ async function fetchSegment(
|
||||
'Unexpected response buffer byte length'
|
||||
);
|
||||
|
||||
let slice: ArrayBufferView;
|
||||
let slice: Uint8Array<ArrayBuffer>;
|
||||
// Trim duplicate bytes from start of last segment
|
||||
if (segmentRange.sliceStart > 0) {
|
||||
slice = data.subarray(segmentRange.sliceStart);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { focusSafely, getFocusableTreeWalker } from '@react-aria/focus';
|
||||
import type { ReactNode, RefObject } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
// @ts-expect-error - Needs tinykeys to update package.json type resolution
|
||||
import { createKeybindingsHandler } from 'tinykeys';
|
||||
import { strictAssert } from '../../../util/assert.std.js';
|
||||
|
||||
|
||||
@@ -89,7 +89,9 @@ export abstract class LeftPaneHelper<T> {
|
||||
i18n: LocalizerType;
|
||||
removeSelectedContact: (_: string) => unknown;
|
||||
renderLeftPaneChatFolders: () => React.JSX.Element;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array) => unknown;
|
||||
setComposeGroupAvatar: (
|
||||
_: undefined | Uint8Array<ArrayBuffer>
|
||||
) => unknown;
|
||||
setComposeGroupExpireTimer: (_: DurationInSeconds) => void;
|
||||
setComposeGroupName: (_: string) => unknown;
|
||||
toggleComposeEditingAvatar: () => unknown;
|
||||
|
||||
@@ -27,7 +27,7 @@ import type {
|
||||
import { AvatarColors } from '../../types/Colors.std.js';
|
||||
|
||||
export type LeftPaneSetGroupMetadataPropsType = {
|
||||
groupAvatar: undefined | Uint8Array;
|
||||
groupAvatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
groupName: string;
|
||||
groupExpireTimer: DurationInSeconds;
|
||||
hasError: boolean;
|
||||
@@ -38,7 +38,7 @@ export type LeftPaneSetGroupMetadataPropsType = {
|
||||
};
|
||||
|
||||
export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGroupMetadataPropsType> {
|
||||
readonly #groupAvatar: undefined | Uint8Array;
|
||||
readonly #groupAvatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
readonly #groupName: string;
|
||||
readonly #groupExpireTimer: DurationInSeconds;
|
||||
readonly #hasError: boolean;
|
||||
@@ -121,7 +121,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr
|
||||
composeSaveAvatarToDisk: SaveAvatarToDiskActionType;
|
||||
createGroup: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array) => unknown;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array<ArrayBuffer>) => unknown;
|
||||
setComposeGroupExpireTimer: (_: DurationInSeconds) => void;
|
||||
setComposeGroupName: (_: string) => unknown;
|
||||
toggleComposeEditingAvatar: () => unknown;
|
||||
|
||||
+20
-16
@@ -7,45 +7,45 @@
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class Bytes {
|
||||
public fromBase64(value: string): Uint8Array {
|
||||
public fromBase64(value: string): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.from(value, 'base64');
|
||||
}
|
||||
|
||||
public fromBase64url(value: string): Uint8Array {
|
||||
public fromBase64url(value: string): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.from(value, 'base64url');
|
||||
}
|
||||
|
||||
public fromHex(value: string): Uint8Array {
|
||||
public fromHex(value: string): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.from(value, 'hex');
|
||||
}
|
||||
|
||||
// TODO(indutny): deprecate it
|
||||
public fromBinary(value: string): Uint8Array {
|
||||
public fromBinary(value: string): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.from(value, 'binary');
|
||||
}
|
||||
|
||||
public fromString(value: string): Uint8Array {
|
||||
public fromString(value: string): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.from(value);
|
||||
}
|
||||
|
||||
public toBase64(data: Uint8Array): string {
|
||||
public toBase64(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Buffer.from(data).toString('base64');
|
||||
}
|
||||
|
||||
public toBase64url(data: Uint8Array): string {
|
||||
public toBase64url(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Buffer.from(data).toString('base64url');
|
||||
}
|
||||
|
||||
public toHex(data: Uint8Array): string {
|
||||
public toHex(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Buffer.from(data).toString('hex');
|
||||
}
|
||||
|
||||
// TODO(indutny): deprecate it
|
||||
public toBinary(data: Uint8Array): string {
|
||||
public toBinary(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Buffer.from(data).toString('binary');
|
||||
}
|
||||
|
||||
public toString(data: Uint8Array): string {
|
||||
public toString(data: Uint8Array<ArrayBuffer>): string {
|
||||
return Buffer.from(data).toString();
|
||||
}
|
||||
|
||||
@@ -53,24 +53,28 @@ export class Bytes {
|
||||
return Buffer.byteLength(value);
|
||||
}
|
||||
|
||||
public concatenate(list: ReadonlyArray<Uint8Array>): Uint8Array {
|
||||
public concatenate(
|
||||
list: ReadonlyArray<Uint8Array<ArrayBuffer>>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return Buffer.concat(list);
|
||||
}
|
||||
|
||||
public isEmpty(data: Uint8Array | null | undefined): boolean {
|
||||
public isEmpty(data: Uint8Array<ArrayBuffer> | null | undefined): boolean {
|
||||
if (!data) {
|
||||
return true;
|
||||
}
|
||||
return data.length === 0;
|
||||
}
|
||||
|
||||
public isNotEmpty(data: Uint8Array | null | undefined): data is Uint8Array {
|
||||
public isNotEmpty(
|
||||
data: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): data is Uint8Array<ArrayBuffer> {
|
||||
return !this.isEmpty(data);
|
||||
}
|
||||
|
||||
public areEqual(
|
||||
a: Uint8Array | null | undefined,
|
||||
b: Uint8Array | null | undefined
|
||||
a: Uint8Array<ArrayBuffer> | null | undefined,
|
||||
b: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): boolean {
|
||||
if (!a || !b) {
|
||||
return !a && !b;
|
||||
@@ -79,7 +83,7 @@ export class Bytes {
|
||||
return Buffer.compare(a, b) === 0;
|
||||
}
|
||||
|
||||
public readBigUint64BE(value: Uint8Array): bigint {
|
||||
public readBigUint64BE(value: Uint8Array<ArrayBuffer>): bigint {
|
||||
const buffer = Buffer.from(value);
|
||||
return buffer.readBigUint64BE();
|
||||
}
|
||||
|
||||
+25
-16
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { Buffer } from 'node:buffer';
|
||||
import type { Decipher } from 'node:crypto';
|
||||
import type { Decipheriv } from 'node:crypto';
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import { strictAssert } from '../util/assert.std.js';
|
||||
@@ -12,14 +12,20 @@ import { CipherType } from '../types/Crypto.std.js';
|
||||
const AUTH_TAG_SIZE = 16;
|
||||
|
||||
export class Crypto {
|
||||
public sign(key: Uint8Array, data: Uint8Array): Uint8Array {
|
||||
public sign(
|
||||
key: Uint8Array<ArrayBuffer>,
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto
|
||||
.createHmac('sha256', Buffer.from(key))
|
||||
.update(Buffer.from(data))
|
||||
.digest();
|
||||
}
|
||||
|
||||
public hash(type: HashType, data: Uint8Array): Uint8Array {
|
||||
public hash(
|
||||
type: HashType,
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return crypto.createHash(type).update(Buffer.from(data)).digest();
|
||||
}
|
||||
|
||||
@@ -31,12 +37,12 @@ export class Crypto {
|
||||
iv,
|
||||
aad,
|
||||
}: Readonly<{
|
||||
key: Uint8Array;
|
||||
plaintext: Uint8Array;
|
||||
iv: Uint8Array;
|
||||
aad?: Uint8Array;
|
||||
key: Uint8Array<ArrayBuffer>;
|
||||
plaintext: Uint8Array<ArrayBuffer>;
|
||||
iv: Uint8Array<ArrayBuffer>;
|
||||
aad?: Uint8Array<ArrayBuffer>;
|
||||
}>
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
if (cipherType === CipherType.AES256GCM) {
|
||||
const gcm = crypto.createCipheriv(
|
||||
cipherType,
|
||||
@@ -76,13 +82,13 @@ export class Crypto {
|
||||
iv,
|
||||
aad,
|
||||
}: Readonly<{
|
||||
key: Uint8Array;
|
||||
ciphertext: Uint8Array;
|
||||
iv: Uint8Array;
|
||||
aad?: Uint8Array;
|
||||
key: Uint8Array<ArrayBuffer>;
|
||||
ciphertext: Uint8Array<ArrayBuffer>;
|
||||
iv: Uint8Array<ArrayBuffer>;
|
||||
aad?: Uint8Array<ArrayBuffer>;
|
||||
}>
|
||||
): Uint8Array {
|
||||
let decipher: Decipher;
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
let decipher: Decipheriv;
|
||||
let input = Buffer.from(ciphertext);
|
||||
if (cipherType === CipherType.AES256GCM) {
|
||||
const gcm = crypto.createDecipheriv(
|
||||
@@ -123,11 +129,14 @@ export class Crypto {
|
||||
return crypto.randomInt(min, max);
|
||||
}
|
||||
|
||||
public getRandomBytes(size: number): Uint8Array {
|
||||
public getRandomBytes(size: number): Uint8Array<ArrayBuffer> {
|
||||
return crypto.randomBytes(size);
|
||||
}
|
||||
|
||||
public constantTimeEqual(left: Uint8Array, right: Uint8Array): boolean {
|
||||
public constantTimeEqual(
|
||||
left: Uint8Array<ArrayBuffer>,
|
||||
right: Uint8Array<ArrayBuffer>
|
||||
): boolean {
|
||||
return crypto.timingSafeEqual(Buffer.from(left), Buffer.from(right));
|
||||
}
|
||||
}
|
||||
|
||||
+41
-36
@@ -146,9 +146,9 @@ const log = createLogger('groups');
|
||||
export { joinViaLink } from './groups/joinViaLink.preload.js';
|
||||
|
||||
export type GroupFields = {
|
||||
readonly id: Uint8Array;
|
||||
readonly secretParams: Uint8Array;
|
||||
readonly publicParams: Uint8Array;
|
||||
readonly id: Uint8Array<ArrayBuffer>;
|
||||
readonly secretParams: Uint8Array<ArrayBuffer>;
|
||||
readonly publicParams: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
|
||||
const MAX_CACHED_GROUP_FIELDS = 100;
|
||||
@@ -211,7 +211,7 @@ type UpdatesResultType = {
|
||||
};
|
||||
|
||||
type UploadedAvatarType = {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
hash: string;
|
||||
key: string;
|
||||
};
|
||||
@@ -260,7 +260,7 @@ const GROUP_INVITE_LINK_PASSWORD_LENGTH = 16;
|
||||
|
||||
// Group Links
|
||||
|
||||
export function generateGroupInviteLinkPassword(): Uint8Array {
|
||||
export function generateGroupInviteLinkPassword(): Uint8Array<ArrayBuffer> {
|
||||
return getRandomBytes(GROUP_INVITE_LINK_PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ async function uploadAvatar(options: {
|
||||
logId: string;
|
||||
publicParams: string;
|
||||
secretParams: string;
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
}): Promise<UploadedAvatarType> {
|
||||
const { logId, publicParams, secretParams, data } = options;
|
||||
|
||||
@@ -397,7 +397,7 @@ async function uploadAvatar(options: {
|
||||
function buildGroupTitleBuffer(
|
||||
clientZkGroupCipher: ClientZkGroupCipher,
|
||||
title: string
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const titleBlobPlaintext = Proto.GroupAttributeBlob.encode({
|
||||
content: {
|
||||
title,
|
||||
@@ -416,7 +416,7 @@ function buildGroupTitleBuffer(
|
||||
function buildGroupDescriptionBuffer(
|
||||
clientZkGroupCipher: ClientZkGroupCipher,
|
||||
description: string
|
||||
): Uint8Array {
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
const attrsBlobPlaintext = Proto.GroupAttributeBlob.encode({
|
||||
content: {
|
||||
descriptionText: description,
|
||||
@@ -474,7 +474,7 @@ function buildGroupProto(
|
||||
const publicKey = Bytes.fromBase64(publicParams);
|
||||
const version = attributes.revision || 0;
|
||||
|
||||
let title: Uint8Array | null = null;
|
||||
let title: Uint8Array<ArrayBuffer> | null = null;
|
||||
if (attributes.name) {
|
||||
title = buildGroupTitleBuffer(clientZkGroupCipher, attributes.name);
|
||||
}
|
||||
@@ -484,7 +484,7 @@ function buildGroupProto(
|
||||
avatarUrl = attributes.avatarUrl;
|
||||
}
|
||||
|
||||
let disappearingMessagesTimer: Uint8Array | null = null;
|
||||
let disappearingMessagesTimer: Uint8Array<ArrayBuffer> | null = null;
|
||||
if (attributes.expireTimer) {
|
||||
const timerBlobPlaintext = Proto.GroupAttributeBlob.encode({
|
||||
content: {
|
||||
@@ -737,7 +737,7 @@ export async function buildUpdateAttributesChange(
|
||||
'id' | 'revision' | 'publicParams' | 'secretParams'
|
||||
>,
|
||||
attributes: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
avatar?: undefined | Uint8Array<ArrayBuffer>;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>
|
||||
@@ -1142,7 +1142,7 @@ export function buildAddMember({
|
||||
return member.serviceId === serviceId;
|
||||
});
|
||||
|
||||
let deletedUserId: Uint8Array | null = null;
|
||||
let deletedUserId: Uint8Array<ArrayBuffer> | null = null;
|
||||
if (doesMemberNeedUnban) {
|
||||
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
|
||||
const userIdCipherText = encryptServiceId(clientZkGroupCipher, serviceId);
|
||||
@@ -1333,8 +1333,8 @@ export function buildModifyMemberLabelChange({
|
||||
const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams);
|
||||
const userIdCipherText = encryptServiceId(clientZkGroupCipher, serviceId);
|
||||
|
||||
let encryptedLabelEmoji: Uint8Array | null = null;
|
||||
let encryptedLabelString: Uint8Array | null = null;
|
||||
let encryptedLabelEmoji: Uint8Array<ArrayBuffer> | null = null;
|
||||
let encryptedLabelString: Uint8Array<ArrayBuffer> | null = null;
|
||||
|
||||
if (labelEmoji) {
|
||||
const labelEmojiBytes = Bytes.fromString(labelEmoji);
|
||||
@@ -1705,7 +1705,9 @@ export function idForLogging(groupId: string | undefined): string {
|
||||
return `groupv2(${groupId})`;
|
||||
}
|
||||
|
||||
export function deriveGroupFields(masterKey: Uint8Array): GroupFields {
|
||||
export function deriveGroupFields(
|
||||
masterKey: Uint8Array<ArrayBuffer>
|
||||
): GroupFields {
|
||||
if (masterKey.length !== MASTER_KEY_LENGTH) {
|
||||
throw new Error(
|
||||
`deriveGroupFields: masterKey had length ${masterKey.length}, ` +
|
||||
@@ -1792,7 +1794,7 @@ export async function fetchMembershipProof({
|
||||
export async function createGroupV2(
|
||||
options: Readonly<{
|
||||
name: string;
|
||||
avatar: undefined | Uint8Array;
|
||||
avatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
expireTimer: undefined | DurationInSeconds;
|
||||
conversationIds: ReadonlyArray<string>;
|
||||
avatars?: ReadonlyArray<AvatarDataType>;
|
||||
@@ -2450,7 +2452,10 @@ export async function initiateMigrationToGroupV2(
|
||||
avatarUrl: avatarAttribute?.url,
|
||||
});
|
||||
|
||||
let groupSendEndorsementsResponse: Uint8Array | null | undefined;
|
||||
let groupSendEndorsementsResponse:
|
||||
| Uint8Array<ArrayBuffer>
|
||||
| null
|
||||
| undefined;
|
||||
try {
|
||||
const groupResponse = await makeRequestWithCredentials({
|
||||
logId: `initiateMigrationToGroupV2/${logId}`,
|
||||
@@ -2790,7 +2795,7 @@ export async function respondToGroupV2Migration({
|
||||
};
|
||||
|
||||
let firstGroupState: Proto.Group.Params | null | undefined;
|
||||
let groupSendEndorsementsResponse: Uint8Array | null | undefined;
|
||||
let groupSendEndorsementsResponse: Uint8Array<ArrayBuffer> | null | undefined;
|
||||
|
||||
try {
|
||||
const fetchedAt = Date.now();
|
||||
@@ -4079,7 +4084,7 @@ async function updateGroupViaLogs({
|
||||
}
|
||||
|
||||
let response: GroupLogResponseType;
|
||||
let groupSendEndorsementsResponse: Uint8Array | null = null;
|
||||
let groupSendEndorsementsResponse: Uint8Array<ArrayBuffer> | null = null;
|
||||
const changes: Array<Proto.GroupChanges.Params> = [];
|
||||
do {
|
||||
const fetchedAt = Date.now();
|
||||
@@ -5155,7 +5160,7 @@ function profileKeysToMap(items: ReadonlyArray<GroupChangeMemberType>) {
|
||||
}
|
||||
|
||||
type GroupChangeMemberType = {
|
||||
profileKey: Uint8Array;
|
||||
profileKey: Uint8Array<ArrayBuffer>;
|
||||
aci: AciString;
|
||||
};
|
||||
type GroupApplyResultType = {
|
||||
@@ -5776,7 +5781,7 @@ async function applyGroupChange({
|
||||
export async function decryptGroupAvatar(
|
||||
avatarKey: string,
|
||||
secretParamsBase64: string
|
||||
): Promise<Uint8Array> {
|
||||
): Promise<Uint8Array<ArrayBuffer>> {
|
||||
const ciphertext = await getGroupAvatar(avatarKey);
|
||||
const clientZkGroupCipher = getClientZkGroupCipher(secretParamsBase64);
|
||||
const plaintext = decryptGroupBlob(clientZkGroupCipher, ciphertext);
|
||||
@@ -5842,7 +5847,7 @@ export async function applyNewAvatar(
|
||||
return result;
|
||||
}
|
||||
|
||||
const data: Uint8Array = await decryptGroupAvatar(
|
||||
const data: Uint8Array<ArrayBuffer> = await decryptGroupAvatar(
|
||||
avatarUrlToUse,
|
||||
attributes.secretParams
|
||||
);
|
||||
@@ -5885,7 +5890,7 @@ export async function applyNewAvatar(
|
||||
|
||||
function profileKeyHasChanged(
|
||||
userId: ServiceIdString,
|
||||
newProfileKey: Uint8Array
|
||||
newProfileKey: Uint8Array<ArrayBuffer>
|
||||
) {
|
||||
const conversation = window.ConversationController.get(userId);
|
||||
if (!conversation) {
|
||||
@@ -6242,7 +6247,7 @@ function isValidLinkAccess(
|
||||
);
|
||||
}
|
||||
|
||||
function isValidProfileKey(buffer?: Uint8Array): boolean {
|
||||
function isValidProfileKey(buffer?: Uint8Array<ArrayBuffer>): boolean {
|
||||
return Boolean(buffer && buffer.length === 32);
|
||||
}
|
||||
|
||||
@@ -6283,7 +6288,7 @@ type DecryptedGroupChangeActions = {
|
||||
}>;
|
||||
modifyMemberLabels?: ReadonlyArray<DecryptedModifyMemberLabelAction>;
|
||||
modifyMemberProfileKeys?: ReadonlyArray<{
|
||||
profileKey: Uint8Array;
|
||||
profileKey: Uint8Array<ArrayBuffer>;
|
||||
aci: AciString;
|
||||
}>;
|
||||
addMembersPendingProfileKey?: ReadonlyArray<{
|
||||
@@ -6294,11 +6299,11 @@ type DecryptedGroupChangeActions = {
|
||||
deletedUserId: ServiceIdString;
|
||||
}>;
|
||||
promoteMembersPendingProfileKey?: ReadonlyArray<{
|
||||
profileKey: Uint8Array;
|
||||
profileKey: Uint8Array<ArrayBuffer>;
|
||||
aci: AciString;
|
||||
}>;
|
||||
promoteMembersPendingPniAciProfileKey?: ReadonlyArray<{
|
||||
profileKey: Uint8Array;
|
||||
profileKey: Uint8Array<ArrayBuffer>;
|
||||
aci: AciString;
|
||||
pni: PniString;
|
||||
}>;
|
||||
@@ -6505,7 +6510,7 @@ function decryptGroupChange(
|
||||
);
|
||||
|
||||
let aci: AciString;
|
||||
let profileKey: Uint8Array;
|
||||
let profileKey: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
aci = decryptAci(clientZkGroupCipher, userId);
|
||||
|
||||
@@ -6630,7 +6635,7 @@ function decryptGroupChange(
|
||||
);
|
||||
|
||||
let aci: AciString;
|
||||
let profileKey: Uint8Array;
|
||||
let profileKey: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
aci = decryptAci(clientZkGroupCipher, userId);
|
||||
|
||||
@@ -6683,7 +6688,7 @@ function decryptGroupChange(
|
||||
|
||||
let aci: AciString;
|
||||
let pni: PniString;
|
||||
let profileKey: Uint8Array;
|
||||
let profileKey: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
aci = decryptAci(clientZkGroupCipher, promotePendingMember.userId);
|
||||
pni = decryptPni(clientZkGroupCipher, promotePendingMember.pni);
|
||||
@@ -6997,7 +7002,7 @@ function decryptGroupChange(
|
||||
}
|
||||
|
||||
export function decryptGroupTitle(
|
||||
title: Uint8Array | undefined,
|
||||
title: Uint8Array<ArrayBuffer> | undefined,
|
||||
secretParams: string
|
||||
): string | undefined {
|
||||
const clientZkGroupCipher = getClientZkGroupCipher(secretParams);
|
||||
@@ -7012,7 +7017,7 @@ export function decryptGroupTitle(
|
||||
}
|
||||
|
||||
export function decryptGroupDescription(
|
||||
description: Uint8Array | undefined,
|
||||
description: Uint8Array<ArrayBuffer> | undefined,
|
||||
secretParams: string
|
||||
): string | undefined {
|
||||
const clientZkGroupCipher = getClientZkGroupCipher(secretParams);
|
||||
@@ -7281,7 +7286,7 @@ function decryptGroupState(
|
||||
|
||||
type DecryptedMember = Readonly<{
|
||||
userId: AciString;
|
||||
profileKey: Uint8Array;
|
||||
profileKey: Uint8Array<ArrayBuffer>;
|
||||
role: MemberRole;
|
||||
joinedAtVersion: number;
|
||||
labelEmoji?: string;
|
||||
@@ -7457,7 +7462,7 @@ function decryptMemberPendingProfileKey(
|
||||
|
||||
type DecryptedMemberPendingAdminApproval = {
|
||||
userId: AciString;
|
||||
profileKey?: Uint8Array;
|
||||
profileKey?: Uint8Array<ArrayBuffer>;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
@@ -7489,7 +7494,7 @@ function decryptMemberPendingAdminApproval(
|
||||
}
|
||||
|
||||
// profileKey
|
||||
let decryptedProfileKey: Uint8Array | undefined;
|
||||
let decryptedProfileKey: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (Bytes.isNotEmpty(profileKey)) {
|
||||
try {
|
||||
decryptedProfileKey = decryptProfileKey(
|
||||
@@ -7522,7 +7527,7 @@ function decryptMemberPendingAdminApproval(
|
||||
|
||||
export function getMembershipList(
|
||||
conversationId: string
|
||||
): Array<{ aci: AciString; uuidCiphertext: Uint8Array }> {
|
||||
): Array<{ aci: AciString; uuidCiphertext: Uint8Array<ArrayBuffer> }> {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('getMembershipList: cannot find conversation');
|
||||
|
||||
Vendored
+2
-2
@@ -3,8 +3,8 @@
|
||||
|
||||
declare module 'heic-convert' {
|
||||
export default function heicConvert(options: {
|
||||
buffer: Uint8Array;
|
||||
buffer: Uint8Array<ArrayBuffer>;
|
||||
format: string;
|
||||
quality: number;
|
||||
}): Promise<Buffer>;
|
||||
}): Promise<Buffer<ArrayBuffer>>;
|
||||
}
|
||||
|
||||
@@ -529,7 +529,7 @@ async function uploadThumbnailToTransitTier({
|
||||
logPrefix,
|
||||
dependencies,
|
||||
}: {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
keys: string;
|
||||
logPrefix: string;
|
||||
dependencies: {
|
||||
@@ -569,8 +569,8 @@ async function copyToBackupTier({
|
||||
cdnKey: string;
|
||||
size: number;
|
||||
mediaId: string;
|
||||
macKey: Uint8Array;
|
||||
aesKey: Uint8Array;
|
||||
macKey: Uint8Array<ArrayBuffer>;
|
||||
aesKey: Uint8Array<ArrayBuffer>;
|
||||
dependencies: {
|
||||
backupMediaBatch?: typeof doBackupMediaBatch;
|
||||
backupsService: BackupsService;
|
||||
|
||||
@@ -136,7 +136,7 @@ export async function sendDeleteForEveryone(
|
||||
` for message ${targetTimestamp}, isStory=${story}`
|
||||
);
|
||||
|
||||
let profileKey: Uint8Array | undefined;
|
||||
let profileKey: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (conversation.get('profileSharing')) {
|
||||
profileKey = await ourProfileKeyService.get();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export async function sendDirectExpirationTimerUpdate(
|
||||
const { expireTimer } = data;
|
||||
|
||||
const sendOptions = await getSendOptions(conversation.attributes);
|
||||
let profileKey: Uint8Array | undefined;
|
||||
let profileKey: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (conversation.get('profileSharing')) {
|
||||
profileKey = await ourProfileKeyService.get();
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function sendGroupUpdate(
|
||||
? Bytes.fromBase64(groupChangeBase64)
|
||||
: undefined;
|
||||
|
||||
let profileKey: Uint8Array | undefined;
|
||||
let profileKey: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (conversation.get('profileSharing')) {
|
||||
profileKey = await ourProfileKeyService.get();
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export async function sendNormalMessage(
|
||||
return;
|
||||
}
|
||||
|
||||
let profileKey: Uint8Array | undefined;
|
||||
let profileKey: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (conversation.get('profileSharing')) {
|
||||
profileKey = await ourProfileKeyService.get();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export type LinkPreviewMetadata = {
|
||||
};
|
||||
|
||||
export type LinkPreviewImage = {
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
contentType: MIMEType;
|
||||
};
|
||||
|
||||
@@ -228,7 +228,7 @@ const emptyHtmlDocument = (): HTMLDocument =>
|
||||
// (This fallback could, perhaps, be smarter based on user locale.)
|
||||
// [0]: https://www.w3.org/International/questions/qa-html-encoding-declarations.en
|
||||
const parseHtmlBytes = (
|
||||
bytes: Readonly<Uint8Array>,
|
||||
bytes: Readonly<Uint8Array<ArrayBuffer>>,
|
||||
httpCharset: string | null
|
||||
): HTMLDocument => {
|
||||
const hasBom = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;
|
||||
@@ -288,7 +288,7 @@ const parseHtmlBytes = (
|
||||
};
|
||||
|
||||
const getHtmlDocument = async (
|
||||
body: AsyncIterable<string | Uint8Array>,
|
||||
body: AsyncIterable<string | Uint8Array<ArrayBuffer>>,
|
||||
httpCharset: string | null,
|
||||
abortSignal: AbortSignal,
|
||||
logger: Pick<LoggerType, 'warn'> = log
|
||||
@@ -522,6 +522,7 @@ export async function fetchLinkPreviewMetadata(
|
||||
}
|
||||
|
||||
const document = await getHtmlDocument(
|
||||
// @ts-expect-error ReadableStream should actually be giving us ArrayBuffers
|
||||
response.body,
|
||||
contentType.charset,
|
||||
abortSignal,
|
||||
@@ -614,7 +615,7 @@ async function processImageResponse(
|
||||
return null;
|
||||
}
|
||||
|
||||
let data: Uint8Array;
|
||||
let data: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
data = await response.buffer();
|
||||
} catch (err) {
|
||||
|
||||
@@ -41,7 +41,7 @@ const parseTokenBody = (
|
||||
};
|
||||
|
||||
export type UploadOptionsType = Readonly<{
|
||||
content: string | Buffer | Uint8Array;
|
||||
content: string | Buffer<ArrayBuffer> | Uint8Array<ArrayBuffer>;
|
||||
appVersion: string;
|
||||
logger: LoggerType;
|
||||
extension?: string;
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function addAttachmentToMessage(
|
||||
|
||||
if (type === 'long-message') {
|
||||
let handledAnywhere = false;
|
||||
let attachmentData: Uint8Array | undefined;
|
||||
let attachmentData: Uint8Array<ArrayBuffer> | undefined;
|
||||
|
||||
try {
|
||||
if (attachment.path) {
|
||||
|
||||
@@ -276,7 +276,7 @@ export async function sendSyncMessageOnly(
|
||||
saveErrors,
|
||||
}: {
|
||||
targetTimestamp: number;
|
||||
dataMessage: Uint8Array;
|
||||
dataMessage: Uint8Array<ArrayBuffer>;
|
||||
saveErrors?: (errors: Array<Error>) => void;
|
||||
}
|
||||
): Promise<CallbackResultType | void> {
|
||||
|
||||
Vendored
+1
-1
@@ -191,7 +191,7 @@ export type MessageAttributesType = {
|
||||
callId?: string;
|
||||
canReplyToStory?: boolean;
|
||||
changedId?: string;
|
||||
dataMessage?: Uint8Array | null;
|
||||
dataMessage?: Uint8Array<ArrayBuffer> | null;
|
||||
decrypted_at?: number;
|
||||
deletedForEveryone?: boolean;
|
||||
deletedForEveryoneByAdminAci?: AciString;
|
||||
|
||||
@@ -1370,7 +1370,7 @@ export class ConversationModel {
|
||||
|
||||
getGroupV2Info(
|
||||
options: Readonly<
|
||||
{ groupChange?: Uint8Array } & (
|
||||
{ groupChange?: Uint8Array<ArrayBuffer> } & (
|
||||
| {
|
||||
includePendingMembers?: boolean;
|
||||
extraConversationsForSend?: ReadonlyArray<string>;
|
||||
@@ -1395,7 +1395,7 @@ export class ConversationModel {
|
||||
};
|
||||
}
|
||||
|
||||
getGroupIdBuffer(): Uint8Array | undefined {
|
||||
getGroupIdBuffer(): Uint8Array<ArrayBuffer> | undefined {
|
||||
const groupIdString = this.get('groupId');
|
||||
|
||||
if (!groupIdString) {
|
||||
@@ -2207,7 +2207,7 @@ export class ConversationModel {
|
||||
this.captureChange('updateServiceId');
|
||||
}
|
||||
|
||||
trackPreviousIdentityKey(publicKey: Uint8Array): void {
|
||||
trackPreviousIdentityKey(publicKey: Uint8Array<ArrayBuffer>): void {
|
||||
const logId = `trackPreviousIdentityKey/${this.idForLogging()}`;
|
||||
const identityKey = Bytes.toBase64(publicKey);
|
||||
|
||||
@@ -2314,7 +2314,7 @@ export class ConversationModel {
|
||||
}
|
||||
}
|
||||
|
||||
async updateReportingToken(token?: Uint8Array): Promise<void> {
|
||||
async updateReportingToken(token?: Uint8Array<ArrayBuffer>): Promise<void> {
|
||||
const oldValue = this.get('reportingToken');
|
||||
const newValue = token ? Bytes.toBase64(token) : undefined;
|
||||
|
||||
@@ -5094,7 +5094,7 @@ export class ConversationModel {
|
||||
|
||||
async setEncryptedProfileName(
|
||||
encryptedName: string,
|
||||
decryptionKey: Uint8Array
|
||||
decryptionKey: Uint8Array<ArrayBuffer>
|
||||
): Promise<void> {
|
||||
if (!encryptedName) {
|
||||
return;
|
||||
@@ -5131,7 +5131,7 @@ export class ConversationModel {
|
||||
|
||||
async setAndMaybeFetchProfileAvatar(options: {
|
||||
avatarUrl: undefined | null | string;
|
||||
decryptionKey?: Uint8Array | null | undefined;
|
||||
decryptionKey?: Uint8Array<ArrayBuffer> | null | undefined;
|
||||
forceFetch?: boolean;
|
||||
}): Promise<void> {
|
||||
const { avatarUrl, decryptionKey, forceFetch } = options;
|
||||
@@ -5210,7 +5210,7 @@ export class ConversationModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
let derivedAccessKey: Uint8Array;
|
||||
let derivedAccessKey: Uint8Array<ArrayBuffer>;
|
||||
try {
|
||||
derivedAccessKey = deriveAccessKeyFromProfileKey(
|
||||
Bytes.fromBase64(profileKey)
|
||||
|
||||
Vendored
+1
-1
@@ -10,6 +10,6 @@ declare module 'better-blockmap' {
|
||||
|
||||
export class BlockMap extends Writable {
|
||||
constructor(options?: BlockMapOptions);
|
||||
compress(compression?: 'gzip' | 'deflate'): Buffer;
|
||||
compress(compression?: 'gzip' | 'deflate'): Buffer<ArrayBuffer>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ for (const lang of REQUIRED_LANGUAGES) {
|
||||
if (lang === 'zh_TW') {
|
||||
fallbacks.unshift('zh-Hant');
|
||||
}
|
||||
let json: Buffer | undefined;
|
||||
let json: Buffer<ArrayBuffer> | undefined;
|
||||
for (const f of fallbacks) {
|
||||
try {
|
||||
json = fs.readFileSync(path.join(LOCALES_DIR, f, 'messages.json'));
|
||||
|
||||
@@ -132,7 +132,7 @@ function trayIconValueToText(
|
||||
|
||||
async function generateTrayIconImage(
|
||||
request: TrayIconImageRequest
|
||||
): Promise<Buffer> {
|
||||
): Promise<Buffer<ArrayBuffer>> {
|
||||
const variant = Variants[request.size];
|
||||
if (variant == null) {
|
||||
throw new TypeError(`Invalid variant size (${request.size})`);
|
||||
|
||||
@@ -68,8 +68,8 @@ const getMemoizedKeyMaterial = memoizee(
|
||||
);
|
||||
|
||||
export type BackupKeyMaterialType = Readonly<{
|
||||
macKey: Uint8Array;
|
||||
aesKey: Uint8Array;
|
||||
macKey: Uint8Array<ArrayBuffer>;
|
||||
aesKey: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
export function getKeyMaterial(
|
||||
@@ -80,8 +80,8 @@ export function getKeyMaterial(
|
||||
}
|
||||
|
||||
export type BackupMediaKeyMaterialType = Readonly<{
|
||||
macKey: Uint8Array;
|
||||
aesKey: Uint8Array;
|
||||
macKey: Uint8Array<ArrayBuffer>;
|
||||
aesKey: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
const BACKUP_MEDIA_AES_KEY_LEN = 32;
|
||||
@@ -89,7 +89,7 @@ const BACKUP_MEDIA_MAC_KEY_LEN = 32;
|
||||
|
||||
export function deriveBackupMediaKeyMaterial(
|
||||
mediaRootKey: BackupKey,
|
||||
mediaId: Uint8Array
|
||||
mediaId: Uint8Array<ArrayBuffer>
|
||||
): BackupMediaKeyMaterialType {
|
||||
if (!mediaId.length) {
|
||||
throw new Error('deriveBackupMediaKeyMaterial: mediaId missing');
|
||||
@@ -108,7 +108,7 @@ export function deriveBackupMediaKeyMaterial(
|
||||
|
||||
export function deriveBackupThumbnailTransitKeyMaterial(
|
||||
mediaRootKey: BackupKey,
|
||||
mediaId: Uint8Array
|
||||
mediaId: Uint8Array<ArrayBuffer>
|
||||
): BackupMediaKeyMaterialType {
|
||||
if (!mediaId.length) {
|
||||
throw new Error('deriveBackupThumbnailTransitKeyMaterial: mediaId missing');
|
||||
@@ -125,11 +125,11 @@ export function deriveBackupThumbnailTransitKeyMaterial(
|
||||
};
|
||||
}
|
||||
|
||||
export function getBackupId(): Uint8Array {
|
||||
export function getBackupId(): Uint8Array<ArrayBuffer> {
|
||||
const aci = itemStorage.user.getCheckedAci();
|
||||
return getBackupKey().deriveBackupId(toAciObject(aci));
|
||||
}
|
||||
|
||||
export function getLocalBackupMetadataKey(): Uint8Array {
|
||||
export function getLocalBackupMetadataKey(): Uint8Array<ArrayBuffer> {
|
||||
return getBackupKey().deriveLocalBackupMetadataKey();
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ const BACKUP_QUOTE_BODY_LIMIT = 2048;
|
||||
|
||||
type ToRecipientOptionsType = Readonly<{
|
||||
identityKeysById: ReadonlyMap<IdentityKeyType['id'], IdentityKeyType>;
|
||||
keyTransparencyData: Uint8Array | undefined;
|
||||
keyTransparencyData: Uint8Array<ArrayBuffer> | undefined;
|
||||
}>;
|
||||
|
||||
type GetRecipientIdOptionsType =
|
||||
@@ -299,7 +299,7 @@ export class BackupExportStream extends Readable {
|
||||
#attachmentBackupJobs: Array<
|
||||
CoreAttachmentBackupJobType | CoreAttachmentLocalBackupJobType
|
||||
> = [];
|
||||
#buffers = new Array<Uint8Array>();
|
||||
#buffers = new Array<Uint8Array<ArrayBuffer>>();
|
||||
#nextRecipientId = 1n;
|
||||
#flushResolve: (() => void) | undefined;
|
||||
#jsonExporter: BackupJsonExporter | undefined;
|
||||
@@ -448,7 +448,7 @@ export class BackupExportStream extends Readable {
|
||||
|
||||
const skippedConversationIds = new Set<string>();
|
||||
for (const { attributes } of window.ConversationController.getAll()) {
|
||||
let keyTransparencyData: Uint8Array | undefined;
|
||||
let keyTransparencyData: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (
|
||||
isDirectConversation(attributes) &&
|
||||
isAciString(attributes.serviceId) &&
|
||||
@@ -562,6 +562,8 @@ export class BackupExportStream extends Readable {
|
||||
|
||||
const id = this.#getNextRecipientId();
|
||||
const rootKey = CallLinkRootKey.parse(rootKeyString);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const rootKeyBytes: Uint8Array<ArrayBuffer> = rootKey.bytes;
|
||||
const roomId = getRoomIdFromRootKey(rootKey);
|
||||
|
||||
this.#roomIdToRecipientId.set(roomId, id);
|
||||
@@ -571,7 +573,7 @@ export class BackupExportStream extends Readable {
|
||||
id,
|
||||
destination: {
|
||||
callLink: {
|
||||
rootKey: rootKey.bytes,
|
||||
rootKey: rootKeyBytes,
|
||||
adminKey: adminKey ? toAdminKeyBytes(adminKey) : null,
|
||||
name,
|
||||
restrictions: toCallLinkRestrictionsProto(restrictions),
|
||||
@@ -2014,11 +2016,11 @@ export class BackupExportStream extends Readable {
|
||||
};
|
||||
}
|
||||
|
||||
#aciToBytes(aci: AciString | string): Uint8Array {
|
||||
#aciToBytes(aci: AciString | string): Uint8Array<ArrayBuffer> {
|
||||
return Aci.parseFromServiceIdString(aci).getRawUuidBytes();
|
||||
}
|
||||
|
||||
#aciToBytesOrNull(aci: AciString | string): Uint8Array | null {
|
||||
#aciToBytesOrNull(aci: AciString | string): Uint8Array<ArrayBuffer> | null {
|
||||
if (isAciString(aci)) {
|
||||
return Aci.parseFromServiceIdString(aci).getRawUuidBytes();
|
||||
}
|
||||
@@ -2026,13 +2028,13 @@ export class BackupExportStream extends Readable {
|
||||
}
|
||||
|
||||
/** For fields explicitly marked as PNI (validator will expect 16 bytes) */
|
||||
#pniToRawBytes(pni: PniString | string): Uint8Array {
|
||||
#pniToRawBytes(pni: PniString | string): Uint8Array<ArrayBuffer> {
|
||||
return Pni.parseFromServiceIdString(pni).getRawUuidBytes();
|
||||
}
|
||||
|
||||
/** For fields that can accept either ACI or PNI bytes */
|
||||
#serviceIdToBytes(serviceId: ServiceIdString): Uint8Array {
|
||||
return ServiceId.parseFromServiceIdString(serviceId).getServiceIdBinary();
|
||||
#serviceIdToBytes(serviceId: ServiceIdString): Uint8Array<ArrayBuffer> {
|
||||
return ServiceId.parseFromServiceIdString(serviceId).getRawUuidBytes();
|
||||
}
|
||||
|
||||
async #toChatItemFromNonBubble(
|
||||
@@ -2208,7 +2210,7 @@ export class BackupExportStream extends Readable {
|
||||
strictAssert(selfId, 'self must exist');
|
||||
patch.authorId = selfId;
|
||||
|
||||
let updaterAci: Uint8Array | null = null;
|
||||
let updaterAci: Uint8Array<ArrayBuffer> | null = null;
|
||||
if (sourceServiceId && Aci.parseFromServiceIdString(sourceServiceId)) {
|
||||
updaterAci = uuidToBytes(sourceServiceId);
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ export class BackupImportStream extends Writable {
|
||||
}
|
||||
|
||||
override async _write(
|
||||
data: Buffer,
|
||||
data: Buffer<ArrayBuffer>,
|
||||
_enc: BufferEncoding,
|
||||
done: (error?: Error) => void
|
||||
): Promise<void> {
|
||||
@@ -4101,7 +4101,7 @@ export class BackupImportStream extends Writable {
|
||||
};
|
||||
}
|
||||
|
||||
let wallpaperPhotoPointer: Uint8Array | undefined;
|
||||
let wallpaperPhotoPointer: Uint8Array<ArrayBuffer> | undefined;
|
||||
let wallpaperPreset: number | undefined;
|
||||
const dimWallpaperInDarkMode = dropNull(chatStyle.dimWallpaperInDarkMode);
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ export type DownloadOptionsType = Readonly<{
|
||||
|
||||
type DoDownloadOptionsType = Readonly<{
|
||||
downloadPath: string;
|
||||
ephemeralKey?: Uint8Array;
|
||||
ephemeralKey?: Uint8Array<ArrayBuffer>;
|
||||
onProgress?: (
|
||||
backupStep: InstallScreenBackupStep,
|
||||
currentBytes: number,
|
||||
@@ -597,10 +597,10 @@ export class BackupsService {
|
||||
// Test harness
|
||||
public async exportBackupData(
|
||||
options: BackupExportOptions
|
||||
): Promise<{ data: Uint8Array } & ExportResultType> {
|
||||
): Promise<{ data: Uint8Array<ArrayBuffer> } & ExportResultType> {
|
||||
const sink = new PassThrough();
|
||||
|
||||
const chunks = new Array<Uint8Array>();
|
||||
const chunks = new Array<Uint8Array<ArrayBuffer>>();
|
||||
sink.on('data', chunk => chunks.push(chunk));
|
||||
const result = await this.#exportBackup(sink, options);
|
||||
|
||||
@@ -850,7 +850,7 @@ export class BackupsService {
|
||||
|
||||
// First pass - don't decrypt, only verify mac
|
||||
let hmac = createHmac(HashType.size256, macKey);
|
||||
let theirMac: Uint8Array | undefined;
|
||||
let theirMac: Uint8Array<ArrayBuffer> | undefined;
|
||||
let totalBytes = 0;
|
||||
|
||||
const sink = new PassThrough();
|
||||
|
||||
@@ -50,12 +50,12 @@ export type BackupImportOptions = (
|
||||
localBackupSnapshotDir: string;
|
||||
}
|
||||
) & {
|
||||
ephemeralKey?: Uint8Array;
|
||||
ephemeralKey?: Uint8Array<ArrayBuffer>;
|
||||
onProgress?: OnProgressCallback;
|
||||
};
|
||||
|
||||
export type LocalChatStyle = Readonly<{
|
||||
wallpaperPhotoPointer: Uint8Array | undefined;
|
||||
wallpaperPhotoPointer: Uint8Array<ArrayBuffer> | undefined;
|
||||
wallpaperPreset: number | undefined;
|
||||
color: ConversationColorType | undefined;
|
||||
customColorId: string | undefined;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class FileStream extends InputStream {
|
||||
await this.#file?.close();
|
||||
}
|
||||
|
||||
async read(amount: number): Promise<Buffer> {
|
||||
async read(amount: number): Promise<Buffer<ArrayBuffer>> {
|
||||
const file = await this.#lazyOpen();
|
||||
if (this.#buffer.length < amount) {
|
||||
this.#buffer = Buffer.alloc(amount);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { sha256 } from '../../../Crypto.node.js';
|
||||
|
||||
export function getMediaIdFromMediaName(mediaName: string): {
|
||||
string: string;
|
||||
bytes: Uint8Array;
|
||||
bytes: Uint8Array<ArrayBuffer>;
|
||||
} {
|
||||
const mediaIdBytes = getBackupMediaRootKey().deriveMediaId(mediaName);
|
||||
return {
|
||||
@@ -23,7 +23,7 @@ export function getMediaIdFromMediaName(mediaName: string): {
|
||||
|
||||
export function getMediaIdForAttachment(attachment: BackupableAttachmentType): {
|
||||
string: string;
|
||||
bytes: Uint8Array;
|
||||
bytes: Uint8Array<ArrayBuffer>;
|
||||
} {
|
||||
const mediaName = getMediaNameForAttachment(attachment);
|
||||
return getMediaIdFromMediaName(mediaName);
|
||||
@@ -33,7 +33,7 @@ export function getMediaIdForAttachmentThumbnail(
|
||||
attachment: BackupableAttachmentType
|
||||
): {
|
||||
string: string;
|
||||
bytes: Uint8Array;
|
||||
bytes: Uint8Array<ArrayBuffer>;
|
||||
} {
|
||||
const mediaName = getMediaNameForAttachmentThumbnail(
|
||||
getMediaNameForAttachment(attachment)
|
||||
@@ -54,8 +54,8 @@ export function getMediaName({
|
||||
plaintextHash,
|
||||
key,
|
||||
}: {
|
||||
plaintextHash: Uint8Array;
|
||||
key: Uint8Array;
|
||||
plaintextHash: Uint8Array<ArrayBuffer>;
|
||||
key: Uint8Array<ArrayBuffer>;
|
||||
}): string {
|
||||
return Bytes.toHex(Bytes.concatenate([plaintextHash, key]));
|
||||
}
|
||||
@@ -73,8 +73,8 @@ export function getLocalBackupFileName({
|
||||
plaintextHash,
|
||||
localKey,
|
||||
}: {
|
||||
plaintextHash: Uint8Array;
|
||||
localKey: Uint8Array;
|
||||
plaintextHash: Uint8Array<ArrayBuffer>;
|
||||
localKey: Uint8Array<ArrayBuffer>;
|
||||
}): string {
|
||||
return Bytes.toHex(sha256(Bytes.concatenate([plaintextHash, localKey])));
|
||||
}
|
||||
@@ -85,7 +85,9 @@ export function getMediaNameForAttachmentThumbnail(
|
||||
return `${fullsizeMediaName}_thumbnail`;
|
||||
}
|
||||
|
||||
export function getBytesFromMediaIdString(mediaId: string): Uint8Array {
|
||||
export function getBytesFromMediaIdString(
|
||||
mediaId: string
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return Bytes.fromBase64url(mediaId);
|
||||
}
|
||||
|
||||
|
||||
@@ -622,10 +622,14 @@ export class CallingClass {
|
||||
RingRTC.handleRejectedIncomingCallRequest =
|
||||
this.#handleRejectedIncomingCallRequest.bind(this);
|
||||
RingRTC.handleLogMessage = this.#handleLogMessage.bind(this);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
RingRTC.handleSendHttpRequest = this.#handleSendHttpRequest.bind(this);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
RingRTC.handleSendCallMessage = this.#handleSendCallMessage.bind(this);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
RingRTC.handleSendCallMessageToGroup =
|
||||
this.#handleSendCallMessageToGroup.bind(this);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
RingRTC.handleGroupCallRingUpdate =
|
||||
this.#handleGroupCallRingUpdate.bind(this);
|
||||
|
||||
@@ -857,15 +861,22 @@ export class CallingClass {
|
||||
);
|
||||
|
||||
const rootKey = CallLinkRootKey.generate();
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const rootKeyBytes: Uint8Array<ArrayBuffer> = rootKey.bytes;
|
||||
const roomId = rootKey.deriveRoomId();
|
||||
const roomIdHex = Bytes.toHex(roomId);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const roomIdBytes: Uint8Array<ArrayBuffer> = roomId;
|
||||
const roomIdHex = Bytes.toHex(roomIdBytes);
|
||||
const logId = `createCallLink(${roomIdHex})`;
|
||||
|
||||
log.info(`${logId}: Creating call link`);
|
||||
|
||||
const adminKey = CallLinkRootKey.generateAdminPassKey();
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const adminKeyBytes: Uint8Array<ArrayBuffer> = adminKey;
|
||||
|
||||
const context = CreateCallLinkCredentialRequestContext.forRoomId(roomId);
|
||||
const context =
|
||||
CreateCallLinkCredentialRequestContext.forRoomId(roomIdBytes);
|
||||
const requestBase64 = Bytes.toBase64(context.getRequest().serialize());
|
||||
|
||||
const { credential: credentialBase64 } =
|
||||
@@ -884,10 +895,10 @@ export class CallingClass {
|
||||
genericServerPublicParams
|
||||
);
|
||||
|
||||
const secretParams = CallLinkSecretParams.deriveFromRootKey(rootKey.bytes);
|
||||
const secretParams = CallLinkSecretParams.deriveFromRootKey(rootKeyBytes);
|
||||
|
||||
const credentialPresentation = credential
|
||||
.present(roomId, userId, genericServerPublicParams, secretParams)
|
||||
.present(roomIdBytes, userId, genericServerPublicParams, secretParams)
|
||||
.serialize();
|
||||
const serializedPublicParams = secretParams.getPublicParams().serialize();
|
||||
|
||||
@@ -913,7 +924,7 @@ export class CallingClass {
|
||||
const callLink: CallLinkType = {
|
||||
roomId: roomIdHex,
|
||||
rootKey: maybeUpdatedRootKey,
|
||||
adminKey: Bytes.toBase64(adminKey),
|
||||
adminKey: Bytes.toBase64(adminKeyBytes),
|
||||
storageNeedsSync: true,
|
||||
...state,
|
||||
};
|
||||
@@ -1078,7 +1089,7 @@ export class CallingClass {
|
||||
preferLocalVideo = true,
|
||||
}: Readonly<{
|
||||
callLinkRootKey: CallLinkRootKey;
|
||||
adminPasskey: Uint8Array | undefined;
|
||||
adminPasskey: Uint8Array<ArrayBuffer> | undefined;
|
||||
hasLocalAudio: boolean;
|
||||
preferLocalVideo?: boolean;
|
||||
}>): Promise<
|
||||
@@ -1468,8 +1479,8 @@ export class CallingClass {
|
||||
roomId: string;
|
||||
authCredentialPresentation: CallLinkAuthCredentialPresentation;
|
||||
callLinkRootKey: CallLinkRootKey;
|
||||
adminPasskey: Uint8Array | undefined;
|
||||
endorsementsPublicKey: Uint8Array;
|
||||
adminPasskey: Uint8Array<ArrayBuffer> | undefined;
|
||||
endorsementsPublicKey: Uint8Array<ArrayBuffer>;
|
||||
}): GroupCall {
|
||||
const existing = this.#getGroupCall(roomId);
|
||||
if (existing) {
|
||||
@@ -1867,7 +1878,13 @@ export class CallingClass {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
const reason = `sendProfileKeysForAdhocCall(${roomId})`;
|
||||
peekInfo.devices.forEach(async device => {
|
||||
const aci = device.userId ? this.#formatUserId(device.userId) : null;
|
||||
let aci: AciString | null = null;
|
||||
|
||||
if (device.userId) {
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const userIdBytes: Uint8Array<ArrayBuffer> = device.userId;
|
||||
aci = this.#formatUserId(userIdBytes);
|
||||
}
|
||||
if (
|
||||
!aci ||
|
||||
aci === ourAci ||
|
||||
@@ -2137,7 +2154,7 @@ export class CallingClass {
|
||||
}
|
||||
}
|
||||
|
||||
#formatUserId(userId: Uint8Array): AciString | null {
|
||||
#formatUserId(userId: Uint8Array<ArrayBuffer>): AciString | null {
|
||||
const uuid = bytesToUuid(userId);
|
||||
if (uuid && isAciString(uuid)) {
|
||||
return uuid;
|
||||
@@ -2152,11 +2169,20 @@ export class CallingClass {
|
||||
public formatGroupCallPeekInfoForRedux(
|
||||
peekInfo: PeekInfo
|
||||
): GroupCallPeekInfoType {
|
||||
const creatorAci = peekInfo.creator && bytesToUuid(peekInfo.creator);
|
||||
let creatorAci: string | undefined;
|
||||
|
||||
if (peekInfo.creator) {
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const creatorBytes: Uint8Array<ArrayBuffer> = peekInfo.creator;
|
||||
creatorAci = bytesToUuid(creatorBytes);
|
||||
}
|
||||
|
||||
return {
|
||||
acis: peekInfo.devices.map(peekDeviceInfo => {
|
||||
if (peekDeviceInfo.userId) {
|
||||
const uuid = this.#formatUserId(peekDeviceInfo.userId);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const userIdBytes: Uint8Array<ArrayBuffer> = peekDeviceInfo.userId;
|
||||
const uuid = this.#formatUserId(userIdBytes);
|
||||
if (uuid) {
|
||||
return uuid;
|
||||
}
|
||||
@@ -2171,7 +2197,11 @@ export class CallingClass {
|
||||
);
|
||||
}),
|
||||
pendingAcis: compact(
|
||||
peekInfo.pendingUsers.map(userId => this.#formatUserId(userId))
|
||||
peekInfo.pendingUsers.map(userId => {
|
||||
// @ts-expect-error needs ringrtc update;
|
||||
const userIdBytes: Uint8Array<ArrayBuffer> = userId;
|
||||
return this.#formatUserId(userIdBytes);
|
||||
})
|
||||
),
|
||||
creatorAci:
|
||||
creatorAci !== undefined
|
||||
@@ -2217,7 +2247,9 @@ export class CallingClass {
|
||||
? this.formatGroupCallPeekInfoForRedux(peekInfo)
|
||||
: undefined,
|
||||
remoteParticipants: remoteDeviceStates.map(remoteDeviceState => {
|
||||
let aci = bytesToUuid(remoteDeviceState.userId);
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const userIdBytes: Uint8Array<ArrayBuffer> = remoteDeviceState.userId;
|
||||
let aci = bytesToUuid(userIdBytes);
|
||||
if (!aci) {
|
||||
log.error(
|
||||
'formatGroupCallForRedux: could not convert remote participant UUID Uint8Array to string; using fallback UUID'
|
||||
@@ -3225,8 +3257,8 @@ export class CallingClass {
|
||||
}
|
||||
|
||||
async #handleSendCallMessage(
|
||||
recipient: Uint8Array,
|
||||
data: Uint8Array,
|
||||
recipient: Uint8Array<ArrayBuffer>,
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
urgency: CallMessageUrgency
|
||||
): Promise<boolean> {
|
||||
const userId = bytesToUuid(recipient);
|
||||
@@ -3242,10 +3274,10 @@ export class CallingClass {
|
||||
|
||||
// Used to send a variety of group call messages, including the initial call message
|
||||
async #handleSendCallMessageToGroup(
|
||||
groupIdBytes: Uint8Array,
|
||||
data: Uint8Array,
|
||||
groupIdBytes: Uint8Array<ArrayBuffer>,
|
||||
data: Uint8Array<ArrayBuffer>,
|
||||
urgency: CallMessageUrgency,
|
||||
overrideRecipients: Array<Uint8Array> = []
|
||||
overrideRecipients: Array<Uint8Array<ArrayBuffer>> = []
|
||||
): Promise<boolean> {
|
||||
const groupId = Bytes.toBase64(groupIdBytes);
|
||||
const conversation = window.ConversationController.get(groupId);
|
||||
@@ -3313,9 +3345,9 @@ export class CallingClass {
|
||||
}
|
||||
|
||||
async #handleGroupCallRingUpdate(
|
||||
groupIdBytes: Uint8Array,
|
||||
groupIdBytes: Uint8Array<ArrayBuffer>,
|
||||
ringId: bigint,
|
||||
ringerBytes: Uint8Array,
|
||||
ringerBytes: Uint8Array<ArrayBuffer>,
|
||||
update: RingUpdate
|
||||
): Promise<void> {
|
||||
log.info(`handleGroupCallRingUpdate(): got ring update ${update}`);
|
||||
@@ -3773,7 +3805,7 @@ export class CallingClass {
|
||||
url: string,
|
||||
method: HttpMethod,
|
||||
headers: { [name: string]: string },
|
||||
body: Uint8Array | undefined
|
||||
body: Uint8Array<ArrayBuffer> | undefined
|
||||
) {
|
||||
const httpMethod = RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD.get(method);
|
||||
if (httpMethod === undefined) {
|
||||
|
||||
@@ -213,7 +213,7 @@ export class KeyTransparency {
|
||||
};
|
||||
}
|
||||
|
||||
let usernameHash: Uint8Array | undefined;
|
||||
let usernameHash: Uint8Array<ArrayBuffer> | undefined;
|
||||
|
||||
const username = me.get('username');
|
||||
if (username != null && !itemStorage.get('usernameCorrupted')) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { StorageInterface } from '../types/Storage.d.ts';
|
||||
const log = createLogger('ourProfileKey');
|
||||
|
||||
export class OurProfileKeyService {
|
||||
private getPromise: undefined | Promise<undefined | Uint8Array>;
|
||||
private getPromise: undefined | Promise<undefined | Uint8Array<ArrayBuffer>>;
|
||||
|
||||
#promisesBlockingGet: Array<Promise<unknown>> = [];
|
||||
#storage?: StorageInterface;
|
||||
@@ -28,7 +28,7 @@ export class OurProfileKeyService {
|
||||
this.#storage = storage;
|
||||
}
|
||||
|
||||
get(): Promise<undefined | Uint8Array> {
|
||||
get(): Promise<undefined | Uint8Array<ArrayBuffer>> {
|
||||
if (this.getPromise) {
|
||||
log.info(
|
||||
'Our profile key service: was already fetching. Piggybacking off of that'
|
||||
@@ -40,7 +40,7 @@ export class OurProfileKeyService {
|
||||
return this.getPromise;
|
||||
}
|
||||
|
||||
async set(newValue: undefined | Uint8Array): Promise<void> {
|
||||
async set(newValue: undefined | Uint8Array<ArrayBuffer>): Promise<void> {
|
||||
assertDev(this.#storage, 'OurProfileKeyService was not initialized');
|
||||
if (newValue != null) {
|
||||
strictAssert(
|
||||
@@ -59,7 +59,7 @@ export class OurProfileKeyService {
|
||||
this.#promisesBlockingGet.push(promise);
|
||||
}
|
||||
|
||||
async #doGet(): Promise<undefined | Uint8Array> {
|
||||
async #doGet(): Promise<undefined | Uint8Array<ArrayBuffer>> {
|
||||
log.info(
|
||||
`Our profile key service: waiting for ${this.#promisesBlockingGet.length} promises before fetching`
|
||||
);
|
||||
|
||||
@@ -458,11 +458,14 @@ async function buildProfileFetchOptions({
|
||||
};
|
||||
}
|
||||
|
||||
function decryptField(field: string, decryptionKey: Uint8Array): Uint8Array {
|
||||
function decryptField(
|
||||
field: string,
|
||||
decryptionKey: Uint8Array<ArrayBuffer>
|
||||
): Uint8Array<ArrayBuffer> {
|
||||
return decryptProfile(Bytes.fromBase64(field), decryptionKey);
|
||||
}
|
||||
|
||||
function formatTextField(decrypted: Uint8Array): string {
|
||||
function formatTextField(decrypted: Uint8Array<ArrayBuffer>): string {
|
||||
return Bytes.toString(trimForDisplay(decrypted));
|
||||
}
|
||||
|
||||
@@ -861,7 +864,7 @@ export type UpdateIdentityKeyOptionsType = Readonly<{
|
||||
}>;
|
||||
|
||||
export async function updateIdentityKey(
|
||||
identityKey: Uint8Array,
|
||||
identityKey: Uint8Array<ArrayBuffer>,
|
||||
serviceId: ServiceIdString,
|
||||
{ noOverwrite = false }: UpdateIdentityKeyOptionsType = {}
|
||||
): Promise<boolean> {
|
||||
|
||||
@@ -154,7 +154,7 @@ const conflictBackOff = new BackOff([
|
||||
|
||||
function encryptRecord(
|
||||
storageID: string | undefined,
|
||||
recordIkm: Uint8Array | undefined,
|
||||
recordIkm: Uint8Array<ArrayBuffer> | undefined,
|
||||
storageRecord: Proto.StorageRecord.Params
|
||||
): Proto.StorageItem.Params {
|
||||
const storageKeyBuffer = storageID
|
||||
@@ -183,13 +183,13 @@ function encryptRecord(
|
||||
};
|
||||
}
|
||||
|
||||
function generateStorageID(): Uint8Array {
|
||||
function generateStorageID(): Uint8Array<ArrayBuffer> {
|
||||
return getRandomBytes(16);
|
||||
}
|
||||
|
||||
type GeneratedManifestType = {
|
||||
postUploadUpdateFunctions: Array<() => unknown>;
|
||||
recordIkm: Uint8Array | undefined;
|
||||
recordIkm: Uint8Array<ArrayBuffer> | undefined;
|
||||
recordsByID: Map<string, GeneratedItemType | RemoteRecord>;
|
||||
insertKeys: Set<string>;
|
||||
deleteKeys: Set<string>;
|
||||
@@ -892,7 +892,7 @@ async function generateManifest(
|
||||
// If we have a copy of what the current remote manifest is then we run these
|
||||
// additional validations comparing our pending manifest to the remote
|
||||
// manifest:
|
||||
let recordIkm: Uint8Array | undefined;
|
||||
let recordIkm: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (previousManifest) {
|
||||
const pendingInserts: Set<string> = new Set();
|
||||
const pendingDeletes: Set<string> = new Set();
|
||||
@@ -984,7 +984,7 @@ async function generateManifest(
|
||||
|
||||
type EncryptManifestOptionsType = {
|
||||
recordsByID: Map<string, GeneratedItemType | RemoteRecord>;
|
||||
recordIkm: Uint8Array | undefined;
|
||||
recordIkm: Uint8Array<ArrayBuffer> | undefined;
|
||||
insertKeys: Set<string>;
|
||||
};
|
||||
|
||||
@@ -1833,7 +1833,7 @@ export type FetchRemoteRecordsResultType = Readonly<{
|
||||
|
||||
async function fetchRemoteRecords(
|
||||
storageVersion: number,
|
||||
recordIkm: Uint8Array | undefined,
|
||||
recordIkm: Uint8Array<ArrayBuffer> | undefined,
|
||||
remoteOnlyRecords: Map<string, RemoteRecord>
|
||||
): Promise<FetchRemoteRecordsResultType> {
|
||||
const storageKeyBase64 = itemStorage.get('storageKey');
|
||||
|
||||
@@ -261,7 +261,7 @@ function addUnknownFieldsToConversation(
|
||||
|
||||
function conversationUnknownFieldsToRecord(
|
||||
conversation: ConversationModel
|
||||
): Array<Uint8Array> | null {
|
||||
): Array<Uint8Array<ArrayBuffer>> | null {
|
||||
const storageUnknownFields = conversation.get('storageUnknownFields');
|
||||
if (storageUnknownFields == null) {
|
||||
return null;
|
||||
@@ -275,8 +275,8 @@ function conversationUnknownFieldsToRecord(
|
||||
|
||||
// Other records save a UInt8Array to the database
|
||||
function toStorageUnknownFields(
|
||||
unknownFields: ReadonlyArray<Uint8Array> | undefined
|
||||
): Uint8Array | null {
|
||||
unknownFields: ReadonlyArray<Uint8Array<ArrayBuffer>> | undefined
|
||||
): Uint8Array<ArrayBuffer> | null {
|
||||
if (!unknownFields) {
|
||||
return null;
|
||||
}
|
||||
@@ -284,8 +284,8 @@ function toStorageUnknownFields(
|
||||
return Bytes.concatenate(unknownFields);
|
||||
}
|
||||
function fromStorageUnknownFields(
|
||||
storageUnknownFields: Uint8Array | null | undefined
|
||||
): Array<Uint8Array> | null {
|
||||
storageUnknownFields: Uint8Array<ArrayBuffer> | null | undefined
|
||||
): Array<Uint8Array<ArrayBuffer>> | null {
|
||||
if (storageUnknownFields == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -974,7 +974,7 @@ function logRecordChanges(
|
||||
|
||||
// Sometimes we have a ByteBuffer and an Uint8Array, this ensures that we
|
||||
// are comparing them both equally by converting them into base64 string.
|
||||
if (localValue instanceof Uint8Array) {
|
||||
if (Bytes.isNonSharedUint8Array(localValue)) {
|
||||
const areEqual = Bytes.areEqual(localValue, remoteValue);
|
||||
if (!areEqual) {
|
||||
details.push(`key=${key}: different bytes`);
|
||||
@@ -1148,7 +1148,7 @@ export async function mergeGroupV1Record(
|
||||
}
|
||||
|
||||
function getGroupV2Conversation(
|
||||
masterKeyBuffer: Uint8Array
|
||||
masterKeyBuffer: Uint8Array<ArrayBuffer>
|
||||
): ConversationModel {
|
||||
const groupFields = deriveGroupFields(masterKeyBuffer);
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ export async function confirmUsername(
|
||||
await itemStorage.remove('usernameLink');
|
||||
|
||||
let serverIdString: string;
|
||||
let entropy: Uint8Array;
|
||||
let entropy: Uint8Array<ArrayBuffer>;
|
||||
if (previousLink && isCaseChange(reservation)) {
|
||||
log.info('confirmUsername: updating link only');
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export async function writeProfile(
|
||||
if (options.keepAvatar) {
|
||||
const profileAvatarUrl = getLocalAvatarUrl(model.attributes);
|
||||
|
||||
let avatarBuffer: Uint8Array | undefined;
|
||||
let avatarBuffer: Uint8Array<ArrayBuffer> | undefined;
|
||||
if (profileAvatarUrl) {
|
||||
try {
|
||||
avatarBuffer = await imagePathToBytes(profileAvatarUrl);
|
||||
|
||||
@@ -301,7 +301,7 @@ function specToBytes<Input, Output>(
|
||||
spec: ObjectMappingSpecType,
|
||||
data: Input
|
||||
): Output {
|
||||
return mapObjectWithSpec<string, Uint8Array>(spec, data, x =>
|
||||
return mapObjectWithSpec<string, Uint8Array<ArrayBuffer>>(spec, data, x =>
|
||||
Bytes.fromBase64(x)
|
||||
);
|
||||
}
|
||||
@@ -310,7 +310,7 @@ function specFromBytes<Input, Output>(
|
||||
spec: ObjectMappingSpecType,
|
||||
data: Input
|
||||
): Output {
|
||||
return mapObjectWithSpec<Uint8Array, string>(spec, data, x =>
|
||||
return mapObjectWithSpec<Uint8Array<ArrayBuffer>, string>(spec, data, x =>
|
||||
Bytes.toBase64(x)
|
||||
);
|
||||
}
|
||||
|
||||
+16
-16
@@ -124,7 +124,7 @@ export type IdentityKeyType = {
|
||||
firstUse: boolean;
|
||||
id: ServiceIdString | `conversation:${string}`;
|
||||
nonblockingApproval: boolean;
|
||||
publicKey: Uint8Array;
|
||||
publicKey: Uint8Array<ArrayBuffer>;
|
||||
timestamp: number;
|
||||
verified: number;
|
||||
};
|
||||
@@ -239,7 +239,7 @@ export type KyberPreKeyType = {
|
||||
id: PreKeyIdType;
|
||||
|
||||
createdAt: number;
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
isConfirmed: boolean;
|
||||
isLastResort: boolean;
|
||||
keyId: number;
|
||||
@@ -254,8 +254,8 @@ export type PreKeyType = {
|
||||
createdAt: number;
|
||||
keyId: number;
|
||||
ourServiceId: ServiceIdString;
|
||||
privateKey: Uint8Array;
|
||||
publicKey: Uint8Array;
|
||||
privateKey: Uint8Array<ArrayBuffer>;
|
||||
publicKey: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
|
||||
export type StoredPreKeyType = PreKeyType & {
|
||||
@@ -281,14 +281,14 @@ export type ClientSearchResultMessageType = MessageType & {
|
||||
|
||||
export type SentProtoType = {
|
||||
contentHint: number;
|
||||
proto: Uint8Array;
|
||||
proto: Uint8Array<ArrayBuffer>;
|
||||
timestamp: number;
|
||||
urgent: boolean;
|
||||
hasPniSignatureMessage: boolean;
|
||||
};
|
||||
export type SentProtoDBType = {
|
||||
contentHint: number;
|
||||
proto: Uint8Array;
|
||||
proto: Uint8Array<ArrayBuffer>;
|
||||
timestamp: number;
|
||||
urgent: number;
|
||||
hasPniSignatureMessage: number;
|
||||
@@ -317,7 +317,7 @@ export type SenderKeyType = {
|
||||
senderId: string;
|
||||
distributionId: string;
|
||||
// Raw data to serialize/deserialize into signal-client SenderKeyRecord
|
||||
data: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
lastUpdatedDate: number;
|
||||
};
|
||||
export type SenderKeyIdType = SenderKeyType['id'];
|
||||
@@ -327,7 +327,7 @@ export type SessionType = {
|
||||
serviceId: ServiceIdString;
|
||||
conversationId: string;
|
||||
deviceId: number;
|
||||
record: Uint8Array;
|
||||
record: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
export type SessionIdType = SessionType['id'];
|
||||
export type SignedPreKeyType = {
|
||||
@@ -336,8 +336,8 @@ export type SignedPreKeyType = {
|
||||
ourServiceId: ServiceIdString;
|
||||
id: `${ServiceIdString}:${number}`;
|
||||
keyId: number;
|
||||
privateKey: Uint8Array;
|
||||
publicKey: Uint8Array;
|
||||
privateKey: Uint8Array<ArrayBuffer>;
|
||||
publicKey: Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
export type StoredSignedPreKeyType = {
|
||||
confirmed: boolean;
|
||||
@@ -381,7 +381,7 @@ export type StickerPackStatusType = (typeof StickerPackStatuses)[number];
|
||||
export type StorageServiceFieldsType = Readonly<{
|
||||
storageID?: string | null;
|
||||
storageVersion?: number | null;
|
||||
storageUnknownFields?: Uint8Array | null;
|
||||
storageUnknownFields?: Uint8Array<ArrayBuffer> | null;
|
||||
storageNeedsSync: boolean;
|
||||
}>;
|
||||
|
||||
@@ -441,7 +441,7 @@ export type UnprocessedType = {
|
||||
attempts: number;
|
||||
type: number;
|
||||
isEncrypted: boolean;
|
||||
content: Uint8Array;
|
||||
content: Uint8Array<ArrayBuffer>;
|
||||
|
||||
messageAgeSec: number;
|
||||
source: string | undefined;
|
||||
@@ -453,7 +453,7 @@ export type UnprocessedType = {
|
||||
serverTimestamp: number;
|
||||
urgent: boolean;
|
||||
story: boolean;
|
||||
reportingToken: Uint8Array | undefined;
|
||||
reportingToken: Uint8Array<ArrayBuffer> | undefined;
|
||||
groupId: string | undefined;
|
||||
};
|
||||
|
||||
@@ -675,7 +675,7 @@ export type NonAttachmentMediaItemDBType =
|
||||
export type KyberPreKeyTripleType = Readonly<{
|
||||
id: PreKeyIdType;
|
||||
signedPreKeyId: number;
|
||||
baseKey: Uint8Array;
|
||||
baseKey: Uint8Array<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
export type GetMessagesBetweenOptions = Readonly<{
|
||||
@@ -1074,7 +1074,7 @@ type ReadableInterface = {
|
||||
hasMegaphone: (megaphoneId: RemoteMegaphoneId) => boolean;
|
||||
|
||||
getAllKTAcis: () => ReadonlyArray<AciString>;
|
||||
getKTAccountData: (aci: AciString) => Uint8Array | undefined;
|
||||
getKTAccountData: (aci: AciString) => Uint8Array<ArrayBuffer> | undefined;
|
||||
|
||||
getAllPinnedMessages: () => ReadonlyArray<PinnedMessage>;
|
||||
getPinnedMessagesPreloadDataForConversation: (
|
||||
@@ -1450,7 +1450,7 @@ type WritableInterface = {
|
||||
internalDeleteAllMegaphones: () => number;
|
||||
|
||||
removeAllKTAccountData: () => void;
|
||||
setKTAccountData: (aci: AciString, data: Uint8Array) => void;
|
||||
setKTAccountData: (aci: AciString, data: Uint8Array<ArrayBuffer>) => void;
|
||||
|
||||
appendPinnedMessage: (
|
||||
pinnedMessagesLimit: number,
|
||||
|
||||
+10
-10
@@ -346,7 +346,7 @@ type StickerRow = Readonly<{
|
||||
type StorageServiceRowFields = Readonly<{
|
||||
storageID?: string;
|
||||
storageVersion?: number;
|
||||
storageUnknownFields?: Uint8Array | null;
|
||||
storageUnknownFields?: Uint8Array<ArrayBuffer> | null;
|
||||
storageNeedsSync: number;
|
||||
}>;
|
||||
type InstalledStickerPackRow = Readonly<{
|
||||
@@ -2978,9 +2978,9 @@ function getAndProtectExistingAttachmentPath(
|
||||
screenshotVersion,
|
||||
screenshotContentType,
|
||||
screenshotSize
|
||||
FROM message_attachments
|
||||
WHERE
|
||||
plaintextHash = ${plaintextHash} AND
|
||||
FROM message_attachments
|
||||
WHERE
|
||||
plaintextHash = ${plaintextHash} AND
|
||||
path IS NOT NULL AND
|
||||
version = ${version} AND
|
||||
contentType = ${contentType}
|
||||
@@ -3017,7 +3017,7 @@ function _protectAttachmentPathFromDeletion(
|
||||
const [protectQuery, protectParams] = sql`
|
||||
INSERT OR REPLACE INTO attachments_protected_from_deletion
|
||||
(path, messageId)
|
||||
VALUES
|
||||
VALUES
|
||||
(${path}, ${messageId});
|
||||
`;
|
||||
db.prepare(protectQuery).run(protectParams);
|
||||
@@ -3038,11 +3038,11 @@ function getAllProtectedAttachmentPaths(db: ReadableDB): Array<string> {
|
||||
function isAttachmentSafeToDelete(db: ReadableDB, path: string): boolean {
|
||||
const [query, params] = sql`
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM attachments_protected_from_deletion
|
||||
SELECT 1 FROM attachments_protected_from_deletion
|
||||
WHERE path = ${path}
|
||||
UNION ALL
|
||||
SELECT 1 FROM message_attachments
|
||||
WHERE
|
||||
SELECT 1 FROM message_attachments
|
||||
WHERE
|
||||
path = ${path} OR
|
||||
thumbnailPath = ${path} OR
|
||||
screenshotPath = ${path} OR
|
||||
@@ -3058,7 +3058,7 @@ function getMostRecentAttachmentUploadData(
|
||||
plaintextHash: string
|
||||
): ExistingAttachmentUploadData | undefined {
|
||||
const [query, params] = sql`
|
||||
SELECT
|
||||
SELECT
|
||||
key,
|
||||
digest,
|
||||
transitCdnKey AS cdnKey,
|
||||
@@ -3068,7 +3068,7 @@ function getMostRecentAttachmentUploadData(
|
||||
incrementalMacChunkSize as chunkSize
|
||||
FROM message_attachments
|
||||
INDEXED BY message_attachments_plaintextHash
|
||||
WHERE
|
||||
WHERE
|
||||
plaintextHash = ${plaintextHash} AND
|
||||
key IS NOT NULL AND
|
||||
digest IS NOT NULL AND
|
||||
|
||||
@@ -7,6 +7,7 @@ import { createLogger } from '../logging/log.std.js';
|
||||
import { isIterable } from '../util/iterables.std.js';
|
||||
|
||||
import { toNumber } from '../util/toNumber.std.js';
|
||||
import { isNonSharedUint8Array } from '../Bytes.std.js';
|
||||
|
||||
const { isPlainObject } = lodash;
|
||||
|
||||
@@ -42,7 +43,7 @@ type CleanedDataValue =
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| Uint8Array
|
||||
| Uint8Array<ArrayBuffer>
|
||||
| CleanedObject
|
||||
| CleanedArray;
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
@@ -129,7 +130,7 @@ function cleanDataInner(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (data instanceof Uint8Array) {
|
||||
if (isNonSharedUint8Array(data)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ type NextSessionRowType = Readonly<{
|
||||
ourServiceId: string;
|
||||
serviceId: string;
|
||||
deviceId: number;
|
||||
record: Buffer;
|
||||
record: Buffer<ArrayBuffer>;
|
||||
}>;
|
||||
|
||||
function migrateSession(
|
||||
|
||||
@@ -224,9 +224,10 @@ export function updateCallLinkAdminKeyByRoomId(
|
||||
}
|
||||
|
||||
function assertRoomIdMatchesRootKey(roomId: string, rootKey: string): void {
|
||||
const derivedRoomId = Bytes.toHex(
|
||||
CallLinkRootKey.parse(rootKey).deriveRoomId()
|
||||
);
|
||||
const parsedRoomId = CallLinkRootKey.parse(rootKey).deriveRoomId();
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const derivedRoomIdBytes: Uint8Array<ArrayBuffer> = parsedRoomId;
|
||||
const derivedRoomId = Bytes.toHex(derivedRoomIdBytes);
|
||||
strictAssert(
|
||||
roomId === derivedRoomId,
|
||||
'passed roomId must match roomId derived from root key'
|
||||
|
||||
@@ -15,19 +15,21 @@ export function getAllKTAcis(db: ReadableDB): Array<AciString> {
|
||||
export function getKTAccountData(
|
||||
db: ReadableDB,
|
||||
aci: AciString
|
||||
): Uint8Array | undefined {
|
||||
): Uint8Array<ArrayBuffer> | undefined {
|
||||
const [query, params] = sql`
|
||||
SELECT data
|
||||
FROM key_transparency_account_data
|
||||
WHERE aci IS ${aci}
|
||||
`;
|
||||
return db.prepare(query, { pluck: true }).get<Uint8Array>(params);
|
||||
return db
|
||||
.prepare(query, { pluck: true })
|
||||
.get<Uint8Array<ArrayBuffer>>(params);
|
||||
}
|
||||
|
||||
export function setKTAccountData(
|
||||
db: WritableDB,
|
||||
aci: AciString,
|
||||
data: Uint8Array
|
||||
data: Uint8Array<ArrayBuffer>
|
||||
): void {
|
||||
const [query, params] = sql`
|
||||
INSERT OR REPLACE INTO key_transparency_account_data
|
||||
|
||||
+5
-1
@@ -38,7 +38,11 @@ export function jsonToObject<T>(json: string): T {
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
export type QueryTemplateParam = Uint8Array | string | number | null;
|
||||
export type QueryTemplateParam =
|
||||
| Uint8Array<ArrayBuffer>
|
||||
| string
|
||||
| number
|
||||
| null;
|
||||
export type QueryFragmentValue = QueryFragment | QueryTemplateParam;
|
||||
|
||||
export class QueryFragment {
|
||||
|
||||
@@ -131,6 +131,7 @@ import {
|
||||
import { itemStorage } from '../../textsecure/Storage.preload.js';
|
||||
import type { SizeCallbackType } from '../../calling/VideoSupport.preload.js';
|
||||
import type { NoopActionType } from './noop.std.js';
|
||||
import type { SignalService } from '../../protobuf/index.std.js';
|
||||
|
||||
const { omit } = lodash;
|
||||
|
||||
@@ -2962,33 +2963,42 @@ function submitCallQualitySurvey(
|
||||
const { qualityStats } = callSummary;
|
||||
const { audioStats, videoStats } = qualityStats;
|
||||
|
||||
const surveyRequest = {
|
||||
userSatisfied,
|
||||
callQualityIssues: userSatisfied ? [] : Array.from(callQualityIssues),
|
||||
additionalIssuesDescription:
|
||||
!userSatisfied &&
|
||||
callQualityIssues.includes(CallQualitySurvey.Issue.OTHER)
|
||||
? additionalIssuesDescription
|
||||
: null,
|
||||
debugLogUrl: debugLogUrl ?? null,
|
||||
startTimestamp: BigInt(callSummary.startTime),
|
||||
endTimestamp: BigInt(callSummary.endTime),
|
||||
callType,
|
||||
success: !isCallFailure(callSummary.callEndReasonText),
|
||||
callEndReason: callSummary.callEndReasonText,
|
||||
connectionRttMedian: qualityStats.rttMedianConnectionMillis ?? null,
|
||||
audioRttMedian: audioStats.rttMedianMillis ?? null,
|
||||
videoRttMedian: videoStats.rttMedianMillis ?? null,
|
||||
audioRecvJitterMedian: audioStats.jitterMedianRecvMillis ?? null,
|
||||
videoRecvJitterMedian: videoStats.jitterMedianRecvMillis ?? null,
|
||||
audioSendJitterMedian: audioStats.jitterMedianSendMillis ?? null,
|
||||
videoSendJitterMedian: videoStats.jitterMedianSendMillis ?? null,
|
||||
audioRecvPacketLossFraction: audioStats.packetLossFractionRecv ?? null,
|
||||
videoRecvPacketLossFraction: videoStats.packetLossFractionRecv ?? null,
|
||||
audioSendPacketLossFraction: audioStats.packetLossFractionSend ?? null,
|
||||
videoSendPacketLossFraction: videoStats.packetLossFractionSend ?? null,
|
||||
callTelemetry: callSummary.rawStats ?? null,
|
||||
};
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const callTelemetry: Uint8Array<ArrayBuffer> | null =
|
||||
callSummary.rawStats ?? null;
|
||||
|
||||
const surveyRequest: SignalService.SubmitCallQualitySurveyRequest.Params =
|
||||
{
|
||||
userSatisfied,
|
||||
callQualityIssues: userSatisfied ? [] : Array.from(callQualityIssues),
|
||||
additionalIssuesDescription:
|
||||
!userSatisfied &&
|
||||
callQualityIssues.includes(CallQualitySurvey.Issue.OTHER)
|
||||
? additionalIssuesDescription
|
||||
: null,
|
||||
debugLogUrl: debugLogUrl ?? null,
|
||||
startTimestamp: BigInt(callSummary.startTime),
|
||||
endTimestamp: BigInt(callSummary.endTime),
|
||||
callType,
|
||||
success: !isCallFailure(callSummary.callEndReasonText),
|
||||
callEndReason: callSummary.callEndReasonText,
|
||||
connectionRttMedian: qualityStats.rttMedianConnectionMillis ?? null,
|
||||
audioRttMedian: audioStats.rttMedianMillis ?? null,
|
||||
videoRttMedian: videoStats.rttMedianMillis ?? null,
|
||||
audioRecvJitterMedian: audioStats.jitterMedianRecvMillis ?? null,
|
||||
videoRecvJitterMedian: videoStats.jitterMedianRecvMillis ?? null,
|
||||
audioSendJitterMedian: audioStats.jitterMedianSendMillis ?? null,
|
||||
videoSendJitterMedian: videoStats.jitterMedianSendMillis ?? null,
|
||||
audioRecvPacketLossFraction:
|
||||
audioStats.packetLossFractionRecv ?? null,
|
||||
videoRecvPacketLossFraction:
|
||||
videoStats.packetLossFractionRecv ?? null,
|
||||
audioSendPacketLossFraction:
|
||||
audioStats.packetLossFractionSend ?? null,
|
||||
videoSendPacketLossFraction:
|
||||
videoStats.packetLossFractionSend ?? null,
|
||||
callTelemetry,
|
||||
};
|
||||
|
||||
await submitCallQualitySurveyToServer(surveyRequest);
|
||||
|
||||
|
||||
@@ -552,7 +552,7 @@ export type PreJoinConversationType = ReadonlyDeep<{
|
||||
}>;
|
||||
|
||||
type ComposerGroupCreationState = ReadonlyDeep<{
|
||||
groupAvatar: undefined | Uint8Array;
|
||||
groupAvatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
groupName: string;
|
||||
groupExpireTimer: DurationInSeconds;
|
||||
maximumGroupSizeModalState: OneTimeModalState;
|
||||
@@ -1003,7 +1003,7 @@ export type ShowArchivedConversationsActionType = ReadonlyDeep<{
|
||||
}>;
|
||||
type SetComposeGroupAvatarActionType = ReadonlyDeep<{
|
||||
type: 'SET_COMPOSE_GROUP_AVATAR';
|
||||
payload: { groupAvatar: undefined | Uint8Array };
|
||||
payload: { groupAvatar: undefined | Uint8Array<ArrayBuffer> };
|
||||
}>;
|
||||
type SetComposeGroupNameActionType = ReadonlyDeep<{
|
||||
type: 'SET_COMPOSE_GROUP_NAME';
|
||||
@@ -4474,7 +4474,7 @@ export function scrollToMessage(
|
||||
}
|
||||
|
||||
function setComposeGroupAvatar(
|
||||
groupAvatar: undefined | Uint8Array
|
||||
groupAvatar: undefined | Uint8Array<ArrayBuffer>
|
||||
): SetComposeGroupAvatarActionType {
|
||||
return {
|
||||
type: 'SET_COMPOSE_GROUP_AVATAR',
|
||||
@@ -4649,7 +4649,7 @@ export type UpdateGroupAttributesType = ReadonlyDeep<
|
||||
function updateGroupAttributes(
|
||||
conversationId: string,
|
||||
attributes: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
avatar?: undefined | Uint8Array<ArrayBuffer>;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>,
|
||||
@@ -6957,7 +6957,7 @@ export function reducer(
|
||||
let recommendedGroupSizeModalState: OneTimeModalState;
|
||||
let maximumGroupSizeModalState: OneTimeModalState;
|
||||
let groupName: string;
|
||||
let groupAvatar: undefined | Uint8Array;
|
||||
let groupAvatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
let groupExpireTimer: DurationInSeconds;
|
||||
let userAvatarData = getDefaultAvatars(true);
|
||||
|
||||
|
||||
@@ -978,7 +978,7 @@ const getGroupCreationComposerState = createSelector(
|
||||
composerState
|
||||
): {
|
||||
groupName: string;
|
||||
groupAvatar: undefined | Uint8Array;
|
||||
groupAvatar: undefined | Uint8Array<ArrayBuffer>;
|
||||
groupExpireTimer: DurationInSeconds;
|
||||
selectedConversationIds: ReadonlyArray<string>;
|
||||
} => {
|
||||
@@ -1003,7 +1003,8 @@ const getGroupCreationComposerState = createSelector(
|
||||
|
||||
export const getComposeGroupAvatar = createSelector(
|
||||
getGroupCreationComposerState,
|
||||
(composerState): undefined | Uint8Array => composerState.groupAvatar
|
||||
(composerState): undefined | Uint8Array<ArrayBuffer> =>
|
||||
composerState.groupAvatar
|
||||
);
|
||||
|
||||
export const getComposeGroupName = createSelector(
|
||||
|
||||
@@ -147,7 +147,7 @@ class SmallChunksTransform extends Transform {
|
||||
}
|
||||
|
||||
override _transform(
|
||||
incomingChunk: Buffer | undefined,
|
||||
incomingChunk: Buffer<ArrayBuffer> | undefined,
|
||||
_encoding: string,
|
||||
done: (error?: Error) => void
|
||||
) {
|
||||
@@ -179,7 +179,7 @@ class SmallChunksTransform extends Transform {
|
||||
}
|
||||
}
|
||||
|
||||
function generateAvatar(): Uint8Array {
|
||||
function generateAvatar(): Uint8Array<ArrayBuffer> {
|
||||
const result = new Uint8Array(255);
|
||||
for (let i = 0; i < result.length; i += 1) {
|
||||
result[i] = i;
|
||||
@@ -187,11 +187,11 @@ function generateAvatar(): Uint8Array {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getTestBuffer(): Uint8Array {
|
||||
function getTestBuffer(): Uint8Array<ArrayBuffer> {
|
||||
const avatarBuffer = generateAvatar();
|
||||
const prefixedContact = generatePrefixedContact(avatarBuffer);
|
||||
|
||||
const chunks: Array<Uint8Array> = [];
|
||||
const chunks: Array<Uint8Array<ArrayBuffer>> = [];
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
chunks.push(...prefixedContact);
|
||||
chunks.push(avatarBuffer);
|
||||
@@ -201,9 +201,9 @@ function getTestBuffer(): Uint8Array {
|
||||
}
|
||||
|
||||
function generatePrefixedContact(
|
||||
avatarBuffer: Uint8Array | undefined,
|
||||
avatarBuffer: Uint8Array<ArrayBuffer> | undefined,
|
||||
aci: AciString | null = DEFAULT_ACI
|
||||
): [Uint8Array, Uint8Array] {
|
||||
): [Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>] {
|
||||
const contactInfoBuffer = Proto.ContactDetails.encode({
|
||||
name: 'Zero Cool',
|
||||
number: '+10000000000',
|
||||
|
||||
@@ -728,9 +728,9 @@ describe('Crypto', () => {
|
||||
overrideSize,
|
||||
}: {
|
||||
path?: string;
|
||||
data: Uint8Array;
|
||||
plaintextHash?: Uint8Array;
|
||||
encryptionKeys?: Uint8Array;
|
||||
data: Uint8Array<ArrayBuffer>;
|
||||
plaintextHash?: Uint8Array<ArrayBuffer>;
|
||||
encryptionKeys?: Uint8Array<ArrayBuffer>;
|
||||
modifyIncrementalMac?: boolean;
|
||||
overrideSize?: number;
|
||||
}): Promise<DecryptedAttachmentV2> {
|
||||
@@ -986,8 +986,8 @@ describe('Crypto', () => {
|
||||
outerKeys,
|
||||
}: {
|
||||
plaintextAbsolutePath: string;
|
||||
innerKeys: Uint8Array;
|
||||
outerKeys: Uint8Array;
|
||||
innerKeys: Uint8Array<ArrayBuffer>;
|
||||
outerKeys: Uint8Array<ArrayBuffer>;
|
||||
}) {
|
||||
let innerCiphertextPath;
|
||||
let outerCiphertextPath;
|
||||
|
||||
@@ -179,10 +179,12 @@ describe('backup/calling', () => {
|
||||
beforeEach(async () => {
|
||||
const adminRootKey = CallLinkRootKey.generate();
|
||||
const adminKey = CallLinkRootKey.generateAdminPassKey();
|
||||
// @ts-expect-error needs ringrtc update
|
||||
const adminKeyBytes: Uint8Array<ArrayBuffer> = adminKey;
|
||||
adminCallLink = {
|
||||
rootKey: adminRootKey.toString(),
|
||||
roomId: getRoomIdFromRootKey(adminRootKey),
|
||||
adminKey: fromAdminKeyBytes(adminKey),
|
||||
adminKey: fromAdminKeyBytes(adminKeyBytes),
|
||||
name: "Let's Talk Rocks",
|
||||
restrictions: CallLinkRestrictions.AdminApproval,
|
||||
revoked: false,
|
||||
|
||||
@@ -18,7 +18,9 @@ import {
|
||||
fetchLinkPreviewMetadata,
|
||||
} from '../../linkPreviews/linkPreviewFetch.preload.js';
|
||||
|
||||
async function readFixtureImage(filename: string): Promise<Uint8Array> {
|
||||
async function readFixtureImage(
|
||||
filename: string
|
||||
): Promise<Uint8Array<ArrayBuffer>> {
|
||||
const result = await fs.promises.readFile(
|
||||
path.join(__dirname, '..', '..', '..', 'fixtures', filename)
|
||||
);
|
||||
@@ -60,11 +62,15 @@ describe('link preview fetching', () => {
|
||||
}: {
|
||||
status?: number;
|
||||
headers?: { [key: string]: null | string };
|
||||
body?: null | string | Uint8Array | AsyncIterable<Uint8Array>;
|
||||
body?:
|
||||
| null
|
||||
| string
|
||||
| Uint8Array<ArrayBuffer>
|
||||
| AsyncIterable<Uint8Array<ArrayBuffer>>;
|
||||
url?: string;
|
||||
} = {}) => {
|
||||
let bodyLength: null | number;
|
||||
let bodyStream: null | AsyncIterable<Uint8Array>;
|
||||
let bodyStream: null | AsyncIterable<Uint8Array<ArrayBuffer>>;
|
||||
if (!body) {
|
||||
bodyLength = 0;
|
||||
bodyStream = null;
|
||||
|
||||
@@ -955,6 +955,7 @@ describe('AttachmentDownloadManager.runDownloadAttachmentJobInner', () => {
|
||||
.returns(Promise.resolve(downloadedAttachment));
|
||||
cleanupAttachmentFiles = sandbox.stub();
|
||||
maybeDeleteAttachmentFile = sandbox.stub();
|
||||
deleteDownloadFile = sandbox.stub();
|
||||
processNewAttachment = sandbox.stub().callsFake(attachment => attachment);
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { DAY } from '../../util/durations/constants.std.js';
|
||||
|
||||
describe('SenderCertificateService', () => {
|
||||
let fakeValidCertificate: SenderCertificate;
|
||||
let fakeValidEncodedCertificate: Uint8Array;
|
||||
let fakeValidEncodedCertificate: Uint8Array<ArrayBuffer>;
|
||||
let fakeValidCertificateExpiry: number;
|
||||
let fakeServer: any;
|
||||
let fakeEvents: Pick<typeof window.Whisper.events, 'on' | 'off'>;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user