diff --git a/.gitignore b/.gitignore index 6098d17..bdfa7c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .env.local .env.*.local .forgejo-token -.zcode/ node_modules/ dist/ target/ diff --git a/backend/src/handlers/auth.rs b/backend/src/handlers/auth.rs index 9935fd5..cbe8836 100644 --- a/backend/src/handlers/auth.rs +++ b/backend/src/handlers/auth.rs @@ -27,11 +27,6 @@ pub struct RegisterRequest { /// Wrapped DEK (base64 ciphertext) — encrypted under the recovery KEK pub recovery_wrapped_dek: Option, pub recovery_wrapped_dek_iv: Option, - /// Account X25519 identity public key (base64 raw). Plaintext. - pub identity_public_key: Option, - /// Account X25519 identity private key, wrapped under the account DEK. - pub identity_private_key_wrapped: Option, - pub identity_private_key_wrapped_iv: Option, } #[derive(Debug, Serialize)] @@ -45,12 +40,6 @@ pub struct AuthResponse { /// user has no wrapped DEK (legacy/pre-recovery accounts). pub wrapped_dek: Option, pub wrapped_dek_iv: Option, - /// Account X25519 identity keypair. The public key is plaintext; the - /// private key is an opaque ciphertext blob wrapped under the account DEK. - /// Absent for accounts that predate the identity-keypair feature. - pub identity_public_key: Option, - pub identity_private_key_wrapped: Option, - pub identity_private_key_wrapped_iv: Option, } pub async fn register( @@ -119,11 +108,6 @@ pub async fn register( user.recovery_wrapped_dek = req.recovery_wrapped_dek.clone(); user.recovery_wrapped_dek_iv = req.recovery_wrapped_dek_iv.clone(); user.recovery_enabled = req.recovery_wrapped_dek.is_some(); - // Store the X25519 identity keypair (public plaintext, private wrapped - // under the account DEK — server stores verbatim, cannot decrypt). - user.identity_public_key = req.identity_public_key; - user.identity_private_key_wrapped = req.identity_private_key_wrapped; - user.identity_private_key_wrapped_iv = req.identity_private_key_wrapped_iv; // Get token_version before saving let token_version = user.token_version; @@ -219,9 +203,6 @@ pub async fn register( username: user.username.clone(), wrapped_dek: user.wrapped_dek.clone(), wrapped_dek_iv: user.wrapped_dek_iv.clone(), - identity_public_key: user.identity_public_key.clone(), - identity_private_key_wrapped: user.identity_private_key_wrapped.clone(), - identity_private_key_wrapped_iv: user.identity_private_key_wrapped_iv.clone(), }; (StatusCode::CREATED, Json(response)).into_response() @@ -432,9 +413,6 @@ pub async fn login( username: user.username.clone(), wrapped_dek: user.wrapped_dek.clone(), wrapped_dek_iv: user.wrapped_dek_iv.clone(), - identity_public_key: user.identity_public_key.clone(), - identity_private_key_wrapped: user.identity_private_key_wrapped.clone(), - identity_private_key_wrapped_iv: user.identity_private_key_wrapped_iv.clone(), }; (StatusCode::OK, Json(response)).into_response() diff --git a/backend/src/models/user.rs b/backend/src/models/user.rs index bbb0eb0..74de914 100644 --- a/backend/src/models/user.rs +++ b/backend/src/models/user.rs @@ -37,21 +37,6 @@ pub struct User { #[serde(skip_serializing_if = "Option::is_none")] pub recovery_wrapped_dek_iv: Option, - /// Account X25519 identity public key (base64 raw). Plaintext — public keys - /// are not secret. Other accounts wrap profile DEKs to this key (Phase B, - /// see docs/adr/multi-person-sharing.md). Generated client-side at - /// registration. - #[serde(skip_serializing_if = "Option::is_none")] - pub identity_public_key: Option, - - /// Account X25519 identity private key, wrapped (AES-256-GCM encrypted) - /// under the account DEK. The server stores this verbatim and cannot - /// decrypt it. The client unwraps it at login after unwrapping the DEK. - #[serde(skip_serializing_if = "Option::is_none")] - pub identity_private_key_wrapped: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub identity_private_key_wrapped_iv: Option, - /// Token version for invalidating all tokens on password change pub token_version: i32, @@ -108,9 +93,6 @@ impl User { wrapped_dek_iv: None, recovery_wrapped_dek: None, recovery_wrapped_dek_iv: None, - identity_public_key: None, - identity_private_key_wrapped: None, - identity_private_key_wrapped_iv: None, token_version: 0, created_at: now, last_active: now, diff --git a/backend/tests/auth_tests.rs b/backend/tests/auth_tests.rs index ce7b24f..3da535a 100644 --- a/backend/tests/auth_tests.rs +++ b/backend/tests/auth_tests.rs @@ -287,80 +287,6 @@ async fn password_change_invalidates_existing_tokens() { common::drop_test_db(&db_name).await; } -#[tokio::test] -async fn identity_keypair_round_trips_through_register_and_login() { - // Phase A1 (#3): the X25519 identity keypair fields (public plaintext, - // private wrapped under the account DEK) are stored verbatim at register - // and echoed back on login. The server never inspects or derives them. - let (app, db_name) = require_app!(common::app_for_test().await); - let email = unique_email(); - - // Opaque blobs — the server treats these as base64 strings, nothing more. - let public_key = "base64-x25519-public-key-for-test"; - let priv_wrapped = "base64-wrapped-priv-ciphertext"; - let priv_wrapped_iv = "base64-12-byte-iv"; - - let (status, body) = common::send_json( - &app, - "POST", - "/api/auth/register", - Some(json!({ - "email": email, - "username": "tester", - "password": "supersecret", - "identity_public_key": public_key, - "identity_private_key_wrapped": priv_wrapped, - "identity_private_key_wrapped_iv": priv_wrapped_iv, - })), - None, - ) - .await; - assert_eq!(status, 201, "register should return 201, body: {body}"); - assert_eq!(body["identity_public_key"], public_key); - assert_eq!(body["identity_private_key_wrapped"], priv_wrapped); - assert_eq!(body["identity_private_key_wrapped_iv"], priv_wrapped_iv); - - // Login must echo the same stored values. - let (status, body) = common::send_json( - &app, - "POST", - "/api/auth/login", - Some(json!({ "email": email, "password": "supersecret" })), - None, - ) - .await; - assert_eq!(status, 200, "login should return 200, body: {body}"); - assert_eq!(body["identity_public_key"], public_key); - assert_eq!(body["identity_private_key_wrapped"], priv_wrapped); - assert_eq!(body["identity_private_key_wrapped_iv"], priv_wrapped_iv); - - common::drop_test_db(&db_name).await; -} - -#[tokio::test] -async fn register_without_identity_keypair_stays_optional() { - // Backward compat: existing clients that don't send identity fields must - // still register successfully, and the fields are absent from the response. - let (app, db_name) = require_app!(common::app_for_test().await); - let email = unique_email(); - - let (status, body) = common::send_json( - &app, - "POST", - "/api/auth/register", - Some(json!({ "email": email, "username": "tester", "password": "supersecret" })), - None, - ) - .await; - assert_eq!(status, 201, "register should return 201, body: {body}"); - assert!( - body.get("identity_public_key").is_none() || body["identity_public_key"].is_null(), - "identity_public_key should be absent when not provided: {body}" - ); - - common::drop_test_db(&db_name).await; -} - // ---- helpers ---- fn unique_email() -> String { diff --git a/web/normogen-web/src/crypto/crypto.e2e.test.ts b/web/normogen-web/src/crypto/crypto.e2e.test.ts index ff6c3e7..b160193 100644 --- a/web/normogen-web/src/crypto/crypto.e2e.test.ts +++ b/web/normogen-web/src/crypto/crypto.e2e.test.ts @@ -9,30 +9,8 @@ import { setEncKey, clearEncKey, hasEncKey, - generateIdentityKeyPair, - wrapIdentityPrivateKey, - unwrapIdentityPrivateKey, - setIdentityPrivate, - clearIdentityPrivate, - hasIdentityPrivate, } from './index'; -/** X25519 in Web Crypto is a recent addition (Node 16+, modern browsers). Some - * test runtimes (older jsdom/Node) may not implement it; detect once and skip - * the identity-keypair tests rather than fail spuriously. */ -async function x25519Supported(): Promise { - try { - await crypto.subtle.generateKey( - { name: 'ECDH', namedCurve: 'X25519' }, - true, - ['deriveBits'], - ); - return true; - } catch { - return false; - } -} - describe('zero-knowledge crypto lifecycle', () => { const password = 'my-secret-password'; const recoveryPhrase = 'my-recovery-phrase'; @@ -121,106 +99,3 @@ describe('zero-knowledge crypto lifecycle', () => { await expect(decryptJson(encrypted, setupB.dek)).rejects.toThrow(); }); }); - -describe('account X25519 identity keypair (Phase A1)', () => { - const password = 'my-secret-password'; - - it('full lifecycle: generate → wrap → unlock DEK → unwrap → deriveBits', async () => { - if (!(await x25519Supported())) { - console.log('[crypto] skipped (X25519 unsupported in this runtime)'); - return; - } - - // 1. Register: set up the account DEK and generate the identity keypair. - const setup = await setupEncryption(password); - const { publicKey, privateKey } = await generateIdentityKeyPair(); - expect(publicKey).not.toBe(''); - setIdentityPrivate(privateKey); - expect(hasIdentityPrivate()).toBe(true); - - // 2. Wrap the private key under the account DEK and "store" it. - const wrapped = await wrapIdentityPrivateKey(privateKey, setup.dek); - expect(wrapped.data).not.toBe(''); - - // 3. Simulate page reload: both keys are lost from memory. - clearIdentityPrivate(); - expect(hasIdentityPrivate()).toBe(false); - - // 4. Unlock the DEK with the password (unlock screen flow). - const { dek } = await unlockWithPassword(password, setup.passwordWrappedDek); - - // 5. Unwrap the identity private key from the stored wrapped form. - const recovered = await unwrapIdentityPrivateKey(wrapped, dek); - setIdentityPrivate(recovered); - expect(hasIdentityPrivate()).toBe(true); - - // 6. The recovered private key is a USABLE X25519 key: it can derive a - // shared secret against the original public key. Import the public key - // and ECDH-derive — this proves the round-trip preserved a working key. - const pubBytes = Uint8Array.from(atob(publicKey), (c) => c.charCodeAt(0)); - const pubKey = await crypto.subtle.importKey( - 'raw', - pubBytes as BufferSource, - { name: 'ECDH', namedCurve: 'X25519' }, - false, - [], - ); - const sharedSecret = await crypto.subtle.deriveBits( - { name: 'ECDH', public: pubKey }, - recovered, - 256, - ); - expect(sharedSecret.byteLength).toBe(32); - - clearIdentityPrivate(); - }); - - it('a wrapped private key cannot be unwrapped with the wrong DEK', async () => { - if (!(await x25519Supported())) { - console.log('[crypto] skipped (X25519 unsupported in this runtime)'); - return; - } - // DEK for account A; a different DEK for account B. - const setupA = await setupEncryption('user-a-password'); - const setupB = await setupEncryption('user-b-password'); - const { privateKey } = await generateIdentityKeyPair(); - const wrapped = await wrapIdentityPrivateKey(privateKey, setupA.dek); - - // User B's DEK cannot unwrap user A's identity private key. - await expect(unwrapIdentityPrivateKey(wrapped, setupB.dek)).rejects.toThrow(); - }); - - it('two identity keypairs derive different shared secrets with the same peer', async () => { - if (!(await x25519Supported())) { - console.log('[crypto] skipped (X25519 unsupported in this runtime)'); - return; - } - // Sanity: distinct keypairs must produce distinct ECDH outputs. - const peer = await generateIdentityKeyPair(); - const peerPubBytes = Uint8Array.from(atob(peer.publicKey), (c) => c.charCodeAt(0)); - const peerPub = await crypto.subtle.importKey( - 'raw', - peerPubBytes as BufferSource, - { name: 'ECDH', namedCurve: 'X25519' }, - false, - [], - ); - - const a = await generateIdentityKeyPair(); - const b = await generateIdentityKeyPair(); - const secretA = await crypto.subtle.deriveBits( - { name: 'ECDH', public: peerPub }, - a.privateKey, - 256, - ); - const secretB = await crypto.subtle.deriveBits( - { name: 'ECDH', public: peerPub }, - b.privateKey, - 256, - ); - - const aHex = Array.from(new Uint8Array(secretA)).map((x) => x.toString(16)).join(''); - const bHex = Array.from(new Uint8Array(secretB)).map((x) => x.toString(16)).join(''); - expect(aHex).not.toBe(bHex); - }); -}); diff --git a/web/normogen-web/src/crypto/index.ts b/web/normogen-web/src/crypto/index.ts index afdf0aa..16ec69f 100644 --- a/web/normogen-web/src/crypto/index.ts +++ b/web/normogen-web/src/crypto/index.ts @@ -8,17 +8,10 @@ export { setupEncryption, unlockWithPassword, unlockWithRecovery, - generateIdentityKeyPair, - wrapIdentityPrivateKey, - unwrapIdentityPrivateKey, setEncKey, getEncKey, clearEncKey, hasEncKey, - setIdentityPrivate, - getIdentityPrivate, - clearIdentityPrivate, - hasIdentityPrivate, AUTH_SALT, PASSWORD_KEK_SALT, RECOVERY_KEK_SALT, diff --git a/web/normogen-web/src/crypto/keys.ts b/web/normogen-web/src/crypto/keys.ts index 69d49fb..25577a5 100644 --- a/web/normogen-web/src/crypto/keys.ts +++ b/web/normogen-web/src/crypto/keys.ts @@ -185,62 +185,6 @@ export async function unlockWithRecovery( return unwrapDek(recoveryWrappedDek, recoveryKek); } -// --------------------------------------------------------------------------- -// Account X25519 identity keypair (Phase A1, multi-person sharing prep). -// -// Each account gets an X25519 keypair at registration. The PUBLIC half is -// stored in plaintext (public keys are not secret); other accounts will wrap -// profile DEKs to it in Phase B. The PRIVATE half is wrapped (AES-GCM -// encrypted) under the account DEK and stored on the server as opaque -// ciphertext. The server never sees the private key. -// -// See docs/adr/multi-person-sharing.md §1. -// --------------------------------------------------------------------------- - -/** Generate an X25519 keypair for the account. Returns the public key as a - * base64 raw string (for the server to store) and the private key as a - * CryptoKey (kept in memory, wrapped before upload). */ -export async function generateIdentityKeyPair(): Promise<{ - publicKey: string; - privateKey: CryptoKey; -}> { - const { publicKey, privateKey } = (await crypto.subtle.generateKey( - { name: 'ECDH', namedCurve: 'X25519' }, - true, // extractable so we can export the private key for wrapping - ['deriveBits'], - )) as CryptoKeyPair; - const rawPublic = await crypto.subtle.exportKey('raw', publicKey); - return { publicKey: base64(new Uint8Array(rawPublic)), privateKey }; -} - -/** Wrap (encrypt) the X25519 private key under the account DEK. The DEK is an - * AES-GCM key, so we reuse the string-cipher from cipher.ts: export the private - * key to raw bytes, base64-encode, and encrypt. */ -export async function wrapIdentityPrivateKey( - privateKey: CryptoKey, - dek: CryptoKey, -): Promise { - const rawPrivate = await crypto.subtle.exportKey('raw', privateKey); - return encrypt(base64(new Uint8Array(rawPrivate)), dek); -} - -/** Unwrap (decrypt) the X25519 private key from its DEK-wrapped form. Inverse - * of wrapIdentityPrivateKey. The result is importable for ECDH deriveBits. */ -export async function unwrapIdentityPrivateKey( - payload: CipherPayload, - dek: CryptoKey, -): Promise { - const rawPrivateB64 = await decrypt(payload, dek); - const rawPrivate = Uint8Array.from(atob(rawPrivateB64), (c) => c.charCodeAt(0)); - return crypto.subtle.importKey( - 'raw', - rawPrivate as BufferSource, - { name: 'ECDH', namedCurve: 'X25519' }, - true, // extractable so it can be re-wrapped if ever needed - ['deriveBits'], - ); -} - // --------------------------------------------------------------------------- // In-memory key store (unchanged from Phase 1) // --------------------------------------------------------------------------- @@ -263,30 +207,6 @@ export function hasEncKey(): boolean { return currentEncKey !== null; } -// --------------------------------------------------------------------------- -// In-memory identity-private-key store (Phase A1). Same lifecycle as the DEK: -// held only for the authenticated session, cleared on logout, re-derived from -// the wrapped form (under the account DEK) on unlock. Not consumed until Phase B. -// --------------------------------------------------------------------------- - -let currentIdentityPrivate: CryptoKey | null = null; - -export function setIdentityPrivate(key: CryptoKey): void { - currentIdentityPrivate = key; -} - -export function getIdentityPrivate(): CryptoKey | null { - return currentIdentityPrivate; -} - -export function clearIdentityPrivate(): void { - currentIdentityPrivate = null; -} - -export function hasIdentityPrivate(): boolean { - return currentIdentityPrivate !== null; -} - // --------------------------------------------------------------------------- // Backward compat: the old deriveAuthAndEncKeys (Phase 1 direct-from-password // enc key). Kept for tests; new code uses setupEncryption/unlockWithPassword. diff --git a/web/normogen-web/src/pages/UnlockPage.tsx b/web/normogen-web/src/pages/UnlockPage.tsx index 8e3b0a3..2752138 100644 --- a/web/normogen-web/src/pages/UnlockPage.tsx +++ b/web/normogen-web/src/pages/UnlockPage.tsx @@ -12,23 +12,11 @@ import { } from '@mui/material'; import { Lock as LockIcon } from '@mui/icons-material'; import { useAuthStore } from '../store/useStore'; -import { - unlockWithPassword, - deriveAuthAndEncKeys, - unwrapIdentityPrivateKey, - setEncKey, - setIdentityPrivate, -} from '../crypto'; +import { unlockWithPassword, deriveAuthAndEncKeys, setEncKey } from '../crypto'; export const UnlockPage: FC = () => { const navigate = useNavigate(); - const { - wrapped_dek, - wrapped_dek_iv, - identity_private_key_wrapped, - identity_private_key_wrapped_iv, - user, - } = useAuthStore(); + const { wrapped_dek, wrapped_dek_iv, user } = useAuthStore(); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [unlocking, setUnlocking] = useState(false); @@ -39,32 +27,13 @@ export const UnlockPage: FC = () => { setUnlocking(true); try { - let dek; if (wrapped_dek && wrapped_dek_iv) { // Wrapped-DEK model: unwrap the DEK using the password. - ({ dek } = await unlockWithPassword(password, { + const { dek } = await unlockWithPassword(password, { data: wrapped_dek, iv: wrapped_dek_iv, - })); + }); setEncKey(dek); - // Phase A1: with the DEK available, also unwrap the X25519 identity - // private key into in-memory storage. Non-fatal if it fails (e.g. - // legacy accounts without an identity keypair). - if (identity_private_key_wrapped && identity_private_key_wrapped_iv) { - try { - const identityPrivate = await unwrapIdentityPrivateKey( - { - data: identity_private_key_wrapped, - iv: identity_private_key_wrapped_iv, - }, - dek, - ); - setIdentityPrivate(identityPrivate); - } catch { - // Wrapped private key present but undecryptable — ignore; Phase B - // features will surface a re-setup need. - } - } } else { // Phase 1 compat: derive the key directly from the password. const { encKey } = await deriveAuthAndEncKeys(password); diff --git a/web/normogen-web/src/store/useStore.ts b/web/normogen-web/src/store/useStore.ts index 7b96b9f..69211ee 100644 --- a/web/normogen-web/src/store/useStore.ts +++ b/web/normogen-web/src/store/useStore.ts @@ -6,13 +6,8 @@ import { unlockWithPassword, unlockWithRecovery, rewrapDek, - generateIdentityKeyPair, - wrapIdentityPrivateKey, - unwrapIdentityPrivateKey, setEncKey, clearEncKey, - clearIdentityPrivate, - setIdentityPrivate, getEncKey, encryptJson, decryptJson, @@ -43,12 +38,6 @@ interface AuthState { // on page reload without a full re-login. wrapped_dek: string | null; wrapped_dek_iv: string | null; - // Account X25519 identity keypair (Phase A1). The public key is plaintext; - // the private key is the DEK-wrapped ciphertext blob. Persisted for the same - // reason as the wrapped DEK — the unlock screen unwraps it into memory. - identity_public_key: string | null; - identity_private_key_wrapped: string | null; - identity_private_key_wrapped_iv: string | null; // Actions login: (email: string, password: string) => Promise; @@ -139,9 +128,6 @@ export const useAuthStore = create()( error: null, wrapped_dek: null, wrapped_dek_iv: null, - identity_public_key: null, - identity_private_key_wrapped: null, - identity_private_key_wrapped_iv: null, login: async (email: string, password: string) => { set({ isLoading: true, error: null }); @@ -158,26 +144,6 @@ export const useAuthStore = create()( iv: response.wrapped_dek_iv, }); setEncKey(dek); - // Once the DEK is available, also unwrap the X25519 identity - // private key (Phase A1) into in-memory storage. - if ( - response.identity_private_key_wrapped && - response.identity_private_key_wrapped_iv - ) { - try { - const identityPrivate = await unwrapIdentityPrivateKey( - { - data: response.identity_private_key_wrapped, - iv: response.identity_private_key_wrapped_iv, - }, - dek, - ); - setIdentityPrivate(identityPrivate); - } catch { - // Wrapped private key present but failed to unwrap — - // non-fatal; Phase B features will surface a re-setup need. - } - } } catch { // Fallback to the PBKDF2-derived key (Phase 1 compat). } @@ -194,9 +160,6 @@ export const useAuthStore = create()( isLoading: false, wrapped_dek: response.wrapped_dek ?? null, wrapped_dek_iv: response.wrapped_dek_iv ?? null, - identity_public_key: response.identity_public_key ?? null, - identity_private_key_wrapped: response.identity_private_key_wrapped ?? null, - identity_private_key_wrapped_iv: response.identity_private_key_wrapped_iv ?? null, }); } catch (error: any) { clearEncKey(); @@ -218,13 +181,6 @@ export const useAuthStore = create()( const setup = await setupEncryption(password, recoveryPhrase); setEncKey(setup.dek); - // Phase A1: generate the account X25519 identity keypair and wrap - // the private half under the account DEK. The public key is sent - // plaintext; the wrapped private key is opaque ciphertext. - const { publicKey, privateKey } = await generateIdentityKeyPair(); - const wrappedPrivate = await wrapIdentityPrivateKey(privateKey, setup.dek); - setIdentityPrivate(privateKey); - const response = await apiService.register({ username, email, @@ -234,10 +190,7 @@ export const useAuthStore = create()( recovery_wrapped_dek: setup.recoveryWrappedDek?.data, recovery_wrapped_dek_iv: setup.recoveryWrappedDek?.iv, recovery_phrase: setup.recoveryKekHash, - identity_public_key: publicKey, - identity_private_key_wrapped: wrappedPrivate.data, - identity_private_key_wrapped_iv: wrappedPrivate.iv, - }); + } as any); set({ user: { user_id: response.user_id, @@ -250,9 +203,6 @@ export const useAuthStore = create()( isLoading: false, wrapped_dek: setup.passwordWrappedDek.data, wrapped_dek_iv: setup.passwordWrappedDek.iv, - identity_public_key: publicKey, - identity_private_key_wrapped: wrappedPrivate.data, - identity_private_key_wrapped_iv: wrappedPrivate.iv, }); } catch (error: any) { set({ @@ -304,7 +254,6 @@ export const useAuthStore = create()( logout: async () => { clearEncKey(); - clearIdentityPrivate(); await apiService.logout(); set({ user: null, @@ -313,9 +262,6 @@ export const useAuthStore = create()( error: null, wrapped_dek: null, wrapped_dek_iv: null, - identity_public_key: null, - identity_private_key_wrapped: null, - identity_private_key_wrapped_iv: null, }); }, @@ -350,9 +296,6 @@ export const useAuthStore = create()( isAuthenticated: state.isAuthenticated, wrapped_dek: state.wrapped_dek, wrapped_dek_iv: state.wrapped_dek_iv, - identity_public_key: state.identity_public_key, - identity_private_key_wrapped: state.identity_private_key_wrapped, - identity_private_key_wrapped_iv: state.identity_private_key_wrapped_iv, }), } ) diff --git a/web/normogen-web/src/types/api.ts b/web/normogen-web/src/types/api.ts index 9c09d34..67ccf49 100644 --- a/web/normogen-web/src/types/api.ts +++ b/web/normogen-web/src/types/api.ts @@ -42,11 +42,6 @@ export interface AuthTokens { email?: string; wrapped_dek?: string; wrapped_dek_iv?: string; - /** Account X25519 identity keypair (Phase A1). Public key is plaintext; - * private key is wrapped under the account DEK. Absent for legacy accounts. */ - identity_public_key?: string; - identity_private_key_wrapped?: string; - identity_private_key_wrapped_iv?: string; } export interface LoginRequest { @@ -59,18 +54,6 @@ export interface RegisterRequest { email: string; password: string; role?: 'patient' | 'provider' | 'admin'; - /** Zero-knowledge wrapped DEK (password KEK). */ - wrapped_dek?: string; - wrapped_dek_iv?: string; - /** Zero-knowledge wrapped DEK (recovery KEK). */ - recovery_wrapped_dek?: string; - recovery_wrapped_dek_iv?: string; - /** Recovery proof (PBKDF2 derivation of the recovery phrase). */ - recovery_phrase?: string; - /** Account X25519 identity keypair (Phase A1). */ - identity_public_key?: string; - identity_private_key_wrapped?: string; - identity_private_key_wrapped_iv?: string; } // Medication Types