fix: replace frontend with Rust OPAQUE API + Flutter keypad UI
- Full OPAQUE auth flow via WASM client SDK (client-wasm crate) - New user: Key Register → Key Login → Code Register (icon selection) → done - Existing user: Key Login → get login-data → icon keypad → Code Login → done - Icon-based keypad matching Flutter design: - 2 cols portrait, 3 cols landscape - Key tiles with 3-col sub-grid of icons - Navy border press feedback - Dot display with backspace + submit - SVGs rendered as-is (no color manipulation) - SusiPage with Login/Signup tabs - LoginKeypadPage and SignupKeypadPage for code flows - Secret key display/copy on signup - Unit tests for Keypad component - WASM pkg bundled locally (no external dep)
This commit is contained in:
@@ -96,6 +96,14 @@ function getStringFromWasm0(ptr, len) {
|
||||
return decodeText(ptr, len);
|
||||
}
|
||||
|
||||
let cachedUint32ArrayMemory0 = null;
|
||||
function getUint32ArrayMemory0() {
|
||||
if (cachedUint32ArrayMemory0 === null || cachedUint32ArrayMemory0.byteLength === 0) {
|
||||
cachedUint32ArrayMemory0 = new Uint32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint32ArrayMemory0;
|
||||
}
|
||||
|
||||
let cachedUint8ArrayMemory0 = null;
|
||||
function getUint8ArrayMemory0() {
|
||||
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
||||
@@ -145,6 +153,13 @@ function makeMutClosure(arg0, arg1, dtor, f) {
|
||||
return real;
|
||||
}
|
||||
|
||||
function passArray32ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 4, 4) >>> 0;
|
||||
getUint32ArrayMemory0().set(arg, ptr / 4);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
||||
getUint8ArrayMemory0().set(arg, ptr / 1);
|
||||
@@ -189,6 +204,12 @@ function passStringToWasm0(arg, malloc, realloc) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function takeFromExternrefTable0(idx) {
|
||||
const value = wasm.__wbindgen_externrefs.get(idx);
|
||||
wasm.__externref_table_dealloc(idx);
|
||||
return value;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
cachedTextDecoder.decode();
|
||||
const MAX_SAFARI_DECODE_BYTES = 2146435072;
|
||||
@@ -400,6 +421,56 @@ export class NKodeClient {
|
||||
const ret = wasm.nkodeclient_updateLoginData(this.__wbg_ptr, ptr0, len0);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Decipher key selections into OPAQUE passcode bytes for code login.
|
||||
* Call this after the user taps their nKode sequence on the keypad.
|
||||
*
|
||||
* @param userId - The user's ID
|
||||
* @param secretKeyHex - The user's secret key as hex
|
||||
* @param loginDataBytes - Raw JSON bytes of login data (from server)
|
||||
* @param keySelections - Array of key indices the user tapped
|
||||
* @returns Uint8Array - The passcode bytes to pass to loginCode()
|
||||
* @param {string} secret_key_hex
|
||||
* @param {string} login_data_json
|
||||
* @param {Uint32Array} key_selections
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
decipherSelection(secret_key_hex, login_data_json, key_selections) {
|
||||
const ptr0 = passStringToWasm0(secret_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(login_data_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ptr2 = passArray32ToWasm0(key_selections, wasm.__wbindgen_malloc);
|
||||
const len2 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.nkodeclient_decipherSelection(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
||||
if (ret[3]) {
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
var v4 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
||||
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
||||
return v4;
|
||||
}
|
||||
/**
|
||||
* Prepare for code login: fetch login data, reconstruct keypad, and fetch icons.
|
||||
* Returns the keypad configuration with icons for UI display.
|
||||
*
|
||||
* Also stores the raw login data JSON internally for decipherSelection().
|
||||
*
|
||||
* @param userId - The user's ID
|
||||
* @param secretKeyHex - The user's secret key as hex string
|
||||
* @returns Promise<CodeLoginData> - { keypadIndices, propertiesPerKey, numberOfKeys, mask, icons, loginDataJson }
|
||||
* @param {string} user_id
|
||||
* @param {string} secret_key_hex
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
prepareCodeLogin(user_id, secret_key_hex) {
|
||||
const ptr0 = passStringToWasm0(user_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(secret_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.nkodeclient_prepareCodeLogin(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Generate a new random 16-byte secret key, returned as a hex string (32 chars).
|
||||
* @returns {string}
|
||||
@@ -416,6 +487,52 @@ export class NKodeClient {
|
||||
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prepare icons for code registration (requires active key session).
|
||||
* Fetches icons from the server and randomizes their names via ChaCha20.
|
||||
* Stores intermediate state internally for completeCodeRegistration().
|
||||
*
|
||||
* @returns Promise<IconsResponse> - JSON: { icons: [{ file_name, file_type, img_data }] }
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
prepareCodeRegistration() {
|
||||
const ret = wasm.nkodeclient_prepareCodeRegistration(this.__wbg_ptr);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Complete code registration after icon selection.
|
||||
* Enciphers the selection, registers OPAQUE code auth, and stores login data.
|
||||
*
|
||||
* @param selectedIndices - Array of icon indices the user selected (global indices, not key indices)
|
||||
* @returns Promise<void>
|
||||
* @param {Uint32Array} selected_indices
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
completeCodeRegistration(selected_indices) {
|
||||
const ptr0 = passArray32ToWasm0(selected_indices, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.nkodeclient_completeCodeRegistration(this.__wbg_ptr, ptr0, len0);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Complete code registration with email (full version).
|
||||
* Enciphers the selection, registers OPAQUE code auth, and stores login data on server.
|
||||
*
|
||||
* @param email - User's email address
|
||||
* @param selectedIndices - Uint32Array of icon indices the user selected
|
||||
* @returns Promise<void>
|
||||
* @param {string} email
|
||||
* @param {Uint32Array} selected_indices
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
completeCodeRegistrationWithEmail(email, selected_indices) {
|
||||
const ptr0 = passStringToWasm0(email, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passArray32ToWasm0(selected_indices, wasm.__wbindgen_malloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.nkodeclient_completeCodeRegistrationWithEmail(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a new client pointed at the given nKode server base URL.
|
||||
* @param {string} base_url
|
||||
@@ -567,6 +684,10 @@ export function __wbg_fetch_8119fbf8d0e4f4d1(arg0, arg1) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function __wbg_getRandomValues_1c61fac11405ffdc() { return handleError(function (arg0, arg1) {
|
||||
globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
||||
}, arguments) };
|
||||
|
||||
export function __wbg_getRandomValues_b8f5dbd5f3995a9e() { return handleError(function (arg0, arg1) {
|
||||
arg0.getRandomValues(arg1);
|
||||
}, arguments) };
|
||||
@@ -816,12 +937,6 @@ export function __wbg_versions_c01dfd4722a88165(arg0) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function __wbindgen_cast_151ffb1b798ab8ff(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { dtor_idx: 76, function: Function { arguments: [Externref], shim_idx: 77, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__h28b97059ae600264, wasm_bindgen__convert__closures_____invoke__h8f97ce5df83102bb);
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function __wbindgen_cast_2241b6af4c4b2941(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(String) -> Externref`.
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
@@ -852,6 +967,12 @@ export function __wbindgen_cast_d6cd19b81560fd6e(arg0) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function __wbindgen_cast_f2cc0f2a96e2ef5b(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { dtor_idx: 115, function: Function { arguments: [Externref], shim_idx: 116, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__h28b97059ae600264, wasm_bindgen__convert__closures_____invoke__h8f97ce5df83102bb);
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function __wbindgen_init_externref_table() {
|
||||
const table = wasm.__wbindgen_externrefs;
|
||||
const offset = table.grow(4);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user