wip ie11 prototype

This commit is contained in:
Bruno Windels 2020-09-16 13:46:26 +02:00
parent 885983c2f7
commit 23a0484ff0
5 changed files with 1769 additions and 41 deletions

View file

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="https://dl.dropboxusercontent.com/s/r55397ld512etib/EncoderDecoderTogether.min.js?dl=0" nomodule="" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<script src="deps/jsSHA/dist/sha512.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/ricmoo/aes-js/e27b99df/index.js"></script>
<script type="text/javascript" src="derive-keys-bundle.js"></script>
<script type="text/javascript">
if (!Math.imul) Math.imul = function(a, b) {
var aHi = (a >>> 16) & 0xffff;
var aLo = a & 0xffff;
var bHi = (b >>> 16) & 0xffff;
var bLo = b & 0xffff;
// the shift by 0 fixes the sign on the high part
// the final |0 converts the unsigned value into a signed value
return ((aLo * bLo) + (((aHi * bLo + aLo * bHi) << 16) >>> 0) | 0);
};
if (!Math.clz32) Math.clz32 = (function(log, LN2){
return function(x) {
// Let n be ToUint32(x).
// Let p be the number of leading zero bits in
// the 32-bit binary representation of n.
// Return p.
var asUint = x >>> 0;
if (asUint === 0) {
return 32;
}
return 31 - (log(asUint) / LN2 | 0) |0; // the "| 0" acts like math.floor
};
})(Math.log, Math.LN2);
</script>
<script type="text/javascript" src="../lib/olm/olm_legacy.js"></script>
<script type="text/javascript">
const ssssKeyAccountData =
{
"type": "m.secret_storage.key.HB6AKfUD4avkZfPfyjcJ6iJPWDp4f9WM",
"content": {
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
"passphrase": {
"algorithm": "m.pbkdf2",
"iterations": 500000,
"salt": "tfY5mgvQBr3Gd5Dy1IBiKf7fLquL4Y9O"
},
"iv": "xitm4hxsqagkbyEmXj0tUw==",
"mac": "nagOYz7FKrdlFEKM9ij78th0O2p7YVGgl+p0LHr4EBE="
}
};
const megolmBackupKeyAccountData = {
"type": "m.megolm_backup.v1",
"content": {
"encrypted": {
"HB6AKfUD4avkZfPfyjcJ6iJPWDp4f9WM": {
"iv": "HpzOY5DxYFJCxw5Vi6BBOQ==",
"ciphertext": "u1TJjaaGKVDGExg9hu2fIUZ0gjToMcMReyhn4nsXgnhm7Dvz6E/4p+nSF3w=",
"mac": "08ckDbQK9wB2jiE4n4sfp2sw83q/0C2/gEz2LuHMEPg="
}
}
}
};
const backupInfo = {
"algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
"auth_data": {
"public_key": "Vw2cwhbxFg/GQ2rr4VRIQ+Oh74lP7IxY6oN4R9q992k",
"signatures": {
"@bruno-test4s:matrix.org": {
"ed25519:XAIKJXBCNZ": "AFBp1T2x8hyPSi2hCHg6IzNy67RxULj3/7LYZgVT3Ruz49v5h1+jAScTxZj5jrItxo2LCzSORH+yABHjPIqOBQ",
"ed25519:lukepZkTmPcJS6wCl12B0tIURIO8YbMd5QJLf8UOugI": "a1ZJa+1+p9Gm5Po1B619ZDy4xidHmLt82vXVPH7vWTjny1r3JI2iM4fB2qh8vEiASNlFyVrFx//gQrz9Y1IJBA"
}
}
},
"count": 1,
"etag": "1",
"version": "1"
};
const sessionResponse = {
"first_message_index": 0,
"forwarded_count": 0,
"is_verified": true,
"session_data": {
"ciphertext": "1NoC8/GZWeGjneuoFDcqpbMYOJ8bjDFiw2O4/YOKC59x9RqSejLyM8qLL5FzlV+uW7anPVED8t9m+p2t1kKa15LxlcdzXjLPCv1QGYlhotbUhN8eRUobQuLqsD5Dl/QqNxv+Xl65tEaQhUeF30NmSesw6GHvP93vB3mTN8Yz9QyaQtvgoI/Q6c4d+yGmFVE2dlhXdOs7Hrylrg8UyM1QI+qpNJ3L9ETcqiXCG/FJIdM87LmNnHPX65TWK5xsu1JKWCI2BY1KFVDyxm40FyHHypUPYoT9RqPnygHtYoTiZzyaVxqUu2vg08Bv0t1VH2SNDGs5aZYQN5S1JNAHrXE+cWSg0rfVb160Z4FJC/89wO8fw/uXqJehqMVuC9BSU/zsKcZ797U92qDnIb6QQuMYKRgh9JrEugqJN9ocL7F8W9fW2oFfUYRyvOZRSf387hGrapEGBKx7Owb7UoXvWyb4C5hc5SFNvej+yg98+Fi4hzlGH26DqzJdLcxU5P/MWfZc222QqPFuFspe6f0Ts5jnJhjCQhXWoM4G6mtvGbOm2ESSJULj8U4JSDz8GsxrmojR/pBpywBvuy/mx//htnacnTRqYJz+PZVtV63rfaZlEtU",
"ephemeral": "wXBeLoazggBmFS0eiVY9H/qq5o1yt2/NIKWcq384EHc",
"mac": "w3IfO5vL9Bc"
}
};
const keyId = "HB6AKfUD4avkZfPfyjcJ6iJPWDp4f9WM";
const cryptoDriver = new bundle.CryptoDriver((window.crypto || window.msCrypto).subtle);
window.Olm.init().then(function() {
//bundle.deriveSSSSKey(cryptoDriver, prompt("passphrase"), ssssKeyAccountData).then(function(ssssKey) {
const ssssKey = new Uint8Array(32);
const bytes = [123, 47, 138, 15, 190, 69, 224, 204, 88, 246, 203, 65, 243, 234, 91, 17, 250, 107, 104, 51, 211, 252, 81, 67, 80, 191, 105, 208, 127, 87, 107, 231];
for (var i = bytes.length - 1; i >= 0; i--) {
ssssKey[i] = bytes[i];
}
console.log("ssssKey", ssssKey);
bundle.decryptSecret(cryptoDriver, keyId, ssssKey, megolmBackupKeyAccountData).then(function(backupKeyBase64) {
console.log("backupKeyBase64", backupKeyBase64);
bundle.decryptSession(backupKeyBase64, backupInfo, sessionResponse).then(function(session) {
console.log("session", session);
alert(session.session_key);
});
});
//});
});
</script>
</body>
</html>

