From 43d17f90abe3b5edc141f347e52785322d422d53 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 17 Aug 2020 15:35:03 -0600 Subject: [PATCH 1/2] Use SAS emoji data from matrix-doc Fixes https://github.com/vector-im/element-web/issues/14947 Much like element-web's Jitsi wrapper build steps, this downloads the emoji JSON at build time to ensure it gets reasonably updated. In the future, the spec might want to consider publishing a dedicated i18n package on npm for this, however this is fine for now. We download rather than copy/paste to ensure we always have an updated copy. --- .gitignore | 1 + package.json | 5 +- scripts/spec-i18n.js | 37 ++++++++++++++ src/crypto/verification/SAS.js | 70 +------------------------ src/crypto/verification/SASEmojiV1.ts | 73 +++++++++++++++++++++++++++ tsconfig.json | 1 + yarn.lock | 5 ++ 7 files changed, 123 insertions(+), 69 deletions(-) create mode 100644 scripts/spec-i18n.js create mode 100644 src/crypto/verification/SASEmojiV1.ts diff --git a/.gitignore b/.gitignore index 5bfe69f16c7..fecc9eba0ea 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ out /dist /lib /specbuild +/res # version file and tarball created by `npm pack` / `yarn pack` /git-revision.txt diff --git a/package.json b/package.json index 38731390ce0..91996baf168 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && babel src -w -s -d lib --verbose --extensions \".ts,.js\"", "dist": "echo 'This is for the release script so it can make assets (browser bundle).' && yarn build", "clean": "rimraf lib dist", - "build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:compile-browser && yarn build:minify-browser && yarn build:types", + "build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:spec-i18n && yarn build:compile && yarn build:compile-browser && yarn build:minify-browser && yarn build:types", + "build:spec-i18n": "node scripts/spec-i18n.js", "build:types": "tsc --emitDeclarationOnly", "build:compile": "babel -d lib --verbose --extensions \".ts,.js\" src", "build:compile-browser": "mkdirp dist && browserify -d src/browser-index.js -p [ tsify -p ./tsconfig.json ] -t [ babelify --sourceMaps=inline --presets [ @babel/preset-env @babel/preset-typescript ] ] | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js", @@ -37,6 +38,7 @@ "dist", "lib", "src", + "res", "git-revision.txt", "CHANGELOG.md", "CONTRIBUTING.rst", @@ -52,6 +54,7 @@ "bs58": "^4.0.1", "content-type": "^1.0.2", "loglevel": "^1.6.4", + "node-fetch": "^2.6.0", "qs": "^6.5.2", "request": "^2.88.0", "unhomoglyph": "^1.0.2" diff --git a/scripts/spec-i18n.js b/scripts/spec-i18n.js new file mode 100644 index 00000000000..b9eb65fbae6 --- /dev/null +++ b/scripts/spec-i18n.js @@ -0,0 +1,37 @@ +/* +Copyright 2020 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. +*/ + +// This is a JS script to remove OS dependencies on downloading a file. Sorry. + +const fs = require("fs"); +const path = require("path"); +const mkdirp = require("mkdirp"); +const fetch = require("node-fetch"); + +console.log("Making res directory"); +mkdirp.sync("res"); + +// curl -s https://github.com/matrix-org/matrix-doc/raw/master/data-definitions/sas-emoji.json > ./res/sas-emoji.json +console.log("Downloading sas-emoji.json"); +const fname = path.join("res", "sas-emoji.json"); +fetch("https://github.com/matrix-org/matrix-doc/raw/master/data-definitions/sas-emoji.json").then(res => { + const stream = fs.createWriteStream(fname); + return new Promise((resolve, reject) => { + res.body.pipe(stream); + res.body.on('error', err => reject(err)); + res.body.on('finish', () => resolve()); + }); +}).then(() => console.log('Done with sas-emoji.json download')); diff --git a/src/crypto/verification/SAS.js b/src/crypto/verification/SAS.js index c69f9f7e81c..1eb1ad920ca 100644 --- a/src/crypto/verification/SAS.js +++ b/src/crypto/verification/SAS.js @@ -29,6 +29,7 @@ import { newUserCancelledError, } from './Error'; import {logger} from '../../logger'; +import {SASEmojiV1} from "./SASEmojiV1"; const START_TYPE = "m.key.verification.start"; @@ -64,73 +65,6 @@ function generateDecimalSas(sasBytes) { ]; } -const 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) { const emojis = [ // just like base64 encoding @@ -143,7 +77,7 @@ function generateEmojiSas(sasBytes) { (sasBytes[4] & 0xf) << 2 | sasBytes[5] >> 6, ]; - return emojis.map((num) => emojiMapping[num]); + return emojis.map((num) => SASEmojiV1.getEmojiPair(num)); } const sasGenerators = { diff --git a/src/crypto/verification/SASEmojiV1.ts b/src/crypto/verification/SASEmojiV1.ts new file mode 100644 index 00000000000..0e71d34f304 --- /dev/null +++ b/src/crypto/verification/SASEmojiV1.ts @@ -0,0 +1,73 @@ +/* +Copyright 2020 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. +*/ + +import v1Emoji from "../../../res/sas-emoji.json"; + +export interface ITranslations { + [languageCode: string]: string; +} + +/** + * Some useful utilities for accessing the SAS v1 emoji data. + */ +export class SASEmojiV1 { + public static getEmojiPair(index: number): [string, string] { + return [SASEmojiV1.getEmoji(index), SASEmojiV1.getName(index).toLowerCase()]; // .toLowerCase() for compat + } + + public static getEmoji(index: number): string { + return v1Emoji[index].emoji; + } + + public static getName(index: number): string { + return v1Emoji[index].description; + } + + public static getTranslations(index: number): ITranslations { + return v1Emoji[index].translated_descriptions; + } + + public static getNameFor(emoji: string): string { + for (const e of v1Emoji) { + if (e.emoji === emoji) { + return e.description; + } + } + throw new Error("Emoji not found"); + } + + public static getNumberFor(emoji: string): number { + for (const e of v1Emoji) { + if (e.emoji === emoji) { + return e.number; + } + } + throw new Error("Emoji not found"); + } + + public static getTranslationsFor(emoji: string): ITranslations { + for (const e of v1Emoji) { + if (e.emoji === emoji) { + return e.translated_descriptions; + } + } + throw new Error("Emoji not found"); + } + + public static getAllEmoji(): string[] { + return v1Emoji.map(e => e.emoji); + } +} diff --git a/tsconfig.json b/tsconfig.json index 548bbe7fb07..7db2017a80d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "sourceMap": true, "outDir": "./lib", "declaration": true, + "resolveJsonModule": true, "types": [ "node" ] diff --git a/yarn.lock b/yarn.lock index d3ce3b70208..315753ce517 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4930,6 +4930,11 @@ node-dir@^0.1.10: dependencies: minimatch "^3.0.2" +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" From 5711ec2b50b692328e2b5827b6eeb435ef9b9b43 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 18 Aug 2020 12:59:53 -0600 Subject: [PATCH 2/2] Add a postinstall step to ensure `res` gets created This is to fix upstream builds which fail to get the json file --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 91996baf168..264f320a961 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Matrix Client-Server SDK for Javascript", "scripts": { "prepare": "yarn build", + "postinstall": "yarn build:spec-i18n", "start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && babel src -w -s -d lib --verbose --extensions \".ts,.js\"", "dist": "echo 'This is for the release script so it can make assets (browser bundle).' && yarn build", "clean": "rimraf lib dist",