mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-14 19:16:02 -04:00
feat(crypto-js): Encode the WASM as base64 for portability
feat(crypto-js): Encode the WASM as base64 for portability
This commit is contained in:
@@ -15,6 +15,14 @@ publish = false
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.wasm-pack.profile.profiling]
|
||||
wasm-opt = false
|
||||
|
||||
[package.metadata.wasm-pack.profile.profiling.wasm-bindgen]
|
||||
debug-js-glue = false
|
||||
demangle-name-section = true
|
||||
dwarf-debug-info = true
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ['-Oz']
|
||||
|
||||
|
||||
@@ -37,12 +37,9 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env RUSTFLAGS='-C opt-level=z' WASM_BINDGEN_WEAKREF=1 wasm-pack build --release --target nodejs --scope matrix-org --out-dir ./pkg",
|
||||
"build": "./scripts/build.sh",
|
||||
"test": "jest --verbose",
|
||||
"doc": "typedoc --tsconfig .",
|
||||
"prepack": "npm run build && npm run test",
|
||||
"pack": "wasm-pack pack",
|
||||
"prepublish": "npm run pack",
|
||||
"publish": "wasm-pack publish"
|
||||
"prepack": "npm run build && npm run test"
|
||||
}
|
||||
}
|
||||
|
||||
37
bindings/matrix-sdk-crypto-js/scripts/build.sh
Executable file
37
bindings/matrix-sdk-crypto-js/scripts/build.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build the JavaScript modules
|
||||
#
|
||||
# This script is really a workaround for https://github.com/rustwasm/wasm-pack/issues/1074.
|
||||
#
|
||||
# Currently, the only reliable way to load WebAssembly in all the JS
|
||||
# environments we want to target (web-via-webpack, web-via-browserify, jest)
|
||||
# seems to be to pack the WASM into base64, and then unpack it and instantiate
|
||||
# it at runtime.
|
||||
#
|
||||
# Hopefully one day, https://github.com/rustwasm/wasm-pack/issues/1074 will be
|
||||
# fixed and this will be unnecessary.
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
RUSTFLAGS='-C opt-level=z' WASM_BINDGEN_WEAKREF=1 wasm-pack build --release --target nodejs --scope matrix-org --out-dir pkg
|
||||
|
||||
# Convert the Wasm into a JS file that exports the base64'ed Wasm.
|
||||
echo "module.exports = \`$(base64 pkg/matrix_sdk_crypto_js_bg.wasm)\`;" > pkg/matrix_sdk_crypto_js_bg.wasm.js
|
||||
|
||||
# Copy in the unbase64 module
|
||||
cp scripts/unbase64.js pkg/
|
||||
|
||||
# In the JavaScript:
|
||||
# 1. Replace the lines that load the Wasm,
|
||||
# 2. Remove the imports of `TextDecoder` and `TextEncoder`. We rely on the global defaults.
|
||||
loadwasm='const bytes = require("./unbase64.js")(require("./matrix_sdk_crypto_js_bg.wasm.js"));'
|
||||
|
||||
# sed on OSX uses different syntax for sed -i, so let's just avoid it.
|
||||
sed -e "/^const path = /d" \
|
||||
-e "s@^const bytes =.*@${loadwasm}@" \
|
||||
-e '/Text..coder.*= require(.util.)/d' \
|
||||
pkg/matrix_sdk_crypto_js.js >pkg/matrix_sdk_crypto_js.js.new
|
||||
mv pkg/matrix_sdk_crypto_js.js.new pkg/matrix_sdk_crypto_js.js
|
||||
32
bindings/matrix-sdk-crypto-js/scripts/unbase64.js
Normal file
32
bindings/matrix-sdk-crypto-js/scripts/unbase64.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// JavaScript module which exports a function which will un-base64 a string.
|
||||
//
|
||||
// Based on the code at https://developer.mozilla.org/en-US/docs/Glossary/Base64#solution_2_%E2%80%93_rewriting_atob_and_btoa_using_typedarrays_and_utf-8
|
||||
|
||||
const lookup = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 62, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]);
|
||||
|
||||
module.exports = (sBase64) => {
|
||||
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
|
||||
const nInLen = sB64Enc.length;
|
||||
const nOutLen = (nInLen * 3 + 1) >> 2;
|
||||
const taBytes = new Uint8Array(nOutLen);
|
||||
|
||||
let nMod3;
|
||||
let nMod4;
|
||||
let nUint24 = 0;
|
||||
let nOutIdx = 0;
|
||||
for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
|
||||
nMod4 = nInIdx & 3;
|
||||
nUint24 |= lookup[sB64Enc.charCodeAt(nInIdx)] << (6 * (3 - nMod4));
|
||||
if (nMod4 === 3 || nInLen - nInIdx === 1) {
|
||||
nMod3 = 0;
|
||||
while (nMod3 < 3 && nOutIdx < nOutLen) {
|
||||
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
|
||||
nMod3++;
|
||||
nOutIdx++;
|
||||
}
|
||||
nUint24 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return taBytes;
|
||||
};
|
||||
@@ -484,8 +484,13 @@ impl Qr {
|
||||
/// The `to_qr_code` method can be used to instead output a QrCode
|
||||
/// object that can be rendered.
|
||||
#[wasm_bindgen(js_name = "toBytes")]
|
||||
pub fn to_bytes(&self) -> Result<Array, JsError> {
|
||||
Ok(self.inner.to_bytes()?.into_iter().map(JsValue::from).collect())
|
||||
pub fn to_bytes(&self) -> Result<Uint8ClampedArray, JsError> {
|
||||
let bytes = self.inner.to_bytes()?;
|
||||
let output = Uint8ClampedArray::new_with_length(bytes.len() as _);
|
||||
|
||||
output.copy_from(&bytes);
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Notify the other side that we have successfully scanned the QR
|
||||
@@ -754,7 +759,7 @@ impl QrCodeScan {
|
||||
/// This method is useful if you would like to do your own custom QR code
|
||||
/// decoding.
|
||||
#[wasm_bindgen(js_name = "fromBytes")]
|
||||
pub fn from_bytes(buffer: Uint8ClampedArray) -> Result<QrCodeScan, JsError> {
|
||||
pub fn from_bytes(buffer: &Uint8ClampedArray) -> Result<QrCodeScan, JsError> {
|
||||
let bytes = buffer.to_vec();
|
||||
|
||||
Ok(Self { inner: matrix_sdk_qrcode::QrVerificationData::from_bytes(&bytes)? })
|
||||
@@ -1029,9 +1034,9 @@ impl VerificationRequest {
|
||||
/// for this verification flow.
|
||||
#[cfg(feature = "qrcode")]
|
||||
#[wasm_bindgen(js_name = "scanQrCode")]
|
||||
pub fn scan_qr_code(&self, data: QrCodeScan) -> Promise {
|
||||
pub fn scan_qr_code(&self, data: &QrCodeScan) -> Promise {
|
||||
let me = self.inner.clone();
|
||||
let qr_verification_data = data.inner;
|
||||
let qr_verification_data = data.inner.clone();
|
||||
|
||||
future_to_promise(
|
||||
async move { Ok(me.scan_qr_code(qr_verification_data).await?.map(Qr::from)) },
|
||||
|
||||
@@ -707,16 +707,14 @@ describe('Key Verification', () => {
|
||||
expect(qr2.roomId).toBeUndefined();
|
||||
});
|
||||
|
||||
let qrCodeBytes;
|
||||
|
||||
test('can read QR code\'s bytes', async () => {
|
||||
const qrCodeHeader = 'MATRIX';
|
||||
const qrCodeVersion = '\x02';
|
||||
|
||||
qrCodeBytes = qr2.toBytes();
|
||||
const qrCodeBytes = qr2.toBytes();
|
||||
|
||||
expect(qrCodeBytes).toHaveLength(122);
|
||||
expect(qrCodeBytes.slice(0, 7)).toStrictEqual([...qrCodeHeader, ...qrCodeVersion].map(char => char.charCodeAt(0)));
|
||||
expect(Array.from(qrCodeBytes.slice(0, 7))).toEqual([...qrCodeHeader, ...qrCodeVersion].map(char => char.charCodeAt(0)));
|
||||
});
|
||||
|
||||
test('can render QR code', async () => {
|
||||
@@ -793,7 +791,7 @@ describe('Key Verification', () => {
|
||||
let qr1;
|
||||
|
||||
test('can scan a QR code from bytes', async () => {
|
||||
const scan = QrCodeScan.fromBytes(qrCodeBytes);
|
||||
const scan = QrCodeScan.fromBytes(qr2.toBytes());
|
||||
|
||||
expect(scan).toBeInstanceOf(QrCodeScan);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user