View file

@ -1,10 +1,10 @@
function subtleCryptoResult(promiseOrOp) { function subtleCryptoResult(promiseOrOp, method) {
if (promiseOrOp instanceof Promise) { if (promiseOrOp instanceof Promise) {
return promiseOrOp; return promiseOrOp;
} else { } else {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promiseOrOp.oncomplete = e => resolve(e.target.result); promiseOrOp.oncomplete = e => resolve(e.target.result);
promiseOrOp.onerror = e => reject(e); promiseOrOp.onerror = e => reject(new Error("Crypto error on " + method));
}); });
} }
} }
@ -22,41 +22,43 @@ class CryptoHMACDriver {
* @return {boolean} * @return {boolean}
*/ */
async verify(key, mac, data, hash) { async verify(key, mac, data, hash) {
const opts = {
name: 'HMAC',
hash: {name: hashName(hash)},
};
const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey( const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(
'raw', 'raw',
key, key,
{ opts,
name: 'HMAC',
hash: {name: hashName(hash)},
},
false, false,
['verify'], ['verify'],
)); ), "importKey");
const isVerified = await subtleCryptoResult(this._subtleCrypto.verify( const isVerified = await subtleCryptoResult(this._subtleCrypto.verify(
{name: "HMAC"}, opts,
hmacKey, hmacKey,
mac, mac,
data, data,
)); ), "verify");
return isVerified; return isVerified;
} }
async compute(key, data, hash) { async compute(key, data, hash) {
const opts = {
name: 'HMAC',
hash: {name: hashName(hash)},
};
const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey( const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(
'raw', 'raw',
key, key,
{ opts,
name: 'HMAC',
hash: {name: hashName(hash)},
},
false, false,
['sign'], ['sign'],
)); ), "importKey");
const buffer = await subtleCryptoResult(this._subtleCrypto.sign( const buffer = await subtleCryptoResult(this._subtleCrypto.sign(
{name: "HMAC"}, opts,
hmacKey, hmacKey,
data, data,
)); ), "sign");
return new Uint8Array(buffer); return new Uint8Array(buffer);
} }
} }
@ -67,11 +69,42 @@ const nwbo = (num, len) => {
return arr; return arr;
}; };
class CryptoLegacyHMACDriver {
constructor(hmacDriver) {
this._hmacDriver = hmacDriver;
}
async verify(key, mac, data, hash) {
if (hash === "SHA-512") {
throw new Error("SHA-512 HMAC verification is not implemented yet");
} else {
return this._hmacDriver.verify(key, mac, data, hash)
}
}
async compute(key, data, hash) {
if (hash === "SHA-256") {
return await this._hmacDriver.compute(key, data, hash);
} else {
const shaObj = new window.jsSHA(hash, "UINT8ARRAY", {
"hmacKey": {
"value": key,
"format": "UINT8ARRAY"
}
});
shaObj.update(data);
return shaObj.getHash("UINT8ARRAY");
}
}
}
class CryptoLegacyDeriveDriver { class CryptoLegacyDeriveDriver {
constructor(cryptoDriver) { constructor(cryptoDriver) {
this._cryptoDriver = cryptoDriver; this._cryptoDriver = cryptoDriver;
} }
// adapted from https://github.com/junkurihara/jscu/blob/develop/packages/js-crypto-pbkdf/src/pbkdf.ts#L21
// could also consider https://github.com/brix/crypto-js/blob/develop/src/pbkdf2.js although not async
async pbkdf2(password, iterations, salt, hash, length) { async pbkdf2(password, iterations, salt, hash, length) {
const dkLen = length / 8; const dkLen = length / 8;
if (iterations <= 0) { if (iterations <= 0) {
@ -163,7 +196,7 @@ class CryptoDeriveDriver {
{name: 'PBKDF2'}, {name: 'PBKDF2'},
false, false,
['deriveBits'], ['deriveBits'],
)); ), "importKey");
const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits( const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits(
{ {
name: 'PBKDF2', name: 'PBKDF2',
@ -173,7 +206,7 @@ class CryptoDeriveDriver {
}, },
key, key,
length, length,
)); ), "deriveBits");
return new Uint8Array(keybits); return new Uint8Array(keybits);
} }
@ -193,7 +226,7 @@ class CryptoDeriveDriver {
{name: "HKDF"}, {name: "HKDF"},
false, false,
["deriveBits"], ["deriveBits"],
)); ), "importKey");
const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits({ const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits({
name: "HKDF", name: "HKDF",
salt, salt,
@ -202,7 +235,7 @@ class CryptoDeriveDriver {
}, },
hkdfkey, hkdfkey,
length, length,
)); ), "deriveBits");
return new Uint8Array(keybits); return new Uint8Array(keybits);
} }
} }
@ -219,24 +252,50 @@ class CryptoAESDriver {
* @return {BufferSource} [description] * @return {BufferSource} [description]
*/ */
async decrypt(key, iv, ciphertext) { async decrypt(key, iv, ciphertext) {
const aesKey = await subtleCryptoResult(this._subtleCrypto.importKey( const opts = {
'raw',
key,
{name: 'AES-CTR'},
false,
['decrypt'],
));
const plaintext = await subtleCryptoResult(this._subtleCrypto.decrypt(
// see https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams
{
name: "AES-CTR", name: "AES-CTR",
counter: iv, counter: iv,
length: 64, length: 64,
}, };
let aesKey;
try {
aesKey = await subtleCryptoResult(this._subtleCrypto.importKey(
'raw',
key,
opts,
false,
['decrypt'],
), "importKey");
} catch (err) {
throw new Error(`Could not import key for AES-CTR decryption: ${err.message}`);
}
try {
const plaintext = await subtleCryptoResult(this._subtleCrypto.decrypt(
// see https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams
opts,
aesKey, aesKey,
ciphertext, ciphertext,
)); ), "decrypt");
return new Uint8Array(plaintext); return new Uint8Array(plaintext);
} catch (err) {
throw new Error(`Could not decrypt with AES-CTR: ${err.message}`);
}
}
}
class CryptoLegacyAESDriver {
/**
* [decrypt description]
* @param {BufferSource} key [description]
* @param {BufferSource} iv [description]
* @param {BufferSource} ciphertext [description]
* @return {BufferSource} [description]
*/
async decrypt(key, iv, ciphertext) {
const aesjs = window.aesjs;
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
return aesCtr.decrypt(ciphertext);
} }
} }
@ -249,13 +308,14 @@ function hashName(name) {
export class CryptoDriver { export class CryptoDriver {
constructor(subtleCrypto) { constructor(subtleCrypto) {
this.aes = new CryptoAESDriver(subtleCrypto); this.aes = new CryptoLegacyAESDriver();
// this.aes = new CryptoAESDriver(subtleCrypto);
//this.derive = new CryptoDeriveDriver(subtleCrypto); //this.derive = new CryptoDeriveDriver(subtleCrypto);
this.derive = new CryptoLegacyDeriveDriver(this); this.derive = new CryptoLegacyDeriveDriver(this);
// subtleCrypto.deriveBits ? // subtleCrypto.deriveBits ?
// new CryptoDeriveDriver(subtleCrypto) : // new CryptoDeriveDriver(subtleCrypto) :
// new CryptoLegacyDeriveDriver(this); // new CryptoLegacyDeriveDriver(this);
this.hmac = new CryptoHMACDriver(subtleCrypto); this.hmac = new CryptoLegacyHMACDriver(new CryptoHMACDriver(subtleCrypto));
this._subtleCrypto = subtleCrypto; this._subtleCrypto = subtleCrypto;
} }
@ -334,6 +394,7 @@ export async function decryptSecret(cryptoDriver, keyId, ssssKey, event) {
export async function decryptSession(backupKeyBase64, backupInfo, sessionResponse) { export async function decryptSession(backupKeyBase64, backupInfo, sessionResponse) {
const privKey = decodeBase64(backupKeyBase64); const privKey = decodeBase64(backupKeyBase64);
console.log("privKey", privKey);
const decryption = new window.Olm.PkDecryption(); const decryption = new window.Olm.PkDecryption();
let backupPubKey; let backupPubKey;
@ -348,7 +409,7 @@ export async function decryptSession(backupKeyBase64, backupInfo, sessionRespons
// doesn't match the one in the auth_data, the user has enetered // doesn't match the one in the auth_data, the user has enetered
// a different recovery key / the wrong passphrase. // a different recovery key / the wrong passphrase.
if (backupPubKey !== backupInfo.auth_data.public_key) { if (backupPubKey !== backupInfo.auth_data.public_key) {
console.log({backupPubKey}) console.log("backupPubKey", backupPubKey.length, backupPubKey);
throw new Error("bad backup key"); throw new Error("bad backup key");
} }

View file

@ -0,0 +1,23 @@
{
"name": "foo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0",
"@rollup/plugin-babel": "^5.1.0",
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-multi-entry": "^4.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"mdn-polyfills": "^5.20.0",
"regenerator-runtime": "^0.13.7",
"rollup": "^2.26.4",
"core-js": "^3.6.5"
}
}

View file

@ -0,0 +1,46 @@
import fsRoot from "fs";
const fs = fsRoot.promises;
import { rollup } from 'rollup';
// needed for legacy bundle
import babel from '@rollup/plugin-babel';
// needed to find the polyfill modules in the main-legacy.js bundle
import { nodeResolve } from '@rollup/plugin-node-resolve';
// needed because some of the polyfills are written as commonjs modules
import commonjs from '@rollup/plugin-commonjs';
// multi-entry plugin so we can add polyfill file to main
import multi from '@rollup/plugin-multi-entry';
import removeJsComments from 'rollup-plugin-cleanup';
// replace urls of asset names with content hashed version
async function build(inputFile, outputFile) {
// compile down to whatever IE 11 needs
const babelPlugin = babel.babel({
babelHelpers: 'bundled',
exclude: '../../node_modules/**',
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "entry",
corejs: "3",
targets: "IE 11"
}
]
]
});
const polyfillFile = '../../src/worker-polyfill.js';
// create js bundle
const rollupConfig = {
input: [polyfillFile, inputFile],
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin, removeJsComments({comments: "none"})]
};
const bundle = await rollup(rollupConfig);
const {output} = await bundle.generate({
format: 'iife',
name: `bundle`
});
const code = output[0].code;
await fs.writeFile(outputFile, code, "utf8");
}
build(process.argv[2], process.argv[3]);

1485
prototypes/tools/yarn.lock Normal file

File diff suppressed because it is too large Load diff