mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2024-12-22 08:16:28 +00:00
moved to occulto
This commit is contained in:
parent
436ae2a7e5
commit
6fb7518b6a
11
CHANGELOG.md
11
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/),
|
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).
|
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
|
## [2.1.0] - 2023-01-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
"test:prepare": "docker compose -f docker-compose.dev.yaml build"
|
"test:prepare": "docker compose -f docker-compose.dev.yaml build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.29.1",
|
"@playwright/test": "^1.29.2",
|
||||||
"@types/node": "^16.18.10",
|
"@types/node": "^16.18.11",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"npm-run-all": "^4.1.5"
|
"npm-run-all": "^4.1.5"
|
||||||
}
|
}
|
||||||
|
2
packages/backend/Cargo.lock
generated
2
packages/backend/Cargo.lock
generated
@ -425,7 +425,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
authors = ["cupcakearmy <hi@nicco.io>"]
|
authors = ["cupcakearmy <hi@nicco.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@ -11,27 +11,28 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lokalise/node-api": "^9.3.0",
|
"@lokalise/node-api": "^9.5.0",
|
||||||
"@sveltejs/adapter-static": "^1.0.0",
|
"@sveltejs/adapter-static": "^1.0.2",
|
||||||
"@sveltejs/kit": "^1.0.1",
|
"@sveltejs/kit": "^1.0.13",
|
||||||
"@types/dompurify": "^2.4.0",
|
"@types/dompurify": "^2.4.0",
|
||||||
"@types/file-saver": "^2.0.5",
|
"@types/file-saver": "^2.0.5",
|
||||||
"@zerodevx/svelte-toast": "^0.7.2",
|
"@zerodevx/svelte-toast": "^0.7.2",
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.10",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"svelte": "^3.55.0",
|
"svelte": "^3.55.1",
|
||||||
"svelte-check": "^2.10.3",
|
"svelte-check": "^2.10.3",
|
||||||
"svelte-intl-precompile": "^0.10.1",
|
"svelte-intl-precompile": "^0.10.1",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"vite": "^4.0.3"
|
"vite": "^4.0.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/fira-mono": "^4.5.10",
|
"@fontsource/fira-mono": "^4.5.10",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"dompurify": "^2.4.1",
|
"dompurify": "^2.4.3",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
"occulto": "2.0.0-rc.10",
|
||||||
"pretty-bytes": "^6.0.0",
|
"pretty-bytes": "^6.0.0",
|
||||||
"qrious": "^4.0.2"
|
"qrious": "^4.0.2"
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,33 @@
|
|||||||
|
import { AES, Bytes, type TypedArray } from 'occulto'
|
||||||
import type { EncryptedFileDTO, FileDTO } from './api'
|
import type { EncryptedFileDTO, FileDTO } from './api'
|
||||||
import { Crypto } from './crypto'
|
|
||||||
|
|
||||||
abstract class CryptAdapter<T> {
|
abstract class CryptAdapter<T> {
|
||||||
abstract encrypt(plaintext: T, key: CryptoKey): Promise<string>
|
abstract encrypt(plaintext: T, key: TypedArray): Promise<string>
|
||||||
abstract decrypt(ciphertext: string, key: CryptoKey): Promise<T>
|
abstract decrypt(ciphertext: string, key: TypedArray): Promise<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
class CryptTextAdapter implements CryptAdapter<string> {
|
class CryptTextAdapter implements CryptAdapter<string> {
|
||||||
async encrypt(plaintext: string, key: CryptoKey) {
|
async encrypt(plaintext: string, key: TypedArray) {
|
||||||
return await Crypto.encrypt(new TextEncoder().encode(plaintext), key)
|
return await AES.encrypt(Bytes.encode(plaintext), key)
|
||||||
}
|
}
|
||||||
async decrypt(ciphertext: string, key: CryptoKey) {
|
async decrypt(ciphertext: string, key: TypedArray) {
|
||||||
const plaintext = await Crypto.decrypt(ciphertext, key)
|
return Bytes.decode(await AES.decrypt(ciphertext, key))
|
||||||
return new TextDecoder().decode(plaintext)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CryptBlobAdapter implements CryptAdapter<Blob> {
|
class CryptBlobAdapter implements CryptAdapter<Blob> {
|
||||||
async encrypt(plaintext: Blob, key: CryptoKey) {
|
async encrypt(plaintext: Blob, key: TypedArray) {
|
||||||
return await Crypto.encrypt(await plaintext.arrayBuffer(), key)
|
return await AES.encrypt(new Uint8Array(await plaintext.arrayBuffer()), key)
|
||||||
}
|
}
|
||||||
|
|
||||||
async decrypt(ciphertext: string, key: CryptoKey) {
|
async decrypt(ciphertext: string, key: TypedArray) {
|
||||||
const plaintext = await Crypto.decrypt(ciphertext, key)
|
const plaintext = await AES.decrypt(ciphertext, key)
|
||||||
return new Blob([plaintext], { type: 'application/octet-stream' })
|
return new Blob([plaintext], { type: 'application/octet-stream' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CryptFilesAdapter implements CryptAdapter<FileDTO[]> {
|
class CryptFilesAdapter implements CryptAdapter<FileDTO[]> {
|
||||||
async encrypt(plaintext: FileDTO[], key: CryptoKey) {
|
async encrypt(plaintext: FileDTO[], key: TypedArray) {
|
||||||
const adapter = new CryptBlobAdapter()
|
const adapter = new CryptBlobAdapter()
|
||||||
const data: Promise<EncryptedFileDTO>[] = plaintext.map(async (file) => ({
|
const data: Promise<EncryptedFileDTO>[] = plaintext.map(async (file) => ({
|
||||||
name: file.name,
|
name: file.name,
|
||||||
@ -39,7 +38,7 @@ class CryptFilesAdapter implements CryptAdapter<FileDTO[]> {
|
|||||||
return JSON.stringify(await Promise.all(data))
|
return JSON.stringify(await Promise.all(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
async decrypt(ciphertext: string, key: CryptoKey) {
|
async decrypt(ciphertext: string, key: TypedArray) {
|
||||||
const adapter = new CryptBlobAdapter()
|
const adapter = new CryptBlobAdapter()
|
||||||
const data: EncryptedFileDTO[] = JSON.parse(ciphertext)
|
const data: EncryptedFileDTO[] = JSON.parse(ciphertext)
|
||||||
const files: FileDTO[] = await Promise.all(
|
const files: FileDTO[] = await Promise.all(
|
||||||
|
@ -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<string> {
|
|
||||||
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<ArrayBuffer> {
|
|
||||||
return fetch(s)
|
|
||||||
.then((r) => r.blob())
|
|
||||||
.then((b) => b.arrayBuffer())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Keys {
|
|
||||||
public static async generateKey(size: 128 | 192 | 256 = 256): Promise<CryptoKey> {
|
|
||||||
const key = await window.crypto.subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'AES-GCM',
|
|
||||||
length: size,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['encrypt', 'decrypt']
|
|
||||||
)
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async export(key: CryptoKey): Promise<string> {
|
|
||||||
return Hex.encode(await window.crypto.subtle.exportKey('raw', key))
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async import(key: string): Promise<CryptoKey> {
|
|
||||||
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<string> {
|
|
||||||
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<ArrayBuffer> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Crypto, Hex } from '$lib/crypto'
|
|
||||||
import Icon from '$lib/ui/Icon.svelte'
|
import Icon from '$lib/ui/Icon.svelte'
|
||||||
import { copy as copyFN } from '$lib/utils'
|
import { copy as copyFN } from '$lib/utils'
|
||||||
|
import { getRandomBytes, Hex } from 'occulto'
|
||||||
|
|
||||||
export let label: string = ''
|
export let label: string = ''
|
||||||
export let value: any
|
export let value: any
|
||||||
@ -23,8 +23,9 @@
|
|||||||
function toggle() {
|
function toggle() {
|
||||||
hidden = !hidden
|
hidden = !hidden
|
||||||
}
|
}
|
||||||
function randomFN() {
|
|
||||||
value = Hex.encode(Crypto.getRandomBytes(32))
|
async function randomFN() {
|
||||||
|
value = Hex.encode(await getRandomBytes(32))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { AES, Hex } from 'occulto'
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
import { blur } from 'svelte/transition'
|
import { blur } from 'svelte/transition'
|
||||||
|
|
||||||
import { Adapters } from '$lib/adapters'
|
import { Adapters } from '$lib/adapters'
|
||||||
import type { FileDTO, Note } from '$lib/api'
|
import type { FileDTO, Note } from '$lib/api'
|
||||||
import { create, PayloadToLargeError } from '$lib/api'
|
import { create, PayloadToLargeError } from '$lib/api'
|
||||||
import { Keys } from '$lib/crypto'
|
|
||||||
import { status } from '$lib/stores/status'
|
import { status } from '$lib/stores/status'
|
||||||
import { notify } from '$lib/toast'
|
import { notify } from '$lib/toast'
|
||||||
import AdvancedParameters from '$lib/ui/AdvancedParameters.svelte'
|
import AdvancedParameters from '$lib/ui/AdvancedParameters.svelte'
|
||||||
@ -58,8 +58,8 @@
|
|||||||
try {
|
try {
|
||||||
loading = $t('common.encrypting')
|
loading = $t('common.encrypting')
|
||||||
|
|
||||||
const key = await Keys.generateKey()
|
const key = await AES.generateKey()
|
||||||
const password = await Keys.export(key)
|
const password = await Hex.encode(key)
|
||||||
|
|
||||||
const data: Note = {
|
const data: Note = {
|
||||||
contents: '',
|
contents: '',
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { Hex } from 'occulto'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
|
|
||||||
import { Adapters } from '$lib/adapters'
|
import { Adapters } from '$lib/adapters'
|
||||||
import { get, info } from '$lib/api'
|
import { get, info } from '$lib/api'
|
||||||
import { Keys } from '$lib/crypto'
|
|
||||||
import Button from '$lib/ui/Button.svelte'
|
import Button from '$lib/ui/Button.svelte'
|
||||||
import Loader from '$lib/ui/Loader.svelte'
|
import Loader from '$lib/ui/Loader.svelte'
|
||||||
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
||||||
@ -43,7 +43,7 @@
|
|||||||
loading = $t('common.downloading')
|
loading = $t('common.downloading')
|
||||||
const data = await get(id)
|
const data = await get(id)
|
||||||
loading = $t('common.decrypting')
|
loading = $t('common.decrypting')
|
||||||
const key = await Keys.import(password)
|
const key = await Hex.decode(password)
|
||||||
switch (data.meta.type) {
|
switch (data.meta.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
note = {
|
note = {
|
||||||
|
451
pnpm-lock.yaml
generated
451
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user