From 972398fa66dd7af205e3cba8f29f563ecc0ae847 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Mon, 12 Sep 2022 19:58:40 +0200 Subject: [PATCH] simplify crypto --- packages/frontend/src/lib/crypto.ts | 70 ++++++++----------- packages/frontend/src/lib/ui/TextInput.svelte | 2 +- packages/frontend/src/lib/views/Create.svelte | 6 +- .../src/routes/note/[id]/+page.svelte | 4 +- 4 files changed, 37 insertions(+), 45 deletions(-) diff --git a/packages/frontend/src/lib/crypto.ts b/packages/frontend/src/lib/crypto.ts index 4a4bab5..44c8513 100644 --- a/packages/frontend/src/lib/crypto.ts +++ b/packages/frontend/src/lib/crypto.ts @@ -35,6 +35,31 @@ export class ArrayBufferUtils { } } +export class Keys { + public static async generateKey(size: 128 | 192 | 256 = 256): Promise { + const key = await window.crypto.subtle.generateKey( + { + name: 'AES-GCM', + length: size, + }, + true, + ['encrypt', 'decrypt'] + ) + return key + } + + public static async export(key: CryptoKey): Promise { + return Hex.encode(await window.crypto.subtle.exportKey('raw', key)) + } + + public static async import(key: string): Promise { + return window.crypto.subtle.importKey('raw', Hex.decode(key), { name: 'AES-GCM' }, true, [ + 'encrypt', + 'decrypt', + ]) + } +} + export class Crypto { private static ALG = 'AES-GCM' private static DELIMITER = ':::' @@ -43,55 +68,22 @@ export class Crypto { return window.crypto.getRandomValues(new Uint8Array(size)) } - public static getKeyFromString(password: string) { - return window.crypto.subtle.importKey( - 'raw', - new TextEncoder().encode(password), - 'PBKDF2', - false, - ['deriveBits', 'deriveKey'] - ) - } - public static async getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) { - const iterations = 100_000 - return window.crypto.subtle.deriveKey( - { - name: 'PBKDF2', - salt, - iterations, - hash: 'SHA-512', - }, - key, - { name: this.ALG, length: 256 }, - true, - ['encrypt', 'decrypt'] - ) - } - public static async encrypt(plaintext: ArrayBuffer, key: CryptoKey): Promise { - const salt = this.getRandomBytes(16) - const derived = await this.getDerivedForKey(key, salt) - const iv = this.getRandomBytes(16) + const iv = this.getRandomBytes(12) // AES-GCM needs a 96bit IV const encrypted: ArrayBuffer = await window.crypto.subtle.encrypt( { name: this.ALG, iv }, - derived, + key, plaintext ) - const data = [ - Hex.encode(salt), - Hex.encode(iv), - await ArrayBufferUtils.toString(encrypted), - ].join(this.DELIMITER) + const data = [Hex.encode(iv), await ArrayBufferUtils.toString(encrypted)].join(this.DELIMITER) return data } public static async decrypt(ciphertext: string, key: CryptoKey): Promise { const splitted = ciphertext.split(this.DELIMITER) - const salt = Hex.decode(splitted[0]) - const iv = Hex.decode(splitted[1]) - const encrypted = await ArrayBufferUtils.fromString(splitted[2]) - const derived = await this.getDerivedForKey(key, salt) - const plaintext = await window.crypto.subtle.decrypt({ name: this.ALG, iv }, derived, encrypted) + const iv = Hex.decode(splitted[0]) + const encrypted = await ArrayBufferUtils.fromString(splitted[1]) + const plaintext = await window.crypto.subtle.decrypt({ name: this.ALG, iv }, key, encrypted) return plaintext } } diff --git a/packages/frontend/src/lib/ui/TextInput.svelte b/packages/frontend/src/lib/ui/TextInput.svelte index e9584e4..808e78f 100644 --- a/packages/frontend/src/lib/ui/TextInput.svelte +++ b/packages/frontend/src/lib/ui/TextInput.svelte @@ -24,7 +24,7 @@ hidden = !hidden } function randomFN() { - value = Hex.encode(Crypto.getRandomBytes(20)) + value = Hex.encode(Crypto.getRandomBytes(32)) } diff --git a/packages/frontend/src/lib/views/Create.svelte b/packages/frontend/src/lib/views/Create.svelte index 6f45ad4..a780c3b 100644 --- a/packages/frontend/src/lib/views/Create.svelte +++ b/packages/frontend/src/lib/views/Create.svelte @@ -5,7 +5,7 @@ import { Adapters } from '$lib/adapters' import type { FileDTO, Note } from '$lib/api' import { create, PayloadToLargeError } from '$lib/api' - import { Crypto, Hex } from '$lib/crypto' + import { Keys } from '$lib/crypto' import { status } from '$lib/stores/status' import { notify } from '$lib/toast' import AdvancedParameters from '$lib/ui/AdvancedParameters.svelte' @@ -58,8 +58,8 @@ try { loading = $t('common.encrypting') - const password = Hex.encode(Crypto.getRandomBytes(32)) - const key = await Crypto.getKeyFromString(password) + const key = await Keys.generateKey() + const password = await Keys.export(key) const data: Note = { contents: '', diff --git a/packages/frontend/src/routes/note/[id]/+page.svelte b/packages/frontend/src/routes/note/[id]/+page.svelte index 834a82a..084b627 100644 --- a/packages/frontend/src/routes/note/[id]/+page.svelte +++ b/packages/frontend/src/routes/note/[id]/+page.svelte @@ -4,7 +4,7 @@ import { Adapters } from '$lib/adapters' import { get, info } from '$lib/api' - import { Crypto } from '$lib/crypto' + import { Keys } from '$lib/crypto' import Button from '$lib/ui/Button.svelte' import Loader from '$lib/ui/Loader.svelte' import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte' @@ -43,7 +43,7 @@ loading = $t('common.downloading') const data = await get(id) loading = $t('common.decrypting') - const key = await Crypto.getKeyFromString(password) + const key = await Keys.import(password) switch (data.meta.type) { case 'text': note = {