From 75688cf6f3f8f49d6e8522c5c7af3f724208882f Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Wed, 22 Feb 2023 13:01:58 +0530 Subject: [PATCH] REFACTOR: Extract methods and functions --- src/matrix/verification/SAS/generator.ts | 122 ++++++++++++++ .../verification/SAS/stages/SendKeyStage.ts | 152 +++++------------- 2 files changed, 161 insertions(+), 113 deletions(-) create mode 100644 src/matrix/verification/SAS/generator.ts diff --git a/src/matrix/verification/SAS/generator.ts b/src/matrix/verification/SAS/generator.ts new file mode 100644 index 00000000..cff46f6f --- /dev/null +++ b/src/matrix/verification/SAS/generator.ts @@ -0,0 +1,122 @@ +/* +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. +*/ + +// Copied from element-web + +type EmojiMapping = [emoji: string, name: string]; + +const emojiMapping: EmojiMapping[] = [ + ["🐶", "dog"], // 0 + ["🐱", "cat"], // 1 + ["🦁", "lion"], // 2 + ["🐎", "horse"], // 3 + ["🦄", "unicorn"], // 4 + ["🐷", "pig"], // 5 + ["🐘", "elephant"], // 6 + ["🐰", "rabbit"], // 7 + ["🐼", "panda"], // 8 + ["🐓", "rooster"], // 9 + ["🐧", "penguin"], // 10 + ["🐢", "turtle"], // 11 + ["🐟", "fish"], // 12 + ["🐙", "octopus"], // 13 + ["🦋", "butterfly"], // 14 + ["🌷", "flower"], // 15 + ["🌳", "tree"], // 16 + ["🌵", "cactus"], // 17 + ["🍄", "mushroom"], // 18 + ["🌏", "globe"], // 19 + ["🌙", "moon"], // 20 + ["☁️", "cloud"], // 21 + ["🔥", "fire"], // 22 + ["🍌", "banana"], // 23 + ["🍎", "apple"], // 24 + ["🍓", "strawberry"], // 25 + ["🌽", "corn"], // 26 + ["🍕", "pizza"], // 27 + ["🎂", "cake"], // 28 + ["❤️", "heart"], // 29 + ["🙂", "smiley"], // 30 + ["🤖", "robot"], // 31 + ["🎩", "hat"], // 32 + ["👓", "glasses"], // 33 + ["🔧", "spanner"], // 34 + ["🎅", "santa"], // 35 + ["👍", "thumbs up"], // 36 + ["☂️", "umbrella"], // 37 + ["⌛", "hourglass"], // 38 + ["⏰", "clock"], // 39 + ["🎁", "gift"], // 40 + ["💡", "light bulb"], // 41 + ["📕", "book"], // 42 + ["✏️", "pencil"], // 43 + ["📎", "paperclip"], // 44 + ["✂️", "scissors"], // 45 + ["🔒", "lock"], // 46 + ["🔑", "key"], // 47 + ["🔨", "hammer"], // 48 + ["☎️", "telephone"], // 49 + ["🏁", "flag"], // 50 + ["🚂", "train"], // 51 + ["🚲", "bicycle"], // 52 + ["✈️", "aeroplane"], // 53 + ["🚀", "rocket"], // 54 + ["🏆", "trophy"], // 55 + ["⚽", "ball"], // 56 + ["🎸", "guitar"], // 57 + ["🎺", "trumpet"], // 58 + ["🔔", "bell"], // 59 + ["⚓️", "anchor"], // 60 + ["🎧", "headphones"], // 61 + ["📁", "folder"], // 62 + ["📌", "pin"], // 63 +]; + +export function generateEmojiSas(sasBytes: number[]): EmojiMapping[] { + const emojis = [ + // just like base64 encoding + sasBytes[0] >> 2, + ((sasBytes[0] & 0x3) << 4) | (sasBytes[1] >> 4), + ((sasBytes[1] & 0xf) << 2) | (sasBytes[2] >> 6), + sasBytes[2] & 0x3f, + sasBytes[3] >> 2, + ((sasBytes[3] & 0x3) << 4) | (sasBytes[4] >> 4), + ((sasBytes[4] & 0xf) << 2) | (sasBytes[5] >> 6), + ]; + return emojis.map((num) => emojiMapping[num]); +} + +/** + * Implementation of decimal encoding of SAS as per: + * https://spec.matrix.org/v1.4/client-server-api/#sas-method-decimal + * @param sasBytes - the five bytes generated by HKDF + * @returns the derived three numbers between 1000 and 9191 inclusive + */ +export function generateDecimalSas(sasBytes: number[]): [number, number, number] { + /* + * +--------+--------+--------+--------+--------+ + * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | + * +--------+--------+--------+--------+--------+ + * bits: 87654321 87654321 87654321 87654321 87654321 + * \____________/\_____________/\____________/ + * 1st number 2nd number 3rd number + */ + return [ + ((sasBytes[0] << 5) | (sasBytes[1] >> 3)) + 1000, + (((sasBytes[1] & 0x7) << 10) | (sasBytes[2] << 2) | (sasBytes[3] >> 6)) + 1000, + (((sasBytes[3] & 0x3f) << 7) | (sasBytes[4] >> 1)) + 1000, + ]; +} diff --git a/src/matrix/verification/SAS/stages/SendKeyStage.ts b/src/matrix/verification/SAS/stages/SendKeyStage.ts index dbbe4490..f4e6d743 100644 --- a/src/matrix/verification/SAS/stages/SendKeyStage.ts +++ b/src/matrix/verification/SAS/stages/SendKeyStage.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; -import anotherjson from "another-json"; +import {generateEmojiSas} from "../generator"; +import {ILogItem} from "../../../../lib"; // From element-web type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519"; @@ -73,45 +74,53 @@ export class SendKeyStage extends BaseSASVerificationStage { async completeStage() { await this.log.wrap("SendKeyStage.completeStage", async (log) => { - const event = this.previousResult["m.key.verification.key"]; - const content = event.content; - const theirKey = content.key; - const ourSasKey = this.previousResult["our_pub_key"]; - console.log("ourSasKey", ourSasKey); - const contentToSend = { - key: ourSasKey, - "m.relates_to": { - event_id: this.requestEventId, - rel_type: "m.reference", - }, - }; - await this.room.sendEvent("m.key.verification.key", contentToSend, null, log); - const keyAgreement = this.previousResult["m.key.verification.accept"].key_agreement_protocol; - const otherUserDeviceId = this.previousResult["m.key.verification.start"].content.from_device; - this.olmSAS.set_their_key(theirKey); - const sasBytes = calculateKeyAgreement[keyAgreement]({ - our: { - userId: this.ourUser.userId, - deviceId: this.ourUser.deviceId, - publicKey: ourSasKey, - }, - their: { - userId: this.otherUserId, - deviceId: otherUserDeviceId, - publicKey: theirKey, - }, - requestId: this.requestEventId, - }, this.olmSAS, 6); + this.olmSAS.set_their_key(this.theirKey); + const ourSasKey = this.olmSAS.get_pubkey(); + await this.sendKey(ourSasKey, log); + const sasBytes = this.generateSASBytes(); const emoji = generateEmojiSas(Array.from(sasBytes)); console.log("emoji", emoji); this.dispose(); }); } + private async sendKey(key: string, log: ILogItem): Promise { + const contentToSend = { + key, + "m.relates_to": { + event_id: this.requestEventId, + rel_type: "m.reference", + }, + }; + await this.room.sendEvent("m.key.verification.key", contentToSend, null, log); + } + + private generateSASBytes(): Uint8Array { + const keyAgreement = this.previousResult["m.key.verification.accept"].key_agreement_protocol; + const otherUserDeviceId = this.previousResult["m.key.verification.start"].content.from_device; + const sasBytes = calculateKeyAgreement[keyAgreement]({ + our: { + userId: this.ourUser.userId, + deviceId: this.ourUser.deviceId, + publicKey: this.olmSAS.get_pubkey(), + }, + their: { + userId: this.otherUserId, + deviceId: otherUserDeviceId, + publicKey: this.theirKey, + }, + requestId: this.requestEventId, + }, this.olmSAS, 6); + return sasBytes; + } get type() { return "m.key.verification.accept"; } + + get theirKey(): string { + return this.previousResult["m.key.verification.key"].content.key; + } } function intersection(anArray: T[], aSet: Set): T[] { @@ -128,86 +137,3 @@ function intersection(anArray: T[], aSet: Set): T[] { // } // return sas; // } - -type EmojiMapping = [emoji: string, name: string]; - -const emojiMapping: EmojiMapping[] = [ - ["🐶", "dog"], // 0 - ["🐱", "cat"], // 1 - ["🦁", "lion"], // 2 - ["🐎", "horse"], // 3 - ["🦄", "unicorn"], // 4 - ["🐷", "pig"], // 5 - ["🐘", "elephant"], // 6 - ["🐰", "rabbit"], // 7 - ["🐼", "panda"], // 8 - ["🐓", "rooster"], // 9 - ["🐧", "penguin"], // 10 - ["🐢", "turtle"], // 11 - ["🐟", "fish"], // 12 - ["🐙", "octopus"], // 13 - ["🦋", "butterfly"], // 14 - ["🌷", "flower"], // 15 - ["🌳", "tree"], // 16 - ["🌵", "cactus"], // 17 - ["🍄", "mushroom"], // 18 - ["🌏", "globe"], // 19 - ["🌙", "moon"], // 20 - ["☁️", "cloud"], // 21 - ["🔥", "fire"], // 22 - ["🍌", "banana"], // 23 - ["🍎", "apple"], // 24 - ["🍓", "strawberry"], // 25 - ["🌽", "corn"], // 26 - ["🍕", "pizza"], // 27 - ["🎂", "cake"], // 28 - ["❤️", "heart"], // 29 - ["🙂", "smiley"], // 30 - ["🤖", "robot"], // 31 - ["🎩", "hat"], // 32 - ["👓", "glasses"], // 33 - ["🔧", "spanner"], // 34 - ["🎅", "santa"], // 35 - ["👍", "thumbs up"], // 36 - ["☂️", "umbrella"], // 37 - ["⌛", "hourglass"], // 38 - ["⏰", "clock"], // 39 - ["🎁", "gift"], // 40 - ["💡", "light bulb"], // 41 - ["📕", "book"], // 42 - ["✏️", "pencil"], // 43 - ["📎", "paperclip"], // 44 - ["✂️", "scissors"], // 45 - ["🔒", "lock"], // 46 - ["🔑", "key"], // 47 - ["🔨", "hammer"], // 48 - ["☎️", "telephone"], // 49 - ["🏁", "flag"], // 50 - ["🚂", "train"], // 51 - ["🚲", "bicycle"], // 52 - ["✈️", "aeroplane"], // 53 - ["🚀", "rocket"], // 54 - ["🏆", "trophy"], // 55 - ["⚽", "ball"], // 56 - ["🎸", "guitar"], // 57 - ["🎺", "trumpet"], // 58 - ["🔔", "bell"], // 59 - ["⚓️", "anchor"], // 60 - ["🎧", "headphones"], // 61 - ["📁", "folder"], // 62 - ["📌", "pin"], // 63 -]; - -function generateEmojiSas(sasBytes: number[]): EmojiMapping[] { - const emojis = [ - // just like base64 encoding - sasBytes[0] >> 2, - ((sasBytes[0] & 0x3) << 4) | (sasBytes[1] >> 4), - ((sasBytes[1] & 0xf) << 2) | (sasBytes[2] >> 6), - sasBytes[2] & 0x3f, - sasBytes[3] >> 2, - ((sasBytes[3] & 0x3) << 4) | (sasBytes[4] >> 4), - ((sasBytes[4] & 0xf) << 2) | (sasBytes[5] >> 6), - ]; - return emojis.map((num) => emojiMapping[num]); -}