Page MenuHomePhorge

D12213.1768524823.diff
No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None

D12213.1768524823.diff

diff --git a/keyserver/src/keyserver.js b/keyserver/src/keyserver.js
--- a/keyserver/src/keyserver.js
+++ b/keyserver/src/keyserver.js
@@ -35,10 +35,7 @@
} from './responders/website-responders.js';
import { webWorkerResponder } from './responders/webworker-responders.js';
import { onConnection } from './socket/socket.js';
-import {
- createAndMaintainTunnelbrokerWebsocket,
- createAndMaintainAnonymousTunnelbrokerWebsocket,
-} from './socket/tunnelbroker.js';
+import { createAndMaintainTunnelbrokerWebsocket } from './socket/tunnelbroker.js';
import {
multerProcessor,
multimediaUploadResponder,
@@ -105,7 +102,7 @@
const aes256Key = crypto.randomBytes(32).toString('hex');
const ed25519Key = await getContentSigningKey();
- await createAndMaintainAnonymousTunnelbrokerWebsocket(aes256Key);
+ await createAndMaintainTunnelbrokerWebsocket(aes256Key);
console.log(
'\nOpen the Comm app on your phone and scan the QR code below\n',
@@ -130,9 +127,7 @@
// We don't await here, as Tunnelbroker communication is not needed for
// normal keyserver behavior yet. In addition, this doesn't return
// information useful for other keyserver functions.
- ignorePromiseRejections(
- createAndMaintainTunnelbrokerWebsocket(identityInfo),
- );
+ ignorePromiseRejections(createAndMaintainTunnelbrokerWebsocket(null));
if (process.env.NODE_ENV === 'development') {
await createAuthoritativeKeyserverConfigFiles(identityInfo.userId);
}
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,5 +1,6 @@
// @flow
+import invariant from 'invariant';
import _debounce from 'lodash/debounce.js';
import { getRustAPI } from 'rust-node-addon';
import uuid from 'uuid';
@@ -22,6 +23,7 @@
refreshKeysRequestValidator,
type QRCodeAuthMessage,
} from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
+import { peerToPeerMessageTypes } from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
import {
type QRCodeAuthMessagePayload,
qrCodeAuthMessagePayloadValidator,
@@ -32,13 +34,18 @@
AnonymousInitializationMessage,
} from 'lib/types/tunnelbroker/session-types.js';
import type { Heartbeat } from 'lib/types/websocket/heartbeat-types.js';
-import { convertBytesToObj } from 'lib/utils/conversion-utils.js';
+import {
+ convertBytesToObj,
+ convertObjToBytes,
+} from 'lib/utils/conversion-utils.js';
import { fetchOlmAccount } from '../updaters/olm-account-updater.js';
-import { decrypt } from '../utils/aes-crypto-utils.js';
+import { saveIdentityInfo } from '../user/identity.js';
+import { encrypt, decrypt } from '../utils/aes-crypto-utils.js';
import {
uploadNewOneTimeKeys,
getNewDeviceKeyUpload,
+ markPrekeysAsPublished,
} from '../utils/olm-utils.js';
type PromiseCallbacks = {
@@ -54,22 +61,59 @@
promises: Promises = {};
heartbeatTimeoutID: ?TimeoutID;
oneTimeKeysPromise: ?Promise<void>;
- anonymous: boolean = false;
+ userID: ?string;
+ accessToken: ?string;
qrAuthEncryptionKey: ?string;
+ primaryDeviceID: ?string;
+ justSuccessfullyAuthenticated: boolean = false;
+ shouldNotifyPrimary: boolean = false;
constructor(
socketURL: string,
- initMessage:
- | ConnectionInitializationMessage
- | AnonymousInitializationMessage,
- onClose: () => mixed,
- qrAuthEncryptionKey?: string,
+ onClose: (boolean, ?string) => mixed,
+ userID: ?string,
+ deviceID: string,
+ accessToken: ?string,
+ qrAuthEncryptionKey: ?string,
+ primaryDeviceID: ?string,
+ justSuccessfullyAuthenticated: boolean,
) {
+ this.userID = userID;
+ this.accessToken = accessToken;
+ this.qrAuthEncryptionKey = qrAuthEncryptionKey;
+ this.primaryDeviceID = primaryDeviceID;
+
+ if (justSuccessfullyAuthenticated) {
+ this.shouldNotifyPrimary = true;
+ }
+
const socket = new WebSocket(socketURL);
socket.on('open', () => {
if (!this.closed) {
- socket.send(JSON.stringify(initMessage));
+ let initMessageString;
+
+ if (userID && accessToken) {
+ console.log('Creating authenticated tunnelbroker connection');
+ const initMessage: ConnectionInitializationMessage = {
+ type: 'ConnectionInitializationMessage',
+ deviceID,
+ accessToken: accessToken,
+ userID: userID,
+ deviceType: 'keyserver',
+ };
+ initMessageString = JSON.stringify(initMessage);
+ } else {
+ console.log('Creating anonymous tunnelbroker connection');
+ const initMessage: AnonymousInitializationMessage = {
+ type: 'AnonymousInitializationMessage',
+ deviceID,
+ deviceType: 'keyserver',
+ };
+ initMessageString = JSON.stringify(initMessage);
+ }
+
+ socket.send(initMessageString);
}
});
@@ -81,7 +125,7 @@
this.connected = false;
this.stopHeartbeatTimeout();
console.error('Connection to Tunnelbroker closed');
- onClose();
+ onClose(this.justSuccessfullyAuthenticated, this.primaryDeviceID);
});
socket.on('error', (error: Error) => {
@@ -91,10 +135,6 @@
socket.on('message', this.onMessage);
this.ws = socket;
- this.anonymous = !initMessage.accessToken;
- if (qrAuthEncryptionKey) {
- this.qrAuthEncryptionKey = qrAuthEncryptionKey;
- }
}
onMessage: (event: ArrayBuffer) => Promise<void> = async (
@@ -123,10 +163,29 @@
if (message.status.type === 'Success' && !this.connected) {
this.connected = true;
console.info(
- this.anonymous
- ? 'anonymous session with Tunnelbroker created'
- : 'session with Tunnelbroker created',
+ this.userID && this.accessToken
+ ? 'session with Tunnelbroker created'
+ : 'anonymous session with Tunnelbroker created',
+ );
+ if (!this.shouldNotifyPrimary) {
+ return;
+ }
+ const primaryDeviceID = this.primaryDeviceID;
+ invariant(
+ primaryDeviceID,
+ 'Primary device ID is not set but should be',
);
+ const payload = await this.encodeQRAuthMessage({
+ type: qrCodeAuthMessageTypes.SECONDARY_DEVICE_REGISTRATION_SUCCESS,
+ });
+ if (!payload) {
+ this.closeConnection();
+ return;
+ }
+ await this.sendMessage({
+ deviceID: primaryDeviceID,
+ payload: JSON.stringify(payload),
+ });
} else if (message.status.type === 'Success' && this.connected) {
console.info(
'received ConnectionInitializationResponse with status: Success for already connected socket',
@@ -162,8 +221,9 @@
) {
return;
}
- // eslint-disable-next-line no-unused-vars
const { primaryDeviceID, userID } = qrCodeAuthMessage;
+ this.primaryDeviceID = primaryDeviceID;
+
const [nonce, deviceKeyUpload] = await Promise.all([
rustAPI.generateNonce(),
getNewDeviceKeyUpload(),
@@ -174,7 +234,7 @@
};
const nonceSignature = accountInfo.account.sign(nonce);
- await rustAPI.uploadSecondaryDeviceKeysAndLogIn(
+ const identityInfo = await rustAPI.uploadSecondaryDeviceKeysAndLogIn(
userID,
nonce,
nonceSignature,
@@ -186,6 +246,12 @@
deviceKeyUpload.contentOneTimeKeys,
deviceKeyUpload.notifOneTimeKeys,
);
+ await Promise.all([
+ markPrekeysAsPublished(),
+ saveIdentityInfo(identityInfo),
+ ]);
+ this.justSuccessfullyAuthenticated = true;
+ this.closeConnection();
} else if (refreshKeysRequestValidator.is(messageToKeyserver)) {
const request: RefreshKeyRequest = messageToKeyserver;
this.debouncedRefreshOneTimeKeys(request.numberOfKeys);
@@ -282,6 +348,11 @@
}, tunnelbrokerHeartbeatTimeout);
}
+ closeConnection() {
+ this.ws.close();
+ this.connected = false;
+ }
+
parseQRCodeAuthMessage: (
message: QRCodeAuthMessage,
) => Promise<?QRCodeAuthMessagePayload> = async message => {
@@ -301,6 +372,24 @@
return payload;
};
+
+ encodeQRAuthMessage: (
+ payload: QRCodeAuthMessagePayload,
+ ) => Promise<?QRCodeAuthMessage> = async payload => {
+ const encryptionKey = this.qrAuthEncryptionKey;
+ if (!encryptionKey) {
+ console.error('Encryption key missing - cannot send QR auth message.');
+ return null;
+ }
+ const payloadBytes = convertObjToBytes(payload);
+ const keyBytes = hexToUintArray(encryptionKey);
+ const encryptedBytes = await encrypt(keyBytes, payloadBytes);
+ const encryptedContent = Buffer.from(encryptedBytes).toString('base64');
+ return Promise.resolve({
+ type: peerToPeerMessageTypes.QR_CODE_AUTH_MESSAGE,
+ encryptedContent,
+ });
+ };
}
export default TunnelbrokerSocket;
diff --git a/keyserver/src/socket/tunnelbroker.js b/keyserver/src/socket/tunnelbroker.js
--- a/keyserver/src/socket/tunnelbroker.js
+++ b/keyserver/src/socket/tunnelbroker.js
@@ -1,15 +1,11 @@
// @flow
import { clientTunnelbrokerSocketReconnectDelay } from 'lib/shared/timeouts.js';
-import type {
- ConnectionInitializationMessage,
- AnonymousInitializationMessage,
-} from 'lib/types/tunnelbroker/session-types.js';
import { getCommConfig } from 'lib/utils/comm-config.js';
import sleep from 'lib/utils/sleep.js';
import TunnelbrokerSocket from './tunnelbroker-socket.js';
-import { type IdentityInfo } from '../user/identity.js';
+import { fetchIdentityInfo } from '../user/identity.js';
import { getContentSigningKey } from '../utils/olm-utils.js';
type TBConnectionInfo = {
@@ -32,66 +28,31 @@
};
}
-async function createAndMaintainTunnelbrokerWebsocket(
- identityInfo: IdentityInfo,
-) {
+async function createAndMaintainTunnelbrokerWebsocket(encryptionKey: ?string) {
const [deviceID, tbConnectionInfo] = await Promise.all([
getContentSigningKey(),
getTBConnectionInfo(),
]);
-
- const initMessage: ConnectionInitializationMessage = {
- type: 'ConnectionInitializationMessage',
- deviceID: deviceID,
- accessToken: identityInfo.accessToken,
- userID: identityInfo.userId,
- deviceType: 'keyserver',
- };
-
- createAndMaintainTunnelbrokerWebsocketBase(tbConnectionInfo.url, initMessage);
-}
-
-async function createAndMaintainAnonymousTunnelbrokerWebsocket(
- encryptionKey: string,
-) {
- const [deviceID, tbConnectionInfo] = await Promise.all([
- getContentSigningKey(),
- getTBConnectionInfo(),
- ]);
-
- const initMessage: AnonymousInitializationMessage = {
- type: 'AnonymousInitializationMessage',
- deviceID: deviceID,
- deviceType: 'keyserver',
- };
-
- createAndMaintainTunnelbrokerWebsocketBase(
- tbConnectionInfo.url,
- initMessage,
- encryptionKey,
- );
-}
-
-function createAndMaintainTunnelbrokerWebsocketBase(
- url: string,
- initMessage: ConnectionInitializationMessage | AnonymousInitializationMessage,
- encryptionKey?: string,
-) {
- const createNewTunnelbrokerSocket = () => {
+ const createNewTunnelbrokerSocket = async (
+ justSuccessfullyAuthenticated: boolean,
+ primaryDeviceID: ?string,
+ ) => {
+ const identityInfo = await fetchIdentityInfo();
new TunnelbrokerSocket(
- url,
- initMessage,
- async () => {
+ tbConnectionInfo.url,
+ async (successfullyAuthed: boolean, primaryID: ?string) => {
await sleep(clientTunnelbrokerSocketReconnectDelay);
- createNewTunnelbrokerSocket();
+ await createNewTunnelbrokerSocket(successfullyAuthed, primaryID);
},
+ identityInfo?.userId,
+ deviceID,
+ identityInfo?.accessToken,
encryptionKey,
+ primaryDeviceID,
+ justSuccessfullyAuthenticated,
);
};
- createNewTunnelbrokerSocket();
+ await createNewTunnelbrokerSocket(false, null);
}
-export {
- createAndMaintainTunnelbrokerWebsocket,
- createAndMaintainAnonymousTunnelbrokerWebsocket,
-};
+export { createAndMaintainTunnelbrokerWebsocket };
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
@@ -103,6 +103,17 @@
return cachedOLMUtility;
}
+async function markPrekeysAsPublished(): Promise<void> {
+ await Promise.all([
+ fetchCallUpdateOlmAccount('content', (contentAccount: OlmAccount) => {
+ contentAccount.mark_prekey_as_published();
+ }),
+ fetchCallUpdateOlmAccount('notifications', (notifAccount: OlmAccount) => {
+ notifAccount.mark_prekey_as_published();
+ }),
+ ]);
+}
+
async function getNewDeviceKeyUpload(): Promise<IdentityNewDeviceKeyUpload> {
let contentIdentityKeys: string;
let contentOneTimeKeys: $ReadOnlyArray<string>;
@@ -319,4 +330,5 @@
validateAndUploadAccountPrekeys,
publishPrekeysToIdentity,
getNewDeviceKeyUpload,
+ markPrekeysAsPublished,
};

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 16, 12:53 AM (13 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5941032
Default Alt Text
D12213.1768524823.diff (13 KB)

Event Timeline