feat(shared): extend CryptoBackend with pwhash + scalarMultBase

This commit is contained in:
byGalax
2026-05-16 16:30:52 +02:00
parent 1eea80c529
commit 2463081949
3 changed files with 75 additions and 0 deletions
@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest';
import type { CryptoBackend } from './backend';
import { makeWasmTestBackend } from './testBackend';
describe('CryptoBackend contract — extended (pwhash + scalarMultBase)', () => {
it('pwhashConsts exposes the constants userKey.ts needs', async () => {
const backend: CryptoBackend = await makeWasmTestBackend();
expect(backend.pwhashConsts.OPSLIMIT_MODERATE).toBeGreaterThan(0);
expect(backend.pwhashConsts.MEMLIMIT_MODERATE).toBeGreaterThan(0);
expect(backend.pwhashConsts.ALG_ARGON2ID13).toBeGreaterThan(0);
});
it('pwhash is deterministic for fixed input + salt', async () => {
const backend: CryptoBackend = await makeWasmTestBackend();
const salt = new Uint8Array(16).fill(7);
const a = backend.pwhash(
32,
'hunter2',
salt,
backend.pwhashConsts.OPSLIMIT_MODERATE,
backend.pwhashConsts.MEMLIMIT_MODERATE,
backend.pwhashConsts.ALG_ARGON2ID13,
);
const b = backend.pwhash(
32,
'hunter2',
salt,
backend.pwhashConsts.OPSLIMIT_MODERATE,
backend.pwhashConsts.MEMLIMIT_MODERATE,
backend.pwhashConsts.ALG_ARGON2ID13,
);
expect(a).toEqual(b);
expect(a.length).toBe(32);
});
it('scalarMultBase maps the private key of a generated keypair to its public key', async () => {
const backend: CryptoBackend = await makeWasmTestBackend();
const kp = backend.generateKeyPair();
const derived = backend.scalarMultBase(kp.privateKey);
expect(derived).toEqual(kp.publicKey);
});
});
+24
View File
@@ -47,6 +47,30 @@ export interface CryptoBackend {
// distributed per recipient via crypto_box envelopes.
secretbox(plaintext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
secretboxOpen(ciphertext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
// ---------------------------------------------------------------------
// Key derivation + public-key derivation. Added so the user-key flow
// (Argon2id KDF, scalarmult-base for public-key derivation from a cached
// private key) does not have to import libsodium-wrappers-sumo at module
// top level — that import does not load in React Native's Hermes runtime.
// ---------------------------------------------------------------------
readonly pwhashConsts: {
OPSLIMIT_MODERATE: number;
MEMLIMIT_MODERATE: number;
ALG_ARGON2ID13: number;
};
pwhash(
outLen: number,
password: string,
salt: Uint8Array,
opslimit: number,
memlimit: number,
alg: number,
): Uint8Array;
scalarMultBase(privateKey: Uint8Array): Uint8Array;
}
let current: CryptoBackend | null = null;
@@ -22,5 +22,13 @@ export async function makeWasmTestBackend(): Promise<CryptoBackend> {
boxOpen: (c, n, pk, sk) => sodium.crypto_box_open_easy(c, n, pk, sk),
secretbox: (m, n, k) => sodium.crypto_secretbox_easy(m, n, k),
secretboxOpen: (c, n, k) => sodium.crypto_secretbox_open_easy(c, n, k),
pwhashConsts: {
OPSLIMIT_MODERATE: sodium.crypto_pwhash_OPSLIMIT_MODERATE,
MEMLIMIT_MODERATE: sodium.crypto_pwhash_MEMLIMIT_MODERATE,
ALG_ARGON2ID13: sodium.crypto_pwhash_ALG_ARGON2ID13,
},
pwhash: (outLen, password, salt, opslimit, memlimit, alg) =>
sodium.crypto_pwhash(outLen, password, salt, opslimit, memlimit, alg),
scalarMultBase: (priv) => sodium.crypto_scalarmult_base(priv),
};
}