Handle loginKey and keyDerivation err during SRP based login (#1522)

This commit is contained in:
Neeraj Gupta 2023-11-20 15:52:52 +05:30 committed by GitHub
commit 1abd0a3178
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 412 additions and 210 deletions

View file

@ -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 {}

View file

@ -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"),

View file

@ -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"),

View file

@ -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(

View file

@ -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"),

View file

@ -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"),

View file

@ -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"),

View file

@ -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"),

View file

@ -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"),

View file

@ -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."),

View file

@ -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":

View file

@ -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(

View file

@ -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":

View file

@ -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> {

View file

@ -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."
}

View file

@ -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: () {},
);
}
}
}

View file

@ -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,106 @@ 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.info('error during loginViaPassword', e);
await dialog.hide();
if (e is LoginKeyDerivationError) {
_logger.severe('loginKey derivation error', e, s);
// LoginKey err, perform regular login via ott verification
await UserService.instance.sendOtt(
context,
email!,
isCreateAccountScreen: true,
);
return;
} else if (e is KeyDerivationError) {
// device is not powerful enough to perform derive key
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;
} else {
_logger.severe('unexpected error while verifying password', e, s);
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: [

View file

@ -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