/
raiden
/
obsidian-git-encrypt
Обзор
Документация
Войти
/
raiden
/
obsidian-git-encrypt
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/crypto/cryptoService.ts
130 строк
4 KB
Robert Kuzhin
feat: add encrypted mirror and manifest store
09 авг 2026, 10:06
09 авг 2026, 10:06
7f1b7fe
Код
Авторство
О чём код?
import { NONCE_BYTES, VAULT_KEY_BYTES } from "./constants.js"; import type { DecryptManifestInput, DecryptObjectInput, EncryptManifestInput, EncryptObjectInput, } from "./cryptoTypes.js"; import { buildAssociatedData, buildManifestKeyInfo, buildObjectKeyInfo, encodeUtf8, } from "./encoding.js"; import { decodeEnvelope, encodeEnvelope } from "./encryptedEnvelope.js"; import { ObjectAuthenticationError } from "./errors.js"; import { copyKeyRecord } from "./keyHandle.js"; import { getSodium } from "./sodium.js"; export class CryptoService { public async encryptObject(input: EncryptObjectInput): Promise<Uint8Array> { return this.encryptPayload(input.key, "file", input.fileId, input.plaintext); } public async decryptObject(input: DecryptObjectInput): Promise<Uint8Array> { return this.decryptPayload(input.key, "file", input.fileId, input.envelope); } public async encryptManifest(input: EncryptManifestInput): Promise<Uint8Array> { return this.encryptPayload(input.key, "manifest", "manifest", input.plaintext); } public async decryptManifest(input: DecryptManifestInput): Promise<Uint8Array> { return this.decryptPayload(input.key, "manifest", "manifest", input.envelope); } private async encryptPayload( key: EncryptObjectInput["key"], objectType: "file" | "manifest", objectId: string, plaintext: Uint8Array, ): Promise<Uint8Array> { const sodium = await getSodium(); const record = copyKeyRecord(key); try { const objectKey = await derivePayloadKey(record.key, record.vaultId, objectType, objectId); try { const nonce = sodium.randombytes_buf(NONCE_BYTES); try { const aad = buildAssociatedData(record.vaultId, objectType, objectId); const ciphertext = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt( plaintext, aad, null, nonce, objectKey, ); return encodeEnvelope(nonce, ciphertext); } finally { sodium.memzero(nonce); } } finally { sodium.memzero(objectKey); } } finally { sodium.memzero(record.key); } } private async decryptPayload( key: DecryptObjectInput["key"], objectType: "file" | "manifest", objectId: string, envelope: Uint8Array, ): Promise<Uint8Array> { const sodium = await getSodium(); const decoded = decodeEnvelope(envelope); const record = copyKeyRecord(key); try { const objectKey = await derivePayloadKey(record.key, record.vaultId, objectType, objectId); try { const aad = buildAssociatedData(record.vaultId, objectType, objectId); try { return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt( null, decoded.ciphertext, aad, decoded.nonce, objectKey, ); } catch (error) { throw new ObjectAuthenticationError({ cause: error }); } } finally { sodium.memzero(objectKey); } } finally { sodium.memzero(record.key); sodium.memzero(decoded.nonce); sodium.memzero(decoded.ciphertext); } } } async function derivePayloadKey( vaultKey: Uint8Array, vaultId: string, objectType: "file" | "manifest", objectId: string, ): Promise<Uint8Array> { const importedKey = await globalThis.crypto.subtle.importKey( "raw", new Uint8Array(vaultKey), "HKDF", false, ["deriveBits"], ); const bits = await globalThis.crypto.subtle.deriveBits( { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(encodeUtf8(vaultId)), info: new Uint8Array( objectType === "file" ? buildObjectKeyInfo(objectId) : buildManifestKeyInfo(), ), }, importedKey, VAULT_KEY_BYTES * 8, ); return new Uint8Array(bits); }