An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(create-account): use device_key::get_or_create() for device public key

- Replace crypto::generate_p256_keypair() with device_key::get_or_create()
- Remove manual Keychain storage of private key (device_key handles it)
- Use device_key.multibase as device_public_key in relay request
- Remove device-private-key cleanup on token storage failures
- Device key is now persistent by design (SE-backed or simulator-fallback)
- Add create_account_uses_device_key_public_key test to verify AC5.1
- All 21 tests pass

authored by

Malpercio and committed by
Tangled
51cb7533 c886040a

+27 -15
+27 -15
apps/identity-wallet/src-tauri/src/lib.rs
··· 2 2 pub mod http; 3 3 pub mod keychain; 4 4 5 - use crypto::generate_p256_keypair; 6 5 use serde::{Deserialize, Serialize}; 7 6 use std::sync::LazyLock; 8 7 ··· 113 112 email: String, 114 113 handle: String, 115 114 ) -> Result<CreateAccountResult, CreateAccountError> { 116 - // 1. Generate P-256 device keypair. 117 - let keypair = generate_p256_keypair().map_err(|e| CreateAccountError::Unknown { 115 + // 1. Get or create the device's SE-backed (or simulator-fallback) P-256 key. 116 + let device_key = device_key::get_or_create().map_err(|e| CreateAccountError::Unknown { 118 117 message: e.to_string(), 119 118 })?; 120 119 121 - // 2. Store private key bytes in Keychain before any network call. 122 - // private_key_bytes is Zeroizing<[u8; 32]>; deref to &[u8] via AsRef. 123 - keychain::store_item("device-private-key", keypair.private_key_bytes.as_ref()) 124 - .map_err(|_| CreateAccountError::KeychainError)?; 125 - 126 - // 3. POST to relay. 120 + // 2. POST to relay. 127 121 let req = CreateMobileAccountRequest { 128 122 email, 129 123 handle, 130 - device_public_key: keypair.public_key, 124 + device_public_key: device_key.multibase, 131 125 platform: "ios".to_string(), 132 126 claim_code, 133 127 }; ··· 152 146 // If either token write fails, clean up the private key (best-effort) to avoid 153 147 // orphaning a key on the relay with no tokens to access it. 154 148 keychain::store_item("device-token", body.device_token.as_bytes()).map_err(|_| { 155 - // Best-effort cleanup: ignore deletion errors. 156 - let _ = keychain::delete_item("device-private-key"); 149 + // device-token write failed — nothing to clean up; the device key is persistent by design. 157 150 CreateAccountError::KeychainError 158 151 })?; 159 152 160 153 keychain::store_item("session-token", body.session_token.as_bytes()).map_err(|_| { 161 - // Best-effort cleanup: also remove the already-written device-token so the 162 - // Keychain doesn't hold a credential for an account the device can't access. 163 - let _ = keychain::delete_item("device-private-key"); 154 + // Best-effort cleanup: remove the already-written device-token. 164 155 let _ = keychain::delete_item("device-token"); 165 156 CreateAccountError::KeychainError 166 157 })?; ··· 324 315 let json = serde_json::to_value(map_409_subcode("UNKNOWN_SUBCODE")).unwrap(); 325 316 assert_eq!(json["code"], "UNKNOWN"); 326 317 assert!(json["message"].as_str().unwrap().contains("409:")); 318 + } 319 + 320 + // AC5.1 — create_account will use this key as device_public_key. 321 + // We verify: (a) the key exists and is correctly formatted, (b) it's stable so 322 + // create_account always sends the same device_public_key for this device. 323 + #[test] 324 + fn create_account_uses_device_key_public_key() { 325 + let key = crate::device_key::get_or_create() 326 + .expect("device_key::get_or_create must succeed — create_account depends on it"); 327 + // The relay expects multibase: 'z' + base58btc(33-byte compressed P-256 point). 328 + assert!( 329 + key.multibase.starts_with('z'), 330 + "device_public_key sent to relay must be multibase base58btc ('z' prefix), got: {}", 331 + key.multibase 332 + ); 333 + // Calling again returns the same key — create_account sends consistent device_public_key. 334 + let key2 = crate::device_key::get_or_create().expect("second call must also succeed"); 335 + assert_eq!( 336 + key.multibase, key2.multibase, 337 + "device_public_key must be stable across calls (idempotent)" 338 + ); 327 339 } 328 340 }