diff --git a/ts/session/snode_api/lokiRpc.ts b/ts/session/snode_api/lokiRpc.ts index 5171285b2..8a72fdcc5 100644 --- a/ts/session/snode_api/lokiRpc.ts +++ b/ts/session/snode_api/lokiRpc.ts @@ -9,8 +9,10 @@ interface FetchOptions { agent?: any; } -// A small wrapper around node-fetch which deserializes response -// returns insecureNodeFetch response or false +/** + * A small wrapper around node-fetch which deserializes response + * returns insecureNodeFetch response or false + */ async function lokiFetch( url: string, options: FetchOptions, diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index a40be7046..2a2800461 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -2,11 +2,12 @@ import { createOrUpdateItem, getItemById, getLatestClosedGroupEncryptionKeyPair, + Snode, } from '../../../ts/data/data'; -import { getMessageQueue } from '..'; +import { getMessageQueue, Utils } from '..'; import { getConversationController } from '../conversations'; import uuid from 'uuid'; -import { UserUtils } from '.'; +import { StringUtils, UserUtils } from '.'; import { ECKeyPair } from '../../receiver/keypairs'; import { ConfigurationMessage, @@ -14,7 +15,7 @@ import { ConfigurationMessageContact, } from '../messages/outgoing/controlMessage/ConfigurationMessage'; import { ConversationModel } from '../../models/conversation'; -import { fromBase64ToArray } from './String'; +import { fromBase64ToArray, fromHexToArray, fromUInt8ArrayToBase64, toHex } from './String'; import { SignalService } from '../../protobuf'; import _ from 'lodash'; import { @@ -27,6 +28,13 @@ import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessag import { getV2OpenGroupRoom } from '../../data/opengroups'; import { getCompleteUrlFromRoom } from '../../opengroup/utils/OpenGroupUtils'; import { DURATION } from '../constants'; +import { SnodePool } from '../snode_api'; +import { snodeHttpsAgent } from '../snode_api/onions'; + +import { default as insecureNodeFetch } from 'node-fetch'; +import { getSodium } from '../crypto'; +import { encryptUsingSessionProtocol } from '../crypto/MessageEncrypter'; +import { snodeRpc } from '../snode_api/lokiRpc'; const ITEM_ID_LAST_SYNC_TIMESTAMP = 'lastSyncedTimestamp'; @@ -76,9 +84,10 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal // tslint:disable-next-line: no-void-expression const callback = waitForMessageSent ? () => { - resolve(true); - } + resolve(true); + } : undefined; + debugger; void getMessageQueue().sendSyncMessage(configMessage, callback as any); // either we resolve from the callback if we need to wait for it, // or we don't want to wait, we resolve it here. @@ -92,6 +101,98 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal }); }); + +/** + * Makes a post to a node to receive the timestamp info. If non-existant, returns -1 + * @param snode Snode to send request to + * @returns timestamp of the response from snode + */ +export const getNetworkTime = async (snode: Snode): Promise => { + // let response: any = await insecureNodeFetch(url, fetchOptions) + try { + + let response: any = await snodeRpc('info', {}, snode); + let body = JSON.parse(response.body); + let timestamp = body['timestamp']; + + debugger; + return timestamp ? timestamp : -1; + } + catch (e) { + debugger; + return -1; + } + +} + +export const forceNetworkDeletion = async () => { + + // get keypair + let userPubKey = await UserUtils.getOurPubKeyFromCache(); + + // get ed255 key + let userED25519Keypair = await UserUtils.getUserED25519KeyPair(); + if (userED25519Keypair === undefined || userED25519Keypair.privKey === undefined) { + return; + } + + // get random snode + let snode: Snode | undefined = await SnodePool.getRandomSnode(); + + let timestamp = await getNetworkTime(snode); + + let sodium = await getSodium(); + + // create data by combining shit. Combines the method + timestamp to a byteArray in android + let verificationData = 'delete_all' + timestamp.toString(); + + // convert vert data to byteArray for signing + + // sign the data with sodium + user ed255key in byte for (userED25519Keypair.bytes) + let userED25519SecretBytes = fromHexToArray(userED25519Keypair.privKey) + + let signature = sodium.crypto_sign_detached(verificationData, userED25519SecretBytes) + + // package into some params + let deleteMessageParams = { + pubkey: userPubKey.key, // not cast as anything in android example + pubkeyED25519: toHex(StringUtils.encode(userED25519Keypair.pubKey, 'base64')) , // not sure if this is a proper hex value? + // pubkeyED25519: userED25519Keypair.pubKey, + timestamp, // -1 atm.. + signature: fromUInt8ArrayToBase64(signature) + } + + // send method to the network. + // await send('delete_all', snode, userPubKey.key, deleteMessageParams) + + let res = await snodeRpc('delete_all', deleteMessageParams, snode, userPubKey.key); + debugger; +} + +const send = async (method: string, snode: Snode, publicKey?: string, parameters?: any) => { + let url = `https://${snode.ip}:${snode.port}/storage_rpc/v1`; + + let payload = { + method, + ...parameters + } + + let fetchOptions = { + method: 'POST', + body: JSON.stringify(payload), + headers: { + 'Content-Type': 'application/json', + // // 'User-Agent': 'WhatsApp', + // 'Accept-Language': 'en-us', + }, + // timeout: 10000, + agent: snodeHttpsAgent + } + + let response: any = await insecureNodeFetch(url, fetchOptions) + console.log({ response }); +} + const getActiveOpenGroupV2CompleteUrls = async ( convos: Array ): Promise> => { diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index 1c7da7ee0..0c5760320 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -4,7 +4,7 @@ import { UserUtils } from '../session/utils'; import { fromArrayBufferToBase64, fromHex, toHex } from '../session/utils/String'; import { getOurPubKeyStrFromCache } from '../session/utils/User'; import { trigger } from '../shims/events'; -import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; +import { forceNetworkDeletion, forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils'; import { actions as userActions } from '../state/ducks/user'; import { mn_decode, mn_encode } from '../session/crypto/mnemonic'; import { ConversationTypeEnum } from '../models/conversation'; @@ -139,15 +139,25 @@ async function bouncyDeleteAccount(reason?: string) { }; try { window?.log?.info('DeleteAccount => Sending a last SyncConfiguration'); + + + // send deletion message to the network + await forceNetworkDeletion(); + // be sure to wait for the message being effectively sent. Otherwise we won't be able to encrypt it for our devices ! await forceSyncConfigurationNowIfNeeded(true); window?.log?.info('Last configuration message sent!'); + + return; + await deleteEverything(); } catch (error) { window?.log?.error( 'Something went wrong deleting all data:', error && error.stack ? error.stack : error ); + debugger; + // return; try { await deleteEverything(); } catch (e) {