Merge pull request #467 from sethoscope/set-repeater-key

Add UI for setting repeater identity keys
This commit is contained in:
zjs81
2026-07-06 09:23:01 -07:00
committed by GitHub
27 changed files with 1171 additions and 44 deletions
+21 -1
View File
@@ -1474,6 +1474,26 @@
"repeater_advancedSettingsSubtitle": "Tuning knobs for experienced operators",
"repeater_pathHashMode": "Path hash mode",
"repeater_pathHashModeHelper": "Bytes used to encode this repeater's ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.",
"repeater_keySettings": "Change Identity Keys",
"repeater_keySettingsSubtitle": "Change the public/private keypair",
"repeater_prvKey": "Private key",
"repeater_prvKeyHelper": "A new private key for the repeater, a 128-character hex string.",
"repeater_generatePrvKey": "Generate a random keypair",
"repeater_stopGeneratingPrvKey": "Interrupt search for keypair",
"repeater_pubKey": "Public key",
"repeater_pubKeyHelper": "This is the public key that goes with the generated private key. You can't set this directly.",
"repeater_pubKeyPrefix": "Desired prefix",
"repeater_pubKeyPrefixHelper": "Find a public key that starts with these hex digits. Expected tries needed: {tries}.",
"@repeater_pubKeyPrefixHelper": {
"placeholders": {
"tries": {
"type": "int"
}
}
},
"repeater_txDelay": "Flood TX delay",
"repeater_txDelayHelper": "Retransmit spacing for flood traffic, as a multiplier of the packet's airtime (0-2, default 0.5). Higher = fewer collisions but slower delivery.",
"repeater_directTxDelay": "Direct TX delay",
@@ -1654,7 +1674,7 @@
"repeater_cliHelpPowersavingOnOff": "Enables or disables powersaving mode (where supported).",
"repeater_cliHelpErase": "(Serial only) Formats the device file system. Wipes all settings and contacts.",
"repeater_cliHelpSetDutyCycle": "Sets the maximum allowed transmit duty cycle as a percentage (1-100). Internally adjusts the airtime factor.",
"repeater_cliHelpSetPrvKey": "(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.",
"repeater_cliHelpSetPrvKey": "Replaces the device identity private key. Reboot required to apply. Generates a new public key.",
"repeater_cliHelpSetRadioRxGain": "(SX126x only) Toggles boosted RX gain for improved sensitivity at higher current draw.",
"repeater_cliHelpSetOwnerInfo": "Sets the owner contact info string included in adverts. Use '|' for newlines.",
"repeater_cliHelpSetPathHashMode": "Sets the path-hash mode. 0 = legacy, 1 = standard, 2 = strict. Affects how routing paths are matched.",
+61 -1
View File
@@ -4844,6 +4844,66 @@ abstract class AppLocalizations {
/// **'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.'**
String get repeater_pathHashModeHelper;
/// No description provided for @repeater_keySettings.
///
/// In en, this message translates to:
/// **'Change Identity Keys'**
String get repeater_keySettings;
/// No description provided for @repeater_keySettingsSubtitle.
///
/// In en, this message translates to:
/// **'Change the public/private keypair'**
String get repeater_keySettingsSubtitle;
/// No description provided for @repeater_prvKey.
///
/// In en, this message translates to:
/// **'Private key'**
String get repeater_prvKey;
/// No description provided for @repeater_prvKeyHelper.
///
/// In en, this message translates to:
/// **'A new private key for the repeater, a 128-character hex string.'**
String get repeater_prvKeyHelper;
/// No description provided for @repeater_generatePrvKey.
///
/// In en, this message translates to:
/// **'Generate a random keypair'**
String get repeater_generatePrvKey;
/// No description provided for @repeater_stopGeneratingPrvKey.
///
/// In en, this message translates to:
/// **'Interrupt search for keypair'**
String get repeater_stopGeneratingPrvKey;
/// No description provided for @repeater_pubKey.
///
/// In en, this message translates to:
/// **'Public key'**
String get repeater_pubKey;
/// No description provided for @repeater_pubKeyHelper.
///
/// In en, this message translates to:
/// **'This is the public key that goes with the generated private key. You can\'t set this directly.'**
String get repeater_pubKeyHelper;
/// No description provided for @repeater_pubKeyPrefix.
///
/// In en, this message translates to:
/// **'Desired prefix'**
String get repeater_pubKeyPrefix;
/// No description provided for @repeater_pubKeyPrefixHelper.
///
/// In en, this message translates to:
/// **'Find a public key that starts with these hex digits. Expected tries needed: {tries}.'**
String repeater_pubKeyPrefixHelper(int tries);
/// No description provided for @repeater_txDelay.
///
/// In en, this message translates to:
@@ -5615,7 +5675,7 @@ abstract class AppLocalizations {
/// No description provided for @repeater_cliHelpSetPrvKey.
///
/// In en, this message translates to:
/// **'(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.'**
/// **'Replaces the device identity private key. Reboot required to apply. Generates a new public key.'**
String get repeater_cliHelpSetPrvKey;
/// No description provided for @repeater_cliHelpSetRadioRxGain.
+35
View File
@@ -2707,6 +2707,41 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get repeater_txDelay => 'Забавяне на Flood TX';
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
@override
String get repeater_txDelayHelper =>
+35
View File
@@ -2701,6 +2701,41 @@ class AppLocalizationsDe extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Flood-TX-Verzögerung';
+36 -1
View File
@@ -2655,6 +2655,41 @@ class AppLocalizationsEn extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Flood TX delay';
@@ -3125,7 +3160,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get repeater_cliHelpSetPrvKey =>
'(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.';
'Replaces the device identity private key. Reboot required to apply. Generates a new public key.';
@override
String get repeater_cliHelpSetRadioRxGain =>
+35
View File
@@ -2700,6 +2700,41 @@ class AppLocalizationsEs extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Retraso TX por inundación';
+35
View File
@@ -2711,6 +2711,41 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Octets utilisés pour encoder l\'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu\'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu\'à 32 sauts), 2 = 3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n\'est plus nécessaire.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Délai de transmission en flood';
+35
View File
@@ -2694,6 +2694,41 @@ class AppLocalizationsHu extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Az átjátszó azonosítójának elárasztási útvonal/hurokészlelési címkékbe való kódolására használt bájtok. 0 = 1 bájt (256 azonosító, legfeljebb 64 ugrás), 1 = 2 bájt (65 000 azonosító, legfeljebb 32 ugrás), 2 = 3 bájt (16 millió azonosító, legfeljebb 21 ugrás). A v1.13 és régebbi firmware eldobja a többbájtos elérési utat csak akkor emelje meg, ha a hálózat a v1.14+ verziót használja.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Áradási TX késleltetés';
+35
View File
@@ -2702,6 +2702,41 @@ class AppLocalizationsIt extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Byte utilizzati per codificare l\'ID di questo ripetitore nei tag per il rilevamento del percorso/loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Le versioni 1.13 e precedenti utilizzano percorsi multi-byte: è necessario attivare la rete prima di utilizzare questa funzionalità (a partire dalla versione 1.14).';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Ritardo a Flood, TX';
+35
View File
@@ -2583,6 +2583,41 @@ class AppLocalizationsJa extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'このリピータのIDをフローパス/ループ検出タグにエンコードするために使用されるバイト数。 0=1バイト (256個のID、最大64ホップ)、1=2バイト (65,000個のID、最大32ホップ)、2=3バイト (160万個のID、最大21ホップ)。 v1.13およびそれ以前のファームウェアでは、マルチバイトパスがサポートされていません。 v1.14以降のバージョンでは、一度ネットワークが起動されると、パスが一度だけ検出されます。';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'フロイド・TXでの遅延';
+35
View File
@@ -2589,6 +2589,41 @@ class AppLocalizationsKo extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수: 0=1 바이트 (256개의 ID, 최대 64개의 홉), 1=2 바이트 (65,000개의 ID, 최대 32개의 홉), 2=3 바이트 (16백만 개의 ID, 최대 21개의 홉). v1.13 및 이전 버전의 펌웨어는 다중 바이트 경로를 지원하지 않으며, 네트워크가 v1.14 이상으로 업그레이드되면 한 번만 감지합니다.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => '플러드 TX 지연';
+35
View File
@@ -2683,6 +2683,41 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID\'s, tot 64 hops), 1=2 bytes (65.000 ID\'s, tot 32 hops), 2=3 bytes (16 miljoen ID\'s, tot 21 hops). Versies 1.13 en ouder gebruiken multi-byte paden alleen na het activeren van het netwerk.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Vertraging bij Flood TX';
+35
View File
@@ -2716,6 +2716,41 @@ class AppLocalizationsPl extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bity wykorzystywane do kodowania identyfikatora tego urządzenia w tagach ścieżek/detekcji pętli. 0=1 bity (256 identyfikatorów, do 64 skoków), 1=2 bity (65 000 identyfikatorów, do 32 skoków), 2=3 bity (1 600 000 identyfikatorów, do 21 skoków). Wersje 1.13 i wcześniejsze nie obsługują ścieżek wielobitowych wykrywają tylko jedną, gdy sieć jest w wersji 1.14 lub nowszej.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Opóźnienie w Flood, TX';
+35
View File
@@ -2701,6 +2701,41 @@ class AppLocalizationsPt extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Bytes utilizados para codificar o ID deste repetidor nas tags de caminho/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). As versões 1.13 e anteriores do firmware não suportam caminhos multi-byte — apenas funcionam uma vez após a ativação da rede (a partir da versão 1.14+).';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Atraso na entrega em Flood, TX';
+35
View File
@@ -2706,6 +2706,41 @@ class AppLocalizationsRu extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Байты, используемые для кодирования идентификатора этого ретранслятора в тегах для обнаружения потоков/циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (1 600 000 идентификаторов, до 21 перехода). Версии прошивки v1.13 и более ранние версии не поддерживают многобайтовые пути — они поднимаются только после того, как ваша сеть будет обновлена до версии v1.14 и выше.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Задержка в работе системы Flood TX';
+35
View File
@@ -2687,6 +2687,41 @@ class AppLocalizationsSk extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Byty použité na zakódovanie ID tohto opakovača v tagoch pre trasu/detekciu slučky. 0 = 1 bytu (256 ID, až 64 skokov), 1 = 2 byty (65 000 ID, až 32 skokov), 2 = 3 byty (16 miliónov ID, až 21 skokov). Verzie 1.13 a staršie nepodporujú viacbytové trasy fungujú len, keď je sieť aktivovaná.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Zpoždanie v Flood, TX';
+35
View File
@@ -2683,6 +2683,41 @@ class AppLocalizationsSl extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Biti, ki so bila uporabljena za kodiranje ID-ja tega releja v oznakah za zaznavanje pot/kroga, imajo naslednje velikosti: 0=1 bit (256 ID-jev, do 64 skokov), 1=2 biti (65.000 ID-jev, do 32 skokov), 2=3 biti (16 milijonov ID-jev, do 21 skokov). V različicah 1.13 in starejših se ustvarjajo večbitne poti vendar se to zgodi šele, ko je omrežje vklopljeno v različicah 1.14 in kasnejših.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Zatemnitevanje zaradi poplav v Texasu';
+35
View File
@@ -2671,6 +2671,41 @@ class AppLocalizationsSv extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Byte används för att koda denna repeaters ID i taggar för att upptäcka loopar/flödesvägar. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Versioner 1.13 och äldre har stöd för multi-byte-vägar endast en gång när nätverket är aktiverat (från och med version 1.14).';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Försening i Flood TX';
+35
View File
@@ -2703,6 +2703,41 @@ class AppLocalizationsUk extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах для виявлення потоків/петлі. 0=1 байт (256 ідентифікаторів, до 64 перехідів), 1=2 байти (65 000 ідентифікаторів, до 32 перехідів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Версії 1.13 та старіші не підтримують багатобайтні шляхи — вони активуються лише після того, як мережа буде оновлена до версії 1.14+.';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => 'Затримка у Flood, штат Техас';
+35
View File
@@ -2558,6 +2558,41 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_pathHashModeHelper =>
'用于编码此复用器的 ID 的字节数,在“洪流路径/环检测”标签中使用。 0=1 字节(256 个 ID,最多 64 个跳跃),1=2 字节(65K 个 ID,最多 32 个跳跃),2=3 字节(16M 个 ID,最多 21 个跳跃)。 v1.13 及更早版本的固件会使用多字节路径——只有在您的网络升级到 v1.14 或更高版本后才会生效。';
@override
String get repeater_keySettings => 'Change Identity Keys';
@override
String get repeater_keySettingsSubtitle =>
'Change the public/private keypair';
@override
String get repeater_prvKey => 'Private key';
@override
String get repeater_prvKeyHelper =>
'A new private key for the repeater, a 128-character hex string.';
@override
String get repeater_generatePrvKey => 'Generate a random keypair';
@override
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
@override
String get repeater_pubKey => 'Public key';
@override
String get repeater_pubKeyHelper =>
'This is the public key that goes with the generated private key. You can\'t set this directly.';
@override
String get repeater_pubKeyPrefix => 'Desired prefix';
@override
String repeater_pubKeyPrefixHelper(int tries) {
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
}
@override
String get repeater_txDelay => '洪水(德克萨斯州)延误';
+3 -12
View File
@@ -1,8 +1,8 @@
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart' as crypto;
import 'package:meshcore_open/utils/keys.dart';
import 'channel.dart';
@@ -37,11 +37,7 @@ class Community {
/// Generate a new community with a random 32-byte secret
factory Community.create({required String id, required String name}) {
final random = Random.secure();
final secret = Uint8List(32);
for (int i = 0; i < 32; i++) {
secret[i] = random.nextInt(256);
}
final secret = randomBytes(32);
return Community(
id: id,
name: name,
@@ -210,12 +206,7 @@ class Community {
/// Create a copy of this community with a regenerated random secret
Community withRegeneratedSecret() {
final random = Random.secure();
final newSecret = Uint8List(32);
for (int i = 0; i < 32; i++) {
newSecret[i] = random.nextInt(256);
}
return withNewSecret(newSecret);
return withNewSecret(randomBytes(32));
}
/// Extract secret from QR data (for updating existing community)
+3 -11
View File
@@ -1,9 +1,9 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:meshcore_open/storage/channel_message_store.dart';
import 'package:meshcore_open/utils/keys.dart';
import 'package:meshcore_open/utils/platform_info.dart';
import 'package:meshcore_open/widgets/app_bar.dart';
import 'package:provider/provider.dart';
@@ -938,11 +938,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
);
return;
}
final random = Random.secure();
final psk = Uint8List(16);
for (int i = 0; i < 16; i++) {
psk[i] = random.nextInt(256);
}
final psk = randomBytes(16);
Navigator.pop(sheetContext);
await connector.setChannel(
nextIndex,
@@ -1571,11 +1567,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
icon: const Icon(Icons.casino),
tooltip: sheetContext.l10n.channels_generateRandomPsk,
onPressed: () {
final random = Random.secure();
final bytes = Uint8List(16);
for (int i = 0; i < 16; i++) {
bytes[i] = random.nextInt(256);
}
final bytes = randomBytes(16);
pskController.text = Channel.formatPskHex(bytes);
},
),
+173 -1
View File
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../l10n/l10n.dart';
import '../models/contact.dart';
@@ -11,6 +12,7 @@ import '../services/storage_service.dart';
import '../theme/mesh_theme.dart';
import '../widgets/mesh_ui.dart';
import '../widgets/routing_sheet.dart';
import '../utils/keys.dart';
import '../helpers/snack_bar_builder.dart';
class RepeaterSettingsScreen extends StatefulWidget {
@@ -44,6 +46,7 @@ enum _SettingField {
advertInterval,
floodAdvertInterval,
pathHashMode,
prvKey,
txDelay,
directTxDelay,
intThresh,
@@ -109,6 +112,8 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
bool _refreshingIntThresh = false;
bool _refreshingAgcResetInterval = false;
bool _runningAction = false;
bool _searchingForKeyPair = false;
bool _stopSearchingForKeyPair = false;
StreamSubscription<Uint8List>? _frameSubscription;
RepeaterCommandService? _commandService;
@@ -154,6 +159,11 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
// Advanced
int _pathHashMode = 0; // 0-2
final TextEditingController _prvKeyController = TextEditingController();
final TextEditingController _pubKeyController = TextEditingController();
final TextEditingController _prvKeyPrefixController = TextEditingController();
final KeyPairSearcher _keyPairSearcher = KeyPairSearcher();
final int _searchChunkTime = 30; // time in ms to search before yielding
final TextEditingController _txDelayController = TextEditingController();
final TextEditingController _directTxDelayController =
TextEditingController();
@@ -206,6 +216,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
_txDelayController.dispose();
_directTxDelayController.dispose();
_intThreshController.dispose();
_prvKeyController.dispose();
_pubKeyController.dispose();
_prvKeyPrefixController.dispose();
_stopSearchingForKeyPair = true;
super.dispose();
}
@@ -860,6 +874,12 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
command: 'set path.hash.mode $_pathHashMode',
));
}
if (_dirtyFields.contains(_SettingField.prvKey)) {
final v = _prvKeyController.text.trim();
if (v != "") {
pending.add((field: _SettingField.prvKey, command: 'set prv.key $v'));
}
}
if (_dirtyFields.contains(_SettingField.txDelay) &&
_txDelayController.text.isNotEmpty) {
final v = double.tryParse(_txDelayController.text.trim());
@@ -1005,6 +1025,36 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
}
}
void _startSearchingForKeyPair() async {
if (_searchingForKeyPair) return;
setState(() {
_searchingForKeyPair = true;
_stopSearchingForKeyPair = false;
});
_searchForKeyPair();
}
void _searchForKeyPair() async {
if (_stopSearchingForKeyPair) {
if (mounted) setState(() => _searchingForKeyPair = false);
return;
}
final String prefix = _prvKeyPrefixController.text.trim();
MCKeyPair? keys = await _keyPairSearcher.findMatchingKeyPair(
prefix,
_searchChunkTime,
);
if (keys == null) {
// Ran out of time; schedule another attempt.
Timer.run(_searchForKeyPair);
} else {
setState(() => _searchingForKeyPair = false);
_prvKeyController.text = pubKeyToHex(keys.private);
_pubKeyController.text = pubKeyToHex(keys.public);
_markChanged(_SettingField.prvKey);
}
}
Widget _buildInlineRefreshButton({
required bool isRefreshing,
required VoidCallback onRefresh,
@@ -1069,6 +1119,7 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
_buildOwnerInfoCard(),
_buildActionsCard(),
_buildAdvancedCard(),
_buildKeysCard(),
const SizedBox(height: 16),
_buildDangerZoneCard(),
],
@@ -1984,6 +2035,127 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
);
}
Widget _buildKeysCard() {
final l10n = context.l10n;
return MeshCard(
child: ExpansionTile(
leading: const Icon(Icons.vpn_key),
title: Text(
l10n.repeater_keySettings,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
subtitle: Text(l10n.repeater_keySettingsSubtitle),
childrenPadding: const EdgeInsets.fromLTRB(0, 8, 0, 4),
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
controller: _prvKeyController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[0-9a-fA-F]")),
],
decoration: InputDecoration(
labelText: l10n.repeater_prvKey,
helperText: l10n.repeater_prvKeyHelper,
helperMaxLines: 3,
border: const OutlineInputBorder(),
),
onChanged: (_) {
_pubKeyController.text = "";
if (_prvKeyController.text.isNotEmpty) {
_markChanged(_SettingField.prvKey);
}
},
),
),
const SizedBox(width: 8),
Padding(
padding: const EdgeInsets.only(top: 8),
child: _searchingForKeyPair
? IconButton(
icon: const Icon(Icons.cancel, size: 24),
onPressed: (() => _stopSearchingForKeyPair = true),
tooltip: l10n.repeater_stopGeneratingPrvKey,
visualDensity: VisualDensity.compact,
)
: IconButton(
icon: const Icon(Icons.casino_rounded, size: 24),
onPressed: () {
_startSearchingForKeyPair();
},
tooltip: l10n.repeater_generatePrvKey,
visualDensity: VisualDensity.compact,
),
),
],
),
const SizedBox(height: 16),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
readOnly: true,
controller: _pubKeyController,
style: TextStyle(
color: Theme.of(
context,
).textTheme.headlineSmall?.color?.withValues(alpha: 0.6),
),
decoration: InputDecoration(
labelText: l10n.repeater_pubKey,
helperText: l10n.repeater_pubKeyHelper,
helperMaxLines: 3,
border: const OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Padding(
padding: const EdgeInsets.only(top: 12),
child: SizedBox(
width: 24,
height: 24,
child: _searchingForKeyPair
? CircularProgressIndicator(strokeWidth: 2)
: null,
),
),
const SizedBox(width: 8),
],
),
const SizedBox(height: 16),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
controller: _prvKeyPrefixController,
maxLength: 4,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[0-9a-fA-F]")),
],
onChanged: (_) => setState(() => {}), // updates helper text
decoration: InputDecoration(
labelText: l10n.repeater_pubKeyPrefix,
helperText: l10n.repeater_pubKeyPrefixHelper(
pow(16, _prvKeyPrefixController.text.length).toInt(),
),
helperMaxLines: 3,
border: const OutlineInputBorder(),
),
),
),
const SizedBox(width: 48),
],
),
],
),
);
}
Widget _buildDangerZoneCard() {
final l10n = context.l10n;
return MeshCard(
+87
View File
@@ -0,0 +1,87 @@
import 'dart:async';
import 'dart:math';
import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:cryptography/cryptography.dart';
Uint8List randomBytes(int length) {
final random = Random.secure();
final bytes = Uint8List(length);
for (int i = 0; i < length; i++) {
bytes[i] = random.nextInt(256);
}
return bytes;
}
class MCKeyPair {
final Uint8List public;
final Uint8List private;
MCKeyPair(this.public, this.private);
}
Future<MCKeyPair> generateKeyPair() async {
final algo = Ed25519();
final seed = randomBytes(32);
final SimpleKeyPair keys = await algo.newKeyPairFromSeed(seed);
final Uint8List pubKeyBytes = Uint8List.fromList(
(await keys.extractPublicKey()).bytes,
);
// Ed25519 gives 32-byte public and private keys, but MeshCore
// uses an expanded 64-byte private key, so we have to make it
// ourselves. Luckily, we can do it such that it has the same public
// key, which we'd otherwise be unable to compute, since although the
// crypto library implements the elliptic curve computations we need,
// it doesn't make them available.
final Uint8List hash = Uint8List.fromList((await Sha512().hash(seed)).bytes);
hash[0] &= 248; // Clamp the scalar. This keeps the chosen point in the
hash[31] &= 63; // large elliptic curve subgroup, and is demanded both by
hash[31] |= 64; // Ed25519 and by the MeshCore repeater key validation code.
return MCKeyPair(pubKeyBytes, hash);
}
class _KeyHashPrefix {
Uint8List _bytes = Uint8List(0);
Uint8List _mask = Uint8List(0);
_KeyHashPrefix(String prefix) {
final bool lengthIsOdd = (prefix.length % 2 == 1) ? true : false;
final String decodableString = lengthIsOdd ? "${prefix}0" : prefix;
_bytes = hex.decoder.convert(decodableString);
_mask = Uint8List(_bytes.length);
for (var i = 0; i < _mask.length; i++) {
_mask[i] = 0xff;
}
if (lengthIsOdd) {
_mask[_mask.length - 1] = 0xf0;
}
}
bool matches(List<int> keyBytes) {
if (keyBytes.length < _bytes.length) {
return false;
}
for (var i = 0; i < _bytes.length; i++) {
if (keyBytes[i] & _mask[i] != _bytes[i]) {
return false;
}
}
return true;
}
}
class KeyPairSearcher {
Future<MCKeyPair?> findMatchingKeyPair(
String prefixStr,
int maxMilliseconds,
) async {
final int now = DateTime.now().millisecondsSinceEpoch;
final int endTime = now + maxMilliseconds;
final _KeyHashPrefix keyHashPrefix = _KeyHashPrefix(prefixStr);
while (DateTime.now().millisecondsSinceEpoch < endTime) {
final MCKeyPair keys = await generateKeyPair();
if (keyHashPrefix.matches(keys.public)) {
return keys;
}
}
return null;
}
}
@@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import cryptography_flutter
import flutter_blue_plus_darwin
import flutter_local_notifications
import mobile_scanner
@@ -15,6 +16,7 @@ import sqflite_darwin
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
CryptographyFlutterPlugin.register(with: registry.registrar(forPlugin: "CryptographyFlutterPlugin"))
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
+3
View File
@@ -73,6 +73,9 @@ dependencies:
ml_dataframe: ^1.0.0
llamadart: ^0.8.0
flutter_langdetect: ^0.0.1
cryptography: ^2.9.0
cryptography_flutter: ^2.3.4
convert: ^3.1.2
hooks:
user_defines:
+187 -17
View File
@@ -15,13 +15,33 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"de": [
"settings_regionFetchRegions",
"settings_regionFetchRegionsFail",
"settings_regionFetchRegionsAlreadyExists"
"settings_regionFetchRegionsAlreadyExists",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"es": [
@@ -40,7 +60,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"fr": [
@@ -59,7 +89,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"hu": [
@@ -78,7 +118,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"it": [
@@ -97,7 +147,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"ja": [
@@ -116,7 +176,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"ko": [
@@ -135,7 +205,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"nl": [
@@ -154,7 +234,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"pl": [
@@ -173,7 +263,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"pt": [
@@ -192,7 +292,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"ru": [
@@ -211,7 +321,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"sk": [
@@ -230,7 +350,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"sl": [
@@ -249,7 +379,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"sv": [
@@ -268,7 +408,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"uk": [
@@ -287,7 +437,17 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
],
"zh": [
@@ -306,6 +466,16 @@
"channels_regionSetTo",
"channels_regionNotSet",
"channels_regionSelect_Title",
"channels_clearRegion"
"channels_clearRegion",
"repeater_keySettings",
"repeater_keySettingsSubtitle",
"repeater_prvKey",
"repeater_prvKeyHelper",
"repeater_generatePrvKey",
"repeater_stopGeneratingPrvKey",
"repeater_pubKey",
"repeater_pubKeyHelper",
"repeater_pubKeyPrefix",
"repeater_pubKeyPrefixHelper"
]
}