diff --git a/keyserver/src/socket/tunnelbroker-socket.js b/keyserver/src/socket/tunnelbroker-socket.js --- a/keyserver/src/socket/tunnelbroker-socket.js +++ b/keyserver/src/socket/tunnelbroker-socket.js @@ -1,6 +1,7 @@ // @flow import _debounce from 'lodash/debounce.js'; +import { getRustAPI } from 'rust-node-addon'; import uuid from 'uuid'; import WebSocket from 'ws'; @@ -33,8 +34,12 @@ import type { Heartbeat } from 'lib/types/websocket/heartbeat-types.js'; import { convertBytesToObj } from 'lib/utils/conversion-utils.js'; +import { fetchOlmAccount } from '../updaters/olm-account-updater.js'; import { decrypt } from '../utils/aes-crypto-utils.js'; -import { uploadNewOneTimeKeys } from '../utils/olm-utils.js'; +import { + uploadNewOneTimeKeys, + getNewDeviceKeyUpload, +} from '../utils/olm-utils.js'; type PromiseCallbacks = { +resolve: () => void, @@ -145,7 +150,11 @@ const messageToKeyserver = JSON.parse(payload); if (qrCodeAuthMessageValidator.is(messageToKeyserver)) { const request: QRCodeAuthMessage = messageToKeyserver; - const qrCodeAuthMessage = await this.parseQRCodeAuthMessage(request); + const [qrCodeAuthMessage, rustAPI, accountInfo] = await Promise.all([ + this.parseQRCodeAuthMessage(request), + getRustAPI(), + fetchOlmAccount('content'), + ]); if ( !qrCodeAuthMessage || qrCodeAuthMessage.type !== @@ -153,6 +162,30 @@ ) { return; } + // eslint-disable-next-line no-unused-vars + const { primaryDeviceID, userID } = qrCodeAuthMessage; + const [nonce, deviceKeyUpload] = await Promise.all([ + rustAPI.generateNonce(), + getNewDeviceKeyUpload(), + ]); + const signedIdentityKeysBlob = { + payload: deviceKeyUpload.keyPayload, + signature: deviceKeyUpload.keyPayloadSignature, + }; + const nonceSignature = accountInfo.account.sign(nonce); + + await rustAPI.uploadSecondaryDeviceKeysAndLogIn( + userID, + nonce, + nonceSignature, + signedIdentityKeysBlob, + deviceKeyUpload.contentPrekey, + deviceKeyUpload.contentPrekeySignature, + deviceKeyUpload.notifPrekey, + deviceKeyUpload.notifPrekeySignature, + deviceKeyUpload.contentOneTimeKeys, + deviceKeyUpload.notifOneTimeKeys, + ); } else if (refreshKeysRequestValidator.is(messageToKeyserver)) { const request: RefreshKeyRequest = messageToKeyserver; this.debouncedRefreshOneTimeKeys(request.numberOfKeys); diff --git a/keyserver/src/utils/olm-utils.js b/keyserver/src/utils/olm-utils.js --- a/keyserver/src/utils/olm-utils.js +++ b/keyserver/src/utils/olm-utils.js @@ -12,11 +12,13 @@ import { getOneTimeKeyValuesFromBlob } from 'lib/shared/crypto-utils.js'; import { olmEncryptedMessageTypes } from 'lib/types/crypto-types.js'; +import type { IdentityNewDeviceKeyUpload } from 'lib/types/identity-service-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { getAccountPrekeysSet, shouldForgetPrekey, shouldRotatePrekey, + retrieveAccountKeysSet, } from 'lib/utils/olm-utils.js'; import { @@ -101,6 +103,98 @@ return cachedOLMUtility; } +async function getNewDeviceKeyUpload(): Promise { + let contentIdentityKeys: string; + let contentOneTimeKeys: $ReadOnlyArray; + let contentPrekey: string; + let contentPrekeySignature: string; + + let notifIdentityKeys: string; + let notifOneTimeKeys: $ReadOnlyArray; + let notifPrekey: string; + let notifPrekeySignature: string; + + let contentAccountInfo: OlmAccount; + + await Promise.all([ + fetchCallUpdateOlmAccount('content', (contentAccount: OlmAccount) => { + const { identityKeys, oneTimeKeys, prekey, prekeySignature } = + retrieveAccountKeysSet(contentAccount); + contentIdentityKeys = identityKeys; + contentOneTimeKeys = oneTimeKeys; + contentPrekey = prekey; + contentPrekeySignature = prekeySignature; + contentAccountInfo = contentAccount; + contentAccount.mark_keys_as_published(); + }), + fetchCallUpdateOlmAccount('notifications', (notifAccount: OlmAccount) => { + const { identityKeys, oneTimeKeys, prekey, prekeySignature } = + retrieveAccountKeysSet(notifAccount); + notifIdentityKeys = identityKeys; + notifOneTimeKeys = oneTimeKeys; + notifPrekey = prekey; + notifPrekeySignature = prekeySignature; + notifAccount.mark_keys_as_published(); + }), + ]); + + invariant( + contentIdentityKeys, + 'content identity keys not set after fetchCallUpdateOlmAccount', + ); + invariant( + notifIdentityKeys, + 'notif identity keys not set after fetchCallUpdateOlmAccount', + ); + invariant( + contentPrekey, + 'content prekey not set after fetchCallUpdateOlmAccount', + ); + invariant( + notifPrekey, + 'notif prekey not set after fetchCallUpdateOlmAccount', + ); + invariant( + contentPrekeySignature, + 'content prekey signature not set after fetchCallUpdateOlmAccount', + ); + invariant( + notifPrekeySignature, + 'notif prekey signature not set after fetchCallUpdateOlmAccount', + ); + invariant( + contentOneTimeKeys, + 'content one-time keys not set after fetchCallUpdateOlmAccount', + ); + invariant( + notifOneTimeKeys, + 'notif one-time keys not set after fetchCallUpdateOlmAccount', + ); + + invariant( + contentAccountInfo, + 'content account info not set after fetchCallUpdateOlmAccount', + ); + + const identityKeysBlob = { + primaryIdentityPublicKeys: JSON.parse(contentIdentityKeys), + notificationIdentityPublicKeys: JSON.parse(notifIdentityKeys), + }; + const keyPayload = JSON.stringify(identityKeysBlob); + const keyPayloadSignature = contentAccountInfo.sign(keyPayload); + + return { + keyPayload, + keyPayloadSignature, + contentPrekey, + contentPrekeySignature, + notifPrekey, + notifPrekeySignature, + contentOneTimeKeys, + notifOneTimeKeys, + }; +} + async function uploadNewOneTimeKeys(numberOfKeys: number) { const [rustAPI, identityInfo, deviceID] = await Promise.all([ getRustAPI(), @@ -224,4 +318,5 @@ getContentSigningKey, validateAndUploadAccountPrekeys, publishPrekeysToIdentity, + getNewDeviceKeyUpload, };