diff --git a/apps/android/app/src/main/java/cn/crossdesk/mobile/LocalizationKeys.java b/apps/android/app/src/main/java/cn/crossdesk/mobile/LocalizationKeys.java
index 4e432470..5fd39427 100644
--- a/apps/android/app/src/main/java/cn/crossdesk/mobile/LocalizationKeys.java
+++ b/apps/android/app/src/main/java/cn/crossdesk/mobile/LocalizationKeys.java
@@ -314,6 +314,13 @@ final class LocalizationKeys {
IDS.put("被控端分辨率", R.string.text_59c9dca5568a6cd3);
IDS.put("设备不支持", R.string.text_36e0073276c0e39f);
IDS.put("等待电脑端确认…", R.string.text_7b3440ad0f81b811);
+ IDS.put("将配对信息添加到被控端的可信设备列表,并核对指纹。", R.string.text_728ea431051d269e);
+ IDS.put("本机配对信息", R.string.text_56a998ff5c730e20);
+ IDS.put("查看指纹", R.string.text_976f1504103f24ac);
+ IDS.put("复制配对信息", R.string.text_3aab57c59aba6543);
+ IDS.put("配对信息已复制。", R.string.text_cbacf1eafbca53e0);
+ IDS.put("无法复制配对信息,请重试。", R.string.text_cde42ccdeacbbbd9);
+ IDS.put("安全存储不可用,请解锁设备后重试", R.string.text_7383b2ae09252c59);
}
private LocalizationKeys() {}
}
diff --git a/apps/android/app/src/main/res/values-b+zh+Hans/strings.xml b/apps/android/app/src/main/res/values-b+zh+Hans/strings.xml
index 398a13c8..5c9a55ed 100644
--- a/apps/android/app/src/main/res/values-b+zh+Hans/strings.xml
+++ b/apps/android/app/src/main/res/values-b+zh+Hans/strings.xml
@@ -308,4 +308,11 @@
"被控端分辨率"
"设备不支持"
"等待电脑端确认…"
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。"
+ "本机配对信息"
+ "查看指纹"
+ "复制配对信息"
+ "配对信息已复制。"
+ "无法复制配对信息,请重试。"
+ "安全存储不可用,请解锁设备后重试"
diff --git a/apps/android/app/src/main/res/values-b+zh+Hant/strings.xml b/apps/android/app/src/main/res/values-b+zh+Hant/strings.xml
index 6651d738..a31ae8bd 100644
--- a/apps/android/app/src/main/res/values-b+zh+Hant/strings.xml
+++ b/apps/android/app/src/main/res/values-b+zh+Hant/strings.xml
@@ -308,4 +308,11 @@
"被控端解析度"
"裝置不支援"
"等待電腦端確認…"
+ "將配對資訊新增至被控端的可信裝置清單,並核對指紋。"
+ "本機配對資訊"
+ "查看指紋"
+ "複製配對資訊"
+ "配對資訊已複製。"
+ "無法複製配對資訊,請重試。"
+ "安全儲存不可用,請解鎖裝置後重試"
diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml
index b426fd42..71a8ab10 100644
--- a/apps/android/app/src/main/res/values-de/strings.xml
+++ b/apps/android/app/src/main/res/values-de/strings.xml
@@ -308,4 +308,11 @@
"Auflösung des entfernten Bildschirms"
"Nicht unterstützt"
"Warten auf Bestätigung vom Computer..."
+ "Fügen Sie diese Kopplungsdaten auf dem gesteuerten Gerät zur Vertrauensliste hinzu und prüfen Sie den Fingerabdruck."
+ "Kopplungsdaten dieses Geräts"
+ "Fingerabdruck anzeigen"
+ "Kopplungsdaten kopieren"
+ "Kopplungsdaten kopiert."
+ "Kopplungsdaten konnten nicht kopiert werden. Bitte erneut versuchen."
+ "Der sichere Speicher ist nicht verfügbar. Bitte entsperren Sie das Gerät und versuchen Sie es erneut"
diff --git a/apps/android/app/src/main/res/values-es/strings.xml b/apps/android/app/src/main/res/values-es/strings.xml
index c1d14e9d..67526de1 100644
--- a/apps/android/app/src/main/res/values-es/strings.xml
+++ b/apps/android/app/src/main/res/values-es/strings.xml
@@ -308,4 +308,11 @@
"Resolución de la pantalla remota"
"No compatible"
"Esperando confirmación del ordenador..."
+ "Añada esta información de emparejamiento a la lista de confianza del dispositivo controlado y verifique la huella."
+ "Información de este dispositivo"
+ "Ver huella"
+ "Copiar información"
+ "Información de emparejamiento copiada."
+ "No se pudo copiar la información de emparejamiento. Inténtelo de nuevo."
+ "El almacenamiento seguro no está disponible. Desbloquee el dispositivo e inténtelo nuevamente."
diff --git a/apps/android/app/src/main/res/values-fr/strings.xml b/apps/android/app/src/main/res/values-fr/strings.xml
index ca3c7372..5fb96ea2 100644
--- a/apps/android/app/src/main/res/values-fr/strings.xml
+++ b/apps/android/app/src/main/res/values-fr/strings.xml
@@ -308,4 +308,11 @@
"Résolution de l’écran distant"
"Non pris en charge"
"En attente de confirmation de l\'ordinateur..."
+ "Ajoutez ces informations à la liste des appareils de confiance de l’appareil contrôlé et vérifiez l’empreinte."
+ "Informations de cet appareil"
+ "Afficher l’empreinte"
+ "Copier les informations"
+ "Informations d’association copiées."
+ "Impossible de copier les informations d’association. Réessayez."
+ "Le stockage sécurisé n\'est pas disponible, veuillez déverrouiller l\'appareil et réessayer"
diff --git a/apps/android/app/src/main/res/values-ja/strings.xml b/apps/android/app/src/main/res/values-ja/strings.xml
index 19f2ecb6..5b15eed3 100644
--- a/apps/android/app/src/main/res/values-ja/strings.xml
+++ b/apps/android/app/src/main/res/values-ja/strings.xml
@@ -308,4 +308,11 @@
"接続先の画面解像度"
"サポートされていません"
"コンピューターからの確認を待っています..."
+ "操作されるデバイスの信頼済みリストにこのペアリング情報を追加し、フィンガープリントを確認してください。"
+ "このデバイスのペアリング情報"
+ "フィンガープリントを表示"
+ "ペアリング情報をコピー"
+ "ペアリング情報をコピーしました。"
+ "ペアリング情報をコピーできません。もう一度お試しください。"
+ "安全なストレージが利用できません。デバイスのロックを解除して、もう一度お試しください。"
diff --git a/apps/android/app/src/main/res/values-ko/strings.xml b/apps/android/app/src/main/res/values-ko/strings.xml
index 413b0bbc..ede8b133 100644
--- a/apps/android/app/src/main/res/values-ko/strings.xml
+++ b/apps/android/app/src/main/res/values-ko/strings.xml
@@ -308,4 +308,11 @@
"원격 화면 해상도"
"지원되지 않음"
"컴퓨터의 확인을 기다리는 중..."
+ "제어 대상 장치의 신뢰 목록에 이 페어링 정보를 추가하고 지문을 확인하세요."
+ "이 장치의 페어링 정보"
+ "지문 보기"
+ "페어링 정보 복사"
+ "페어링 정보를 복사했습니다."
+ "페어링 정보를 복사할 수 없습니다. 다시 시도하세요."
+ "보안 저장소를 사용할 수 없습니다. 장치를 잠금 해제한 후 다시 시도해 주세요."
diff --git a/apps/android/app/src/main/res/values-pt/strings.xml b/apps/android/app/src/main/res/values-pt/strings.xml
index 0cfc69ce..018200ed 100644
--- a/apps/android/app/src/main/res/values-pt/strings.xml
+++ b/apps/android/app/src/main/res/values-pt/strings.xml
@@ -308,4 +308,11 @@
"Resolução da tela remota"
"Não suportado"
"Aguardando confirmação do computador..."
+ "Adicione estas informações de pareamento à lista de confiança do dispositivo controlado e confira a impressão digital."
+ "Informações deste dispositivo"
+ "Ver impressão digital"
+ "Copiar informações"
+ "Informações de pareamento copiadas."
+ "Não foi possível copiar as informações de pareamento. Tente novamente."
+ "O armazenamento seguro não está disponível. Desbloqueie o dispositivo e tente novamente"
diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml
index 1be6ed77..086ce7d6 100644
--- a/apps/android/app/src/main/res/values-ru/strings.xml
+++ b/apps/android/app/src/main/res/values-ru/strings.xml
@@ -308,4 +308,11 @@
"Разрешение удалённого экрана"
"Не поддерживается"
"Ожидание подтверждения от компьютера..."
+ "Добавьте эти данные сопряжения в список доверенных устройств на управляемом компьютере и сверьте отпечаток."
+ "Данные этого устройства"
+ "Показать отпечаток"
+ "Копировать данные"
+ "Данные сопряжения скопированы."
+ "Не удалось скопировать данные сопряжения. Повторите попытку."
+ "Безопасное хранилище недоступно. Разблокируйте устройство и повторите попытку."
diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml
index caf92b08..eb1f0272 100644
--- a/apps/android/app/src/main/res/values/strings.xml
+++ b/apps/android/app/src/main/res/values/strings.xml
@@ -308,4 +308,11 @@
"Remote display resolution"
"Unsupported"
"Waiting for confirmation from the computer..."
+ "Add this pairing information to the controlled device’s trusted list and verify the fingerprint."
+ "This device’s pairing info"
+ "View fingerprint"
+ "Copy pairing info"
+ "Pairing information copied."
+ "Unable to copy pairing information. Please try again."
+ "Secure storage is unavailable, please unlock the device and try again"
diff --git a/apps/harmony/entry/src/main/ets/mobile/LocalizationData.ets b/apps/harmony/entry/src/main/ets/mobile/LocalizationData.ets
index 4d80d75a..a3a34dd8 100644
--- a/apps/harmony/entry/src/main/ets/mobile/LocalizationData.ets
+++ b/apps/harmony/entry/src/main/ets/mobile/LocalizationData.ets
@@ -242,4 +242,10 @@ export const translations: Record = {
"鼠标控制": ["鼠标控制", "Mouse control", "Управление мышью", "滑鼠控制", "マウスコントロール", "마우스 제어", "Maussteuerung", "Contrôle de la souris", "Control del ratón", "Controle do mouse"],
"被控端分辨率": ["被控端分辨率", "Remote display resolution", "Разрешение удалённого экрана", "被控端解析度", "接続先の画面解像度", "원격 화면 해상도", "Auflösung des entfernten Bildschirms", "Résolution de l’écran distant", "Resolución de la pantalla remota", "Resolução da tela remota"],
"设备不支持": ["设备不支持", "Unsupported", "Не поддерживается", "裝置不支援", "サポートされていません", "지원되지 않음", "Nicht unterstützt", "Non pris en charge", "No compatible", "Não suportado"],
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。": ["将配对信息添加到被控端的可信设备列表,并核对指纹。", "Add this pairing information to the controlled device’s trusted list and verify the fingerprint.", "Добавьте эти данные сопряжения в список доверенных устройств на управляемом компьютере и сверьте отпечаток.", "將配對資訊新增至被控端的可信裝置清單,並核對指紋。", "操作されるデバイスの信頼済みリストにこのペアリング情報を追加し、フィンガープリントを確認してください。", "제어 대상 장치의 신뢰 목록에 이 페어링 정보를 추가하고 지문을 확인하세요.", "Fügen Sie diese Kopplungsdaten auf dem gesteuerten Gerät zur Vertrauensliste hinzu und prüfen Sie den Fingerabdruck.", "Ajoutez ces informations à la liste des appareils de confiance de l’appareil contrôlé et vérifiez l’empreinte.", "Añada esta información de emparejamiento a la lista de confianza del dispositivo controlado y verifique la huella.", "Adicione estas informações de pareamento à lista de confiança do dispositivo controlado e confira a impressão digital."],
+ "本机配对信息": ["本机配对信息", "This device’s pairing info", "Данные этого устройства", "本機配對資訊", "このデバイスのペアリング情報", "이 장치의 페어링 정보", "Kopplungsdaten dieses Geräts", "Informations de cet appareil", "Información de este dispositivo", "Informações deste dispositivo"],
+ "查看指纹": ["查看指纹", "View fingerprint", "Показать отпечаток", "查看指紋", "フィンガープリントを表示", "지문 보기", "Fingerabdruck anzeigen", "Afficher l’empreinte", "Ver huella", "Ver impressão digital"],
+ "复制配对信息": ["复制配对信息", "Copy pairing info", "Копировать данные", "複製配對資訊", "ペアリング情報をコピー", "페어링 정보 복사", "Kopplungsdaten kopieren", "Copier les informations", "Copiar información", "Copiar informações"],
+ "配对信息已复制。": ["配对信息已复制。", "Pairing information copied.", "Данные сопряжения скопированы.", "配對資訊已複製。", "ペアリング情報をコピーしました。", "페어링 정보를 복사했습니다.", "Kopplungsdaten kopiert.", "Informations d’association copiées.", "Información de emparejamiento copiada.", "Informações de pareamento copiadas."],
+ "无法复制配对信息,请重试。": ["无法复制配对信息,请重试。", "Unable to copy pairing information. Please try again.", "Не удалось скопировать данные сопряжения. Повторите попытку.", "無法複製配對資訊,請重試。", "ペアリング情報をコピーできません。もう一度お試しください。", "페어링 정보를 복사할 수 없습니다. 다시 시도하세요.", "Kopplungsdaten konnten nicht kopiert werden. Bitte erneut versuchen.", "Impossible de copier les informations d’association. Réessayez.", "No se pudo copiar la información de emparejamiento. Inténtelo de nuevo.", "Não foi possível copiar as informações de pareamento. Tente novamente."],
};
diff --git a/apps/ios/CrossDeskMobile/Resources/de.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/de.lproj/Localizable.strings
index ac435264..1cc60210 100644
--- a/apps/ios/CrossDeskMobile/Resources/de.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/de.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Auflösung des entfernten Bildschirms";
"设备不支持" = "Nicht unterstützt";
"等待电脑端确认…" = "Warten auf Bestätigung vom Computer...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Fügen Sie diese Kopplungsdaten auf dem gesteuerten Gerät zur Vertrauensliste hinzu und prüfen Sie den Fingerabdruck.";
+"本机配对信息" = "Kopplungsdaten dieses Geräts";
+"查看指纹" = "Fingerabdruck anzeigen";
+"复制配对信息" = "Kopplungsdaten kopieren";
+"配对信息已复制。" = "Kopplungsdaten kopiert.";
+"无法复制配对信息,请重试。" = "Kopplungsdaten konnten nicht kopiert werden. Bitte erneut versuchen.";
+"安全存储不可用,请解锁设备后重试" = "Der sichere Speicher ist nicht verfügbar. Bitte entsperren Sie das Gerät und versuchen Sie es erneut";
diff --git a/apps/ios/CrossDeskMobile/Resources/en.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/en.lproj/Localizable.strings
index 79796d3d..fe996cba 100644
--- a/apps/ios/CrossDeskMobile/Resources/en.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/en.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Remote display resolution";
"设备不支持" = "Unsupported";
"等待电脑端确认…" = "Waiting for confirmation from the computer...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Add this pairing information to the controlled device’s trusted list and verify the fingerprint.";
+"本机配对信息" = "This device’s pairing info";
+"查看指纹" = "View fingerprint";
+"复制配对信息" = "Copy pairing info";
+"配对信息已复制。" = "Pairing information copied.";
+"无法复制配对信息,请重试。" = "Unable to copy pairing information. Please try again.";
+"安全存储不可用,请解锁设备后重试" = "Secure storage is unavailable, please unlock the device and try again";
diff --git a/apps/ios/CrossDeskMobile/Resources/es.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/es.lproj/Localizable.strings
index 1d213a6d..1958424d 100644
--- a/apps/ios/CrossDeskMobile/Resources/es.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/es.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Resolución de la pantalla remota";
"设备不支持" = "No compatible";
"等待电脑端确认…" = "Esperando confirmación del ordenador...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Añada esta información de emparejamiento a la lista de confianza del dispositivo controlado y verifique la huella.";
+"本机配对信息" = "Información de este dispositivo";
+"查看指纹" = "Ver huella";
+"复制配对信息" = "Copiar información";
+"配对信息已复制。" = "Información de emparejamiento copiada.";
+"无法复制配对信息,请重试。" = "No se pudo copiar la información de emparejamiento. Inténtelo de nuevo.";
+"安全存储不可用,请解锁设备后重试" = "El almacenamiento seguro no está disponible. Desbloquee el dispositivo e inténtelo nuevamente.";
diff --git a/apps/ios/CrossDeskMobile/Resources/fr.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/fr.lproj/Localizable.strings
index 9a38a8b6..2293e41e 100644
--- a/apps/ios/CrossDeskMobile/Resources/fr.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/fr.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Résolution de l’écran distant";
"设备不支持" = "Non pris en charge";
"等待电脑端确认…" = "En attente de confirmation de l'ordinateur...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Ajoutez ces informations à la liste des appareils de confiance de l’appareil contrôlé et vérifiez l’empreinte.";
+"本机配对信息" = "Informations de cet appareil";
+"查看指纹" = "Afficher l’empreinte";
+"复制配对信息" = "Copier les informations";
+"配对信息已复制。" = "Informations d’association copiées.";
+"无法复制配对信息,请重试。" = "Impossible de copier les informations d’association. Réessayez.";
+"安全存储不可用,请解锁设备后重试" = "Le stockage sécurisé n'est pas disponible, veuillez déverrouiller l'appareil et réessayer";
diff --git a/apps/ios/CrossDeskMobile/Resources/ja.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/ja.lproj/Localizable.strings
index 3938da1e..8e31d547 100644
--- a/apps/ios/CrossDeskMobile/Resources/ja.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/ja.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "接続先の画面解像度";
"设备不支持" = "サポートされていません";
"等待电脑端确认…" = "コンピューターからの確認を待っています...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "操作されるデバイスの信頼済みリストにこのペアリング情報を追加し、フィンガープリントを確認してください。";
+"本机配对信息" = "このデバイスのペアリング情報";
+"查看指纹" = "フィンガープリントを表示";
+"复制配对信息" = "ペアリング情報をコピー";
+"配对信息已复制。" = "ペアリング情報をコピーしました。";
+"无法复制配对信息,请重试。" = "ペアリング情報をコピーできません。もう一度お試しください。";
+"安全存储不可用,请解锁设备后重试" = "安全なストレージが利用できません。デバイスのロックを解除して、もう一度お試しください。";
diff --git a/apps/ios/CrossDeskMobile/Resources/ko.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/ko.lproj/Localizable.strings
index 907cb9da..96e607ce 100644
--- a/apps/ios/CrossDeskMobile/Resources/ko.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/ko.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "원격 화면 해상도";
"设备不支持" = "지원되지 않음";
"等待电脑端确认…" = "컴퓨터의 확인을 기다리는 중...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "제어 대상 장치의 신뢰 목록에 이 페어링 정보를 추가하고 지문을 확인하세요.";
+"本机配对信息" = "이 장치의 페어링 정보";
+"查看指纹" = "지문 보기";
+"复制配对信息" = "페어링 정보 복사";
+"配对信息已复制。" = "페어링 정보를 복사했습니다.";
+"无法复制配对信息,请重试。" = "페어링 정보를 복사할 수 없습니다. 다시 시도하세요.";
+"安全存储不可用,请解锁设备后重试" = "보안 저장소를 사용할 수 없습니다. 장치를 잠금 해제한 후 다시 시도해 주세요.";
diff --git a/apps/ios/CrossDeskMobile/Resources/pt-BR.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/pt-BR.lproj/Localizable.strings
index 03132bea..1fd0ed72 100644
--- a/apps/ios/CrossDeskMobile/Resources/pt-BR.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/pt-BR.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Resolução da tela remota";
"设备不支持" = "Não suportado";
"等待电脑端确认…" = "Aguardando confirmação do computador...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Adicione estas informações de pareamento à lista de confiança do dispositivo controlado e confira a impressão digital.";
+"本机配对信息" = "Informações deste dispositivo";
+"查看指纹" = "Ver impressão digital";
+"复制配对信息" = "Copiar informações";
+"配对信息已复制。" = "Informações de pareamento copiadas.";
+"无法复制配对信息,请重试。" = "Não foi possível copiar as informações de pareamento. Tente novamente.";
+"安全存储不可用,请解锁设备后重试" = "O armazenamento seguro não está disponível. Desbloqueie o dispositivo e tente novamente";
diff --git a/apps/ios/CrossDeskMobile/Resources/pt.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/pt.lproj/Localizable.strings
index 03132bea..1fd0ed72 100644
--- a/apps/ios/CrossDeskMobile/Resources/pt.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/pt.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Resolução da tela remota";
"设备不支持" = "Não suportado";
"等待电脑端确认…" = "Aguardando confirmação do computador...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Adicione estas informações de pareamento à lista de confiança do dispositivo controlado e confira a impressão digital.";
+"本机配对信息" = "Informações deste dispositivo";
+"查看指纹" = "Ver impressão digital";
+"复制配对信息" = "Copiar informações";
+"配对信息已复制。" = "Informações de pareamento copiadas.";
+"无法复制配对信息,请重试。" = "Não foi possível copiar as informações de pareamento. Tente novamente.";
+"安全存储不可用,请解锁设备后重试" = "O armazenamento seguro não está disponível. Desbloqueie o dispositivo e tente novamente";
diff --git a/apps/ios/CrossDeskMobile/Resources/ru.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/ru.lproj/Localizable.strings
index e6a3453a..1547cd28 100644
--- a/apps/ios/CrossDeskMobile/Resources/ru.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/ru.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "Разрешение удалённого экрана";
"设备不支持" = "Не поддерживается";
"等待电脑端确认…" = "Ожидание подтверждения от компьютера...";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "Добавьте эти данные сопряжения в список доверенных устройств на управляемом компьютере и сверьте отпечаток.";
+"本机配对信息" = "Данные этого устройства";
+"查看指纹" = "Показать отпечаток";
+"复制配对信息" = "Копировать данные";
+"配对信息已复制。" = "Данные сопряжения скопированы.";
+"无法复制配对信息,请重试。" = "Не удалось скопировать данные сопряжения. Повторите попытку.";
+"安全存储不可用,请解锁设备后重试" = "Безопасное хранилище недоступно. Разблокируйте устройство и повторите попытку.";
diff --git a/apps/ios/CrossDeskMobile/Resources/zh-Hans.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/zh-Hans.lproj/Localizable.strings
index 7a3fe982..106e6a98 100644
--- a/apps/ios/CrossDeskMobile/Resources/zh-Hans.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/zh-Hans.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "被控端分辨率";
"设备不支持" = "设备不支持";
"等待电脑端确认…" = "等待电脑端确认…";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "将配对信息添加到被控端的可信设备列表,并核对指纹。";
+"本机配对信息" = "本机配对信息";
+"查看指纹" = "查看指纹";
+"复制配对信息" = "复制配对信息";
+"配对信息已复制。" = "配对信息已复制。";
+"无法复制配对信息,请重试。" = "无法复制配对信息,请重试。";
+"安全存储不可用,请解锁设备后重试" = "安全存储不可用,请解锁设备后重试";
diff --git a/apps/ios/CrossDeskMobile/Resources/zh-Hant.lproj/Localizable.strings b/apps/ios/CrossDeskMobile/Resources/zh-Hant.lproj/Localizable.strings
index 8eed55c4..77b4f5af 100644
--- a/apps/ios/CrossDeskMobile/Resources/zh-Hant.lproj/Localizable.strings
+++ b/apps/ios/CrossDeskMobile/Resources/zh-Hant.lproj/Localizable.strings
@@ -288,3 +288,10 @@
"被控端分辨率" = "被控端解析度";
"设备不支持" = "裝置不支援";
"等待电脑端确认…" = "等待電腦端確認…";
+"将配对信息添加到被控端的可信设备列表,并核对指纹。" = "將配對資訊新增至被控端的可信裝置清單,並核對指紋。";
+"本机配对信息" = "本機配對資訊";
+"查看指纹" = "查看指紋";
+"复制配对信息" = "複製配對資訊";
+"配对信息已复制。" = "配對資訊已複製。";
+"无法复制配对信息,请重试。" = "無法複製配對資訊,請重試。";
+"安全存储不可用,请解锁设备后重试" = "安全儲存不可用,請解鎖裝置後重試";
diff --git a/libs/controller/include/mobile_device_trust.h b/libs/controller/include/mobile_device_trust.h
new file mode 100644
index 00000000..af62a94d
--- /dev/null
+++ b/libs/controller/include/mobile_device_trust.h
@@ -0,0 +1,37 @@
+/*
+ * @Author: DI JUNKUN
+ * @Date: 2026-11-13
+ * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
+ */
+
+#ifndef _MOBILE_DEVICE_TRUST_H_
+#define _MOBILE_DEVICE_TRUST_H_
+
+#include
+#include
+#include
+#include
+
+namespace crossdesk::mobile_controller {
+// The platform must durably save the seed in its secure store before using it.
+// Existing invalid/unavailable identities must never be replaced automatically.
+class DeviceTrustIdentity {
+ public:
+ explicit DeviceTrustIdentity(std::string_view seed);
+ ~DeviceTrustIdentity();
+ static std::string GeneratePrivateKey();
+ static void ClearSecret(std::string& value);
+ static std::string Advertise(std::string_view host_information);
+ static bool IsChallenge(std::string_view data) noexcept;
+ std::string PairingInfo(const std::string& device_id, const std::string& name) const;
+ const std::string& Fingerprint() const;
+ // nullopt: ordinary control message; empty: unsupported/malformed challenge.
+ std::optional ChallengeResponse(std::string_view data,
+ const std::string& device_id, const std::string& name) const noexcept;
+ private:
+ class Impl;
+ std::unique_ptr impl_;
+};
+} // namespace crossdesk::mobile_controller
+
+#endif
diff --git a/libs/controller/src/mobile_device_trust.cpp b/libs/controller/src/mobile_device_trust.cpp
new file mode 100644
index 00000000..5a705c74
--- /dev/null
+++ b/libs/controller/src/mobile_device_trust.cpp
@@ -0,0 +1,140 @@
+#include "mobile_device_trust.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace crossdesk::mobile_controller {
+// The platform must durably save the seed in its secure store before using it.
+// A missing seed may be generated on first use; a malformed one must never be
+// replaced automatically, since that would invalidate existing pairings.
+class DeviceTrustIdentity::Impl {
+ public:
+ explicit Impl(std::string_view seed) {
+ Require(IsHex(seed, 32));
+ Secret bytes;
+ for (size_t i = 0; i < bytes.value.size(); ++i)
+ bytes.value[i] = (Digit(seed[2 * i]) << 4) | Digit(seed[2 * i + 1]);
+ key_.reset(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr,
+ bytes.value.data(), bytes.value.size()));
+ Require(bool(key_));
+ std::array public_key{};
+ size_t size = public_key.size();
+ Require(EVP_PKEY_get_raw_public_key(key_.get(), public_key.data(), &size) == 1 && size == 32);
+ public_key_ = Hex(public_key.data(), public_key.size());
+ std::array hash{};
+ Require(SHA256(public_key.data(), public_key.size(), hash.data()) != nullptr);
+ fingerprint_ = Hex(hash.data(), hash.size());
+ }
+
+ static std::string GeneratePrivateKey() {
+ Key key(EVP_PKEY_Q_keygen(nullptr, nullptr, "ED25519"), EVP_PKEY_free);
+ Require(bool(key));
+ Secret bytes;
+ size_t size = bytes.value.size();
+ Require(EVP_PKEY_get_raw_private_key(key.get(), bytes.value.data(), &size) == 1 && size == 32);
+ return Hex(bytes.value.data(), bytes.value.size());
+ }
+
+ std::string PairingInfo(const std::string& device_id, const std::string& name) const {
+ Require(Label(device_id) && Label(name));
+ return nlohmann::json{{"type", "crossdesk-pairing"}, {"version", 1},
+ {"public_key", public_key_}, {"device_id", device_id}, {"name", name}}.dump();
+ }
+
+ const std::string& Fingerprint() const { return fingerprint_; }
+
+ // nullopt: ordinary control message; empty: unsupported/malformed challenge.
+ // No peer-controlled parse or crypto exception may escape a data callback.
+ std::optional ChallengeResponse(std::string_view data,
+ const std::string& device_id, const std::string& name) const noexcept {
+ if (data.empty() || data.size() > 2048) return std::nullopt;
+ try {
+ auto message = nlohmann::json::parse(data.begin(), data.end(), nullptr, false);
+ if (!message.is_object() || !message.contains("type") ||
+ message["type"] != "device_trust_challenge") return std::nullopt;
+ if (!message.at("version").is_number_integer() || message.at("version") != 1 ||
+ !message.at("nonce").is_string()) return std::string{};
+ const auto nonce = message.at("nonce").get();
+ if (!IsHex(nonce, 32) || !Label(device_id) || !Label(name)) return std::string{};
+ const auto transcript = nlohmann::json::array({"crossdesk.device-trust.v1", nonce,
+ public_key_, device_id, name}).dump();
+ std::unique_ptr context(EVP_MD_CTX_new(), EVP_MD_CTX_free);
+ std::array signature{};
+ size_t size = signature.size();
+ Require(context && EVP_DigestSignInit(context.get(), nullptr, nullptr, nullptr, key_.get()) == 1 &&
+ EVP_DigestSign(context.get(), signature.data(), &size,
+ reinterpret_cast(transcript.data()), transcript.size()) == 1 && size == 64);
+ return nlohmann::json{{"type", "device_trust_proof"}, {"version", 1}, {"nonce", nonce},
+ {"public_key", public_key_}, {"device_id", device_id}, {"name", name},
+ {"signature", Hex(signature.data(), signature.size())}}.dump();
+ } catch (const std::exception&) { return std::string{}; }
+ }
+
+ private:
+ using Key = std::unique_ptr;
+ struct Secret {
+ std::array value{};
+ ~Secret() { OPENSSL_cleanse(value.data(), value.size()); }
+ };
+ static void Require(bool ok) {
+ if (!ok) throw std::runtime_error("Device signing identity unavailable");
+ }
+ static int Digit(char c) { return c <= '9' ? c - '0' : c - 'a' + 10; }
+ static bool IsHex(std::string_view value, size_t bytes) {
+ return value.size() == bytes * 2 && std::all_of(value.begin(), value.end(),
+ [](char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); });
+ }
+ static std::string Hex(const unsigned char* bytes, size_t size) {
+ constexpr char digits[] = "0123456789abcdef";
+ std::string result;
+ result.reserve(size * 2);
+ for (size_t i = 0; i < size; ++i) {
+ result += digits[bytes[i] >> 4]; result += digits[bytes[i] & 15];
+ }
+ return result;
+ }
+ static bool Label(const std::string& value) {
+ return value.size() <= 128 && std::none_of(value.begin(), value.end(),
+ [](unsigned char c) { return c < 32 || c == 127; });
+ }
+ Key key_{nullptr, EVP_PKEY_free};
+ std::string public_key_, fingerprint_;
+};
+DeviceTrustIdentity::DeviceTrustIdentity(std::string_view seed) : impl_(std::make_unique(seed)) {}
+DeviceTrustIdentity::~DeviceTrustIdentity() = default;
+std::string DeviceTrustIdentity::GeneratePrivateKey() { return Impl::GeneratePrivateKey(); }
+std::string DeviceTrustIdentity::PairingInfo(const std::string& id, const std::string& name) const {
+ return impl_->PairingInfo(id, name);
+}
+const std::string& DeviceTrustIdentity::Fingerprint() const { return impl_->Fingerprint(); }
+std::optional DeviceTrustIdentity::ChallengeResponse(std::string_view data,
+ const std::string& id, const std::string& name) const noexcept {
+ return impl_->ChallengeResponse(data, id, name);
+}
+std::string DeviceTrustIdentity::Advertise(std::string_view host_information) {
+ auto info = nlohmann::json::parse(host_information.begin(), host_information.end());
+ info["device_trust_version"] = 1;
+ return info.dump();
+}
+void DeviceTrustIdentity::ClearSecret(std::string& value) {
+ OPENSSL_cleanse(value.data(), value.size());
+ value.clear();
+}
+bool DeviceTrustIdentity::IsChallenge(std::string_view data) noexcept {
+ if (data.empty() || data.size() > 2048) return false;
+ try {
+ const auto message = nlohmann::json::parse(data.begin(), data.end(), nullptr, false);
+ return message.is_object() && message.contains("type") && message["type"] == "device_trust_challenge";
+ } catch (const std::exception&) { return false; }
+}
+} // namespace crossdesk::mobile_controller
diff --git a/localization/catalog.json b/localization/catalog.json
index 740e2899..fa892a76 100644
--- a/localization/catalog.json
+++ b/localization/catalog.json
@@ -11051,6 +11051,18 @@
"fr": "Résolution de l’écran distant",
"es": "Resolución de la pantalla remota",
"pt-BR": "Resolução da tela remota"
+ },
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。": {
+ "zh-Hans": "将配对信息添加到被控端的可信设备列表,并核对指纹。",
+ "en": "Add this pairing information to the controlled device’s trusted list and verify the fingerprint.",
+ "ru": "Добавьте эти данные сопряжения в список доверенных устройств на управляемом компьютере и сверьте отпечаток.",
+ "zh-Hant": "將配對資訊新增至被控端的可信裝置清單,並核對指紋。",
+ "ja": "操作されるデバイスの信頼済みリストにこのペアリング情報を追加し、フィンガープリントを確認してください。",
+ "ko": "제어 대상 장치의 신뢰 목록에 이 페어링 정보를 추가하고 지문을 확인하세요.",
+ "de": "Fügen Sie diese Kopplungsdaten auf dem gesteuerten Gerät zur Vertrauensliste hinzu und prüfen Sie den Fingerabdruck.",
+ "fr": "Ajoutez ces informations à la liste des appareils de confiance de l’appareil contrôlé et vérifiez l’empreinte.",
+ "es": "Añada esta información de emparejamiento a la lista de confianza del dispositivo controlado y verifique la huella.",
+ "pt-BR": "Adicione estas informações de pareamento à lista de confiança do dispositivo controlado e confira a impressão digital."
}
},
"desktop": {
@@ -12413,7 +12425,14 @@
",点击重试",
"被控端分辨率",
"设备不支持",
- "等待电脑端确认…"
+ "等待电脑端确认…",
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。",
+ "本机配对信息",
+ "查看指纹",
+ "复制配对信息",
+ "配对信息已复制。",
+ "无法复制配对信息,请重试。",
+ "安全存储不可用,请解锁设备后重试"
],
"ios": [
"%1$@ 传输失败",
@@ -12704,7 +12723,14 @@
"新版本需要 iOS %1$@ 或更高版本",
"被控端分辨率",
"设备不支持",
- "等待电脑端确认…"
+ "等待电脑端确认…",
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。",
+ "本机配对信息",
+ "查看指纹",
+ "复制配对信息",
+ "配对信息已复制。",
+ "无法复制配对信息,请重试。",
+ "安全存储不可用,请解锁设备后重试"
],
"harmony": [
"OpenFEC 告知",
@@ -12948,7 +12974,13 @@
"鼠标左键,按住拖动",
"鼠标控制",
"被控端分辨率",
- "设备不支持"
+ "设备不支持",
+ "将配对信息添加到被控端的可信设备列表,并核对指纹。",
+ "本机配对信息",
+ "查看指纹",
+ "复制配对信息",
+ "配对信息已复制。",
+ "无法复制配对信息,请重试。"
]
}
}