Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33171547
D12213.1768525246.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D12213.1768525246.diff
View Options
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;
encryptionKey: ?string;
+ primaryDeviceID: ?string;
+ justSuccessfullyAuthenticated: boolean = false;
+ shouldNotifyPrimary: boolean = false;
constructor(
socketURL: string,
- initMessage:
- | ConnectionInitializationMessage
- | AnonymousInitializationMessage,
- onClose: () => mixed,
- encryptionKey?: string,
+ onClose: (boolean, ?string) => mixed,
+ userID: ?string,
+ deviceID: string,
+ accessToken: ?string,
+ encryptionKey: ?string,
+ primaryDeviceID: ?string,
+ justSuccessfullyAuthenticated: boolean,
) {
+ this.userID = userID;
+ this.accessToken = accessToken;
+ this.encryptionKey = encryptionKey;
+ 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 (encryptionKey) {
- this.encryptionKey = encryptionKey;
- }
}
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,21 +221,21 @@
) {
return;
}
- const { primaryDeviceID: receivedPrimaryDeviceID, userID } =
- qrCodeAuthMessage;
- console.log(receivedPrimaryDeviceID, userID);
+ const { primaryDeviceID, userID } = qrCodeAuthMessage;
+ this.primaryDeviceID = primaryDeviceID;
+
const [nonce, deviceKeyUpload] = await Promise.all([
rustAPI.generateNonce(),
getNewDeviceKeyUpload(),
]);
- console.log(deviceKeyUpload);
+
const signedIdentityKeysBlob = {
payload: deviceKeyUpload.keyPayload,
signature: deviceKeyUpload.keyPayloadSignature,
};
const nonceSignature = accountInfo.account.sign(nonce);
- await rustAPI.uploadSecondaryDeviceKeysAndLogIn(
+ const identityInfo = await rustAPI.uploadSecondaryDeviceKeysAndLogIn(
userID,
nonce,
nonceSignature,
@@ -188,6 +247,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);
@@ -284,6 +349,11 @@
}, tunnelbrokerHeartbeatTimeout);
}
+ closeConnection() {
+ this.ws.close();
+ this.connected = false;
+ }
+
parseQRCodeAuthMessage: (
message: QRCodeAuthMessage,
) => Promise<?QRCodeAuthMessagePayload> = async message => {
@@ -303,6 +373,24 @@
return payload;
};
+
+ encodeQRAuthMessage: (
+ payload: QRCodeAuthMessagePayload,
+ ) => Promise<?QRCodeAuthMessage> = async payload => {
+ const encryptionKey = this.encryptionKey;
+ 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>;
@@ -320,4 +331,5 @@
validateAndUploadAccountPrekeys,
publishPrekeysToIdentity,
getNewDeviceKeyUpload,
+ markPrekeysAsPublished,
};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 16, 1:00 AM (13 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5941058
Default Alt Text
D12213.1768525246.diff (13 KB)
Attached To
Mode
D12213: [keyserver] finish secondary login via qr code
Attached
Detach File
Event Timeline
Log In to Comment