From 7432689bb1bb2705d8e2765884805bc9cf0318f1 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 29 Aug 2022 16:01:25 +0530 Subject: [PATCH 1/2] Support sending logs on password verification screen --- lib/core/configuration.dart | 11 +++++ lib/ui/account/password_reentry_page.dart | 31 +++++++++++++- lib/utils/validator_util.dart | 50 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 lib/utils/validator_util.dart diff --git a/lib/core/configuration.dart b/lib/core/configuration.dart index f705dd54c..0cdfc3727 100644 --- a/lib/core/configuration.dart +++ b/lib/core/configuration.dart @@ -29,6 +29,7 @@ import 'package:photos/services/memories_service.dart'; import 'package:photos/services/search_service.dart'; import 'package:photos/services/sync_service.dart'; import 'package:photos/utils/crypto_util.dart'; +import 'package:photos/utils/validator_util.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; import 'package:wakelock/wakelock.dart'; @@ -242,12 +243,18 @@ class Configuration { String password, KeyAttributes attributes, ) async { + _logger.info('Start decryptAndSaveSecrets'); + validatePreVerificationStateCheck( + attributes, password, getEncryptedToken()); + _logger.info('state validation done'); final kek = await CryptoUtil.deriveKey( utf8.encode(password), Sodium.base642bin(attributes.kekSalt), attributes.memLimit, attributes.opsLimit, ); + + _logger.info('user-key done'); Uint8List key; try { key = CryptoUtil.decryptSync( @@ -256,20 +263,24 @@ class Configuration { Sodium.base642bin(attributes.keyDecryptionNonce), ); } catch (e) { + _logger.severe('master-key failed, incorrect password?'); throw Exception("Incorrect password"); } + _logger.info("master-key done"); await setKey(Sodium.bin2base64(key)); final secretKey = CryptoUtil.decryptSync( Sodium.base642bin(attributes.encryptedSecretKey), key, Sodium.base642bin(attributes.secretKeyDecryptionNonce), ); + _logger.info("secret-key done"); await setSecretKey(Sodium.bin2base64(secretKey)); final token = CryptoUtil.openSealSync( Sodium.base642bin(getEncryptedToken()), Sodium.base642bin(attributes.publicKey), secretKey, ); + _logger.info('appToken done'); await setToken( Sodium.bin2base64(token, variant: Sodium.base64VariantUrlsafe), ); diff --git a/lib/ui/account/password_reentry_page.dart b/lib/ui/account/password_reentry_page.dart index 30d51e138..ad89437d0 100644 --- a/lib/ui/account/password_reentry_page.dart +++ b/lib/ui/account/password_reentry_page.dart @@ -3,10 +3,13 @@ import 'package:logging/logging.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/event_bus.dart'; import 'package:photos/events/subscription_purchased_event.dart'; +import 'package:photos/models/key_attributes.dart'; import 'package:photos/ui/account/recovery_page.dart'; +import 'package:photos/ui/common/dialogs.dart'; import 'package:photos/ui/common/dynamic_fab.dart'; import 'package:photos/ui/home_widget.dart'; import 'package:photos/utils/dialog_util.dart'; +import 'package:photos/utils/email_util.dart'; class PasswordReentryPage extends StatefulWidget { const PasswordReentryPage({Key key}) : super(key: key); @@ -16,6 +19,7 @@ class PasswordReentryPage extends StatefulWidget { } class _PasswordReentryPageState extends State { + final _logger = Logger((_PasswordReentryPageState).toString()); final _passwordController = TextEditingController(); final FocusNode _passwordFocusNode = FocusNode(); String email; @@ -72,9 +76,26 @@ class _PasswordReentryPageState extends State { Configuration.instance.getKeyAttributes(), ); } catch (e, s) { - Logger("PRP").severe("Password verification failed", e, s); + _logger.severe("Password verification failed", e, s); await dialog.hide(); - showErrorDialog(context, "Incorrect password", "Please try again"); + + var dialogUserChoice = await showChoiceDialog( + context, + "Incorrect password", + "Please try again", + firstAction: "Contact Support", + firstActionColor: Theme.of(context).colorScheme.primary, + secondAction: "Ok", + secondActionColor: Theme.of(context).colorScheme.primary, + ); + if (dialogUserChoice == DialogUserChoice.firstChoice) { + await sendLogs( + context, + "Contact support", + "support@ente.io", + postShare: () {}, + ); + } return; } await dialog.hide(); @@ -231,4 +252,10 @@ class _PasswordReentryPageState extends State { ], ); } + + void validatePreVerificationState(KeyAttributes keyAttributes) { + if (keyAttributes == null) { + throw Exception("Key Attributes can not be null"); + } + } } diff --git a/lib/utils/validator_util.dart b/lib/utils/validator_util.dart new file mode 100644 index 000000000..cd6cc41ee --- /dev/null +++ b/lib/utils/validator_util.dart @@ -0,0 +1,50 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:logging/logging.dart'; +import 'package:photos/models/key_attributes.dart'; + +Logger _logger = Logger("Validator"); + +void validatePreVerificationStateCheck( + KeyAttributes keyAttr, + String password, + String encryptedToken, +) { + nullOrEmptyArgCheck(encryptedToken, "encryptedToken"); + nullOrEmptyArgCheck(password, "userPassword"); + if (keyAttr == null) { + throw ArgumentError("key Attributes can not be null"); + } + nullOrEmptyArgCheck(keyAttr.kekSalt, "keySalt"); + nullOrEmptyArgCheck(keyAttr.encryptedKey, "encryptedKey"); + nullOrEmptyArgCheck(keyAttr.keyDecryptionNonce, "keyDecryptionNonce"); + nullOrEmptyArgCheck(keyAttr.encryptedSecretKey, "encryptedSecretKey"); + nullOrEmptyArgCheck( + keyAttr.secretKeyDecryptionNonce, + "secretKeyDecryptionNonce", + ); + nullOrEmptyArgCheck(keyAttr.publicKey, "publicKey"); + if ((keyAttr.memLimit ?? 0) <= 0 || (keyAttr.opsLimit ?? 0) <= 0) { + throw ArgumentError("Key mem/OpsLimit can not be null or <0"); + } + // check password encoding issues + try { + Uint8List passwordL = utf8.encode(password); + try { + utf8.decode(passwordL); + } catch (e) { + _logger.severe("CRITICAL: password decode failed", e); + rethrow; + } + } catch (e) { + _logger.severe('CRITICAL: password encode failed'); + rethrow; + } +} + +void nullOrEmptyArgCheck(String value, String name) { + if (value == null || value.isEmpty) { + throw ArgumentError("Critical: $name is nullOrEmpty"); + } +} From bb0af1c795a404258c3ab12c7c05c423dceff47c Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 29 Aug 2022 16:02:17 +0530 Subject: [PATCH 2/2] bump version code to 0.6.28+358 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 552de205e..ad635b170 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: ente photos application # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.6.26+356 +version: 0.6.28+358 environment: sdk: ">=2.10.0 <3.0.0"