made all verifi signatures in a single call to the worker

This commit is contained in:
Audric Ackermann 2022-04-06 13:11:50 +10:00
parent 940972db2f
commit 5047e8921b
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
7 changed files with 393 additions and 872 deletions

View File

@ -39,9 +39,7 @@
"clean-transpile": "rimraf 'ts/**/*.js' 'ts/*.js' 'ts/*.js.map' 'ts/**/*.js.map' && rimraf tsconfig.tsbuildinfo;",
"ready": "yarn grunt && yarn lint-full && yarn test",
"sedtoAppImage": "sed -i 's/\"target\": \\[\"deb\", \"rpm\", \"freebsd\"\\]/\"target\": \"AppImage\"/g' package.json",
"sedtoDeb": "sed -i 's/\"target\": \"AppImage\"/\"target\": \\[\"deb\", \"rpm\", \"freebsd\"\\]/g' package.json",
"build:webpack": "webpack --config ./webpack.config.js",
"start:webpack": "yarn build && electron ./dist/electron_main.js"
"sedtoDeb": "sed -i 's/\"target\": \"AppImage\"/\"target\": \\[\"deb\", \"rpm\", \"freebsd\"\\]/g' package.json"
},
"dependencies": {
"@reduxjs/toolkit": "^1.4.0",
@ -182,7 +180,6 @@
"grunt-exec": "3.0.0",
"grunt-gitinfo": "0.1.7",
"grunt-sass": "3.0.1",
"html-webpack-plugin": "^5.5.0",
"mocha": "4.1.0",
"mocha-testcheck": "1.0.0-rc.0",
"node-bindings-loader": "^1.5.0",
@ -195,15 +192,12 @@
"prettier": "1.19.0",
"run-script-os": "^1.1.6",
"sinon": "9.0.2",
"terser-webpack-plugin": "^5.3.1",
"ts-loader": "^9.2.8",
"ts-mock-imports": "^1.3.0",
"tslint": "5.19.0",
"tslint-microsoft-contrib": "6.0.0",
"tslint-react": "3.6.0",
"typescript": "^4.6.3",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2"
"typescript": "^4.6.3"
},
"engines": {
"node": "16.13.0"
@ -256,15 +250,12 @@
"StartupWMClass": "Session"
},
"asarUnpack": "node_modules/spellchecker/vendor/hunspell_dictionaries",
"target": [
"deb",
"rpm",
"freebsd"
],
"target": ["deb", "rpm", "freebsd"],
"icon": "build/icon-linux.icns"
},
"asarUnpack": [
"node_modules/better-sqlite3/build/Release/better_sqlite3.node"
"node_modules/better-sqlite3/build/Release/better_sqlite3.node",
"ts/mains/main_node.js"
],
"deb": {
"depends": [
@ -293,12 +284,11 @@
"stylesheets/*.css",
"!js/register.js",
"js/views/standalone_registration_view.js",
"dist/preload.bundled.js",
"preload.js",
"about_preload.js",
"settings_preload.js",
"debug_log_preload.js",
"password_preload.js",
"main.js",
"certificates/**",
"images/**",
"fonts/*",

View File

@ -0,0 +1,295 @@
diff --git a/node_modules/os-locale/execa.js b/node_modules/os-locale/execa.js
new file mode 100644
index 0000000..b8e0294
--- /dev/null
+++ b/node_modules/os-locale/execa.js
@@ -0,0 +1,29 @@
+// Mini wrapper around child_process to make it behave a little like execa
+
+const { promisify } = require('util');
+const childProcess = require('child_process');
+
+const execFile = promisify(childProcess.execFile);
+
+/**
+@param {string} command
+@param {string[]} args
+@returns Promise<import('child_process').ChildProcess>
+*/
+async function execa(command, args) {
+ const child = await execFile(command, args, { encoding: 'utf-8' });
+ child.stdout = child.stdout.trim();
+ return child;
+}
+
+/**
+@param {string} command
+@param {string[]} args
+@returns string
+*/
+function execaSync(command, args) {
+ return childProcess.execFileSync(command, args, { encoding: 'utf-8' }).trim();
+}
+
+module.exports = execa;
+module.exports.sync = execaSync;
diff --git a/node_modules/os-locale/index.js b/node_modules/os-locale/index.js
index 4500f22..799033e 100644
--- a/node_modules/os-locale/index.js
+++ b/node_modules/os-locale/index.js
@@ -1,128 +1,131 @@
'use strict';
-const execa = require('execa');
+const execa = require('./execa');
const lcid = require('lcid');
const mem = require('mem');
-const defaultOptions = {spawn: true};
+const defaultOptions = { spawn: true };
const defaultLocale = 'en-US';
async function getStdOut(command, args) {
- return (await execa(command, args)).stdout;
+ return (await execa(command, args)).stdout;
}
function getStdOutSync(command, args) {
- return execa.sync(command, args).stdout;
+ return execa.sync(command, args).stdout;
}
function getEnvLocale(env = process.env) {
- return env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
+ return env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
}
function parseLocale(string) {
- const env = string.split('\n').reduce((env, definition) => {
- const [key, value] = definition.split('=');
- env[key] = value.replace(/^"|"$/g, '');
- return env;
- }, {});
+ const env = string.split('\n').reduce((env, definition) => {
+ const [key, value] = definition.split('=');
+ env[key] = value.replace(/^"|"$/g, '');
+ return env;
+ }, {});
- return getEnvLocale(env);
+ return getEnvLocale(env);
}
function getLocale(string) {
- return (string && string.replace(/[.:].*/, ''));
+ return string && string.replace(/[.:].*/, '');
}
async function getLocales() {
- return getStdOut('locale', ['-a']);
+ return getStdOut('locale', ['-a']);
}
function getLocalesSync() {
- return getStdOutSync('locale', ['-a']);
+ return getStdOutSync('locale', ['-a']);
}
function getSupportedLocale(locale, locales = '') {
- return locales.includes(locale) ? locale : defaultLocale;
+ return locales.includes(locale) ? locale : defaultLocale;
}
async function getAppleLocale() {
- const results = await Promise.all([
- getStdOut('defaults', ['read', '-globalDomain', 'AppleLocale']),
- getLocales()
- ]);
+ const results = await Promise.all([
+ getStdOut('defaults', ['read', '-globalDomain', 'AppleLocale']),
+ getLocales(),
+ ]);
- return getSupportedLocale(results[0], results[1]);
+ return getSupportedLocale(results[0], results[1]);
}
function getAppleLocaleSync() {
- return getSupportedLocale(
- getStdOutSync('defaults', ['read', '-globalDomain', 'AppleLocale']),
- getLocalesSync()
- );
+ return getSupportedLocale(
+ getStdOutSync('defaults', ['read', '-globalDomain', 'AppleLocale']),
+ getLocalesSync()
+ );
}
async function getUnixLocale() {
- return getLocale(parseLocale(await getStdOut('locale')));
+ return getLocale(parseLocale(await getStdOut('locale')));
}
function getUnixLocaleSync() {
- return getLocale(parseLocale(getStdOutSync('locale')));
+ return getLocale(parseLocale(getStdOutSync('locale')));
}
async function getWinLocale() {
- const stdout = await getStdOut('wmic', ['os', 'get', 'locale']);
- const lcidCode = parseInt(stdout.replace('Locale', ''), 16);
+ const stdout = await getStdOut('wmic', ['os', 'get', 'locale']);
+ const lcidCode = parseInt(stdout.replace('Locale', ''), 16);
- return lcid.from(lcidCode);
+ return lcid.from(lcidCode);
}
function getWinLocaleSync() {
- const stdout = getStdOutSync('wmic', ['os', 'get', 'locale']);
- const lcidCode = parseInt(stdout.replace('Locale', ''), 16);
+ const stdout = getStdOutSync('wmic', ['os', 'get', 'locale']);
+ const lcidCode = parseInt(stdout.replace('Locale', ''), 16);
- return lcid.from(lcidCode);
+ return lcid.from(lcidCode);
}
function normalise(input) {
- return input.replace(/_/, '-');
+ return input.replace(/_/, '-');
}
-const osLocale = mem(async (options = defaultOptions) => {
- let locale;
-
- try {
- const envLocale = getEnvLocale();
-
- if (envLocale || options.spawn === false) {
- locale = getLocale(envLocale);
- } else if (process.platform === 'win32') {
- locale = await getWinLocale();
- } else if (process.platform === 'darwin') {
- locale = await getAppleLocale();
- } else {
- locale = await getUnixLocale();
- }
- } catch (_) {}
-
- return normalise(locale || defaultLocale);
-}, {cachePromiseRejection: false});
+const osLocale = mem(
+ async (options = defaultOptions) => {
+ let locale;
+
+ try {
+ const envLocale = getEnvLocale();
+
+ if (envLocale || options.spawn === false) {
+ locale = getLocale(envLocale);
+ } else if (process.platform === 'win32') {
+ locale = await getWinLocale();
+ } else if (process.platform === 'darwin') {
+ locale = await getAppleLocale();
+ } else {
+ locale = await getUnixLocale();
+ }
+ } catch (_) {}
+
+ return normalise(locale || defaultLocale);
+ },
+ { cachePromiseRejection: false }
+);
module.exports = osLocale;
module.exports.sync = mem((options = defaultOptions) => {
- let locale;
- try {
- const envLocale = getEnvLocale();
-
- if (envLocale || options.spawn === false) {
- locale = getLocale(envLocale);
- } else if (process.platform === 'win32') {
- locale = getWinLocaleSync();
- } else if (process.platform === 'darwin') {
- locale = getAppleLocaleSync();
- } else {
- locale = getUnixLocaleSync();
- }
- } catch (_) {}
-
- return normalise(locale || defaultLocale);
+ let locale;
+ try {
+ const envLocale = getEnvLocale();
+
+ if (envLocale || options.spawn === false) {
+ locale = getLocale(envLocale);
+ } else if (process.platform === 'win32') {
+ locale = getWinLocaleSync();
+ } else if (process.platform === 'darwin') {
+ locale = getAppleLocaleSync();
+ } else {
+ locale = getUnixLocaleSync();
+ }
+ } catch (_) {}
+
+ return normalise(locale || defaultLocale);
});
diff --git a/node_modules/os-locale/readme.md b/node_modules/os-locale/readme.md
deleted file mode 100644
index aec4e23..0000000
--- a/node_modules/os-locale/readme.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# os-locale [![Build Status](https://travis-ci.org/sindresorhus/os-locale.svg?branch=master)](https://travis-ci.org/sindresorhus/os-locale)
-
-> Get the system [locale](https://en.wikipedia.org/wiki/Locale_(computer_software))
-
-Useful for localizing your module or app.
-
-POSIX systems: The returned locale refers to the [`LC_MESSAGE`](http://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html#Locale-Categories) category, suitable for selecting the language used in the user interface for message translation.
-
-## Install
-
-```
-$ npm install os-locale
-```
-
-## Usage
-
-```js
-const osLocale = require('os-locale');
-
-(async () => {
- console.log(await osLocale());
- //=> 'en-US'
-})();
-```
-## API
-
-### osLocale(options?)
-
-Returns a `Promise` for the locale.
-
-### osLocale.sync(options?)
-
-Returns the locale.
-
-#### options
-
-Type: `object`
-
-##### spawn
-
-Type: `boolean`\
-Default: `true`
-
-Set to `false` to avoid spawning subprocesses and instead only resolve the locale from environment variables.
-
-## os-locale for enterprise
-
-Available as part of the Tidelift Subscription.
-
-The maintainers of os-locale and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-os-locale?utm_source=npm-os-locale&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

View File

@ -44,26 +44,6 @@ export type OpenGroupV2InfoJoinable = OpenGroupV2Info & {
base64Data?: string;
};
export const TextToBase64 = async (text: string) => {
const arrayBuffer = await callUtilsWorker('bytesFromString', text);
const base64 = await callUtilsWorker('arrayBufferToStringBase64', arrayBuffer);
return base64;
};
export const textToArrayBuffer = async (text: string) => {
return callUtilsWorker('bytesFromString', text);
};
export const verifyED25519Signature = async (
pubkey: string,
base64EncodedData: string,
base64EncondedSignature: string
): Promise<Boolean> => {
return callUtilsWorker('verifySignature', pubkey, base64EncodedData, base64EncondedSignature);
};
export const parseMessages = async (
rawMessages: Array<Record<string, any>>
): Promise<Array<OpenGroupMessageV2>> => {
@ -71,39 +51,48 @@ export const parseMessages = async (
window?.log?.info('no new messages');
return [];
}
const parsedMessages = [];
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < rawMessages.length; i++) {
try {
const opengroupv2Message = OpenGroupMessageV2.fromJson(rawMessages[i]);
if (
!opengroupv2Message?.serverId ||
!opengroupv2Message.sentTimestamp || // this is our serverTimestamp
!opengroupv2Message.base64EncodedData ||
!opengroupv2Message.base64EncodedSignature
) {
window?.log?.warn('invalid open group message received');
continue;
const opengroupMessagesSignatureUnchecked = _.compact(
rawMessages.map(rawMessage => {
try {
const opengroupv2Message = OpenGroupMessageV2.fromJson(rawMessage);
if (
!opengroupv2Message?.serverId ||
!opengroupv2Message.sentTimestamp || // this is our serverTimestamp
!opengroupv2Message.base64EncodedData ||
!opengroupv2Message.base64EncodedSignature
) {
window?.log?.warn('invalid open group message received');
return null;
}
const sender = PubKey.cast(opengroupv2Message.sender).withoutPrefix();
return { opengroupv2Message, sender };
} catch (e) {
window.log.warn('an error happened with opengroup message', e);
return null;
}
// Validate the message signature
const senderPubKey = PubKey.cast(opengroupv2Message.sender).withoutPrefix();
})
);
const signatureValid = (await callUtilsWorker(
'verifySignature',
senderPubKey,
opengroupv2Message.base64EncodedData,
opengroupv2Message.base64EncodedSignature
)) as boolean;
if (!signatureValid) {
throw new Error('opengroup message signature invalisd');
}
const sentToWorker = opengroupMessagesSignatureUnchecked.map(m => {
return {
sender: m.sender,
base64EncodedSignature: m.opengroupv2Message.base64EncodedSignature,
base64EncodedData: m.opengroupv2Message.base64EncodedData,
};
});
parsedMessages.push(opengroupv2Message);
} catch (e) {
window?.log?.error('An error happened while fetching getMessages output:', e);
}
}
const now = Date.now();
// this filters out any invalid signature and returns the array of valid encoded data
const signatureValidEncodedData = (await callUtilsWorker(
'verifyAllSignatures',
sentToWorker
)) as Array<string>;
window.log.info(`[perf] verifyAllSignatures took ${Date.now() - now}ms.`);
const parsedMessages = opengroupMessagesSignatureUnchecked
.filter(m => signatureValidEncodedData.includes(m.opengroupv2Message.base64EncodedData))
.map(m => m.opengroupv2Message);
return _.compact(
parsedMessages.map(m =>
@ -111,6 +100,7 @@ export const parseMessages = async (
)
).sort((a, b) => (a.serverId || 0) - (b.serverId || 0));
};
// tslint:disable: no-http-string
const defaultServerUrl = 'http://116.203.70.33';
const defaultServerPublicKey = 'a03c383cf63c3c4efe67acc52112a6dd734b3a946b9545f488aaa93da7991238';

View File

@ -18,6 +18,7 @@ import { isOpenGroupV2Request } from '../../file_server_api/FileServerApiV2';
import { getAuthToken } from './ApiAuth';
import pRetry from 'p-retry';
import { callUtilsWorker } from '../../../../webworker/workers/util_worker_interface';
import { UserUtils } from '../../../utils';
// used to be overwritten by testing
export const getMinTimeout = () => 1000;
@ -211,7 +212,9 @@ export const postMessageRetryable = async (
message: OpenGroupMessageV2,
room: OpenGroupRequestCommonType
) => {
const signedMessage = await message.sign();
const ourKeyPair = await UserUtils.getIdentityKeyPair();
const signedMessage = await message.sign(ourKeyPair);
const json = signedMessage.toJson();
const request: OpenGroupV2Request = {

View File

@ -1,6 +1,6 @@
import { sign } from 'curve25519-js';
import { SessionKeyPair } from '../../../../receiver/keypairs';
import { callUtilsWorker } from '../../../../webworker/workers/util_worker_interface';
import { UserUtils } from '../../../utils';
import { fromBase64ToArray } from '../../../utils/String';
export class OpenGroupMessageV2 {
@ -53,8 +53,7 @@ export class OpenGroupMessageV2 {
sender,
});
}
public async sign(): Promise<OpenGroupMessageV2> {
const ourKeyPair = await UserUtils.getIdentityKeyPair();
public async sign(ourKeyPair: SessionKeyPair | undefined): Promise<OpenGroupMessageV2> {
if (!ourKeyPair) {
window?.log?.warn("Couldn't find user X25519 key pair.");
throw new Error("Couldn't sign message");

View File

@ -1,26 +1,26 @@
import ByteBuffer from 'bytebuffer';
import { generateKeyPair, sharedKey, verify } from 'curve25519-js';
import { default as sodiumWrappers } from 'libsodium-wrappers-sumo';
import _ from 'lodash';
import {
decryptAttachmentBufferNode,
encryptAttachmentBufferNode,
} from '../../node/encrypt_attachment_buffer';
/* eslint-disable no-console */
/* eslint-disable strict */
async function getSodiumWorker() {
await sodiumWrappers.ready;
return sodiumWrappers;
}
/* global dcodeIO, Internal */
/* eslint-disable no-console */
/* eslint-disable strict */
const functions = {
arrayBufferToStringBase64,
fromBase64ToArrayBuffer,
fromHexToArrayBuffer,
verifySignature,
verifyAllSignatures,
DecryptAESGCM,
deriveSymmetricKey,
encryptForPubkey,
@ -78,6 +78,36 @@ function bytesFromString(str: string) {
return ByteBuffer.wrap(str, 'utf8').toArrayBuffer();
}
// hexString, base64String, base64String
async function verifyAllSignatures(
uncheckedSignatureMessages: Array<{
base64EncodedData: string;
base64EncodedSignature: string;
sender: string;
}>
) {
const checked = await Promise.all(
uncheckedSignatureMessages.map(async unchecked => {
try {
const valid = await verifySignature(
unchecked.sender,
unchecked.base64EncodedData,
unchecked.base64EncodedSignature
);
if (valid) {
return unchecked.base64EncodedData;
}
console.info('got an opengroup message with an invalid signature');
return null;
} catch (e) {
return null;
}
})
);
return _.compact(checked) || [];
}
// hexString, base64String, base64String
async function verifySignature(
senderPubKey: string,

810
yarn.lock

File diff suppressed because it is too large Load Diff