From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../channels/MSC3903ECDHv2RendezvousChannel.js | 194 +++++++++++++++++++++ .../lib/matrix-sdk/rendezvous/channels/index.js | 16 ++ 2 files changed, 210 insertions(+) create mode 100644 comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/MSC3903ECDHv2RendezvousChannel.js create mode 100644 comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/index.js (limited to 'comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels') diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/MSC3903ECDHv2RendezvousChannel.js b/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/MSC3903ECDHv2RendezvousChannel.js new file mode 100644 index 0000000000..3c9e5793bc --- /dev/null +++ b/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/MSC3903ECDHv2RendezvousChannel.js @@ -0,0 +1,194 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.MSC3903ECDHv2RendezvousChannel = void 0; +var _ = require(".."); +var _olmlib = require("../../crypto/olmlib"); +var _crypto = require("../../crypto/crypto"); +var _SASDecimal = require("../../crypto/verification/SASDecimal"); +var _NamespacedValue = require("../../NamespacedValue"); +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* + Copyright 2023 The Matrix.org Foundation C.I.C. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +const ECDH_V2 = new _NamespacedValue.UnstableValue("m.rendezvous.v2.curve25519-aes-sha256", "org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256"); +async function importKey(key) { + if (!_crypto.subtleCrypto) { + throw new Error("Web Crypto is not available"); + } + const imported = _crypto.subtleCrypto.importKey("raw", key, { + name: "AES-GCM" + }, false, ["encrypt", "decrypt"]); + return imported; +} + +/** + * Implementation of the unstable [MSC3903](https://github.com/matrix-org/matrix-spec-proposals/pull/3903) + * X25519/ECDH key agreement based secure rendezvous channel. + * Note that this is UNSTABLE and may have breaking changes without notice. + */ +class MSC3903ECDHv2RendezvousChannel { + constructor(transport, theirPublicKey, onFailure) { + this.transport = transport; + this.theirPublicKey = theirPublicKey; + this.onFailure = onFailure; + _defineProperty(this, "olmSAS", void 0); + _defineProperty(this, "ourPublicKey", void 0); + _defineProperty(this, "aesKey", void 0); + _defineProperty(this, "connected", false); + this.olmSAS = new global.Olm.SAS(); + this.ourPublicKey = (0, _olmlib.decodeBase64)(this.olmSAS.get_pubkey()); + } + async generateCode(intent) { + if (this.transport.ready) { + throw new Error("Code already generated"); + } + await this.transport.send({ + algorithm: ECDH_V2.name + }); + const rendezvous = { + rendezvous: { + algorithm: ECDH_V2.name, + key: (0, _olmlib.encodeUnpaddedBase64)(this.ourPublicKey), + transport: await this.transport.details() + }, + intent + }; + return rendezvous; + } + async connect() { + if (this.connected) { + throw new Error("Channel already connected"); + } + if (!this.olmSAS) { + throw new Error("Channel closed"); + } + const isInitiator = !this.theirPublicKey; + if (isInitiator) { + // wait for the other side to send us their public key + const rawRes = await this.transport.receive(); + if (!rawRes) { + throw new Error("No response from other device"); + } + const res = rawRes; + const { + key, + algorithm + } = res; + if (!algorithm || !ECDH_V2.matches(algorithm) || !key) { + throw new _.RendezvousError("Unsupported algorithm: " + algorithm, _.RendezvousFailureReason.UnsupportedAlgorithm); + } + this.theirPublicKey = (0, _olmlib.decodeBase64)(key); + } else { + // send our public key unencrypted + await this.transport.send({ + algorithm: ECDH_V2.name, + key: (0, _olmlib.encodeUnpaddedBase64)(this.ourPublicKey) + }); + } + this.connected = true; + this.olmSAS.set_their_key((0, _olmlib.encodeUnpaddedBase64)(this.theirPublicKey)); + const initiatorKey = isInitiator ? this.ourPublicKey : this.theirPublicKey; + const recipientKey = isInitiator ? this.theirPublicKey : this.ourPublicKey; + let aesInfo = ECDH_V2.name; + aesInfo += `|${(0, _olmlib.encodeUnpaddedBase64)(initiatorKey)}`; + aesInfo += `|${(0, _olmlib.encodeUnpaddedBase64)(recipientKey)}`; + const aesKeyBytes = this.olmSAS.generate_bytes(aesInfo, 32); + this.aesKey = await importKey(aesKeyBytes); + + // blank the bytes out to make sure not kept in memory + aesKeyBytes.fill(0); + const rawChecksum = this.olmSAS.generate_bytes(aesInfo, 5); + return (0, _SASDecimal.generateDecimalSas)(Array.from(rawChecksum)).join("-"); + } + async encrypt(data) { + if (!_crypto.subtleCrypto) { + throw new Error("Web Crypto is not available"); + } + const iv = new Uint8Array(32); + _crypto.crypto.getRandomValues(iv); + const encodedData = new _crypto.TextEncoder().encode(JSON.stringify(data)); + const ciphertext = await _crypto.subtleCrypto.encrypt({ + name: "AES-GCM", + iv, + tagLength: 128 + }, this.aesKey, encodedData); + return { + iv: (0, _olmlib.encodeUnpaddedBase64)(iv), + ciphertext: (0, _olmlib.encodeUnpaddedBase64)(ciphertext) + }; + } + async send(payload) { + if (!this.olmSAS) { + throw new Error("Channel closed"); + } + if (!this.aesKey) { + throw new Error("Shared secret not set up"); + } + return this.transport.send(await this.encrypt(payload)); + } + async decrypt({ + iv, + ciphertext + }) { + if (!ciphertext || !iv) { + throw new Error("Missing ciphertext and/or iv"); + } + const ciphertextBytes = (0, _olmlib.decodeBase64)(ciphertext); + if (!_crypto.subtleCrypto) { + throw new Error("Web Crypto is not available"); + } + const plaintext = await _crypto.subtleCrypto.decrypt({ + name: "AES-GCM", + iv: (0, _olmlib.decodeBase64)(iv), + tagLength: 128 + }, this.aesKey, ciphertextBytes); + return JSON.parse(new TextDecoder().decode(new Uint8Array(plaintext))); + } + async receive() { + if (!this.olmSAS) { + throw new Error("Channel closed"); + } + if (!this.aesKey) { + throw new Error("Shared secret not set up"); + } + const rawData = await this.transport.receive(); + if (!rawData) { + return undefined; + } + const data = rawData; + if (data.ciphertext && data.iv) { + return this.decrypt(data); + } + throw new Error("Data received but no ciphertext"); + } + async close() { + if (this.olmSAS) { + this.olmSAS.free(); + this.olmSAS = undefined; + } + } + async cancel(reason) { + try { + await this.transport.cancel(reason); + } finally { + await this.close(); + } + } +} +exports.MSC3903ECDHv2RendezvousChannel = MSC3903ECDHv2RendezvousChannel; \ No newline at end of file diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/index.js b/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/index.js new file mode 100644 index 0000000000..e2d30513fd --- /dev/null +++ b/comm/chat/protocols/matrix/lib/matrix-sdk/rendezvous/channels/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _MSC3903ECDHv2RendezvousChannel = require("./MSC3903ECDHv2RendezvousChannel"); +Object.keys(_MSC3903ECDHv2RendezvousChannel).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _MSC3903ECDHv2RendezvousChannel[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _MSC3903ECDHv2RendezvousChannel[key]; + } + }); +}); \ No newline at end of file -- cgit v1.2.3