simplify crypto
This commit is contained in:
parent
4fe5a52f2a
commit
972398fa66
|
@ -35,6 +35,31 @@ export class ArrayBufferUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
export class Crypto {
|
||||||
private static ALG = 'AES-GCM'
|
private static ALG = 'AES-GCM'
|
||||||
private static DELIMITER = ':::'
|
private static DELIMITER = ':::'
|
||||||
|
@ -43,55 +68,22 @@ export class Crypto {
|
||||||
return window.crypto.getRandomValues(new Uint8Array(size))
|
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<string> {
|
public static async encrypt(plaintext: ArrayBuffer, key: CryptoKey): Promise<string> {
|
||||||
const salt = this.getRandomBytes(16)
|
const iv = this.getRandomBytes(12) // AES-GCM needs a 96bit IV
|
||||||
const derived = await this.getDerivedForKey(key, salt)
|
|
||||||
const iv = this.getRandomBytes(16)
|
|
||||||
const encrypted: ArrayBuffer = await window.crypto.subtle.encrypt(
|
const encrypted: ArrayBuffer = await window.crypto.subtle.encrypt(
|
||||||
{ name: this.ALG, iv },
|
{ name: this.ALG, iv },
|
||||||
derived,
|
key,
|
||||||
plaintext
|
plaintext
|
||||||
)
|
)
|
||||||
const data = [
|
const data = [Hex.encode(iv), await ArrayBufferUtils.toString(encrypted)].join(this.DELIMITER)
|
||||||
Hex.encode(salt),
|
|
||||||
Hex.encode(iv),
|
|
||||||
await ArrayBufferUtils.toString(encrypted),
|
|
||||||
].join(this.DELIMITER)
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async decrypt(ciphertext: string, key: CryptoKey): Promise<ArrayBuffer> {
|
public static async decrypt(ciphertext: string, key: CryptoKey): Promise<ArrayBuffer> {
|
||||||
const splitted = ciphertext.split(this.DELIMITER)
|
const splitted = ciphertext.split(this.DELIMITER)
|
||||||
const salt = Hex.decode(splitted[0])
|
const iv = Hex.decode(splitted[0])
|
||||||
const iv = Hex.decode(splitted[1])
|
const encrypted = await ArrayBufferUtils.fromString(splitted[1])
|
||||||
const encrypted = await ArrayBufferUtils.fromString(splitted[2])
|
const plaintext = await window.crypto.subtle.decrypt({ name: this.ALG, iv }, key, encrypted)
|
||||||
const derived = await this.getDerivedForKey(key, salt)
|
|
||||||
const plaintext = await window.crypto.subtle.decrypt({ name: this.ALG, iv }, derived, encrypted)
|
|
||||||
return plaintext
|
return plaintext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
hidden = !hidden
|
hidden = !hidden
|
||||||
}
|
}
|
||||||
function randomFN() {
|
function randomFN() {
|
||||||
value = Hex.encode(Crypto.getRandomBytes(20))
|
value = Hex.encode(Crypto.getRandomBytes(32))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
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 { Crypto, Hex } from '$lib/crypto'
|
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 password = Hex.encode(Crypto.getRandomBytes(32))
|
const key = await Keys.generateKey()
|
||||||
const key = await Crypto.getKeyFromString(password)
|
const password = await Keys.export(key)
|
||||||
|
|
||||||
const data: Note = {
|
const data: Note = {
|
||||||
contents: '',
|
contents: '',
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
import { Adapters } from '$lib/adapters'
|
import { Adapters } from '$lib/adapters'
|
||||||
import { get, info } from '$lib/api'
|
import { get, info } from '$lib/api'
|
||||||
import { Crypto } from '$lib/crypto'
|
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 Crypto.getKeyFromString(password)
|
const key = await Keys.import(password)
|
||||||
switch (data.meta.type) {
|
switch (data.meta.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
note = {
|
note = {
|
||||||
|
|
Loading…
Reference in New Issue