diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 5a72f5dc2..cc3e06131 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -381,6 +381,7 @@ class DialogTextField extends StatelessWidget { final FocusNode? focusNode; final TextInputType? keyboardType; final List? inputFormatters; + final int? maxLength; static const kUsernameTitle = 'Username'; static const kUsernameIcon = Icon(Icons.account_circle_outlined); @@ -398,6 +399,7 @@ class DialogTextField extends StatelessWidget { this.hintText, this.keyboardType, this.inputFormatters, + this.maxLength, required this.title, required this.controller}) : super(key: key); @@ -424,6 +426,7 @@ class DialogTextField extends StatelessWidget { obscureText: obscureText, keyboardType: keyboardType, inputFormatters: inputFormatters, + maxLength: maxLength, ), ), ], @@ -681,6 +684,7 @@ class PasswordWidget extends StatefulWidget { this.hintText, this.errorText, this.title, + this.maxLength, }) : super(key: key); final TextEditingController controller; @@ -689,6 +693,7 @@ class PasswordWidget extends StatefulWidget { final String? hintText; final String? errorText; final String? title; + final int? maxLength; @override State createState() => _PasswordWidgetState(); @@ -751,6 +756,7 @@ class _PasswordWidgetState extends State { obscureText: !_passwordVisible, errorText: widget.errorText, focusNode: _focusNode, + maxLength: widget.maxLength, ); } } @@ -2245,6 +2251,7 @@ void changeUnlockPinDialog(String oldPin, Function() callback) { final confirmController = TextEditingController(text: oldPin); String? pinErrorText; String? confirmationErrorText; + final maxLength = bind.mainMaxEncryptLen(); gFFI.dialogManager.show((setState, close, context) { submit() async { pinErrorText = null; @@ -2278,12 +2285,14 @@ void changeUnlockPinDialog(String oldPin, Function() callback) { controller: pinController, obscureText: true, errorText: pinErrorText, + maxLength: maxLength, ), DialogTextField( title: translate('Confirmation'), controller: confirmController, obscureText: true, errorText: confirmationErrorText, + maxLength: maxLength, ) ], ).marginOnly(bottom: 12), diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 7760f7a03..15ca8932d 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -1200,6 +1200,7 @@ class MyGroupPeerCard extends BasePeerCard { } void _rdpDialog(String id) async { + final maxLength = bind.mainMaxEncryptLen(); final port = await bind.mainGetPeerOption(id: id, key: 'rdp_port'); final username = await bind.mainGetPeerOption(id: id, key: 'rdp_username'); final portController = TextEditingController(text: port); @@ -1288,6 +1289,7 @@ void _rdpDialog(String id) async { Expanded( child: Obx(() => TextField( obscureText: secure.value, + maxLength: maxLength, decoration: InputDecoration( labelText: isDesktop ? null diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 31a8e1374..dee990af4 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -857,6 +857,7 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async { // SpecialCharacterValidationRule(), MinCharactersValidationRule(8), ]; + final maxLength = bind.mainMaxEncryptLen(); gFFI.dialogManager.show((setState, close, context) { submit() { @@ -915,6 +916,7 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async { errMsg0 = ''; }); }, + maxLength: maxLength, ), ), ], @@ -941,6 +943,7 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async { errMsg1 = ''; }); }, + maxLength: maxLength, ), ), ], diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 0e5fac4ca..041b5569e 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -2512,6 +2512,7 @@ void changeSocks5Proxy() async { : Icons.visibility))), controller: pwdController, enabled: !isOptFixed, + maxLength: bind.mainMaxEncryptLen(), )), ), ], diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d0b908b55..c118070dd 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -39,7 +39,7 @@ pub const REG_INTERVAL: i64 = 15_000; pub const COMPRESS_LEVEL: i32 = 3; const SERIAL: i32 = 3; const PASSWORD_ENC_VERSION: &str = "00"; -const ENCRYPT_MAX_LEN: usize = 128; +pub const ENCRYPT_MAX_LEN: usize = 128; // used for password, pin, etc, not for all #[cfg(target_os = "macos")] lazy_static::lazy_static! { diff --git a/libs/hbb_common/src/password_security.rs b/libs/hbb_common/src/password_security.rs index 49a2d4d94..5c04cc97b 100644 --- a/libs/hbb_common/src/password_security.rs +++ b/libs/hbb_common/src/password_security.rs @@ -89,11 +89,11 @@ pub fn encrypt_str_or_original(s: &str, version: &str, max_len: usize) -> String log::error!("Duplicate encryption!"); return s.to_owned(); } - if s.bytes().len() > max_len { + if s.chars().count() > max_len { return String::default(); } if version == "00" { - if let Ok(s) = encrypt(s.as_bytes(), max_len) { + if let Ok(s) = encrypt(s.as_bytes()) { return version.to_owned() + &s; } } @@ -130,7 +130,7 @@ pub fn encrypt_vec_or_original(v: &[u8], version: &str, max_len: usize) -> Vec (Vec, boo (v.to_owned(), false, !v.is_empty()) } -fn encrypt(v: &[u8], max_len: usize) -> Result { - if !v.is_empty() && v.len() <= max_len { +fn encrypt(v: &[u8]) -> Result { + if !v.is_empty() { symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original)) } else { Err(()) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 8cc475316..72b0e5b37 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2282,6 +2282,10 @@ pub fn main_clear_trusted_devices() { clear_trusted_devices() } +pub fn main_max_encrypt_len() -> SyncReturn { + SyncReturn(max_encrypt_len()) +} + pub fn session_request_new_display_init_msgs(session_id: SessionID, display: usize) { if let Some(session) = sessions::get_session_by_session_id(&session_id) { session.request_init_msgs(display); diff --git a/src/ipc.rs b/src/ipc.rs index 3f093c758..6d5e247d4 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -928,16 +928,23 @@ pub fn set_permanent_password(v: String) -> ResultType<()> { pub fn set_unlock_pin(v: String, translate: bool) -> ResultType<()> { let v = v.trim().to_owned(); let min_len = 4; - if !v.is_empty() && v.len() < min_len { - let err = if translate { - crate::lang::translate( - "Requires at least {".to_string() + &format!("{min_len}") + "} characters", - ) - } else { - // Sometimes, translated can't show normally in command line - format!("Requires at least {} characters", min_len) - }; - bail!(err); + let max_len = crate::ui_interface::max_encrypt_len(); + let len = v.chars().count(); + if !v.is_empty() { + if len < min_len { + let err = if translate { + crate::lang::translate( + "Requires at least {".to_string() + &format!("{min_len}") + "} characters", + ) + } else { + // Sometimes, translated can't show normally in command line + format!("Requires at least {} characters", min_len) + }; + bail!(err); + } + if len > max_len { + bail!("No more than {max_len} characters"); + } } Config::set_unlock_pin(&v); set_config("unlock-pin", v) diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 8489179ba..aad968bb4 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1495,3 +1495,8 @@ pub fn clear_trusted_devices() { #[cfg(not(any(target_os = "android", target_os = "ios")))] ipc::clear_trusted_devices(); } + +#[cfg(feature = "flutter")] +pub fn max_encrypt_len() -> usize { + hbb_common::config::ENCRYPT_MAX_LEN +}