Move web_api.js and js/modules/crypto.js to TypeScript
This commit is contained in:
parent
71436d18e2
commit
9ab54b9b83
29 changed files with 770 additions and 427 deletions
|
@ -19,7 +19,7 @@ const {
|
|||
saveMessage,
|
||||
setAttachmentDownloadJobPending,
|
||||
} = require('./data');
|
||||
const { stringFromBytes } = require('./crypto');
|
||||
const { stringFromBytes } = require('../../ts/Crypto');
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
|
|
|
@ -19,7 +19,7 @@ const pify = require('pify');
|
|||
const rimraf = require('rimraf');
|
||||
const electronRemote = require('electron').remote;
|
||||
|
||||
const crypto = require('./crypto');
|
||||
const crypto = require('../../ts/Crypto');
|
||||
|
||||
const { dialog, BrowserWindow } = electronRemote;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const {
|
|||
set,
|
||||
} = require('lodash');
|
||||
|
||||
const { base64ToArrayBuffer, arrayBufferToBase64 } = require('./crypto');
|
||||
const { base64ToArrayBuffer, arrayBufferToBase64 } = require('../../ts/Crypto');
|
||||
const MessageType = require('./types/message');
|
||||
const { createBatcher } = require('../../ts/util/batcher');
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ const nodeUrl = require('url');
|
|||
const LinkifyIt = require('linkify-it');
|
||||
|
||||
const linkify = LinkifyIt();
|
||||
const { concatenateBytes, getViewOfArrayBuffer } = require('./crypto');
|
||||
const { concatenateBytes, getViewOfArrayBuffer } = require('../../ts/Crypto');
|
||||
|
||||
module.exports = {
|
||||
assembleChunks,
|
||||
|
|
|
@ -17,7 +17,7 @@ const {
|
|||
intsToByteHighAndLow,
|
||||
splitBytes,
|
||||
trimBytes,
|
||||
} = require('../crypto');
|
||||
} = require('../../../ts/Crypto');
|
||||
|
||||
const REVOKED_CERTIFICATES = [];
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const { bindActionCreators } = require('redux');
|
||||
const Backbone = require('../../ts/backbone');
|
||||
const Crypto = require('./crypto');
|
||||
const Crypto = require('../../ts/Crypto');
|
||||
const Data = require('./data');
|
||||
const Database = require('./database');
|
||||
const Emojis = require('./emojis');
|
||||
|
|
2
js/modules/stickers.d.ts
vendored
2
js/modules/stickers.d.ts
vendored
|
@ -9,3 +9,5 @@ export function downloadStickerPack(
|
|||
fromSync?: boolean;
|
||||
}
|
||||
): Promise<void>;
|
||||
|
||||
export function redactPackId(packId: string): string;
|
||||
|
|
|
@ -33,7 +33,10 @@ const Queue = require('p-queue').default;
|
|||
const qs = require('qs');
|
||||
|
||||
const { makeLookup } = require('../../ts/util/makeLookup');
|
||||
const { base64ToArrayBuffer, deriveStickerPackKey } = require('./crypto');
|
||||
const {
|
||||
base64ToArrayBuffer,
|
||||
deriveStickerPackKey,
|
||||
} = require('../../ts/Crypto');
|
||||
const {
|
||||
addStickerPackReference,
|
||||
createOrUpdateSticker,
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
const { isFunction, isNumber } = require('lodash');
|
||||
const { createLastMessageUpdate } = require('../../../ts/types/Conversation');
|
||||
const { arrayBufferToBase64, base64ToArrayBuffer } = require('../crypto');
|
||||
const {
|
||||
arrayBufferToBase64,
|
||||
base64ToArrayBuffer,
|
||||
} = require('../../../ts/Crypto');
|
||||
|
||||
async function computeHash(arraybuffer) {
|
||||
const hash = await crypto.subtle.digest({ name: 'SHA-512' }, arraybuffer);
|
||||
|
|
|
@ -1321,7 +1321,7 @@ MessageReceiver.prototype.extend({
|
|||
throw new Error('Failure: Ask sender to update Signal and resend.');
|
||||
}
|
||||
|
||||
const data = await textsecure.crypto.decryptAttachment(
|
||||
const paddedData = await textsecure.crypto.decryptAttachment(
|
||||
encrypted,
|
||||
window.Signal.Crypto.base64ToArrayBuffer(key),
|
||||
window.Signal.Crypto.base64ToArrayBuffer(digest)
|
||||
|
@ -1329,15 +1329,15 @@ MessageReceiver.prototype.extend({
|
|||
|
||||
if (!_.isNumber(size)) {
|
||||
throw new Error(
|
||||
`downloadAttachment: Size was not provided, actual size was ${data.byteLength}`
|
||||
`downloadAttachment: Size was not provided, actual size was ${paddedData.byteLength}`
|
||||
);
|
||||
}
|
||||
|
||||
const typedArray = window.Signal.Crypto.getFirstBytes(data, size);
|
||||
const data = window.Signal.Crypto.getFirstBytes(paddedData, size);
|
||||
|
||||
return {
|
||||
..._.omit(attachment, 'digest', 'key'),
|
||||
data: window.Signal.Crypto.typedArrayToArrayBuffer(typedArray),
|
||||
data,
|
||||
};
|
||||
},
|
||||
handleAttachment(attachment) {
|
||||
|
|
|
@ -63,6 +63,8 @@
|
|||
"dependencies": {
|
||||
"@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b",
|
||||
"@sindresorhus/is": "0.8.0",
|
||||
"@types/node-fetch": "2.5.5",
|
||||
"@types/websocket": "1.0.0",
|
||||
"array-move": "2.1.0",
|
||||
"backbone": "1.3.3",
|
||||
"blob-util": "1.3.0",
|
||||
|
|
18
preload.js
18
preload.js
|
@ -222,7 +222,7 @@ try {
|
|||
|
||||
window.nodeSetImmediate = setImmediate;
|
||||
|
||||
const { initialize: initializeWebAPI } = require('./js/modules/web_api');
|
||||
const { initialize: initializeWebAPI } = require('./ts/WebAPI');
|
||||
|
||||
window.WebAPI = initializeWebAPI({
|
||||
url: config.serverUrl,
|
||||
|
@ -308,17 +308,13 @@ try {
|
|||
function wrapWithPromise(fn) {
|
||||
return (...args) => Promise.resolve(fn(...args));
|
||||
}
|
||||
function typedArrayToArrayBuffer(typedArray) {
|
||||
const { buffer, byteOffset, byteLength } = typedArray;
|
||||
return buffer.slice(byteOffset, byteLength + byteOffset);
|
||||
}
|
||||
const externalCurve = {
|
||||
generateKeyPair: () => {
|
||||
const { privKey, pubKey } = curve.generateKeyPair();
|
||||
|
||||
return {
|
||||
privKey: typedArrayToArrayBuffer(privKey),
|
||||
pubKey: typedArrayToArrayBuffer(pubKey),
|
||||
privKey: window.Signal.Crypto.typedArrayToArrayBuffer(privKey),
|
||||
pubKey: window.Signal.Crypto.typedArrayToArrayBuffer(pubKey),
|
||||
};
|
||||
},
|
||||
createKeyPair: incomingKey => {
|
||||
|
@ -326,8 +322,8 @@ try {
|
|||
const { privKey, pubKey } = curve.createKeyPair(incomingKeyBuffer);
|
||||
|
||||
return {
|
||||
privKey: typedArrayToArrayBuffer(privKey),
|
||||
pubKey: typedArrayToArrayBuffer(pubKey),
|
||||
privKey: window.Signal.Crypto.typedArrayToArrayBuffer(privKey),
|
||||
pubKey: window.Signal.Crypto.typedArrayToArrayBuffer(pubKey),
|
||||
};
|
||||
},
|
||||
calculateAgreement: (pubKey, privKey) => {
|
||||
|
@ -336,7 +332,7 @@ try {
|
|||
|
||||
const buffer = curve.calculateAgreement(pubKeyBuffer, privKeyBuffer);
|
||||
|
||||
return typedArrayToArrayBuffer(buffer);
|
||||
return window.Signal.Crypto.typedArrayToArrayBuffer(buffer);
|
||||
},
|
||||
verifySignature: (pubKey, message, signature) => {
|
||||
const pubKeyBuffer = Buffer.from(pubKey);
|
||||
|
@ -357,7 +353,7 @@ try {
|
|||
|
||||
const buffer = curve.calculateSignature(privKeyBuffer, messageBuffer);
|
||||
|
||||
return typedArrayToArrayBuffer(buffer);
|
||||
return window.Signal.Crypto.typedArrayToArrayBuffer(buffer);
|
||||
},
|
||||
validatePubKeyFormat: pubKey => {
|
||||
const pubKeyBuffer = Buffer.from(pubKey);
|
||||
|
|
|
@ -6,7 +6,7 @@ const { readFile } = require('fs');
|
|||
const config = require('url').parse(window.location.toString(), true).query;
|
||||
const { noop, uniqBy } = require('lodash');
|
||||
const pMap = require('p-map');
|
||||
const { deriveStickerPackKey } = require('../js/modules/crypto');
|
||||
const { deriveStickerPackKey } = require('../ts/Crypto');
|
||||
const { makeGetter } = require('../preload_utils');
|
||||
|
||||
const { dialog } = remote;
|
||||
|
@ -29,7 +29,7 @@ const Signal = require('../js/modules/signal');
|
|||
|
||||
window.Signal = Signal.setup({});
|
||||
|
||||
const { initialize: initializeWebAPI } = require('../js/modules/web_api');
|
||||
const { initialize: initializeWebAPI } = require('../ts/WebAPI');
|
||||
|
||||
const WebAPI = initializeWebAPI({
|
||||
url: config.serverUrl,
|
||||
|
@ -143,8 +143,7 @@ window.encryptAndUpload = async (
|
|||
|
||||
async function encrypt(data, key, iv) {
|
||||
const { ciphertext } = await window.textsecure.crypto.encryptAttachment(
|
||||
// Convert Node Buffer to ArrayBuffer
|
||||
window.Signal.Crypto.concatenateBytes(data),
|
||||
window.Signal.Crypto.typedArrayToArrayBuffer(data),
|
||||
key,
|
||||
iv
|
||||
);
|
||||
|
|
|
@ -1,79 +1,48 @@
|
|||
/* eslint-env browser */
|
||||
/* global dcodeIO, libsignal */
|
||||
// Yep, we're doing some bitwise stuff in an encryption-related file
|
||||
// tslint:disable no-bitwise
|
||||
|
||||
/* eslint-disable camelcase, no-bitwise */
|
||||
// We want some extra variables to make the decrption algorithm easier to understand
|
||||
// tslint:disable no-unnecessary-local-variable
|
||||
|
||||
module.exports = {
|
||||
arrayBufferToBase64,
|
||||
typedArrayToArrayBuffer,
|
||||
base64ToArrayBuffer,
|
||||
bytesFromHexString,
|
||||
bytesFromString,
|
||||
concatenateBytes,
|
||||
constantTimeEqual,
|
||||
decryptAesCtr,
|
||||
decryptDeviceName,
|
||||
decryptAttachment,
|
||||
decryptFile,
|
||||
decryptSymmetric,
|
||||
deriveAccessKey,
|
||||
deriveStickerPackKey,
|
||||
encryptAesCtr,
|
||||
encryptDeviceName,
|
||||
encryptAttachment,
|
||||
encryptFile,
|
||||
encryptSymmetric,
|
||||
fromEncodedBinaryToArrayBuffer,
|
||||
getAccessKeyVerifier,
|
||||
getFirstBytes,
|
||||
getRandomBytes,
|
||||
getRandomValue,
|
||||
getViewOfArrayBuffer,
|
||||
getZeroes,
|
||||
hexFromBytes,
|
||||
highBitsToInt,
|
||||
hmacSha256,
|
||||
intsToByteHighAndLow,
|
||||
splitBytes,
|
||||
stringFromBytes,
|
||||
trimBytes,
|
||||
verifyAccessKey,
|
||||
};
|
||||
// Seems that tslint doesn't understand that crypto.subtle.importKey does return a Promise
|
||||
// tslint:disable await-promise
|
||||
|
||||
function typedArrayToArrayBuffer(typedArray) {
|
||||
export function typedArrayToArrayBuffer(typedArray: Uint8Array): ArrayBuffer {
|
||||
const { buffer, byteOffset, byteLength } = typedArray;
|
||||
return buffer.slice(byteOffset, byteLength + byteOffset);
|
||||
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
return buffer.slice(byteOffset, byteLength + byteOffset) as ArrayBuffer;
|
||||
}
|
||||
|
||||
function arrayBufferToBase64(arrayBuffer) {
|
||||
return dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');
|
||||
export function arrayBufferToBase64(arrayBuffer: ArrayBuffer) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');
|
||||
}
|
||||
function base64ToArrayBuffer(base64string) {
|
||||
return dcodeIO.ByteBuffer.wrap(base64string, 'base64').toArrayBuffer();
|
||||
export function base64ToArrayBuffer(base64string: string) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(base64string, 'base64').toArrayBuffer();
|
||||
}
|
||||
|
||||
function fromEncodedBinaryToArrayBuffer(key) {
|
||||
return dcodeIO.ByteBuffer.wrap(key, 'binary').toArrayBuffer();
|
||||
export function fromEncodedBinaryToArrayBuffer(key: string) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(key, 'binary').toArrayBuffer();
|
||||
}
|
||||
|
||||
function bytesFromString(string) {
|
||||
return dcodeIO.ByteBuffer.wrap(string, 'utf8').toArrayBuffer();
|
||||
export function bytesFromString(string: string) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(string, 'utf8').toArrayBuffer();
|
||||
}
|
||||
function stringFromBytes(buffer) {
|
||||
return dcodeIO.ByteBuffer.wrap(buffer).toString('utf8');
|
||||
export function stringFromBytes(buffer: ArrayBuffer) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(buffer).toString('utf8');
|
||||
}
|
||||
function hexFromBytes(buffer) {
|
||||
return dcodeIO.ByteBuffer.wrap(buffer).toString('hex');
|
||||
export function hexFromBytes(buffer: ArrayBuffer) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(buffer).toString('hex');
|
||||
}
|
||||
function bytesFromHexString(string) {
|
||||
return dcodeIO.ByteBuffer.wrap(string, 'hex').toArrayBuffer();
|
||||
export function bytesFromHexString(string: string) {
|
||||
return window.dcodeIO.ByteBuffer.wrap(string, 'hex').toArrayBuffer();
|
||||
}
|
||||
|
||||
async function deriveStickerPackKey(packKey) {
|
||||
export async function deriveStickerPackKey(packKey: ArrayBuffer) {
|
||||
const salt = getZeroes(32);
|
||||
const info = bytesFromString('Sticker Pack');
|
||||
|
||||
const [part1, part2] = await libsignal.HKDF.deriveSecrets(
|
||||
const [part1, part2] = await window.libsignal.HKDF.deriveSecrets(
|
||||
packKey,
|
||||
salt,
|
||||
info
|
||||
|
@ -84,10 +53,13 @@ async function deriveStickerPackKey(packKey) {
|
|||
|
||||
// High-level Operations
|
||||
|
||||
async function encryptDeviceName(deviceName, identityPublic) {
|
||||
export async function encryptDeviceName(
|
||||
deviceName: string,
|
||||
identityPublic: ArrayBuffer
|
||||
) {
|
||||
const plaintext = bytesFromString(deviceName);
|
||||
const ephemeralKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
|
||||
const masterSecret = await libsignal.Curve.async.calculateAgreement(
|
||||
const ephemeralKeyPair = await window.libsignal.KeyHelper.generateIdentityKeyPair();
|
||||
const masterSecret = await window.libsignal.Curve.async.calculateAgreement(
|
||||
identityPublic,
|
||||
ephemeralKeyPair.privKey
|
||||
);
|
||||
|
@ -108,11 +80,19 @@ async function encryptDeviceName(deviceName, identityPublic) {
|
|||
};
|
||||
}
|
||||
|
||||
async function decryptDeviceName(
|
||||
{ ephemeralPublic, syntheticIv, ciphertext } = {},
|
||||
identityPrivate
|
||||
export async function decryptDeviceName(
|
||||
{
|
||||
ephemeralPublic,
|
||||
syntheticIv,
|
||||
ciphertext,
|
||||
}: {
|
||||
ephemeralPublic: ArrayBuffer;
|
||||
syntheticIv: ArrayBuffer;
|
||||
ciphertext: ArrayBuffer;
|
||||
},
|
||||
identityPrivate: ArrayBuffer
|
||||
) {
|
||||
const masterSecret = await libsignal.Curve.async.calculateAgreement(
|
||||
const masterSecret = await window.libsignal.Curve.async.calculateAgreement(
|
||||
ephemeralPublic,
|
||||
identityPrivate
|
||||
);
|
||||
|
@ -134,38 +114,58 @@ async function decryptDeviceName(
|
|||
}
|
||||
|
||||
// Path structure: 'fa/facdf99c22945b1c9393345599a276f4b36ad7ccdc8c2467f5441b742c2d11fa'
|
||||
function getAttachmentLabel(path) {
|
||||
export function getAttachmentLabel(path: string) {
|
||||
const filename = path.slice(3);
|
||||
|
||||
return base64ToArrayBuffer(filename);
|
||||
}
|
||||
|
||||
const PUB_KEY_LENGTH = 32;
|
||||
async function encryptAttachment(staticPublicKey, path, plaintext) {
|
||||
export async function encryptAttachment(
|
||||
staticPublicKey: ArrayBuffer,
|
||||
path: string,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const uniqueId = getAttachmentLabel(path);
|
||||
|
||||
return encryptFile(staticPublicKey, uniqueId, plaintext);
|
||||
}
|
||||
|
||||
async function decryptAttachment(staticPrivateKey, path, data) {
|
||||
export async function decryptAttachment(
|
||||
staticPrivateKey: ArrayBuffer,
|
||||
path: string,
|
||||
data: ArrayBuffer
|
||||
) {
|
||||
const uniqueId = getAttachmentLabel(path);
|
||||
|
||||
return decryptFile(staticPrivateKey, uniqueId, data);
|
||||
}
|
||||
|
||||
async function encryptFile(staticPublicKey, uniqueId, plaintext) {
|
||||
const ephemeralKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
|
||||
const agreement = await libsignal.Curve.async.calculateAgreement(
|
||||
export async function encryptFile(
|
||||
staticPublicKey: ArrayBuffer,
|
||||
uniqueId: ArrayBuffer,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const ephemeralKeyPair = await window.libsignal.KeyHelper.generateIdentityKeyPair();
|
||||
const agreement = await window.libsignal.Curve.async.calculateAgreement(
|
||||
staticPublicKey,
|
||||
ephemeralKeyPair.privKey
|
||||
);
|
||||
const key = await hmacSha256(agreement, uniqueId);
|
||||
|
||||
const prefix = ephemeralKeyPair.pubKey.slice(1);
|
||||
|
||||
return concatenateBytes(prefix, await encryptSymmetric(key, plaintext));
|
||||
}
|
||||
|
||||
async function decryptFile(staticPrivateKey, uniqueId, data) {
|
||||
export async function decryptFile(
|
||||
staticPrivateKey: ArrayBuffer,
|
||||
uniqueId: ArrayBuffer,
|
||||
data: ArrayBuffer
|
||||
) {
|
||||
const ephemeralPublicKey = getFirstBytes(data, PUB_KEY_LENGTH);
|
||||
const ciphertext = _getBytes(data, PUB_KEY_LENGTH, data.byteLength);
|
||||
const agreement = await libsignal.Curve.async.calculateAgreement(
|
||||
const agreement = await window.libsignal.Curve.async.calculateAgreement(
|
||||
ephemeralPublicKey,
|
||||
staticPrivateKey
|
||||
);
|
||||
|
@ -175,21 +175,24 @@ async function decryptFile(staticPrivateKey, uniqueId, data) {
|
|||
return decryptSymmetric(key, ciphertext);
|
||||
}
|
||||
|
||||
async function deriveAccessKey(profileKey) {
|
||||
export async function deriveAccessKey(profileKey: ArrayBuffer) {
|
||||
const iv = getZeroes(12);
|
||||
const plaintext = getZeroes(16);
|
||||
const accessKey = await _encrypt_aes_gcm(profileKey, iv, plaintext);
|
||||
|
||||
return getFirstBytes(accessKey, 16);
|
||||
}
|
||||
|
||||
async function getAccessKeyVerifier(accessKey) {
|
||||
export async function getAccessKeyVerifier(accessKey: ArrayBuffer) {
|
||||
const plaintext = getZeroes(32);
|
||||
const hmac = await hmacSha256(accessKey, plaintext);
|
||||
|
||||
return hmac;
|
||||
return hmacSha256(accessKey, plaintext);
|
||||
}
|
||||
|
||||
async function verifyAccessKey(accessKey, theirVerifier) {
|
||||
export async function verifyAccessKey(
|
||||
accessKey: ArrayBuffer,
|
||||
theirVerifier: ArrayBuffer
|
||||
) {
|
||||
const ourVerifier = await getAccessKeyVerifier(accessKey);
|
||||
|
||||
if (constantTimeEqual(ourVerifier, theirVerifier)) {
|
||||
|
@ -203,7 +206,10 @@ const IV_LENGTH = 16;
|
|||
const MAC_LENGTH = 16;
|
||||
const NONCE_LENGTH = 16;
|
||||
|
||||
async function encryptSymmetric(key, plaintext) {
|
||||
export async function encryptSymmetric(
|
||||
key: ArrayBuffer,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const iv = getZeroes(IV_LENGTH);
|
||||
const nonce = getRandomBytes(NONCE_LENGTH);
|
||||
|
||||
|
@ -220,7 +226,7 @@ async function encryptSymmetric(key, plaintext) {
|
|||
return concatenateBytes(nonce, cipherText, mac);
|
||||
}
|
||||
|
||||
async function decryptSymmetric(key, data) {
|
||||
export async function decryptSymmetric(key: ArrayBuffer, data: ArrayBuffer) {
|
||||
const iv = getZeroes(IV_LENGTH);
|
||||
|
||||
const nonce = getFirstBytes(data, NONCE_LENGTH);
|
||||
|
@ -247,23 +253,25 @@ async function decryptSymmetric(key, data) {
|
|||
return _decrypt_aes256_CBC_PKCSPadding(cipherKey, iv, cipherText);
|
||||
}
|
||||
|
||||
function constantTimeEqual(left, right) {
|
||||
export function constantTimeEqual(left: ArrayBuffer, right: ArrayBuffer) {
|
||||
if (left.byteLength !== right.byteLength) {
|
||||
return false;
|
||||
}
|
||||
let result = 0;
|
||||
const ta1 = new Uint8Array(left);
|
||||
const ta2 = new Uint8Array(right);
|
||||
for (let i = 0, max = left.byteLength; i < max; i += 1) {
|
||||
const max = left.byteLength;
|
||||
for (let i = 0; i < max; i += 1) {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
result |= ta1[i] ^ ta2[i];
|
||||
}
|
||||
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
// Encryption
|
||||
|
||||
async function hmacSha256(key, plaintext) {
|
||||
export async function hmacSha256(key: ArrayBuffer, plaintext: ArrayBuffer) {
|
||||
const algorithm = {
|
||||
name: 'HMAC',
|
||||
hash: 'SHA-256',
|
||||
|
@ -273,7 +281,7 @@ async function hmacSha256(key, plaintext) {
|
|||
const cryptoKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['sign']
|
||||
);
|
||||
|
@ -281,7 +289,11 @@ async function hmacSha256(key, plaintext) {
|
|||
return window.crypto.subtle.sign(algorithm, cryptoKey, plaintext);
|
||||
}
|
||||
|
||||
async function _encrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
|
||||
export async function _encrypt_aes256_CBC_PKCSPadding(
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const algorithm = {
|
||||
name: 'AES-CBC',
|
||||
iv,
|
||||
|
@ -291,7 +303,7 @@ async function _encrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
|
|||
const cryptoKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['encrypt']
|
||||
);
|
||||
|
@ -299,7 +311,11 @@ async function _encrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
|
|||
return window.crypto.subtle.encrypt(algorithm, cryptoKey, plaintext);
|
||||
}
|
||||
|
||||
async function _decrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
|
||||
export async function _decrypt_aes256_CBC_PKCSPadding(
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const algorithm = {
|
||||
name: 'AES-CBC',
|
||||
iv,
|
||||
|
@ -309,14 +325,19 @@ async function _decrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
|
|||
const cryptoKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['decrypt']
|
||||
);
|
||||
|
||||
return window.crypto.subtle.decrypt(algorithm, cryptoKey, plaintext);
|
||||
}
|
||||
|
||||
async function encryptAesCtr(key, plaintext, counter) {
|
||||
export async function encryptAesCtr(
|
||||
key: ArrayBuffer,
|
||||
plaintext: ArrayBuffer,
|
||||
counter: ArrayBuffer
|
||||
) {
|
||||
const extractable = false;
|
||||
const algorithm = {
|
||||
name: 'AES-CTR',
|
||||
|
@ -327,7 +348,7 @@ async function encryptAesCtr(key, plaintext, counter) {
|
|||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['encrypt']
|
||||
);
|
||||
|
@ -341,7 +362,11 @@ async function encryptAesCtr(key, plaintext, counter) {
|
|||
return ciphertext;
|
||||
}
|
||||
|
||||
async function decryptAesCtr(key, ciphertext, counter) {
|
||||
export async function decryptAesCtr(
|
||||
key: ArrayBuffer,
|
||||
ciphertext: ArrayBuffer,
|
||||
counter: ArrayBuffer
|
||||
) {
|
||||
const extractable = false;
|
||||
const algorithm = {
|
||||
name: 'AES-CTR',
|
||||
|
@ -352,7 +377,7 @@ async function decryptAesCtr(key, ciphertext, counter) {
|
|||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['decrypt']
|
||||
);
|
||||
|
@ -361,10 +386,15 @@ async function decryptAesCtr(key, ciphertext, counter) {
|
|||
cryptoKey,
|
||||
ciphertext
|
||||
);
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
async function _encrypt_aes_gcm(key, iv, plaintext) {
|
||||
export async function _encrypt_aes_gcm(
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer,
|
||||
plaintext: ArrayBuffer
|
||||
) {
|
||||
const algorithm = {
|
||||
name: 'AES-GCM',
|
||||
iv,
|
||||
|
@ -374,32 +404,35 @@ async function _encrypt_aes_gcm(key, iv, plaintext) {
|
|||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
algorithm,
|
||||
algorithm as any,
|
||||
extractable,
|
||||
['encrypt']
|
||||
);
|
||||
|
||||
return crypto.subtle.encrypt(algorithm, cryptoKey, plaintext);
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
||||
function getRandomBytes(n) {
|
||||
export function getRandomBytes(n: number) {
|
||||
const bytes = new Uint8Array(n);
|
||||
window.crypto.getRandomValues(bytes);
|
||||
return bytes;
|
||||
|
||||
return typedArrayToArrayBuffer(bytes);
|
||||
}
|
||||
|
||||
function getRandomValue(low, high) {
|
||||
export function getRandomValue(low: number, high: number): number {
|
||||
const diff = high - low;
|
||||
const bytes = new Uint32Array(1);
|
||||
window.crypto.getRandomValues(bytes);
|
||||
|
||||
// Because high and low are inclusive
|
||||
const mod = diff + 1;
|
||||
|
||||
return (bytes[0] % mod) + low;
|
||||
}
|
||||
|
||||
function getZeroes(n) {
|
||||
export function getZeroes(n: number) {
|
||||
const result = new Uint8Array(n);
|
||||
|
||||
const value = 0;
|
||||
|
@ -407,28 +440,36 @@ function getZeroes(n) {
|
|||
const endExclusive = n;
|
||||
result.fill(value, startIndex, endExclusive);
|
||||
|
||||
return result;
|
||||
return typedArrayToArrayBuffer(result);
|
||||
}
|
||||
|
||||
function highBitsToInt(byte) {
|
||||
export function highBitsToInt(byte: number): number {
|
||||
return (byte & 0xff) >> 4;
|
||||
}
|
||||
|
||||
function intsToByteHighAndLow(highValue, lowValue) {
|
||||
export function intsToByteHighAndLow(
|
||||
highValue: number,
|
||||
lowValue: number
|
||||
): number {
|
||||
return ((highValue << 4) | lowValue) & 0xff;
|
||||
}
|
||||
|
||||
function trimBytes(buffer, length) {
|
||||
export function trimBytes(buffer: ArrayBuffer, length: number) {
|
||||
return getFirstBytes(buffer, length);
|
||||
}
|
||||
|
||||
function getViewOfArrayBuffer(buffer, start, finish) {
|
||||
export function getViewOfArrayBuffer(
|
||||
buffer: ArrayBuffer,
|
||||
start: number,
|
||||
finish: number
|
||||
) {
|
||||
const source = new Uint8Array(buffer);
|
||||
const result = source.slice(start, finish);
|
||||
|
||||
return result.buffer;
|
||||
}
|
||||
|
||||
function concatenateBytes(...elements) {
|
||||
export function concatenateBytes(...elements: Array<ArrayBuffer | Uint8Array>) {
|
||||
const length = elements.reduce(
|
||||
(total, element) => total + element.byteLength,
|
||||
0
|
||||
|
@ -437,7 +478,8 @@ function concatenateBytes(...elements) {
|
|||
const result = new Uint8Array(length);
|
||||
let position = 0;
|
||||
|
||||
for (let i = 0, max = elements.length; i < max; i += 1) {
|
||||
const max = elements.length;
|
||||
for (let i = 0; i < max; i += 1) {
|
||||
const element = new Uint8Array(elements[i]);
|
||||
result.set(element, position);
|
||||
position += element.byteLength;
|
||||
|
@ -446,10 +488,13 @@ function concatenateBytes(...elements) {
|
|||
throw new Error('problem concatenating!');
|
||||
}
|
||||
|
||||
return result.buffer;
|
||||
return typedArrayToArrayBuffer(result);
|
||||
}
|
||||
|
||||
function splitBytes(buffer, ...lengths) {
|
||||
export function splitBytes(
|
||||
buffer: ArrayBuffer,
|
||||
...lengths: Array<number>
|
||||
): Array<ArrayBuffer> {
|
||||
const total = lengths.reduce((acc, length) => acc + length, 0);
|
||||
|
||||
if (total !== buffer.byteLength) {
|
||||
|
@ -462,27 +507,34 @@ function splitBytes(buffer, ...lengths) {
|
|||
const results = [];
|
||||
let position = 0;
|
||||
|
||||
for (let i = 0, max = lengths.length; i < max; i += 1) {
|
||||
const max = lengths.length;
|
||||
for (let i = 0; i < max; i += 1) {
|
||||
const length = lengths[i];
|
||||
const result = new Uint8Array(length);
|
||||
const section = source.slice(position, position + length);
|
||||
result.set(section);
|
||||
position += result.byteLength;
|
||||
|
||||
results.push(result);
|
||||
results.push(typedArrayToArrayBuffer(result));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getFirstBytes(data, n) {
|
||||
export function getFirstBytes(data: ArrayBuffer, n: number) {
|
||||
const source = new Uint8Array(data);
|
||||
return source.subarray(0, n);
|
||||
|
||||
return typedArrayToArrayBuffer(source.subarray(0, n));
|
||||
}
|
||||
|
||||
// Internal-only
|
||||
|
||||
function _getBytes(data, start, n) {
|
||||
export function _getBytes(
|
||||
data: ArrayBuffer | Uint8Array,
|
||||
start: number,
|
||||
n: number
|
||||
) {
|
||||
const source = new Uint8Array(data);
|
||||
return source.subarray(start, start + n);
|
||||
|
||||
return typedArrayToArrayBuffer(source.subarray(start, start + n));
|
||||
}
|
File diff suppressed because it is too large
Load diff
7
ts/proxy-agent.d.ts
vendored
Normal file
7
ts/proxy-agent.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
declare module 'proxy-agent' {
|
||||
import { Agent } from 'http';
|
||||
|
||||
export default class ProxyAgent extends Agent {
|
||||
constructor(url: string);
|
||||
}
|
||||
}
|
|
@ -11,17 +11,8 @@ type NetworkActions = {
|
|||
|
||||
const REFRESH_INTERVAL = 5000;
|
||||
|
||||
interface ShimmedWindow extends Window {
|
||||
log: {
|
||||
info: (...args: any) => void;
|
||||
};
|
||||
}
|
||||
|
||||
const unknownWindow = window as unknown;
|
||||
const shimmedWindow = unknownWindow as ShimmedWindow;
|
||||
|
||||
export function initializeNetworkObserver(networkActions: NetworkActions) {
|
||||
const { log } = shimmedWindow;
|
||||
const { log } = window;
|
||||
log.info(`Initializing network observer every ${REFRESH_INTERVAL}ms`);
|
||||
|
||||
const refresh = () => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export function trigger(name: string, param1?: any, param2?: any) {
|
||||
// @ts-ignore
|
||||
window.Whisper.events.trigger(name, param1, param2);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
interface ShimmedWindow extends Window {
|
||||
getSocketStatus: () => number;
|
||||
}
|
||||
|
||||
const unknownWindow = window as unknown;
|
||||
const shimmedWindow = unknownWindow as ShimmedWindow;
|
||||
|
||||
export function getSocketStatus() {
|
||||
const { getSocketStatus: getMessageReceiverStatus } = shimmedWindow;
|
||||
const { getSocketStatus: getMessageReceiverStatus } = window;
|
||||
|
||||
return getMessageReceiverStatus();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
export async function put(key: string, value: any) {
|
||||
// @ts-ignore
|
||||
return window.storage.put(key, value);
|
||||
export function put(key: string, value: any) {
|
||||
window.storage.put(key, value);
|
||||
}
|
||||
|
||||
export async function remove(key: string) {
|
||||
// @ts-ignore
|
||||
return window.storage.remove(key);
|
||||
export function remove(key: string) {
|
||||
window.storage.remove(key);
|
||||
}
|
||||
|
|
|
@ -1,52 +1,9 @@
|
|||
type LoggerType = (...args: Array<any>) => void;
|
||||
|
||||
type TextSecureType = {
|
||||
storage: {
|
||||
user: {
|
||||
getNumber: () => string;
|
||||
};
|
||||
get: (item: string) => any;
|
||||
};
|
||||
messaging: {
|
||||
sendStickerPackSync: (
|
||||
operations: Array<{
|
||||
packId: string;
|
||||
packKey: string;
|
||||
installed: boolean;
|
||||
}>,
|
||||
options: Object
|
||||
) => Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
type ConversationControllerType = {
|
||||
prepareForSend: (
|
||||
id: string,
|
||||
options: Object
|
||||
) => {
|
||||
wrap: (promise: Promise<any>) => Promise<void>;
|
||||
sendOptions: Object;
|
||||
};
|
||||
};
|
||||
|
||||
interface ShimmedWindow extends Window {
|
||||
log: {
|
||||
error: LoggerType;
|
||||
info: LoggerType;
|
||||
};
|
||||
textsecure: TextSecureType;
|
||||
ConversationController: ConversationControllerType;
|
||||
}
|
||||
|
||||
const unknownWindow = window as unknown;
|
||||
const shimmedWindow = unknownWindow as ShimmedWindow;
|
||||
|
||||
export function sendStickerPackSync(
|
||||
packId: string,
|
||||
packKey: string,
|
||||
installed: boolean
|
||||
) {
|
||||
const { ConversationController, textsecure, log } = shimmedWindow;
|
||||
const { ConversationController, textsecure, log } = window;
|
||||
const ourNumber = textsecure.storage.user.getNumber();
|
||||
const { wrap, sendOptions } = ConversationController.prepareForSend(
|
||||
ourNumber,
|
||||
|
|
|
@ -11,7 +11,7 @@ export type ItemsStateType = {
|
|||
|
||||
type ItemPutAction = {
|
||||
type: 'items/PUT';
|
||||
payload: Promise<void>;
|
||||
payload: null;
|
||||
};
|
||||
|
||||
type ItemPutExternalAction = {
|
||||
|
@ -24,7 +24,7 @@ type ItemPutExternalAction = {
|
|||
|
||||
type ItemRemoveAction = {
|
||||
type: 'items/REMOVE';
|
||||
payload: Promise<void>;
|
||||
payload: null;
|
||||
};
|
||||
|
||||
type ItemRemoveExternalAction = {
|
||||
|
@ -54,9 +54,11 @@ export const actions = {
|
|||
};
|
||||
|
||||
function putItem(key: string, value: any): ItemPutAction {
|
||||
storageShim.put(key, value);
|
||||
|
||||
return {
|
||||
type: 'items/PUT',
|
||||
payload: storageShim.put(key, value),
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,9 +73,11 @@ function putItemExternal(key: string, value: any): ItemPutExternalAction {
|
|||
}
|
||||
|
||||
function removeItem(key: string): ItemRemoveAction {
|
||||
storageShim.remove(key);
|
||||
|
||||
return {
|
||||
type: 'items/REMOVE',
|
||||
payload: storageShim.remove(key),
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
interface ShimmedWindow extends Window {
|
||||
getExpiration: () => string;
|
||||
log: {
|
||||
info: (...args: any) => void;
|
||||
error: (...args: any) => void;
|
||||
};
|
||||
}
|
||||
|
||||
const unknownWindow = window as unknown;
|
||||
const shimmedWindow = unknownWindow as ShimmedWindow;
|
||||
|
||||
// @ts-ignore
|
||||
const env = window.getEnvironment();
|
||||
|
||||
const NINETY_ONE_DAYS = 86400 * 91 * 1000;
|
||||
|
||||
export function hasExpired() {
|
||||
const { getExpiration, log } = shimmedWindow;
|
||||
const { getExpiration, log } = window;
|
||||
|
||||
let buildExpiration = 0;
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@
|
|||
"rule": "jQuery-load(",
|
||||
"path": "js/modules/stickers.js",
|
||||
"line": "async function load() {",
|
||||
"lineNumber": 74,
|
||||
"lineNumber": 77,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2019-04-26T17:48:30.675Z"
|
||||
},
|
||||
|
@ -11613,7 +11613,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/shims/textsecure.js",
|
||||
"line": " wrap(textsecure.messaging.sendStickerPackSync([",
|
||||
"lineNumber": 13,
|
||||
"lineNumber": 11,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-02-07T19:52:28.522Z"
|
||||
},
|
||||
|
@ -11621,7 +11621,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/shims/textsecure.ts",
|
||||
"line": " wrap(",
|
||||
"lineNumber": 64,
|
||||
"lineNumber": 21,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-02-07T19:52:28.522Z"
|
||||
}
|
||||
|
|
|
@ -54,9 +54,10 @@ const excludedFiles = [
|
|||
|
||||
// High-traffic files in our project
|
||||
'^js/models/messages.js',
|
||||
'^js/modules/crypto.js',
|
||||
'^js/views/conversation_view.js',
|
||||
'^js/background.js',
|
||||
'^ts/Crypto.js',
|
||||
'^ts/Crypto.ts',
|
||||
|
||||
// Generated files
|
||||
'^js/components.js',
|
||||
|
|
|
@ -5,23 +5,19 @@ export function markEverDone() {
|
|||
|
||||
export function markDone() {
|
||||
markEverDone();
|
||||
// @ts-ignore
|
||||
window.storage.put('chromiumRegistrationDone', '');
|
||||
}
|
||||
|
||||
export function remove() {
|
||||
// @ts-ignore
|
||||
window.storage.remove('chromiumRegistrationDone');
|
||||
}
|
||||
|
||||
export function isDone() {
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line no-backbone-get-set-outside-model
|
||||
return window.storage.get('chromiumRegistrationDone') === '';
|
||||
}
|
||||
|
||||
export function everDone() {
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line no-backbone-get-set-outside-model
|
||||
return window.storage.get('chromiumRegistrationDoneEver') === '' || isDone();
|
||||
}
|
||||
|
|
98
ts/window.d.ts
vendored
Normal file
98
ts/window.d.ts
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Captures the globals put in place by preload.js, background.js and others
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
dcodeIO: DCodeIOType;
|
||||
getExpiration: () => string;
|
||||
getEnvironment: () => string;
|
||||
getSocketStatus: () => number;
|
||||
libsignal: LibSignalType;
|
||||
log: {
|
||||
info: LoggerType;
|
||||
warn: LoggerType;
|
||||
error: LoggerType;
|
||||
};
|
||||
storage: {
|
||||
put: (key: string, value: any) => void;
|
||||
remove: (key: string) => void;
|
||||
get: (key: string) => any;
|
||||
};
|
||||
textsecure: TextSecureType;
|
||||
|
||||
ConversationController: ConversationControllerType;
|
||||
Whisper: WhisperType;
|
||||
}
|
||||
}
|
||||
|
||||
export type ConversationControllerType = {
|
||||
prepareForSend: (
|
||||
id: string,
|
||||
options: Object
|
||||
) => {
|
||||
wrap: (promise: Promise<any>) => Promise<void>;
|
||||
sendOptions: Object;
|
||||
};
|
||||
};
|
||||
|
||||
export type DCodeIOType = {
|
||||
ByteBuffer: {
|
||||
wrap: (
|
||||
value: any,
|
||||
type?: string
|
||||
) => {
|
||||
toString: (type: string) => string;
|
||||
toArrayBuffer: () => ArrayBuffer;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type LibSignalType = {
|
||||
KeyHelper: {
|
||||
generateIdentityKeyPair: () => Promise<{
|
||||
privKey: ArrayBuffer;
|
||||
pubKey: ArrayBuffer;
|
||||
}>;
|
||||
};
|
||||
Curve: {
|
||||
async: {
|
||||
calculateAgreement: (
|
||||
publicKey: ArrayBuffer,
|
||||
privateKey: ArrayBuffer
|
||||
) => Promise<ArrayBuffer>;
|
||||
};
|
||||
};
|
||||
HKDF: {
|
||||
deriveSecrets: (
|
||||
packKey: ArrayBuffer,
|
||||
salt: ArrayBuffer,
|
||||
info: ArrayBuffer
|
||||
) => Promise<Array<ArrayBuffer>>;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoggerType = (...args: Array<any>) => void;
|
||||
|
||||
export type TextSecureType = {
|
||||
storage: {
|
||||
user: {
|
||||
getNumber: () => string;
|
||||
};
|
||||
get: (key: string) => any;
|
||||
};
|
||||
messaging: {
|
||||
sendStickerPackSync: (
|
||||
operations: Array<{
|
||||
packId: string;
|
||||
packKey: string;
|
||||
installed: boolean;
|
||||
}>,
|
||||
options: Object
|
||||
) => Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
export type WhisperType = {
|
||||
events: {
|
||||
trigger: (name: string, param1: any, param2: any) => void;
|
||||
};
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
"align": false,
|
||||
"newline-per-chained-call": false,
|
||||
"array-type": [true, "generic"],
|
||||
"number-literal-format": false,
|
||||
|
||||
// Preferred by Prettier:
|
||||
"arrow-parens": [true, "ban-single-arg-parens"],
|
||||
|
|
31
yarn.lock
31
yarn.lock
|
@ -2066,6 +2066,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.0.0.tgz#a3014921991066193f6c8e47290d4d598dfd19e6"
|
||||
integrity sha512-ZS0vBV7Jn5Z/Q4T3VXauEKMDCV8nWOtJJg90OsDylkYJiQwcWtKuLzohWzrthBkerUF7DLMmJcwOPEP0i/AOXw==
|
||||
|
||||
"@types/node-fetch@2.5.5":
|
||||
version "2.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.5.tgz#cd264e20a81f4600a6c52864d38e7fef72485e92"
|
||||
integrity sha512-IWwjsyYjGw+em3xTvWVQi5MgYKbRs0du57klfTaZkv/B24AEQ/p/IopNeqIYNy3EsfHOpg8ieQSDomPcsYMHpA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
form-data "^3.0.0"
|
||||
|
||||
"@types/node@*":
|
||||
version "11.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.2.tgz#d7f302e74b10e9801d52852137f652d9ee235da8"
|
||||
|
@ -2317,6 +2325,13 @@
|
|||
"@types/webpack-sources" "*"
|
||||
source-map "^0.6.0"
|
||||
|
||||
"@types/websocket@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.0.tgz#828c794b0a50949ad061aa311af1009934197e4b"
|
||||
integrity sha512-MLr8hDM8y7vvdAdnoDEP5LotRoYJj7wgT6mWzCUQH/gHqzS4qcnOT/K4dhC0WimWIUiA3Arj9QAJGGKNRiRZKA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "15.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
|
||||
|
@ -4757,6 +4772,13 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
|
|||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
comma-separated-tokens@^1.0.0:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.7.tgz#419cd7fb3258b1ed838dc0953167a25e152f5b59"
|
||||
|
@ -7380,6 +7402,15 @@ form-data@2.3.2, form-data@~2.3.2:
|
|||
combined-stream "1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
|
||||
integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.1.1:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
|
||||
|
|
Loading…
Reference in a new issue