You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys-App/src/lib/dkc/testing/encrypt.ts

83 lines
3.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Tests only: the writer with its random values fixed by name (plan of phase
// 3, decision 4), to reproduce the deterministic sections of the fixtures of
// the Go reference and to reach the checks of the writer. A value that is
// not given is drawn from crypto.getRandomValues, as encrypt does. The draws
// hand the writer copies, and the writer wipes them.
import { cryptoWords, type RandomWords } from '../random.ts';
import { type Draws, type EncryptOptions, type Encrypted, type EncryptSource, writeCapsule } from '../writer.ts';
import { newX25519Identity } from '../x25519.ts';
/** The random values to fix; any other is drawn at random. */
export interface FixedDraws {
readonly capsuleId?: Uint8Array;
readonly payloadIdentity?: Uint8Array;
readonly accessIdentity?: Uint8Array;
readonly credentialId?: Uint8Array;
/** The scalars of the dummies, in the order they are drawn. */
readonly dummies?: readonly Uint8Array[];
/** The words of the permutation of the slots; see wordsFor. */
readonly words?: readonly number[];
/**
* Where to record every secret handed to the writer: I_PAYLOAD, I_ACCESS
* and the scalars of the dummies, which the writer must leave zeroed.
*/
readonly handed?: Uint8Array[];
}
/** Draws that return copies of the fixed values, and random ones otherwise. */
export function fixedDraws(f: FixedDraws): Draws {
const bytes = (n: number) => (): Uint8Array => crypto.getRandomValues(new Uint8Array(n));
const fixed = (v: Uint8Array | undefined, otherwise: () => Uint8Array) => (): Uint8Array => (v === undefined ? otherwise() : v.slice());
const dummies = [...(f.dummies ?? [])];
let words: RandomWords = cryptoWords();
if (f.words !== undefined) {
const list = [...f.words];
words = () => {
const w = list.shift();
if (w === undefined) throw new Error('testing: the fixed words are exhausted');
return w;
};
}
const secret = (draw: () => Uint8Array) => (): Uint8Array => {
const b = draw();
f.handed?.push(b);
return b;
};
return {
capsuleId: fixed(f.capsuleId, bytes(16)),
payloadIdentity: secret(fixed(f.payloadIdentity, newX25519Identity)),
accessIdentity: secret(fixed(f.accessIdentity, newX25519Identity)),
dummy: secret(() => dummies.shift()?.slice() ?? newX25519Identity()),
words,
credentialId: fixed(f.credentialId, bytes(16)),
};
}
/** encrypt with some of its random values fixed. */
export function encryptWith(src: EncryptSource, opts: EncryptOptions, f: FixedDraws): Promise<Encrypted> {
return writeCapsule(src, opts, fixedDraws(f));
}
/**
* The words that make the permutation of the writer leave credential i, the
* item at position i before it, in slot `slots[i]`, the dummies filling the
* slots left in their order. The permutation is Fisher–Yates from the last
* position down: at position p it swaps with an index j ≤ p, drawn as the
* word j, which randomIndex accepts as it is.
*/
export function wordsFor(slots: readonly number[], n = 16): number[] {
const target = new Array<number>(n).fill(-1);
for (const [i, s] of slots.entries()) target[s] = i;
let next = slots.length;
for (let p = 0; p < n; p++) if (target[p] === -1) target[p] = next++;
const items = Array.from({ length: n }, (_, i) => i);
const words: number[] = [];
for (let p = n - 1; p > 0; p--) {
const j = items.indexOf(target[p]!);
words.push(j);
[items[p], items[j]] = [items[j]!, items[p]!];
}
return words;
}

Powered by TurnKey Linux.