1package com.peachpasswords.sync23import android.content.Context4import java.util.UUID56private const val DEVICE_ID_KEY = "peach_chirp_device_id"7private const val DEVICE_NAME_KEY = "peach_chirp_device_name"8private const val PREFS_FILE = "peach_chirp_identity"910data class LocalChirpIdentity(11 val deviceId: String,12 val deviceName: String,13 val deviceType: DeviceType,14 val longTermPublicKeyHex: String,15 val signingPublicKeyHex: String,16 val wsPort: Int? = null,17 val relayUrl: String = Rendezvous.DEFAULT_RELAY_URL,18 1920212223 val license: String? = null,24)2526fun getOrCreateDeviceId(context: Context): String {27 val prefs = context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)28 val current = prefs.getString(DEVICE_ID_KEY, null)29 if (!current.isNullOrEmpty()) return current30 val fresh = UUID.randomUUID().toString()31 prefs.edit().putString(DEVICE_ID_KEY, fresh).apply()32 return fresh33}3435fun getDeviceName(context: Context): String {36 val prefs = context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)37 return prefs.getString(DEVICE_NAME_KEY, null).orEmpty().ifBlank { defaultDeviceName() }38}3940fun setDeviceName(context: Context, name: String) {41 val trimmed = name.trim()42 require(trimmed.isNotEmpty()) { "device name must not be empty" }43 require(trimmed.length <= 64) { "device name must be 64 chars or fewer" }44 context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)45 .edit()46 .putString(DEVICE_NAME_KEY, trimmed)47 .apply()48}4950fun buildLocalIdentity(context: Context, masterKey: ByteArray): LocalChirpIdentity {51 val deviceId = getOrCreateDeviceId(context)52 val deviceName = getDeviceName(context)53 val kp = deriveSyncIdentityKeypair(masterKey, deviceId)54 val signing = deriveSyncSigningKeypair(masterKey, deviceId)55 return LocalChirpIdentity(56 deviceId = deviceId,57 deviceName = deviceName,58 deviceType = extensionDeviceType(),59 longTermPublicKeyHex = pubkeyToHex(kp.publicKey),60 signingPublicKeyHex = pubkeyToHex(signing.publicKeyRaw),61 62 63 64 wsPort = SyncReconnectListener.SYNC_PORT,65 relayUrl = RelaySettingsStore.readRelayUrl(context),66 )67}6869fun deriveLocalSyncKeypair(masterKey: ByteArray, deviceId: String): SyncIdentityKeypair {70 return deriveSyncIdentityKeypair(masterKey, deviceId)71}7273private fun defaultDeviceName(): String {74 return "Android"75}7677private fun extensionDeviceType(): DeviceType {78 return DeviceType.ANDROID79}