forked from mystiq/hydrogen-web
wip ie11 prototype
This commit is contained in:
parent
885983c2f7
commit
23a0484ff0
5 changed files with 1769 additions and 41 deletions
113
prototypes/derive-key-ie11.html
Normal file
113
prototypes/derive-key-ie11.html
Normal 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>
|
||||
|
131
prototypes/derive-keys.js
vendored
131
prototypes/derive-keys.js
vendored
|
@ -1,10 +1,10 @@
|
|||
function subtleCryptoResult(promiseOrOp) {
|
||||
function subtleCryptoResult(promiseOrOp, method) {
|
||||
if (promiseOrOp instanceof Promise) {
|
||||
return promiseOrOp;
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
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}
|
||||
*/
|
||||
async verify(key, mac, data, hash) {
|
||||
const opts = {
|
||||
name: 'HMAC',
|
||||
hash: {name: hashName(hash)},
|
||||
};
|
||||
const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(
|
||||
'raw',
|
||||
key,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: {name: hashName(hash)},
|
||||
},
|
||||
opts,
|
||||
false,
|
||||
['verify'],
|
||||
));
|
||||
), "importKey");
|
||||
const isVerified = await subtleCryptoResult(this._subtleCrypto.verify(
|
||||
{name: "HMAC"},
|
||||
opts,
|
||||
hmacKey,
|
||||
mac,
|
||||
data,
|
||||
));
|
||||
), "verify");
|
||||
return isVerified;
|
||||
}
|
||||
|
||||
async compute(key, data, hash) {
|
||||
const opts = {
|
||||
name: 'HMAC',
|
||||
hash: {name: hashName(hash)},
|
||||
};
|
||||
const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(
|
||||
'raw',
|
||||
key,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: {name: hashName(hash)},
|
||||
},
|
||||
opts,
|
||||
false,
|
||||
['sign'],
|
||||
));
|
||||
), "importKey");
|
||||
const buffer = await subtleCryptoResult(this._subtleCrypto.sign(
|
||||
{name: "HMAC"},
|
||||
opts,
|
||||
hmacKey,
|
||||
data,
|
||||
));
|
||||
), "sign");
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
}
|
||||
|
@ -67,11 +69,42 @@ const nwbo = (num, len) => {
|
|||
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 {
|
||||
constructor(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) {
|
||||
const dkLen = length / 8;
|
||||
if (iterations <= 0) {
|
||||
|
@ -163,7 +196,7 @@ class CryptoDeriveDriver {
|
|||
{name: 'PBKDF2'},
|
||||
false,
|
||||
['deriveBits'],
|
||||
));
|
||||
), "importKey");
|
||||
const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
|
@ -173,7 +206,7 @@ class CryptoDeriveDriver {
|
|||
},
|
||||
key,
|
||||
length,
|
||||
));
|
||||
), "deriveBits");
|
||||
return new Uint8Array(keybits);
|
||||
}
|
||||
|
||||
|
@ -193,7 +226,7 @@ class CryptoDeriveDriver {
|
|||
{name: "HKDF"},
|
||||
false,
|
||||
["deriveBits"],
|
||||
));
|
||||
), "importKey");
|
||||
const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits({
|
||||
name: "HKDF",
|
||||
salt,
|
||||
|
@ -202,7 +235,7 @@ class CryptoDeriveDriver {
|
|||
},
|
||||
hkdfkey,
|
||||
length,
|
||||
));
|
||||
), "deriveBits");
|
||||
return new Uint8Array(keybits);
|
||||
}
|
||||
}
|
||||
|
@ -219,24 +252,50 @@ class CryptoAESDriver {
|
|||
* @return {BufferSource} [description]
|
||||
*/
|
||||
async decrypt(key, iv, ciphertext) {
|
||||
const aesKey = await subtleCryptoResult(this._subtleCrypto.importKey(
|
||||
'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
|
||||
{
|
||||
const opts = {
|
||||
name: "AES-CTR",
|
||||
counter: iv,
|
||||
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,
|
||||
ciphertext,
|
||||
));
|
||||
), "decrypt");
|
||||
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 {
|
||||
constructor(subtleCrypto) {
|
||||
this.aes = new CryptoAESDriver(subtleCrypto);
|
||||
this.aes = new CryptoLegacyAESDriver();
|
||||
// this.aes = new CryptoAESDriver(subtleCrypto);
|
||||
//this.derive = new CryptoDeriveDriver(subtleCrypto);
|
||||
this.derive = new CryptoLegacyDeriveDriver(this);
|
||||
// subtleCrypto.deriveBits ?
|
||||
// new CryptoDeriveDriver(subtleCrypto) :
|
||||
// new CryptoLegacyDeriveDriver(this);
|
||||
this.hmac = new CryptoHMACDriver(subtleCrypto);
|
||||
this.hmac = new CryptoLegacyHMACDriver(new CryptoHMACDriver(subtleCrypto));
|
||||
this._subtleCrypto = subtleCrypto;
|
||||
}
|
||||
|
||||
|
@ -334,6 +394,7 @@ export async function decryptSecret(cryptoDriver, keyId, ssssKey, event) {
|
|||
|
||||
export async function decryptSession(backupKeyBase64, backupInfo, sessionResponse) {
|
||||
const privKey = decodeBase64(backupKeyBase64);
|
||||
console.log("privKey", privKey);
|
||||
|
||||
const decryption = new window.Olm.PkDecryption();
|
||||
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
|
||||
// a different recovery key / the wrong passphrase.
|
||||
if (backupPubKey !== backupInfo.auth_data.public_key) {
|
||||
console.log({backupPubKey})
|
||||
console.log("backupPubKey", backupPubKey.length, backupPubKey);
|
||||
throw new Error("bad backup key");
|
||||
}
|
||||
|
||||
|
|
23
prototypes/tools/package.json
Normal file
23
prototypes/tools/package.json
Normal 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"
|
||||
}
|
||||
}
|
46
prototypes/tools/transpile-ie11.mjs
Normal file
46
prototypes/tools/transpile-ie11.mjs
Normal 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
1485
prototypes/tools/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue