mirror of
https://codeberg.org/valpackett/tiddlypwa.git
synced 2026-03-11 10:30:27 -07:00
Plugin: crypto change: less Argon2 iterations, more GCM keys
- make argon2 less annoying on low-end devices / on battery power saving mode etc. - the real way to get more security is "use an actually strong passphrase" anyway - do something about key wear-out just because why not - realistically 4 billion encryptions is a lot anyway for us buuut why not - the real solution is "move your stuff to a new wiki with a new password every N years" anyway - (should be mentioned in the docs)
This commit is contained in:
parent
e7033ca2c2
commit
6ac60dd282
1 changed files with 39 additions and 19 deletions
|
|
@ -221,7 +221,10 @@ Formatted with `deno fmt`.
|
|||
if (!confirm('Are you sure you want to remember the password?')) {
|
||||
return;
|
||||
}
|
||||
this.db.transaction('session', 'readwrite').objectStore('session').put({ key: this.key, mackey: this.mackey })
|
||||
this.db.transaction('session', 'readwrite').objectStore('session').put({
|
||||
enckeys: this.enckeys,
|
||||
mackey: this.mackey,
|
||||
})
|
||||
.onsuccess = (
|
||||
_evt,
|
||||
) => {
|
||||
|
|
@ -446,7 +449,7 @@ Formatted with `deno fmt`.
|
|||
}
|
||||
|
||||
isReady() {
|
||||
return !!(this.db && this.key);
|
||||
return !!(this.db && this.enckeys);
|
||||
}
|
||||
|
||||
async initialRead() {
|
||||
|
|
@ -477,7 +480,7 @@ Formatted with `deno fmt`.
|
|||
const dectitle = utfdec.decode(
|
||||
await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: tiv },
|
||||
this.key,
|
||||
this.enckey(thash),
|
||||
title,
|
||||
),
|
||||
).trimStart();
|
||||
|
|
@ -549,15 +552,15 @@ Formatted with `deno fmt`.
|
|||
this.salt = meta[meta.length - 1].salt;
|
||||
}
|
||||
}
|
||||
if (this.db && !this.key) {
|
||||
if (this.db && !this.enckeys) {
|
||||
const ses = await adb(this.db.transaction('session').objectStore('session').getAll());
|
||||
if (ses.length > 0) {
|
||||
this.key = ses[ses.length - 1].key;
|
||||
this.enckeys = ses[ses.length - 1].enckeys;
|
||||
this.mackey = ses[ses.length - 1].mackey;
|
||||
}
|
||||
this.wiki.addTiddler({ title: '$:/status/TiddlyPWARemembered', text: ses.length > 0 ? 'yes' : 'no' });
|
||||
}
|
||||
if (!this.key) {
|
||||
if (!this.enckeys) {
|
||||
let bootstrapEndpoint;
|
||||
$tw.utils.addClass($tw.pageContainer, 'tc-modal-displayed');
|
||||
$tw.utils.addClass(document.body, 'tc-modal-prevent-scroll');
|
||||
|
|
@ -740,16 +743,28 @@ Formatted with `deno fmt`.
|
|||
});
|
||||
if (!this.salt) this.salt = crypto.getRandomValues(new Uint8Array(32));
|
||||
console.time('hash');
|
||||
const basebits = await argon.hash(utfenc.encode(passInput.value), this.salt, { m: 1 << 17 });
|
||||
const basebits = await argon.hash(utfenc.encode(passInput.value), this.salt, { m: 1 << 17, t: 2 });
|
||||
console.timeLog('hash');
|
||||
const basekey = await crypto.subtle.importKey('raw', basebits, 'HKDF', false, ['deriveKey']);
|
||||
// fun: https://soatok.blog/2021/11/17/understanding-hkdf/ (but we don't have any randomness to shove into info)
|
||||
this.key = await crypto.subtle.deriveKey(
|
||||
{ name: 'HKDF', hash: 'SHA-256', salt: utfenc.encode('tiddly.pwa.tiddlers'), info: new Uint8Array() },
|
||||
basekey,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
false,
|
||||
['encrypt', 'decrypt'],
|
||||
// not fun: https://soatok.blog/2020/12/24/cryptographic-wear-out-for-symmetric-encryption/
|
||||
// realistically 4 billion encryptions is already actually *a lot* for a notes app even with really heavy use lol
|
||||
// but by having just 8 keys we get to 34 billion which is Better
|
||||
this.enckeys = await Promise.all(
|
||||
[...Array(8).keys()].map((i) =>
|
||||
crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'HKDF',
|
||||
hash: 'SHA-256',
|
||||
salt: utfenc.encode('tiddly.pwa.tiddlers.' + i),
|
||||
info: new Uint8Array(),
|
||||
},
|
||||
basekey,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
false,
|
||||
['encrypt', 'decrypt'],
|
||||
)
|
||||
),
|
||||
);
|
||||
this.mackey = await crypto.subtle.deriveKey(
|
||||
{ name: 'HKDF', hash: 'SHA-256', salt: utfenc.encode('tiddly.pwa.titles'), info: new Uint8Array() },
|
||||
|
|
@ -827,16 +842,21 @@ Formatted with `deno fmt`.
|
|||
return crypto.subtle.sign('HMAC', this.mackey, utfenc.encode(x));
|
||||
}
|
||||
|
||||
enckey(thash) {
|
||||
return this.enckeys[new DataView(thash).getUint8(0) % this.enckeys.length];
|
||||
}
|
||||
|
||||
async _saveTiddler(tiddler) {
|
||||
const thash = await this.titlehash(tiddler.fields.title);
|
||||
const key = this.enckey(thash);
|
||||
const encoded = await encodeTiddler(tiddler, this.wiki.isBinaryTiddler(tiddler.fields.title));
|
||||
// "if you use nonces longer than 12 bytes, they get hashed into 12 bytes anyway" - soatok.blog
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const data = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, this.key, encoded);
|
||||
const data = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded);
|
||||
const tiv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const title = await crypto.subtle.encrypt(
|
||||
{ name: 'AES-GCM', iv: tiv },
|
||||
this.key,
|
||||
key,
|
||||
utfenc.encode('\n'.repeat(64 - (tiddler.fields.title.length % 64)) + tiddler.fields.title),
|
||||
);
|
||||
await adb(
|
||||
|
|
@ -883,7 +903,7 @@ Formatted with `deno fmt`.
|
|||
const thash = await this.titlehash(title);
|
||||
const obj = await adb(this.db.transaction('tiddlers').objectStore('tiddlers').get(thash));
|
||||
if (obj.deleted) return null;
|
||||
const data = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: obj.iv }, this.key, obj.data);
|
||||
const data = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: obj.iv }, this.enckey(thash), obj.data);
|
||||
return await decodeTiddler(data);
|
||||
}
|
||||
|
||||
|
|
@ -1114,7 +1134,7 @@ Formatted with `deno fmt`.
|
|||
if (deleted) {
|
||||
titleHashesToDelete.add(thash);
|
||||
} else {
|
||||
titlesToRead.push({ title: tid.title, iv: tid.tiv });
|
||||
titlesToRead.push({ title: tid.title, thash: tid.thash, iv: tid.tiv });
|
||||
}
|
||||
if (tid.mtime > newestChg) {
|
||||
newestChg = tid.mtime;
|
||||
|
|
@ -1126,11 +1146,11 @@ Formatted with `deno fmt`.
|
|||
this.changesChannel.postMessage({ title, del: true });
|
||||
}
|
||||
}
|
||||
for (const { title, iv } of titlesToRead) {
|
||||
for (const { title, thash, iv } of titlesToRead) {
|
||||
const dectitle = utfdec.decode(
|
||||
await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv },
|
||||
this.key,
|
||||
this.enckey(thash),
|
||||
title,
|
||||
),
|
||||
).trimStart();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue