Handle loginKey and keyDerivation err during SRP based login
This commit is contained in:
parent
8a8654bfc8
commit
f910da0964
18 changed files with 400 additions and 210 deletions
|
@ -7,8 +7,8 @@ enum InvalidReason {
|
|||
livePhotoVideoMissing,
|
||||
thumbnailMissing,
|
||||
unknown,
|
||||
|
||||
}
|
||||
|
||||
extension InvalidReasonExn on InvalidReason {
|
||||
bool get isLivePhotoErr =>
|
||||
this == InvalidReason.livePhotoToImageTypeChanged ||
|
||||
|
@ -73,6 +73,8 @@ class InvalidStateError extends AssertionError {
|
|||
|
||||
class KeyDerivationError extends Error {}
|
||||
|
||||
class LoginKeyDerivationError extends Error {}
|
||||
|
||||
class SrpSetupNotCompleteError extends Error {}
|
||||
|
||||
class SharingNotPermittedForFreeAccountsError extends Error {}
|
||||
|
|
1
lib/generated/intl/messages_cs.dart
generated
1
lib/generated/intl/messages_cs.dart
generated
|
@ -24,6 +24,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"addToHiddenAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Add to hidden album"),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("File types"),
|
||||
|
|
1
lib/generated/intl/messages_de.dart
generated
1
lib/generated/intl/messages_de.dart
generated
|
@ -442,6 +442,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Support kontaktieren"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Weiter"),
|
||||
"continueOnFreeTrial": MessageLookupByLibrary.simpleMessage(
|
||||
"Mit kostenloser Testversion fortfahren"),
|
||||
|
|
9
lib/generated/intl/messages_en.dart
generated
9
lib/generated/intl/messages_en.dart
generated
|
@ -441,6 +441,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Contact support"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"contents": MessageLookupByLibrary.simpleMessage("Contents"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Continue"),
|
||||
"continueOnFreeTrial":
|
||||
|
@ -879,6 +880,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("No hidden photos or videos"),
|
||||
"noImagesWithLocation":
|
||||
MessageLookupByLibrary.simpleMessage("No images with location"),
|
||||
"noInternetConnection":
|
||||
MessageLookupByLibrary.simpleMessage("No internet connection"),
|
||||
"noPhotosAreBeingBackedUpRightNow":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"No photos are being backed up right now"),
|
||||
|
@ -926,7 +929,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"paymentFailedTalkToProvider": m33,
|
||||
"paymentFailedWithReason": m34,
|
||||
"pendingSync": MessageLookupByLibrary.simpleMessage("Pending sync"),
|
||||
"people": MessageLookupByLibrary.simpleMessage("People"),
|
||||
"peopleUsingYourCode":
|
||||
MessageLookupByLibrary.simpleMessage("People using your code"),
|
||||
"permDeleteWarning": MessageLookupByLibrary.simpleMessage(
|
||||
|
@ -950,6 +952,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"playStoreFreeTrialValidTill": m35,
|
||||
"playstoreSubscription":
|
||||
MessageLookupByLibrary.simpleMessage("PlayStore subscription"),
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Please check your internet connection and try again."),
|
||||
"pleaseContactSupportAndWeWillBeHappyToHelp":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Please contact support@ente.io and we will be happy to help!"),
|
||||
|
@ -1120,7 +1125,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"Albums, file names, and types"),
|
||||
"searchHint4": MessageLookupByLibrary.simpleMessage("Location"),
|
||||
"searchHint5": MessageLookupByLibrary.simpleMessage(
|
||||
"Coming soon: Photo contents, faces"),
|
||||
"Coming soon: Faces & magic search ✨"),
|
||||
"searchHintText": MessageLookupByLibrary.simpleMessage(
|
||||
"Albums, months, days, years, ..."),
|
||||
"searchLocationEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
|
|
1
lib/generated/intl/messages_es.dart
generated
1
lib/generated/intl/messages_es.dart
generated
|
@ -398,6 +398,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Contactar con soporte"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Continuar"),
|
||||
"continueOnFreeTrial": MessageLookupByLibrary.simpleMessage(
|
||||
"Continuar con el plan gratuito"),
|
||||
|
|
66
lib/generated/intl/messages_fr.dart
generated
66
lib/generated/intl/messages_fr.dart
generated
|
@ -140,6 +140,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
|
||||
static String m41(endDate) => "Renouvellement le ${endDate}";
|
||||
|
||||
static String m64(count) =>
|
||||
"${Intl.plural(count, one: '${count} résultat trouvé', other: '${count} résultats trouvés')}";
|
||||
|
||||
static String m42(count) => "${count} sélectionné(s)";
|
||||
|
||||
static String m43(count, yourCount) =>
|
||||
|
@ -189,6 +192,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
static String m59(count) =>
|
||||
"${Intl.plural(count, zero: '0 jour', one: '1 jour', other: '${count} jours')}";
|
||||
|
||||
static String m65(endDate) => "Valable jusqu\'au ${endDate}";
|
||||
|
||||
static String m60(email) => "Vérifier ${email}";
|
||||
|
||||
static String m61(email) =>
|
||||
|
@ -223,6 +228,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Ajouter la localisation"),
|
||||
"addLocationButton": MessageLookupByLibrary.simpleMessage("Ajouter"),
|
||||
"addMore": MessageLookupByLibrary.simpleMessage("Ajouter Plus"),
|
||||
"addNew": MessageLookupByLibrary.simpleMessage("Ajouter un nouveau"),
|
||||
"addOnPageSubtitle": MessageLookupByLibrary.simpleMessage(
|
||||
"Détails des modules complémentaires"),
|
||||
"addOns":
|
||||
MessageLookupByLibrary.simpleMessage("Modules complémentaires"),
|
||||
"addPhotos": MessageLookupByLibrary.simpleMessage("Ajouter des photos"),
|
||||
"addSelected":
|
||||
MessageLookupByLibrary.simpleMessage("Ajouter la sélection"),
|
||||
|
@ -233,6 +243,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Ajouter à un album masqué"),
|
||||
"addViewer":
|
||||
MessageLookupByLibrary.simpleMessage("Ajouter un observateur"),
|
||||
"addYourPhotosNow": MessageLookupByLibrary.simpleMessage(
|
||||
"Ajoutez vos photos maintenant"),
|
||||
"addedAs": MessageLookupByLibrary.simpleMessage("Ajouté comme"),
|
||||
"addedBy": m1,
|
||||
"addedSuccessfullyTo": m2,
|
||||
|
@ -356,6 +368,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Paramètres de la sauvegarde"),
|
||||
"backupVideos":
|
||||
MessageLookupByLibrary.simpleMessage("Sauvegarde des vidéos"),
|
||||
"blackFridaySale":
|
||||
MessageLookupByLibrary.simpleMessage("Offre Black Friday"),
|
||||
"blog": MessageLookupByLibrary.simpleMessage("Blog"),
|
||||
"cachedData":
|
||||
MessageLookupByLibrary.simpleMessage("Données mises en cache"),
|
||||
|
@ -446,6 +460,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Contacter l\'assistance"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"contents": MessageLookupByLibrary.simpleMessage("Contenus"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Continuer"),
|
||||
"continueOnFreeTrial": MessageLookupByLibrary.simpleMessage(
|
||||
"Poursuivre avec la version d\'essai gratuite"),
|
||||
|
@ -510,7 +526,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"Ceci supprimera tous les albums vides. Ceci est utile lorsque vous voulez réduire l\'encombrement dans votre liste d\'albums."),
|
||||
"deleteAll": MessageLookupByLibrary.simpleMessage("Tout Supprimer"),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"Ce compte est lié à d\'autres applications ente, si vous en utilisez une.\\n\\nVos données téléchargées, dans toutes les applications ente, seront planifiées pour suppression, et votre compte sera définitivement supprimé."),
|
||||
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
|
||||
"Veuillez envoyer un e-mail à <warning>account-deletion@ente.io</warning> à partir de votre adresse e-mail enregistrée."),
|
||||
"deleteEmptyAlbums":
|
||||
|
@ -658,6 +674,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"exportLogs": MessageLookupByLibrary.simpleMessage("Exporter les logs"),
|
||||
"exportYourData":
|
||||
MessageLookupByLibrary.simpleMessage("Exportez vos données"),
|
||||
"faces": MessageLookupByLibrary.simpleMessage("Visages"),
|
||||
"failedToApplyCode": MessageLookupByLibrary.simpleMessage(
|
||||
"Impossible d\'appliquer le code"),
|
||||
"failedToCancel":
|
||||
|
@ -689,7 +706,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Ajouter une description..."),
|
||||
"fileSavedToGallery": MessageLookupByLibrary.simpleMessage(
|
||||
"Fichier enregistré dans la galerie"),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("File types"),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("Types de fichiers"),
|
||||
"fileTypesAndNames":
|
||||
MessageLookupByLibrary.simpleMessage("Types et noms de fichiers"),
|
||||
"filesBackedUpFromDevice": m19,
|
||||
"filesBackedUpInAlbum": m20,
|
||||
"filesDeleted":
|
||||
|
@ -729,6 +748,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Accorder la permission"),
|
||||
"groupNearbyPhotos": MessageLookupByLibrary.simpleMessage(
|
||||
"Grouper les photos à proximité"),
|
||||
"hearUsExplanation": MessageLookupByLibrary.simpleMessage(
|
||||
"Nous ne suivons pas les installations d\'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !"),
|
||||
"hearUsWhereTitle": MessageLookupByLibrary.simpleMessage(
|
||||
"Comment avez-vous entendu parler de Ente? (facultatif)"),
|
||||
"hidden": MessageLookupByLibrary.simpleMessage("Masqué"),
|
||||
"hide": MessageLookupByLibrary.simpleMessage("Masquer"),
|
||||
"hiding": MessageLookupByLibrary.simpleMessage("Masquage en cours..."),
|
||||
|
@ -810,6 +833,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"linkHasExpired":
|
||||
MessageLookupByLibrary.simpleMessage("Le lien a expiré"),
|
||||
"linkNeverExpires": MessageLookupByLibrary.simpleMessage("Jamais"),
|
||||
"livePhotos": MessageLookupByLibrary.simpleMessage("Photos en direct"),
|
||||
"loadMessage1": MessageLookupByLibrary.simpleMessage(
|
||||
"Vous pouvez partager votre abonnement avec votre famille"),
|
||||
"loadMessage2": MessageLookupByLibrary.simpleMessage(
|
||||
|
@ -876,7 +900,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Sécurité moyenne"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Modify your query, or try searching for"),
|
||||
"Modifiez votre requête, ou essayez de rechercher"),
|
||||
"moments": MessageLookupByLibrary.simpleMessage("Souvenirs"),
|
||||
"monthly": MessageLookupByLibrary.simpleMessage("Mensuel"),
|
||||
"moveItem": m30,
|
||||
"moveToAlbum":
|
||||
|
@ -966,9 +991,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Supprimer définitivement"),
|
||||
"permanentlyDeleteFromDevice": MessageLookupByLibrary.simpleMessage(
|
||||
"Supprimer définitivement de l\'appareil ?"),
|
||||
"photoDescriptions":
|
||||
MessageLookupByLibrary.simpleMessage("Descriptions de la photo"),
|
||||
"photoGridSize":
|
||||
MessageLookupByLibrary.simpleMessage("Taille de la grille photo"),
|
||||
"photoSmallCase": MessageLookupByLibrary.simpleMessage("photo"),
|
||||
"photos": MessageLookupByLibrary.simpleMessage("Photos"),
|
||||
"photosAddedByYouWillBeRemovedFromTheAlbum":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Les photos ajoutées par vous seront retirées de l\'album"),
|
||||
|
@ -1136,12 +1164,36 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"scanThisBarcodeWithnyourAuthenticatorApp":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Scannez ce code-barres avec\nvotre application d\'authentification"),
|
||||
"searchAlbumsEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("Albums"),
|
||||
"searchByAlbumNameHint":
|
||||
MessageLookupByLibrary.simpleMessage("Nom de l\'album"),
|
||||
"searchByExamples": MessageLookupByLibrary.simpleMessage(
|
||||
"• Noms d\'albums (par exemple \"Caméra\")\n• Types de fichiers (par exemple \"Vidéos\", \".gif\")\n• Années et mois (par exemple \"2022\", \"Janvier\")\n• Vacances (par exemple \"Noël\")\n• Descriptions de photos (par exemple \"#fun\")"),
|
||||
"searchCaptionEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"Ajoutez des descriptions comme \"#trip\" dans les infos photo pour les retrouver ici plus rapidement"),
|
||||
"searchDatesEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"Recherche par date, mois ou année"),
|
||||
"searchFaceEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"Trouver toutes les photos d\'une personne"),
|
||||
"searchFileTypesAndNamesEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("Types et noms de fichiers"),
|
||||
"searchHint1": MessageLookupByLibrary.simpleMessage(
|
||||
"Recherche rapide, sur l\'appareil"),
|
||||
"searchHint2": MessageLookupByLibrary.simpleMessage(
|
||||
"Dates des photos, descriptions"),
|
||||
"searchHint3": MessageLookupByLibrary.simpleMessage(
|
||||
"Albums, noms de fichiers et types"),
|
||||
"searchHint4": MessageLookupByLibrary.simpleMessage("Emplacement"),
|
||||
"searchHint5": MessageLookupByLibrary.simpleMessage(
|
||||
"Bientôt: Visages & recherche magique ✨"),
|
||||
"searchHintText": MessageLookupByLibrary.simpleMessage(
|
||||
"Albums, mois, jours, années, ..."),
|
||||
"searchLocationEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"Grouper les photos qui sont prises dans un certain angle d\'une photo"),
|
||||
"searchPeopleEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"Invitez des gens, et vous verrez ici toutes les photos qu\'ils partagent"),
|
||||
"searchResultCount": m64,
|
||||
"security": MessageLookupByLibrary.simpleMessage("Sécurité"),
|
||||
"selectAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Sélectionner album"),
|
||||
|
@ -1400,6 +1452,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"upgrade": MessageLookupByLibrary.simpleMessage("Améliorer"),
|
||||
"uploadingFilesToAlbum": MessageLookupByLibrary.simpleMessage(
|
||||
"Envoi des fichiers vers l\'album..."),
|
||||
"upto50OffUntil4thDec": MessageLookupByLibrary.simpleMessage(
|
||||
"Jusqu\'à 50% de réduction, jusqu\'au 4ème déc."),
|
||||
"usableReferralStorageInfo": MessageLookupByLibrary.simpleMessage(
|
||||
"Le stockage utilisable est limité par votre offre actuelle. Le stockage excédentaire deviendra automatiquement utilisable lorsque vous mettez à niveau votre offre."),
|
||||
"usePublicLinksForPeopleNotOnEnte": MessageLookupByLibrary.simpleMessage(
|
||||
|
@ -1409,6 +1463,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"useSelectedPhoto": MessageLookupByLibrary.simpleMessage(
|
||||
"Utiliser la photo sélectionnée"),
|
||||
"usedSpace": MessageLookupByLibrary.simpleMessage("Mémoire utilisée"),
|
||||
"validTill": m65,
|
||||
"verificationFailedPleaseTryAgain":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"La vérification a échouée, veuillez réessayer"),
|
||||
|
@ -1426,8 +1481,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"verifyingRecoveryKey": MessageLookupByLibrary.simpleMessage(
|
||||
"Vérification de la clé de récupération..."),
|
||||
"videoSmallCase": MessageLookupByLibrary.simpleMessage("vidéo"),
|
||||
"videos": MessageLookupByLibrary.simpleMessage("Vidéos"),
|
||||
"viewActiveSessions": MessageLookupByLibrary.simpleMessage(
|
||||
"Afficher les sessions actives"),
|
||||
"viewAddOnButton": MessageLookupByLibrary.simpleMessage(
|
||||
"Afficher les modules complémentaires"),
|
||||
"viewAll": MessageLookupByLibrary.simpleMessage("Tout afficher"),
|
||||
"viewAllExifData": MessageLookupByLibrary.simpleMessage(
|
||||
"Visualiser toutes les données EXIF"),
|
||||
|
@ -1481,7 +1539,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"youHaveSuccessfullyFreedUp": m63,
|
||||
"yourAccountHasBeenDeleted":
|
||||
MessageLookupByLibrary.simpleMessage("Votre compte a été supprimé"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("Your map"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("Votre carte"),
|
||||
"yourPlanWasSuccessfullyDowngraded":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Votre plan a été rétrogradé avec succès"),
|
||||
|
|
1
lib/generated/intl/messages_it.dart
generated
1
lib/generated/intl/messages_it.dart
generated
|
@ -446,6 +446,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Contatta il supporto"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Continua"),
|
||||
"continueOnFreeTrial":
|
||||
MessageLookupByLibrary.simpleMessage("Continua la prova gratuita"),
|
||||
|
|
1
lib/generated/intl/messages_ko.dart
generated
1
lib/generated/intl/messages_ko.dart
generated
|
@ -24,6 +24,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"addToHiddenAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Add to hidden album"),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("File types"),
|
||||
|
|
1
lib/generated/intl/messages_nl.dart
generated
1
lib/generated/intl/messages_nl.dart
generated
|
@ -441,6 +441,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Contacteer klantenservice"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Doorgaan"),
|
||||
"continueOnFreeTrial": MessageLookupByLibrary.simpleMessage(
|
||||
"Doorgaan met gratis proefversie"),
|
||||
|
|
1
lib/generated/intl/messages_no.dart
generated
1
lib/generated/intl/messages_no.dart
generated
|
@ -33,6 +33,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Bekreft sletting av konto"),
|
||||
"confirmDeletePrompt": MessageLookupByLibrary.simpleMessage(
|
||||
"Ja, jeg ønsker å slette denne kontoen og all dataen dens permanent."),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"deleteAccount": MessageLookupByLibrary.simpleMessage("Slett konto"),
|
||||
"deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage(
|
||||
"Vi er lei oss for at du forlater oss. Gi oss gjerne en tilbakemelding så vi kan forbedre oss."),
|
||||
|
|
1
lib/generated/intl/messages_pl.dart
generated
1
lib/generated/intl/messages_pl.dart
generated
|
@ -50,6 +50,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("Powtórz hasło"),
|
||||
"contactSupport": MessageLookupByLibrary.simpleMessage(
|
||||
"Skontaktuj się z pomocą techniczną"),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Kontynuuj"),
|
||||
"createAccount": MessageLookupByLibrary.simpleMessage("Stwórz konto"),
|
||||
"createNewAccount":
|
||||
|
|
1
lib/generated/intl/messages_pt.dart
generated
1
lib/generated/intl/messages_pt.dart
generated
|
@ -136,6 +136,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"Confirme sua chave de recuperação"),
|
||||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Falar com o suporte"),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Continuar"),
|
||||
"copypasteThisCodentoYourAuthenticatorApp":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
|
|
42
lib/generated/intl/messages_zh.dart
generated
42
lib/generated/intl/messages_zh.dart
generated
|
@ -127,6 +127,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
|
||||
static String m41(endDate) => "在 ${endDate} 前续费";
|
||||
|
||||
static String m64(count) =>
|
||||
"${Intl.plural(count, other: '已找到 ${count} 个结果')}";
|
||||
|
||||
static String m42(count) => "已选择 ${count} 个";
|
||||
|
||||
static String m43(count, yourCount) => "选择了 ${count} 个 (您的 ${yourCount} 个)";
|
||||
|
@ -198,6 +201,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"addLocation": MessageLookupByLibrary.simpleMessage("添加地点"),
|
||||
"addLocationButton": MessageLookupByLibrary.simpleMessage("添加"),
|
||||
"addMore": MessageLookupByLibrary.simpleMessage("添加更多"),
|
||||
"addNew": MessageLookupByLibrary.simpleMessage("新建"),
|
||||
"addOnPageSubtitle": MessageLookupByLibrary.simpleMessage("附加组件详情"),
|
||||
"addOns": MessageLookupByLibrary.simpleMessage("附加组件"),
|
||||
"addPhotos": MessageLookupByLibrary.simpleMessage("添加照片"),
|
||||
|
@ -206,6 +210,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"addToEnte": MessageLookupByLibrary.simpleMessage("添加到 ente"),
|
||||
"addToHiddenAlbum": MessageLookupByLibrary.simpleMessage("添加到隐藏相册"),
|
||||
"addViewer": MessageLookupByLibrary.simpleMessage("添加查看者"),
|
||||
"addYourPhotosNow": MessageLookupByLibrary.simpleMessage("立即添加您的照片"),
|
||||
"addedAs": MessageLookupByLibrary.simpleMessage("已添加为"),
|
||||
"addedBy": m1,
|
||||
"addedSuccessfullyTo": m2,
|
||||
|
@ -308,6 +313,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("通过移动数据备份"),
|
||||
"backupSettings": MessageLookupByLibrary.simpleMessage("备份设置"),
|
||||
"backupVideos": MessageLookupByLibrary.simpleMessage("备份视频"),
|
||||
"blackFridaySale": MessageLookupByLibrary.simpleMessage("黑色星期五特惠"),
|
||||
"blog": MessageLookupByLibrary.simpleMessage("博客"),
|
||||
"cachedData": MessageLookupByLibrary.simpleMessage("缓存数据"),
|
||||
"calculating": MessageLookupByLibrary.simpleMessage("正在计算..."),
|
||||
|
@ -374,6 +380,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"contactFamilyAdmin": m9,
|
||||
"contactSupport": MessageLookupByLibrary.simpleMessage("联系支持"),
|
||||
"contactToManageSubscription": m10,
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("联系人"),
|
||||
"contents": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("继续"),
|
||||
"continueOnFreeTrial": MessageLookupByLibrary.simpleMessage("继续免费试用"),
|
||||
"convertToAlbum": MessageLookupByLibrary.simpleMessage("转换为相册"),
|
||||
|
@ -535,6 +543,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("此链接已过期。请选择新的过期时间或禁用链接过期。"),
|
||||
"exportLogs": MessageLookupByLibrary.simpleMessage("导出日志"),
|
||||
"exportYourData": MessageLookupByLibrary.simpleMessage("导出您的数据"),
|
||||
"faces": MessageLookupByLibrary.simpleMessage("人脸"),
|
||||
"failedToApplyCode": MessageLookupByLibrary.simpleMessage("无法应用代码"),
|
||||
"failedToCancel": MessageLookupByLibrary.simpleMessage("取消失败"),
|
||||
"failedToDownloadVideo": MessageLookupByLibrary.simpleMessage("视频下载失败"),
|
||||
|
@ -558,6 +567,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("无法将文件保存到相册"),
|
||||
"fileInfoAddDescHint": MessageLookupByLibrary.simpleMessage("添加说明..."),
|
||||
"fileSavedToGallery": MessageLookupByLibrary.simpleMessage("文件已保存到相册"),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("文件类型"),
|
||||
"fileTypesAndNames": MessageLookupByLibrary.simpleMessage("文件类型和名称"),
|
||||
"filesBackedUpFromDevice": m19,
|
||||
"filesBackedUpInAlbum": m20,
|
||||
"filesDeleted": MessageLookupByLibrary.simpleMessage("文件已删除"),
|
||||
|
@ -655,6 +666,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"linkExpiry": MessageLookupByLibrary.simpleMessage("链接过期"),
|
||||
"linkHasExpired": MessageLookupByLibrary.simpleMessage("链接已过期"),
|
||||
"linkNeverExpires": MessageLookupByLibrary.simpleMessage("永不"),
|
||||
"livePhotos": MessageLookupByLibrary.simpleMessage("实况照片"),
|
||||
"loadMessage1": MessageLookupByLibrary.simpleMessage("您可以与家庭分享您的订阅"),
|
||||
"loadMessage2":
|
||||
MessageLookupByLibrary.simpleMessage("到目前为止,我们已经保存了1 000多万个回忆"),
|
||||
|
@ -710,8 +722,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
MessageLookupByLibrary.simpleMessage("移动端, 网页端, 桌面端"),
|
||||
"moderateStrength": MessageLookupByLibrary.simpleMessage("中等"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Modify your query, or try searching for"),
|
||||
MessageLookupByLibrary.simpleMessage("修改您的查询,或尝试搜索"),
|
||||
"moments": MessageLookupByLibrary.simpleMessage("瞬间"),
|
||||
"monthly": MessageLookupByLibrary.simpleMessage("每月"),
|
||||
"moveItem": m30,
|
||||
"moveToAlbum": MessageLookupByLibrary.simpleMessage("移动到相册"),
|
||||
|
@ -784,8 +796,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"permanentlyDelete": MessageLookupByLibrary.simpleMessage("永久删除"),
|
||||
"permanentlyDeleteFromDevice":
|
||||
MessageLookupByLibrary.simpleMessage("要从设备中永久删除吗?"),
|
||||
"photoDescriptions": MessageLookupByLibrary.simpleMessage("照片说明"),
|
||||
"photoGridSize": MessageLookupByLibrary.simpleMessage("照片网格大小"),
|
||||
"photoSmallCase": MessageLookupByLibrary.simpleMessage("照片"),
|
||||
"photos": MessageLookupByLibrary.simpleMessage("照片"),
|
||||
"photosAddedByYouWillBeRemovedFromTheAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("您添加的照片将从相册中移除"),
|
||||
"pickCenterPoint": MessageLookupByLibrary.simpleMessage("选择中心点"),
|
||||
|
@ -908,10 +922,29 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"scanCode": MessageLookupByLibrary.simpleMessage("扫描代码"),
|
||||
"scanThisBarcodeWithnyourAuthenticatorApp":
|
||||
MessageLookupByLibrary.simpleMessage("用您的身份验证器应用\n扫描此条码"),
|
||||
"searchAlbumsEmptySection": MessageLookupByLibrary.simpleMessage("相册"),
|
||||
"searchByAlbumNameHint": MessageLookupByLibrary.simpleMessage("相册名称"),
|
||||
"searchByExamples": MessageLookupByLibrary.simpleMessage(
|
||||
"• 相册名称(例如“相机”)\n• 文件类型(例如“视频”、“.gif”)\n• 年份和月份(例如“2022”、“一月”)\n• 假期(例如“圣诞节”)\n• 照片说明(例如“#和女儿独居,好开心啊”)"),
|
||||
"searchCaptionEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"在照片信息中添加“#旅游”等描述,以便在此处快速找到它们"),
|
||||
"searchDatesEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("按日期搜索,月份或年份"),
|
||||
"searchFaceEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("查找一个人的所有照片"),
|
||||
"searchFileTypesAndNamesEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("文件类型和名称"),
|
||||
"searchHint1": MessageLookupByLibrary.simpleMessage("在设备上快速搜索"),
|
||||
"searchHint2": MessageLookupByLibrary.simpleMessage("照片日期、描述"),
|
||||
"searchHint3": MessageLookupByLibrary.simpleMessage("相册、文件名和类型"),
|
||||
"searchHint4": MessageLookupByLibrary.simpleMessage("位置"),
|
||||
"searchHint5": MessageLookupByLibrary.simpleMessage("即将到来:面部和魔法搜索✨"),
|
||||
"searchHintText": MessageLookupByLibrary.simpleMessage("相册,月,日,年,..."),
|
||||
"searchLocationEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("在照片的一定半径内拍摄的几组照片"),
|
||||
"searchPeopleEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("邀请他人,您将在此看到他们分享的所有照片"),
|
||||
"searchResultCount": m64,
|
||||
"security": MessageLookupByLibrary.simpleMessage("安全"),
|
||||
"selectAlbum": MessageLookupByLibrary.simpleMessage("选择相册"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
|
||||
|
@ -1113,6 +1146,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"upgrade": MessageLookupByLibrary.simpleMessage("升级"),
|
||||
"uploadingFilesToAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("正在将文件上传到相册..."),
|
||||
"upto50OffUntil4thDec":
|
||||
MessageLookupByLibrary.simpleMessage("最高五折优惠,直至12月4日。"),
|
||||
"usableReferralStorageInfo": MessageLookupByLibrary.simpleMessage(
|
||||
"可用存储空间受您当前计划的限制。 当您升级您的计划时,超出要求的存储空间将自动变为可用。"),
|
||||
"usePublicLinksForPeopleNotOnEnte":
|
||||
|
@ -1133,6 +1168,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"verifyingRecoveryKey":
|
||||
MessageLookupByLibrary.simpleMessage("正在验证恢复密钥..."),
|
||||
"videoSmallCase": MessageLookupByLibrary.simpleMessage("视频"),
|
||||
"videos": MessageLookupByLibrary.simpleMessage("视频"),
|
||||
"viewActiveSessions": MessageLookupByLibrary.simpleMessage("查看活动会话"),
|
||||
"viewAddOnButton": MessageLookupByLibrary.simpleMessage("查看附加组件"),
|
||||
"viewAll": MessageLookupByLibrary.simpleMessage("查看全部"),
|
||||
|
@ -1178,7 +1214,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"youHaveSuccessfullyFreedUp": m63,
|
||||
"yourAccountHasBeenDeleted":
|
||||
MessageLookupByLibrary.simpleMessage("您的账户已删除"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("Your map"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("您的地图"),
|
||||
"yourPlanWasSuccessfullyDowngraded":
|
||||
MessageLookupByLibrary.simpleMessage("您的计划已成功降级"),
|
||||
"yourPlanWasSuccessfullyUpgraded":
|
||||
|
|
24
lib/generated/l10n.dart
generated
24
lib/generated/l10n.dart
generated
|
@ -7995,10 +7995,10 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Coming soon: Photo contents, faces`
|
||||
/// `Coming soon: Faces & magic search ✨`
|
||||
String get searchHint5 {
|
||||
return Intl.message(
|
||||
'Coming soon: Photo contents, faces',
|
||||
'Coming soon: Faces & magic search ✨',
|
||||
name: 'searchHint5',
|
||||
desc: '',
|
||||
args: [],
|
||||
|
@ -8067,6 +8067,26 @@ class S {
|
|||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `No internet connection`
|
||||
String get noInternetConnection {
|
||||
return Intl.message(
|
||||
'No internet connection',
|
||||
name: 'noInternetConnection',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Please check your internet connection and try again.`
|
||||
String get pleaseCheckYourInternetConnectionAndTryAgain {
|
||||
return Intl.message(
|
||||
'Please check your internet connection and try again.',
|
||||
name: 'pleaseCheckYourInternetConnectionAndTryAgain',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
|
|
@ -1149,5 +1149,7 @@
|
|||
"@addNew": {
|
||||
"description": "Text to add a new item (location tag, album, caption etc)"
|
||||
},
|
||||
"contacts": "Contacts"
|
||||
"contacts": "Contacts",
|
||||
"noInternetConnection": "No internet connection",
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again."
|
||||
}
|
|
@ -34,11 +34,10 @@ import "package:photos/ui/account/recovery_page.dart";
|
|||
import 'package:photos/ui/account/two_factor_authentication_page.dart';
|
||||
import 'package:photos/ui/account/two_factor_recovery_page.dart';
|
||||
import 'package:photos/ui/account/two_factor_setup_page.dart';
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/ui/common/progress_dialog.dart";
|
||||
import "package:photos/ui/tabs/home_widget.dart";
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import "package:photos/utils/email_util.dart";
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
import "package:pointycastle/export.dart";
|
||||
|
@ -586,120 +585,92 @@ class UserService {
|
|||
BuildContext context,
|
||||
SrpAttributes srpAttributes,
|
||||
String userPassword,
|
||||
ProgressDialog dialog,
|
||||
) async {
|
||||
final dialog = createProgressDialog(
|
||||
context,
|
||||
S.of(context).pleaseWait,
|
||||
isDismissible: true,
|
||||
);
|
||||
await dialog.show();
|
||||
late Uint8List keyEncryptionKey;
|
||||
try {
|
||||
keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||
utf8.encode(userPassword) as Uint8List,
|
||||
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
||||
srpAttributes.memLimit,
|
||||
srpAttributes.opsLimit,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(keyEncryptionKey);
|
||||
final Uint8List identity = Uint8List.fromList(
|
||||
utf8.encode(srpAttributes.srpUserID),
|
||||
);
|
||||
final Uint8List salt = base64Decode(srpAttributes.srpSalt);
|
||||
final Uint8List password = loginKey;
|
||||
final SecureRandom random = _getSecureRandom();
|
||||
_logger.finest('Start deriving key');
|
||||
keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||
utf8.encode(userPassword) as Uint8List,
|
||||
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
||||
srpAttributes.memLimit,
|
||||
srpAttributes.opsLimit,
|
||||
);
|
||||
_logger.finest('keyDerivation done, derive LoginKey');
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(keyEncryptionKey);
|
||||
final Uint8List identity = Uint8List.fromList(
|
||||
utf8.encode(srpAttributes.srpUserID),
|
||||
);
|
||||
_logger.finest('longinKey derivation done');
|
||||
final Uint8List salt = base64Decode(srpAttributes.srpSalt);
|
||||
final Uint8List password = loginKey;
|
||||
final SecureRandom random = _getSecureRandom();
|
||||
|
||||
final client = SRP6Client(
|
||||
group: kDefaultSrpGroup,
|
||||
digest: Digest('SHA-256'),
|
||||
random: random,
|
||||
);
|
||||
final client = SRP6Client(
|
||||
group: kDefaultSrpGroup,
|
||||
digest: Digest('SHA-256'),
|
||||
random: random,
|
||||
);
|
||||
|
||||
final A = client.generateClientCredentials(salt, identity, password);
|
||||
final createSessionResponse = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/srp/create-session",
|
||||
data: {
|
||||
"srpUserID": srpAttributes.srpUserID,
|
||||
"srpA": base64Encode(SRP6Util.encodeBigInt(A!)),
|
||||
},
|
||||
);
|
||||
final String sessionID = createSessionResponse.data["sessionID"];
|
||||
final String srpB = createSessionResponse.data["srpB"];
|
||||
final A = client.generateClientCredentials(salt, identity, password);
|
||||
final createSessionResponse = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/srp/create-session",
|
||||
data: {
|
||||
"srpUserID": srpAttributes.srpUserID,
|
||||
"srpA": base64Encode(SRP6Util.encodeBigInt(A!)),
|
||||
},
|
||||
);
|
||||
final String sessionID = createSessionResponse.data["sessionID"];
|
||||
final String srpB = createSessionResponse.data["srpB"];
|
||||
|
||||
final serverB = SRP6Util.decodeBigInt(base64Decode(srpB));
|
||||
// ignore: need to calculate secret to get M1, unused_local_variable
|
||||
final clientS = client.calculateSecret(serverB);
|
||||
final clientM = client.calculateClientEvidenceMessage();
|
||||
final response = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/srp/verify-session",
|
||||
data: {
|
||||
"sessionID": sessionID,
|
||||
"srpUserID": srpAttributes.srpUserID,
|
||||
"srpM1": base64Encode(SRP6Util.encodeBigInt(clientM!)),
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
Widget page;
|
||||
final String twoFASessionID = response.data["twoFactorSessionID"];
|
||||
Configuration.instance.setVolatilePassword(userPassword);
|
||||
if (twoFASessionID.isNotEmpty) {
|
||||
setTwoFactor(value: true);
|
||||
page = TwoFactorAuthenticationPage(twoFASessionID);
|
||||
} else {
|
||||
await _saveConfiguration(response);
|
||||
if (Configuration.instance.getEncryptedToken() != null) {
|
||||
await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
||||
userPassword,
|
||||
Configuration.instance.getKeyAttributes()!,
|
||||
keyEncryptionKey: keyEncryptionKey,
|
||||
);
|
||||
page = const HomeWidget();
|
||||
} else {
|
||||
throw Exception("unexpected response during email verification");
|
||||
}
|
||||
}
|
||||
await dialog.hide();
|
||||
if (page is HomeWidget) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
Bus.instance.fire(AccountConfiguredEvent());
|
||||
} else {
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return page;
|
||||
},
|
||||
),
|
||||
(route) => route.isFirst,
|
||||
final serverB = SRP6Util.decodeBigInt(base64Decode(srpB));
|
||||
// ignore: need to calculate secret to get M1, unused_local_variable
|
||||
final clientS = client.calculateSecret(serverB);
|
||||
final clientM = client.calculateClientEvidenceMessage();
|
||||
final response = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/srp/verify-session",
|
||||
data: {
|
||||
"sessionID": sessionID,
|
||||
"srpUserID": srpAttributes.srpUserID,
|
||||
"srpM1": base64Encode(SRP6Util.encodeBigInt(clientM!)),
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
Widget page;
|
||||
final String twoFASessionID = response.data["twoFactorSessionID"];
|
||||
Configuration.instance.setVolatilePassword(userPassword);
|
||||
if (twoFASessionID.isNotEmpty) {
|
||||
setTwoFactor(value: true);
|
||||
page = TwoFactorAuthenticationPage(twoFASessionID);
|
||||
} else {
|
||||
await _saveConfiguration(response);
|
||||
if (Configuration.instance.getEncryptedToken() != null) {
|
||||
await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
||||
userPassword,
|
||||
Configuration.instance.getKeyAttributes()!,
|
||||
keyEncryptionKey: keyEncryptionKey,
|
||||
);
|
||||
page = const HomeWidget();
|
||||
} else {
|
||||
throw Exception("unexpected response during email verification");
|
||||
}
|
||||
} else {
|
||||
// should never reach here
|
||||
throw Exception("unexpected response during email verification");
|
||||
}
|
||||
} on DioError catch (e, s) {
|
||||
await dialog.hide();
|
||||
if (e.response != null && e.response!.statusCode == 401) {
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).incorrectPasswordTitle,
|
||||
S.of(context).pleaseTryAgain,
|
||||
);
|
||||
if (page is HomeWidget) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
Bus.instance.fire(AccountConfiguredEvent());
|
||||
} else {
|
||||
_logger.severe('failed to verify password', e, s);
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).oops,
|
||||
S.of(context).verificationFailedPleaseTryAgain,
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return page;
|
||||
},
|
||||
),
|
||||
(route) => route.isFirst,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe('failed to verify password', e, s);
|
||||
await dialog.hide();
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).oops,
|
||||
S.of(context).verificationFailedPleaseTryAgain,
|
||||
);
|
||||
} else {
|
||||
// should never reach here
|
||||
throw Exception("unexpected response during email verification");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1164,26 +1135,4 @@ class UserService {
|
|||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showContactSupportDialog(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String message,
|
||||
) async {
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: title,
|
||||
body: message,
|
||||
firstButtonLabel: S.of(context).contactSupport,
|
||||
secondButtonLabel: S.of(context).ok,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
await sendLogs(
|
||||
context,
|
||||
S.of(context).contactSupport,
|
||||
"support@ente.io",
|
||||
postShare: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
import "package:dio/dio.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:logging/logging.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import "package:photos/core/errors.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/api/user/srp.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/common/dynamic_fab.dart';
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/email_util.dart";
|
||||
|
||||
// LoginPasswordVerificationPage is a page that allows the user to enter their password to verify their identity.
|
||||
// If the password is correct, then the user is either directed to
|
||||
|
@ -31,6 +36,7 @@ class _LoginPasswordVerificationPageState
|
|||
String? email;
|
||||
bool _passwordInFocus = false;
|
||||
bool _passwordVisible = false;
|
||||
final Logger _logger = Logger("LoginPasswordVerificationPage");
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -85,11 +91,7 @@ class _LoginPasswordVerificationPageState
|
|||
buttonText: S.of(context).logInLabel,
|
||||
onPressedFunction: () async {
|
||||
FocusScope.of(context).unfocus();
|
||||
await UserService.instance.verifyEmailViaPassword(
|
||||
context,
|
||||
widget.srpAttributes,
|
||||
_passwordController.text,
|
||||
);
|
||||
await verifyPassword(context, _passwordController.text);
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: fabLocation(),
|
||||
|
@ -97,6 +99,94 @@ class _LoginPasswordVerificationPageState
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> verifyPassword(BuildContext context, String password) async {
|
||||
final dialog = createProgressDialog(
|
||||
context,
|
||||
S.of(context).pleaseWait,
|
||||
isDismissible: true,
|
||||
);
|
||||
await dialog.show();
|
||||
try {
|
||||
await UserService.instance.verifyEmailViaPassword(
|
||||
context,
|
||||
widget.srpAttributes,
|
||||
password,
|
||||
dialog,
|
||||
);
|
||||
} on DioError catch (e, s) {
|
||||
await dialog.hide();
|
||||
if (e.response != null && e.response!.statusCode == 401) {
|
||||
_logger.severe('server reject, failed verify SRP login', e, s);
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).incorrectPasswordTitle,
|
||||
S.of(context).pleaseTryAgain,
|
||||
);
|
||||
} else {
|
||||
_logger.severe('API failure during SRP login', e, s);
|
||||
if (e.type == DioErrorType.other) {
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).noInternetConnection,
|
||||
S.of(context).pleaseCheckYourInternetConnectionAndTryAgain,
|
||||
);
|
||||
} else {
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).somethingWentWrong,
|
||||
S.of(context).verificationFailedPleaseTryAgain,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe('error while verifying password', e, s);
|
||||
await dialog.hide();
|
||||
if (e is KeyDerivationError || e is LoginKeyDerivationError) {
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: S.of(context).recreatePasswordTitle,
|
||||
body: S.of(context).recreatePasswordBody,
|
||||
firstButtonLabel: S.of(context).useRecoveryKey,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
await UserService.instance.sendOtt(
|
||||
context,
|
||||
email!,
|
||||
isResetPasswordScreen: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
S.of(context).oops,
|
||||
S.of(context).verificationFailedPleaseTryAgain,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showContactSupportDialog(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String message,
|
||||
) async {
|
||||
final dialogChoice = await showChoiceDialog(
|
||||
context,
|
||||
title: title,
|
||||
body: message,
|
||||
firstButtonLabel: S.of(context).contactSupport,
|
||||
secondButtonLabel: S.of(context).ok,
|
||||
);
|
||||
if (dialogChoice!.action == ButtonAction.first) {
|
||||
await sendLogs(
|
||||
context,
|
||||
S.of(context).contactSupport,
|
||||
"support@ente.io",
|
||||
postShare: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _getBody() {
|
||||
return Column(
|
||||
children: [
|
||||
|
|
|
@ -77,7 +77,7 @@ Future<Uint8List> cryptoGenericHash(Map<String, dynamic> args) async {
|
|||
|
||||
EncryptionResult chachaEncryptData(Map<String, dynamic> args) {
|
||||
final initPushResult =
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(args["key"]);
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(args["key"]);
|
||||
final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
|
||||
initPushResult.state,
|
||||
args["source"],
|
||||
|
@ -102,7 +102,7 @@ Future<EncryptionResult> chachaEncryptFile(Map<String, dynamic> args) async {
|
|||
final inputFile = sourceFile.openSync(mode: FileMode.read);
|
||||
final key = args["key"] ?? Sodium.cryptoSecretstreamXchacha20poly1305Keygen();
|
||||
final initPushResult =
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
|
||||
var bytesRead = 0;
|
||||
var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
|
||||
while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
|
||||
|
@ -156,7 +156,7 @@ Future<void> chachaDecryptFile(Map<String, dynamic> args) async {
|
|||
final buffer = await inputFile.read(chunkSize);
|
||||
bytesRead += chunkSize;
|
||||
final pullResult =
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, buffer, null);
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, buffer, null);
|
||||
await destinationFile.writeAsBytes(pullResult.m, mode: FileMode.append);
|
||||
tag = pullResult.tag;
|
||||
}
|
||||
|
@ -190,20 +190,22 @@ class CryptoUtil {
|
|||
Sodium.init();
|
||||
}
|
||||
|
||||
static Uint8List base642bin(String b64, {
|
||||
static Uint8List base642bin(
|
||||
String b64, {
|
||||
String? ignore,
|
||||
int variant = Sodium.base64VariantOriginal,
|
||||
}) {
|
||||
return Sodium.base642bin(b64, ignore: ignore, variant: variant);
|
||||
}
|
||||
|
||||
static String bin2base64(Uint8List bin, {
|
||||
static String bin2base64(
|
||||
Uint8List bin, {
|
||||
bool urlSafe = false,
|
||||
}) {
|
||||
return Sodium.bin2base64(
|
||||
bin,
|
||||
variant:
|
||||
urlSafe ? Sodium.base64VariantUrlsafe : Sodium.base64VariantOriginal,
|
||||
urlSafe ? Sodium.base64VariantUrlsafe : Sodium.base64VariantOriginal,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -237,9 +239,11 @@ class CryptoUtil {
|
|||
|
||||
// Decrypts the given cipher, with the given key and nonce using XSalsa20
|
||||
// (w Poly1305 MAC).
|
||||
static Future<Uint8List> decrypt(Uint8List cipher,
|
||||
Uint8List key,
|
||||
Uint8List nonce,) async {
|
||||
static Future<Uint8List> decrypt(
|
||||
Uint8List cipher,
|
||||
Uint8List key,
|
||||
Uint8List nonce,
|
||||
) async {
|
||||
final args = <String, dynamic>{};
|
||||
args["cipher"] = cipher;
|
||||
args["nonce"] = nonce;
|
||||
|
@ -256,9 +260,11 @@ class CryptoUtil {
|
|||
// This function runs on the same thread as the caller, so should be used only
|
||||
// for small amounts of data where thread switching can result in a degraded
|
||||
// user experience
|
||||
static Uint8List decryptSync(Uint8List cipher,
|
||||
Uint8List key,
|
||||
Uint8List nonce,) {
|
||||
static Uint8List decryptSync(
|
||||
Uint8List cipher,
|
||||
Uint8List key,
|
||||
Uint8List nonce,
|
||||
) {
|
||||
final args = <String, dynamic>{};
|
||||
args["cipher"] = cipher;
|
||||
args["nonce"] = nonce;
|
||||
|
@ -270,8 +276,10 @@ class CryptoUtil {
|
|||
// nonce, using XChaCha20 (w Poly1305 MAC).
|
||||
// This function runs on the isolate pool held by `_computer`.
|
||||
// TODO: Remove "ChaCha", an implementation detail from the function name
|
||||
static Future<EncryptionResult> encryptChaCha(Uint8List source,
|
||||
Uint8List key,) async {
|
||||
static Future<EncryptionResult> encryptChaCha(
|
||||
Uint8List source,
|
||||
Uint8List key,
|
||||
) async {
|
||||
final args = <String, dynamic>{};
|
||||
args["source"] = source;
|
||||
args["key"] = key;
|
||||
|
@ -285,9 +293,11 @@ class CryptoUtil {
|
|||
// Decrypts the given source, with the given key and header using XChaCha20
|
||||
// (w Poly1305 MAC).
|
||||
// TODO: Remove "ChaCha", an implementation detail from the function name
|
||||
static Future<Uint8List> decryptChaCha(Uint8List source,
|
||||
Uint8List key,
|
||||
Uint8List header,) async {
|
||||
static Future<Uint8List> decryptChaCha(
|
||||
Uint8List source,
|
||||
Uint8List key,
|
||||
Uint8List header,
|
||||
) async {
|
||||
final args = <String, dynamic>{};
|
||||
args["source"] = source;
|
||||
args["key"] = key;
|
||||
|
@ -304,10 +314,10 @@ class CryptoUtil {
|
|||
// to the destinationFilePath.
|
||||
// If a key is not provided, one is generated and returned.
|
||||
static Future<EncryptionResult> encryptFile(
|
||||
String sourceFilePath,
|
||||
String destinationFilePath, {
|
||||
Uint8List? key,
|
||||
}) {
|
||||
String sourceFilePath,
|
||||
String destinationFilePath, {
|
||||
Uint8List? key,
|
||||
}) {
|
||||
final args = <String, dynamic>{};
|
||||
args["sourceFilePath"] = sourceFilePath;
|
||||
args["destinationFilePath"] = destinationFilePath;
|
||||
|
@ -322,10 +332,11 @@ class CryptoUtil {
|
|||
// Decrypts the file at sourceFilePath, with the given key and header using
|
||||
// XChaCha20 (w Poly1305 MAC), and writes it to the destinationFilePath.
|
||||
static Future<void> decryptFile(
|
||||
String sourceFilePath,
|
||||
String destinationFilePath,
|
||||
Uint8List header,
|
||||
Uint8List key,) {
|
||||
String sourceFilePath,
|
||||
String destinationFilePath,
|
||||
Uint8List header,
|
||||
Uint8List key,
|
||||
) {
|
||||
final args = <String, dynamic>{};
|
||||
args["sourceFilePath"] = sourceFilePath;
|
||||
args["destinationFilePath"] = destinationFilePath;
|
||||
|
@ -356,10 +367,10 @@ class CryptoUtil {
|
|||
|
||||
// Decrypts the input using the given publicKey-secretKey pair
|
||||
static Uint8List openSealSync(
|
||||
Uint8List input,
|
||||
Uint8List publicKey,
|
||||
Uint8List secretKey,
|
||||
) {
|
||||
Uint8List input,
|
||||
Uint8List publicKey,
|
||||
Uint8List secretKey,
|
||||
) {
|
||||
return Sodium.cryptoBoxSealOpen(input, publicKey, secretKey);
|
||||
}
|
||||
|
||||
|
@ -377,9 +388,9 @@ class CryptoUtil {
|
|||
// At all points, we ensure that the product of these two variables (the area
|
||||
// under the graph that determines the amount of work required) is a constant.
|
||||
static Future<DerivedKeyResult> deriveSensitiveKey(
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
) async {
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
) async {
|
||||
final logger = Logger("pwhash");
|
||||
int memLimit = Sodium.cryptoPwhashMemlimitSensitive;
|
||||
int opsLimit = Sodium.cryptoPwhashOpslimitSensitive;
|
||||
|
@ -407,7 +418,10 @@ class CryptoUtil {
|
|||
return DerivedKeyResult(key, memLimit, opsLimit);
|
||||
} catch (e, s) {
|
||||
logger.warning(
|
||||
"failed to deriveKey mem: $memLimit, ops: $opsLimit", e, s,);
|
||||
"failed to deriveKey mem: $memLimit, ops: $opsLimit",
|
||||
e,
|
||||
s,
|
||||
);
|
||||
}
|
||||
memLimit = (memLimit / 2).round();
|
||||
opsLimit = opsLimit * 2;
|
||||
|
@ -421,9 +435,9 @@ class CryptoUtil {
|
|||
// extra layer of authentication (atop the access token and collection key).
|
||||
// More details @ https://ente.io/blog/building-shareable-links/
|
||||
static Future<DerivedKeyResult> deriveInteractiveKey(
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
) async {
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
) async {
|
||||
final int memLimit = Sodium.cryptoPwhashMemlimitInteractive;
|
||||
final int opsLimit = Sodium.cryptoPwhashOpslimitInteractive;
|
||||
final key = await deriveKey(password, salt, memLimit, opsLimit);
|
||||
|
@ -433,23 +447,23 @@ class CryptoUtil {
|
|||
// Derives a key for a given password, salt, memLimit and opsLimit using
|
||||
// Argon2id, v1.3.
|
||||
static Future<Uint8List> deriveKey(
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
int memLimit,
|
||||
int opsLimit,
|
||||
) {
|
||||
Uint8List password,
|
||||
Uint8List salt,
|
||||
int memLimit,
|
||||
int opsLimit,
|
||||
) {
|
||||
try {
|
||||
return _computer.compute(
|
||||
cryptoPwHash,
|
||||
param: {
|
||||
"password": password,
|
||||
"salt": salt,
|
||||
"memLimit": memLimit,
|
||||
"opsLimit": opsLimit,
|
||||
},
|
||||
taskName: "deriveKey",
|
||||
);
|
||||
} catch(e,s) {
|
||||
return _computer.compute(
|
||||
cryptoPwHash,
|
||||
param: {
|
||||
"password": password,
|
||||
"salt": salt,
|
||||
"memLimit": memLimit,
|
||||
"opsLimit": opsLimit,
|
||||
},
|
||||
taskName: "deriveKey",
|
||||
);
|
||||
} catch (e, s) {
|
||||
final String errMessage = 'failed to deriveKey memLimit: $memLimit and '
|
||||
'opsLimit: $opsLimit';
|
||||
Logger("CryptoUtilDeriveKey").warning(errMessage, e, s);
|
||||
|
@ -461,20 +475,25 @@ class CryptoUtil {
|
|||
// (Key Derivation Function) with the `loginSubKeyId` and
|
||||
// `loginSubKeyLen` and `loginSubKeyContext` as context
|
||||
static Future<Uint8List> deriveLoginKey(
|
||||
Uint8List key,
|
||||
) async {
|
||||
final Uint8List derivedKey = await _computer.compute(
|
||||
cryptoKdfDeriveFromKey,
|
||||
param: {
|
||||
"key": key,
|
||||
"subkeyId": loginSubKeyId,
|
||||
"subkeyLen": loginSubKeyLen,
|
||||
"context": utf8.encode(loginSubKeyContext),
|
||||
},
|
||||
taskName: "deriveLoginKey",
|
||||
);
|
||||
// return the first 16 bytes of the derived key
|
||||
return derivedKey.sublist(0, 16);
|
||||
Uint8List key,
|
||||
) async {
|
||||
try {
|
||||
final Uint8List derivedKey = await _computer.compute(
|
||||
cryptoKdfDeriveFromKey,
|
||||
param: {
|
||||
"key": key,
|
||||
"subkeyId": loginSubKeyId,
|
||||
"subkeyLen": loginSubKeyLen,
|
||||
"context": utf8.encode(loginSubKeyContext),
|
||||
},
|
||||
taskName: "deriveLoginKey",
|
||||
);
|
||||
// return the first 16 bytes of the derived key
|
||||
return derivedKey.sublist(0, 16);
|
||||
} catch (e, s) {
|
||||
Logger("deriveLoginKey").severe("loginKeyDerivation failed", e, s);
|
||||
throw LoginKeyDerivationError();
|
||||
}
|
||||
}
|
||||
|
||||
// Computes and returns the hash of the source file
|
||||
|
|
Loading…
Add table
Reference in a new issue