diff --git a/package.json b/package.json index ac968ff..1632efd 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "matrix-synapse-join-room": "src/matrix-synapse-join-room.js", "matrix-whois-user": "src/matrix-whois-user.js", "matrix-room-users": "src/matrix-room-users.js", - "matrix-device-verify": "src/matrix-device-verify.js" + "matrix-device-verify": "src/matrix-device-verify.js", + "matrix-secret-storage": "src/matrix-secret-storage.js" } }, "engines": { diff --git a/src/matrix-device-verify.js b/src/matrix-device-verify.js index e86c93c..41f01ed 100644 --- a/src/matrix-device-verify.js +++ b/src/matrix-device-verify.js @@ -39,13 +39,14 @@ module.exports = function(RED) { * rejected. */ node.server.matrixClient.on("crypto.verification.request", async function(data){ - console.log("[######### crypto.verification.request #########]"); + console.log("[######### crypto.verification.request #########]", data.phase, data.methods); if(data.isSelfVerification) { - if(data.requested && data.methods) { + if(data.requested || true) { let verifyRequestId = data.targetDevice.userId + ':' + data.targetDevice.deviceId; verificationRequests.set(verifyRequestId, data); node.send({ - verifyRequestId: verifyRequestId, // internally used to reference between nodes + verifyRequestId: verifyRequestId, // internally used to reference between nodesc + verifyMethods: data.methods, userId: data.targetDevice.userId, deviceId: data.targetDevice.deviceId, type: 'crypto.verification.request', @@ -139,6 +140,7 @@ module.exports = function(RED) { }; data._verifier.on('cancel', function(e){ + node.warn("Device verificaiton cancelled " + e); verifierCancel(); }); @@ -180,6 +182,8 @@ module.exports = function(RED) { } }); + data.emit("change"); + await data.accept(); } catch(e) { console.log("ERROR", e); diff --git a/src/matrix-secret-storage.html b/src/matrix-secret-storage.html new file mode 100644 index 0000000..4c99aed --- /dev/null +++ b/src/matrix-secret-storage.html @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/src/matrix-secret-storage.js b/src/matrix-secret-storage.js new file mode 100644 index 0000000..9e11a18 --- /dev/null +++ b/src/matrix-secret-storage.js @@ -0,0 +1,92 @@ +module.exports = function(RED) { + const verificationRequests = new Map(); + + function MatrixSecretStorage(n) { + RED.nodes.createNode(this, n); + + var node = this; + + this.name = n.name; + this.server = RED.nodes.getNode(n.server); + + if (!node.server) { + node.warn("No configuration node"); + return; + } + + node.status({ fill: "red", shape: "ring", text: "disconnected" }); + + node.server.on("disconnected", function(){ + node.status({ fill: "red", shape: "ring", text: "disconnected" }); + }); + + node.server.on("connected", function() { + node.status({ fill: "green", shape: "ring", text: "connected" }); + }); + + node.on('input', async function(msg){ + try { + msg.hasSecretStorage = await node.server.matrixClient.hasSecretStorageKey(); + } catch(e) { + console.log("ERROR", e); + } + + if(msg.action) { + if(msg.action === 'create') { + if(msg.hasSecretStorage && !msg.forceReset) { + node.error("Secret storage already setup. Pass msg.forceReset to bypass and regenerate."); + return; + } + + + // copying this from https://github.com/matrix-org/matrix-react-sdk/blob/e78a1adb6f1af2ea425b0bae9034fb7344a4b2e8/src/SecurityManager.ts#L294 + const recoveryKey = await node.server.matrixClient.createRecoveryKeyFromPassphrase(msg.key || undefined); + if(msg.forceReset) { + await node.server.matrixClient.bootstrapSecretStorage({ + createSecretStorageKey: async () => recoveryKey, + setupNewKeyBackup: true, + setupNewSecretStorage: true, + }); + } else { + // For password authentication users after 2020-09, this cross-signing + // step will be a no-op since it is now setup during registration or login + // when needed. We should keep this here to cover other cases such as: + // * Users with existing sessions prior to 2020-09 changes + // * SSO authentication users which require interactive auth to upload + // keys (and also happen to skip all post-authentication flows at the + // moment via token login) + await node.server.matrixClient.bootstrapCrossSigning({ + // maybe we can skip this? + // authUploadDeviceSigningKeys: this._doBootstrapUIAuth, + }); + const backupState = await node.server.matrixClient.getKeyBackupVersion(); + await node.server.matrixClient.bootstrapSecretStorage({ + createSecretStorageKey: async () => this._recoveryKey, + keyBackupInfo: backupState.backupInfo, + setupNewKeyBackup: !backupState.backupInfo, + getKeyBackupPassphrase: () => { + // We may already have the backup key if we earlier went + // through the restore backup path, so pass it along + // rather than prompting again. + if (this._backupKey) { + return this._backupKey; + } + return promptForBackupPassphrase(); + }, + }); + } + } + + if(msg.action === 'download') { + if(!msg.hasSecretStorage) { + node.error("Secret storage not setup so cannot download."); + return; + } + } + } + + node.send(msg); + }); + } + RED.nodes.registerType("matrix-secret-storage", MatrixSecretStorage); +} \ No newline at end of file