refactor(shared): route userKey through CryptoBackend (no libsodium-wrappers-sumo)

This commit is contained in:
byGalax
2026-05-16 16:33:50 +02:00
parent 9b9802ff6e
commit 61a1eb37ea
+17 -22
View File
@@ -1,5 +1,3 @@
import sodium from 'libsodium-wrappers-sumo';
import { getCryptoBackend } from './backend';
import type { KeyPair } from './backend';
@@ -23,24 +21,25 @@ export async function generateUserKeyPair(): Promise<KeyPair> {
return getCryptoBackend().generateKeyPair();
}
async function deriveKek(pin: string, salt: Uint8Array, params: KdfParams): Promise<Uint8Array> {
await sodium.ready;
return sodium.crypto_pwhash(
sodium.crypto_secretbox_KEYBYTES,
function deriveKek(pin: string, salt: Uint8Array, params: KdfParams): Uint8Array {
const backend = getCryptoBackend();
return backend.pwhash(
backend.secretboxKeyLength,
pin,
salt,
params.opslimit,
params.memlimit,
sodium.crypto_pwhash_ALG_ARGON2ID13,
backend.pwhashConsts.ALG_ARGON2ID13,
);
}
function defaultKdfParams(): KdfParams {
const backend = getCryptoBackend();
return {
algo: 'argon2id',
preset: KDF_PRESET,
opslimit: sodium.crypto_pwhash_OPSLIMIT_MODERATE,
memlimit: sodium.crypto_pwhash_MEMLIMIT_MODERATE,
opslimit: backend.pwhashConsts.OPSLIMIT_MODERATE,
memlimit: backend.pwhashConsts.MEMLIMIT_MODERATE,
};
}
@@ -50,19 +49,16 @@ export async function sealUserKey(opts: {
salt?: Uint8Array;
kdfParams?: KdfParams;
}): Promise<SealedUserKey> {
await sodium.ready;
const backend = getCryptoBackend();
const salt = opts.salt ?? backend.randomBytes(SALT_LEN);
const kdfParams = opts.kdfParams ?? defaultKdfParams();
const kek = await deriveKek(opts.pin, salt, kdfParams);
try {
const nonce = backend.randomBytes(backend.secretboxNonceLength);
const cipher = backend.secretbox(opts.privateKey, nonce, kek);
const sealedPrivateKey = new Uint8Array(nonce.length + cipher.length);
sealedPrivateKey.set(nonce, 0);
sealedPrivateKey.set(cipher, nonce.length);
return { sealedPrivateKey, salt, kdfParams };
} finally { sodium.memzero(kek); }
const kek = deriveKek(opts.pin, salt, kdfParams);
const nonce = backend.randomBytes(backend.secretboxNonceLength);
const cipher = backend.secretbox(opts.privateKey, nonce, kek);
const sealedPrivateKey = new Uint8Array(nonce.length + cipher.length);
sealedPrivateKey.set(nonce, 0);
sealedPrivateKey.set(cipher, nonce.length);
return { sealedPrivateKey, salt, kdfParams };
}
export async function openUserKey(opts: {
@@ -71,12 +67,11 @@ export async function openUserKey(opts: {
salt: Uint8Array;
kdfParams: KdfParams;
}): Promise<Uint8Array> {
await sodium.ready;
const backend = getCryptoBackend();
const nonceLen = backend.secretboxNonceLength;
if (opts.sealed.length <= nonceLen) throw new Error('sealed user key blob too short');
const nonce = opts.sealed.slice(0, nonceLen);
const cipher = opts.sealed.slice(nonceLen);
const kek = await deriveKek(opts.pin, opts.salt, opts.kdfParams);
try { return backend.secretboxOpen(cipher, nonce, kek); } finally { sodium.memzero(kek); }
const kek = deriveKek(opts.pin, opts.salt, opts.kdfParams);
return backend.secretboxOpen(cipher, nonce, kek);
}