diff --git a/CHANGELOG.md b/CHANGELOG.md index eebffc3..dd6c4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.0] - 2023-01-13 + +### Changed + +- Default port is now 8000, not 5000. +- Moved to generic encryption library `occulto`. + +### Security + +- Updated dependencies. + ## [2.1.0] - 2023-01-04 ### Added diff --git a/package.json b/package.json index 3ce7170..55f8537 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "test:prepare": "docker compose -f docker-compose.dev.yaml build" }, "devDependencies": { - "@playwright/test": "^1.29.1", - "@types/node": "^16.18.10", + "@playwright/test": "^1.29.2", + "@types/node": "^16.18.11", "http-proxy": "^1.18.1", "npm-run-all": "^4.1.5" } diff --git a/packages/backend/Cargo.lock b/packages/backend/Cargo.lock index 4fed3f6..b2198bd 100644 --- a/packages/backend/Cargo.lock +++ b/packages/backend/Cargo.lock @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "cryptgeon" -version = "2.1.0" +version = "2.2.0" dependencies = [ "actix-files", "actix-web", diff --git a/packages/backend/Cargo.toml b/packages/backend/Cargo.toml index 00a3e27..6de3029 100644 --- a/packages/backend/Cargo.toml +++ b/packages/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cryptgeon" -version = "2.1.0" +version = "2.2.0" authors = ["cupcakearmy "] edition = "2021" diff --git a/packages/frontend/package.json b/packages/frontend/package.json index ee87eb8..45fb968 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -11,27 +11,28 @@ }, "type": "module", "devDependencies": { - "@lokalise/node-api": "^9.3.0", - "@sveltejs/adapter-static": "^1.0.0", - "@sveltejs/kit": "^1.0.1", + "@lokalise/node-api": "^9.5.0", + "@sveltejs/adapter-static": "^1.0.2", + "@sveltejs/kit": "^1.0.13", "@types/dompurify": "^2.4.0", "@types/file-saver": "^2.0.5", "@zerodevx/svelte-toast": "^0.7.2", "adm-zip": "^0.5.10", "dotenv": "^16.0.3", - "svelte": "^3.55.0", + "svelte": "^3.55.1", "svelte-check": "^2.10.3", "svelte-intl-precompile": "^0.10.1", "svelte-preprocess": "^4.10.7", "tslib": "^2.4.1", "typescript": "^4.9.4", - "vite": "^4.0.3" + "vite": "^4.0.4" }, "dependencies": { "@fontsource/fira-mono": "^4.5.10", "copy-to-clipboard": "^3.3.3", - "dompurify": "^2.4.1", + "dompurify": "^2.4.3", "file-saver": "^2.0.5", + "occulto": "2.0.0-rc.10", "pretty-bytes": "^6.0.0", "qrious": "^4.0.2" } diff --git a/packages/frontend/src/lib/adapters.ts b/packages/frontend/src/lib/adapters.ts index 5d2c91e..2bf965a 100644 --- a/packages/frontend/src/lib/adapters.ts +++ b/packages/frontend/src/lib/adapters.ts @@ -1,34 +1,33 @@ +import { AES, Bytes, type TypedArray } from 'occulto' import type { EncryptedFileDTO, FileDTO } from './api' -import { Crypto } from './crypto' abstract class CryptAdapter { - abstract encrypt(plaintext: T, key: CryptoKey): Promise - abstract decrypt(ciphertext: string, key: CryptoKey): Promise + abstract encrypt(plaintext: T, key: TypedArray): Promise + abstract decrypt(ciphertext: string, key: TypedArray): Promise } class CryptTextAdapter implements CryptAdapter { - async encrypt(plaintext: string, key: CryptoKey) { - return await Crypto.encrypt(new TextEncoder().encode(plaintext), key) + async encrypt(plaintext: string, key: TypedArray) { + return await AES.encrypt(Bytes.encode(plaintext), key) } - async decrypt(ciphertext: string, key: CryptoKey) { - const plaintext = await Crypto.decrypt(ciphertext, key) - return new TextDecoder().decode(plaintext) + async decrypt(ciphertext: string, key: TypedArray) { + return Bytes.decode(await AES.decrypt(ciphertext, key)) } } class CryptBlobAdapter implements CryptAdapter { - async encrypt(plaintext: Blob, key: CryptoKey) { - return await Crypto.encrypt(await plaintext.arrayBuffer(), key) + async encrypt(plaintext: Blob, key: TypedArray) { + return await AES.encrypt(new Uint8Array(await plaintext.arrayBuffer()), key) } - async decrypt(ciphertext: string, key: CryptoKey) { - const plaintext = await Crypto.decrypt(ciphertext, key) + async decrypt(ciphertext: string, key: TypedArray) { + const plaintext = await AES.decrypt(ciphertext, key) return new Blob([plaintext], { type: 'application/octet-stream' }) } } class CryptFilesAdapter implements CryptAdapter { - async encrypt(plaintext: FileDTO[], key: CryptoKey) { + async encrypt(plaintext: FileDTO[], key: TypedArray) { const adapter = new CryptBlobAdapter() const data: Promise[] = plaintext.map(async (file) => ({ name: file.name, @@ -39,7 +38,7 @@ class CryptFilesAdapter implements CryptAdapter { return JSON.stringify(await Promise.all(data)) } - async decrypt(ciphertext: string, key: CryptoKey) { + async decrypt(ciphertext: string, key: TypedArray) { const adapter = new CryptBlobAdapter() const data: EncryptedFileDTO[] = JSON.parse(ciphertext) const files: FileDTO[] = await Promise.all( diff --git a/packages/frontend/src/lib/crypto.ts b/packages/frontend/src/lib/crypto.ts deleted file mode 100644 index 44c8513..0000000 --- a/packages/frontend/src/lib/crypto.ts +++ /dev/null @@ -1,89 +0,0 @@ -export class Hex { - static encode(buffer: ArrayBuffer): string { - let s = '' - for (const i of new Uint8Array(buffer)) { - s += i.toString(16).padStart(2, '0') - } - return s - } - - static decode(s: string): ArrayBuffer { - const size = s.length / 2 - const buffer = new Uint8Array(size) - for (let i = 0; i < size; i++) { - const idx = i * 2 - const segment = s.slice(idx, idx + 2) - buffer[i] = parseInt(segment, 16) - } - return buffer - } -} - -export class ArrayBufferUtils { - static async toString(buffer: ArrayBuffer): Promise { - const reader = new window.FileReader() - reader.readAsDataURL(new Blob([buffer])) - return new Promise((resolve) => { - reader.onloadend = () => resolve(reader.result as string) - }) - } - - static async fromString(s: string): Promise { - return fetch(s) - .then((r) => r.blob()) - .then((b) => b.arrayBuffer()) - } -} - -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 = ':::' - - public static getRandomBytes(size: number): Uint8Array { - return window.crypto.getRandomValues(new Uint8Array(size)) - } - - public static async encrypt(plaintext: ArrayBuffer, key: CryptoKey): Promise { - const iv = this.getRandomBytes(12) // AES-GCM needs a 96bit IV - const encrypted: ArrayBuffer = await window.crypto.subtle.encrypt( - { name: this.ALG, iv }, - key, - plaintext - ) - 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 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 808e78f..612146d 100644 --- a/packages/frontend/src/lib/ui/TextInput.svelte +++ b/packages/frontend/src/lib/ui/TextInput.svelte @@ -1,7 +1,7 @@ diff --git a/packages/frontend/src/lib/views/Create.svelte b/packages/frontend/src/lib/views/Create.svelte index a780c3b..a225483 100644 --- a/packages/frontend/src/lib/views/Create.svelte +++ b/packages/frontend/src/lib/views/Create.svelte @@ -1,11 +1,11 @@