Replace typescript compiler with native tsgo compiler

This commit is contained in:
Jamie
2026-03-18 11:26:18 -07:00
committed by GitHub
parent 5e6af4708b
commit c90ca2b4e0
207 changed files with 1819 additions and 1270 deletions
+41
View File
@@ -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],
},
],
});
+2
View File
@@ -274,6 +274,8 @@ const typescriptRules = {
],
},
],
'local-rules/enforce-array-buffer': 'error',
};
const TAILWIND_REPLACEMENTS = [
+1
View File
@@ -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
+58
View File
@@ -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
+6 -5
View File
@@ -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);
}
+6 -4
View File
@@ -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();
+4 -4
View File
@@ -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>({
+3 -3
View File
@@ -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
View File
@@ -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 {
+1
View File
@@ -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
View File
@@ -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",
+61
View File
@@ -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>;
+13
View File
@@ -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
+324 -284
View File
File diff suppressed because it is too large Load Diff
+32 -30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+7 -4
View File
@@ -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);
}
}
+13 -11
View File
@@ -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);
+1 -1
View File
@@ -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>;
+2 -2
View File
@@ -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) {
+15 -12
View File
@@ -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;
+3 -1
View File
@@ -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);
+5 -3
View File
@@ -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
+1 -1
View File
@@ -28,7 +28,7 @@ import {
const { noop } = lodash;
type DoneHandleType = (
avatarBuffer: Uint8Array,
avatarBuffer: Uint8Array<ArrayBuffer>,
avatarData: AvatarDataType
) => unknown;
+2 -2
View File
@@ -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) {
+4 -4
View File
@@ -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(() => {
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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 =
+1 -1
View File
@@ -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>;
+1 -1
View File
@@ -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>;
+2 -2
View File
@@ -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>;
+1 -1
View File
@@ -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>;
+6 -6
View File
@@ -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);
},
+1 -1
View File
@@ -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 (
<>
+1 -1
View File
@@ -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(
+2 -2
View File
@@ -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;
}>
@@ -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;
} = {};
+1
View File
@@ -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';
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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');
+2 -2
View File
@@ -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>>;
}
+3 -3
View File
@@ -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();
}
+1 -1
View File
@@ -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();
}
+1 -1
View File
@@ -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();
}
+5 -4
View File
@@ -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) {
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -276,7 +276,7 @@ export async function sendSyncMessageOnly(
saveErrors,
}: {
targetTimestamp: number;
dataMessage: Uint8Array;
dataMessage: Uint8Array<ArrayBuffer>;
saveErrors?: (errors: Array<Error>) => void;
}
): Promise<CallbackResultType | void> {
+1 -1
View File
@@ -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;
+7 -7
View File
@@ -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)
+1 -1
View File
@@ -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>;
}
}
+1 -1
View File
@@ -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'));
+1 -1
View File
@@ -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})`);
+8 -8
View File
@@ -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();
}
+12 -10
View File
@@ -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);
}
+2 -2
View File
@@ -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);
+4 -4
View File
@@ -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();
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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);
+10 -8
View File
@@ -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);
}
+54 -22
View File
@@ -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) {
+1 -1
View File
@@ -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')) {
+4 -4
View File
@@ -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`
);
+6 -3
View File
@@ -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> {
+6 -6
View File
@@ -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');
+7 -7
View File
@@ -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);
+1 -1
View File
@@ -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');
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -2
View File
@@ -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;
}
+1 -1
View File
@@ -48,7 +48,7 @@ type NextSessionRowType = Readonly<{
ourServiceId: string;
serviceId: string;
deviceId: number;
record: Buffer;
record: Buffer<ArrayBuffer>;
}>;
function migrateSession(
+4 -3
View File
@@ -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'
+5 -3
View File
@@ -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
View File
@@ -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 {
+37 -27
View File
@@ -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);
+5 -5
View File
@@ -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);
+3 -2
View File
@@ -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',
+5 -5
View File
@@ -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