From 1436667e7d4359f22b33c5ce52276b7faeb74a1f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 May 2019 10:23:57 +0200 Subject: [PATCH] Crypto --- build.gradle | 7 +- matrix-sdk-android/build.gradle | 5 +- matrix-sdk-android/libs/olm-sdk.aar | Bin 1281913 -> 0 bytes .../api/comparators/DatedObjectComparators.kt | 41 + .../api/extensions/MatrixSdkExtensions.kt | 35 + .../matrix/android/api/failure/Failure.kt | 2 + .../matrix/android/api/failure/MatrixError.kt | 1 + .../android/api/interfaces/DatedObject.kt | 25 + .../android/api/listeners/ProgressListener.kt | 28 + .../api/listeners/StepProgressListener.kt | 34 + .../api/session/crypto/CryptoService.kt | 103 +- .../api/session/crypto/MXCryptoError.kt | 131 + .../crypto/keysbackup/KeysBackupService.kt | 63 + .../crypto/keysbackup/KeysBackupState.kt | 75 + .../keyshare/RoomKeysRequestListener.kt | 39 + .../api/session/crypto/sas/CancelCode.kt | 33 + .../session/crypto/sas/EmojiRepresentation.kt | 22 + .../sas/IncomingSasVerificationTransaction.kt | 34 + .../android/api/session/crypto/sas/Mode.kt | 22 + .../sas/OutgoingSasVerificationRequest.kt | 32 + .../crypto/sas/SasVerificationService.kt | 39 + .../crypto/sas/SasVerificationTransaction.kt | 50 + .../crypto/sas/SasVerificationTxState.kt | 49 + .../android/api/session/events/model/Event.kt | 153 +- .../api/session/events/model/EventType.kt | 13 +- .../matrix/android/api/session/room/Room.kt | 8 +- .../session/room/crypto/RoomCryptoService.kt | 31 + .../room/members/RoomMembersService.kt | 14 + .../vector/matrix/android/api/util/Types.kt | 19 + .../internal/crypto/CryptoAsyncHelper.kt | 64 + .../internal/crypto/CryptoConstants.kt | 32 + .../android/internal/crypto/CryptoManager.kt | 2102 +++++++++++++++++ .../android/internal/crypto/CryptoModule.kt | 240 ++ .../internal/crypto/DefaultCryptoService.kt | 22 + .../internal/crypto/DeviceListManager.kt | 747 ++++++ .../internal/crypto/IncomingRoomKeyRequest.kt | 81 + .../IncomingRoomKeyRequestCancellation.kt | 29 + .../crypto/IncomingRoomKeyRequestManager.kt | 235 ++ .../internal/crypto/MXCryptoAlgorithms.kt | 98 + .../android/internal/crypto/MXCryptoConfig.kt | 27 + .../internal/crypto/MXDecryptionException.kt | 39 + .../internal/crypto/MXEncryptedAttachments.kt | 269 +++ .../crypto/MXEventDecryptionResult.kt | 50 + .../crypto/MXMegolmExportEncryption.kt | 373 +++ .../android/internal/crypto/MXOlmDevice.kt | 814 +++++++ .../crypto/MXOutgoingRoomKeyRequestManager.kt | 312 +++ .../internal/crypto/MegolmSessionData.kt | 80 + .../internal/crypto/OutgoingRoomKeyRequest.kt | 119 + .../internal/crypto/RoomDecryptorProvider.kt | 109 + .../crypto/algorithms/IMXDecrypting.kt | 84 + .../crypto/algorithms/IMXEncrypting.kt | 57 + .../crypto/algorithms/MXDecryptionResult.kt | 45 + .../algorithms/megolm/MXMegolmDecryption.kt | 422 ++++ .../algorithms/megolm/MXMegolmEncryption.kt | 548 +++++ .../megolm/MXOutboundSessionInfo.kt | 76 + .../crypto/algorithms/olm/MXOlmDecryption.kt | 276 +++ .../crypto/algorithms/olm/MXOlmEncryption.kt | 135 ++ .../android/internal/crypto/api/CryptoApi.kt | 113 + .../internal/crypto/keysbackup/KeysBackup.kt | 1503 ++++++++++++ .../crypto/keysbackup/KeysBackupPassword.kt | 153 ++ .../keysbackup/KeysBackupStateManager.kt | 70 + .../crypto/keysbackup/api/RoomKeysApi.kt | 180 ++ .../keysbackup/model/KeyBackupVersionTrust.kt | 36 + .../model/KeyBackupVersionTrustSignature.kt | 36 + .../model/KeysBackupVersionTrust.kt | 33 + .../model/KeysBackupVersionTrustSignature.kt | 42 + .../keysbackup/model/MegolmBackupAuthData.kt | 75 + .../model/MegolmBackupCreationInfo.kt | 39 + .../keysbackup/model/rest/BackupKeysResult.kt | 30 + .../model/rest/CreateKeysBackupVersionBody.kt | 22 + .../keysbackup/model/rest/KeyBackupData.kt | 56 + .../model/rest/KeysAlgorithmAndData.kt | 62 + .../keysbackup/model/rest/KeysBackupData.kt | 32 + .../keysbackup/model/rest/KeysVersion.kt | 22 + .../model/rest/KeysVersionResult.kt | 31 + .../model/rest/RoomKeysBackupData.kt | 31 + .../model/rest/UpdateKeysBackupVersionBody.kt | 25 + .../tasks/CreateKeysBackupVersionTask.kt | 38 + .../keysbackup/tasks/DeleteBackupTask.kt | 41 + .../tasks/DeleteRoomSessionDataTask.kt | 44 + .../tasks/DeleteRoomSessionsDataTask.kt | 42 + .../tasks/DeleteSessionsDataTask.kt | 40 + .../tasks/GetKeysBackupLastVersionTask.kt | 37 + .../tasks/GetKeysBackupVersionTask.kt | 37 + .../tasks/GetRoomSessionDataTask.kt | 45 + .../tasks/GetRoomSessionsDataTask.kt | 44 + .../keysbackup/tasks/GetSessionsDataTask.kt | 41 + .../tasks/StoreRoomSessionDataTask.kt | 48 + .../tasks/StoreRoomSessionsDataTask.kt | 46 + .../keysbackup/tasks/StoreSessionsDataTask.kt | 44 + .../tasks/UpdateKeysBackupVersionTask.kt | 42 + .../internal/crypto/keysbackup/util/Base58.kt | 85 + .../crypto/keysbackup/util/RecoveryKey.kt | 119 + .../crypto/model/ImportRoomKeysResult.kt | 20 + .../internal/crypto/model/MXDeviceInfo.kt | 189 ++ .../model/MXEncryptEventContentResult.kt | 30 + .../android/internal/crypto/model/MXKey.java | 139 ++ .../model/MXOlmInboundGroupSession.java | 59 + .../crypto/model/MXOlmInboundGroupSession2.kt | 166 ++ .../internal/crypto/model/MXOlmSession.kt | 36 + .../crypto/model/MXOlmSessionResult.java | 43 + .../crypto/model/MXQueuedEncryption.kt | 34 + .../crypto/model/MXUsersDevicesMap.java | 186 ++ .../model/event/EncryptedEventContent.kt | 56 + .../model/event/EncryptionEventContent.kt | 44 + .../crypto/model/event/NewDeviceContent.kt | 31 + .../crypto/model/event/OlmEventContent.kt | 37 + .../crypto/model/event/OlmPayloadContent.kt | 62 + .../crypto/model/event/RoomKeyContent.kt | 42 + .../crypto/model/rest/DeleteDeviceAuth.kt | 37 + .../crypto/model/rest/DeleteDeviceParams.kt | 26 + .../internal/crypto/model/rest/DeviceInfo.kt | 63 + .../internal/crypto/model/rest/DeviceKeys.kt | 39 + .../crypto/model/rest/DevicesListResponse.kt | 28 + .../model/rest/EncryptedBodyFileInfo.kt | 29 + .../crypto/model/rest/EncryptedFileInfo.kt | 28 + .../crypto/model/rest/EncryptedFileKey.kt | 28 + .../crypto/model/rest/EncryptedMessage.kt | 32 + .../model/rest/ForwardedRoomKeyContent.kt | 47 + .../crypto/model/rest/KeyChangesResponse.kt | 33 + .../model/rest/KeyVerificationAccept.kt | 96 + .../model/rest/KeyVerificationCancel.kt | 60 + .../crypto/model/rest/KeyVerificationKey.kt | 58 + .../crypto/model/rest/KeyVerificationMac.kt | 66 + .../crypto/model/rest/KeyVerificationStart.kt | 97 + .../crypto/model/rest/KeysClaimBody.kt | 37 + .../crypto/model/rest/KeysClaimResponse.kt | 35 + .../crypto/model/rest/KeysQueryBody.kt | 50 + .../crypto/model/rest/KeysQueryResponse.kt | 42 + .../crypto/model/rest/KeysUploadBody.kt | 30 + .../crypto/model/rest/KeysUploadResponse.kt | 54 + .../crypto/model/rest/RoomKeyRequestBody.kt | 37 + .../crypto/model/rest/RoomKeyShare.kt | 38 + .../model/rest/RoomKeyShareCancellation.kt | 28 + .../crypto/model/rest/RoomKeyShareRequest.kt | 32 + .../crypto/model/rest/SendToDeviceBody.kt | 27 + .../crypto/model/rest/SendToDeviceObject.kt | 19 + .../crypto/model/rest/UpdateDeviceInfoBody.kt | 32 + .../internal/crypto/store/IMXCryptoStore.kt | 375 +++ .../internal/crypto/store/db/Helper.kt | 127 + .../crypto/store/db/RealmCryptoStore.kt | 708 ++++++ .../store/db/RealmCryptoStoreMigration.kt | 30 + .../crypto/store/db/RealmCryptoStoreModule.kt | 37 + .../store/db/model/CryptoMetadataEntity.kt | 50 + .../crypto/store/db/model/CryptoRoomEntity.kt | 30 + .../crypto/store/db/model/DeviceInfoEntity.kt | 50 + .../db/model/IncomingRoomKeyRequestEntity.kt | 56 + .../store/db/model/KeysBackupDataEntity.kt | 30 + .../db/model/OlmInboundGroupSessionEntity.kt | 47 + .../crypto/store/db/model/OlmSessionEntity.kt | 44 + .../db/model/OutgoingRoomKeyRequestEntity.kt | 77 + .../crypto/store/db/model/UserEntity.kt | 29 + .../store/db/query/CryptoRoomEntityQueries.kt | 41 + .../store/db/query/DeviceInfoEntityQueries.kt | 37 + .../store/db/query/UserEntitiesQueries.kt | 45 + .../ClaimOneTimeKeysForUsersDeviceTask.kt | 74 + .../internal/crypto/tasks/DeleteDeviceTask.kt | 43 + .../crypto/tasks/DownloadKeysForUsersTask.kt | 60 + .../internal/crypto/tasks/GetDevicesTask.kt | 35 + .../crypto/tasks/GetKeyChangesTask.kt | 43 + .../internal/crypto/tasks/SendToDeviceTask.kt | 53 + .../crypto/tasks/SetDeviceNameTask.kt | 47 + .../internal/crypto/tasks/UploadKeysTask.kt | 63 + .../DefaultSasVerificationService.kt | 446 ++++ .../IncomingSASVerificationTransaction.kt | 242 ++ .../OutgoingSASVerificationRequest.kt | 216 ++ .../SASVerificationTransaction.kt | 420 ++++ .../crypto/verification/VerificationEmoji.kt | 88 + .../verification/VerificationTransaction.kt | 48 + .../android/internal/di/MoshiProvider.kt | 37 +- .../internal/session/DefaultSession.kt | 137 +- .../android/internal/session/SessionModule.kt | 10 +- .../internal/session/room/RoomModule.kt | 2 +- .../room/members/DefaultRoomMembersService.kt | 22 + .../session/room/members/RoomMembers.kt | 6 +- .../session/sync/CryptoSyncHandler.kt | 37 + .../internal/session/sync/RoomSyncHandler.kt | 21 +- .../internal/session/sync/SyncModule.kt | 10 +- .../session/sync/SyncResponseHandler.kt | 10 +- .../DeviceOneTimeKeysCountSyncResponse.kt | 1 - .../android/internal/util/CompatUtil.kt | 325 +++ .../internal/util/SecretKeyAndVersion.kt | 32 + .../android/internal/util/StringUtils.kt | 63 + .../src/main/res/values-bg/strings.xml | 4 + .../src/main/res/values-eo/strings.xml | 4 +- .../src/main/res/values-eu/strings.xml | 4 + .../src/main/res/values-fi/strings.xml | 32 +- .../src/main/res/values-fr/strings.xml | 4 + .../src/main/res/values-hu/strings.xml | 4 + .../src/main/res/values-it/strings.xml | 4 + .../src/main/res/values-nl/strings.xml | 4 + .../src/main/res/values-pl/strings.xml | 2 +- .../src/main/res/values-sk/strings.xml | 2 +- .../src/main/res/values-sq/strings.xml | 4 + .../src/main/res/values-zh-rCN/strings.xml | 4 + .../src/main/res/values-zh-rTW/strings.xml | 4 + .../src/main/res/values/strings.xml | 130 + .../src/test/java/ModuleTest.kt | 28 + vector/build.gradle | 6 + .../fcm/troubleshoot/TestTokenRegistration.kt | 2 +- vector/src/main/AndroidManifest.xml | 12 + .../vector/riotredesign/VectorApplication.kt | 12 +- .../vector/riotredesign/core/di/AppModule.kt | 5 + .../core/platform/SimpleFragmentActivity.kt | 102 + .../core/platform/VectorBaseActivity.kt | 34 + .../core/platform/WaitingViewData.kt | 27 + .../core/preference/VectorPreference.kt | 2 +- .../core/resources/ResourceUtils.kt | 98 + .../core/ui/list/GenericItemViewHolder.kt | 104 + .../core/ui/list/GenericRecyclerViewItem.kt | 48 + .../core/ui/views/KeysBackupBanner.kt | 293 +++ .../core/ui/views/PasswordStrengthBar.kt | 119 + .../riotredesign/core/utils/FileUtils.kt | 44 + .../core/utils/PermissionsTools.kt | 5 +- .../restore/KeysBackupRestoreActivity.kt | 113 + .../KeysBackupRestoreFromKeyFragment.kt | 122 + .../KeysBackupRestoreFromKeyViewModel.kt | 113 + ...KeysBackupRestoreFromPassphraseFragment.kt | 135 ++ ...eysBackupRestoreFromPassphraseViewModel.kt | 120 + .../KeysBackupRestoreSharedViewModel.kt | 102 + .../KeysBackupRestoreSuccessFragment.kt | 66 + .../settings/KeysBackupManageActivity.kt | 76 + .../settings/KeysBackupSettingsFragment.kt | 130 + .../KeysBackupSettingsRecyclerViewAdapter.kt | 233 ++ .../settings/KeysBackupSettingsViewModel.kt | 91 + .../setup/KeysBackupSetupActivity.kt | 206 ++ .../setup/KeysBackupSetupSharedViewModel.kt | 176 ++ .../setup/KeysBackupSetupStep1Fragment.kt | 75 + .../setup/KeysBackupSetupStep2Fragment.kt | 212 ++ .../setup/KeysBackupSetupStep3Fragment.kt | 184 ++ .../IncomingVerificationRequestHandler.kt | 99 + .../verification/SASVerificationActivity.kt | 245 ++ .../SASVerificationIncomingFragment.kt | 99 + .../SASVerificationShortCodeFragment.kt | 183 ++ .../SASVerificationStartFragment.kt | 133 ++ .../SASVerificationVerifiedFragment.kt | 47 + .../verification/SasVerificationViewModel.kt | 160 ++ .../features/home/AvatarRenderer.kt | 6 + .../riotredesign/features/home/HomeModule.kt | 2 + .../timeline/factory/EncryptedItemFactory.kt | 84 + .../timeline/factory/EncryptionItemFactory.kt | 51 + .../timeline/factory/TimelineItemFactory.kt | 8 +- .../VectorActivityLifecycleCallbacks.kt | 47 + .../NotificationBroadcastReceiver.kt | 4 +- .../features/popup/PopupAlertManager.kt | 229 ++ .../features/rageshake/BugReporter.kt | 2 +- ...sAdvancedNotificationPreferenceFragment.kt | 2 +- .../VectorSettingsPreferencesFragment.kt | 406 ++-- .../SignOutBottomSheetDialogFragment.kt | 121 +- .../workers/signout/SignOutViewModel.kt | 107 +- .../main/res/anim/anim_alerter_no_anim.xml | 10 + vector/src/main/res/anim/enter_fade_in.xml | 10 + vector/src/main/res/anim/enter_from_left.xml | 10 + vector/src/main/res/anim/enter_from_right.xml | 10 + vector/src/main/res/anim/exit_fade_out.xml | 10 + vector/src/main/res/anim/exit_to_left.xml | 10 + vector/src/main/res/anim/exit_to_right.xml | 10 + vector/src/main/res/anim/no_anim.xml | 6 + vector/src/main/res/drawable-hdpi/shield.png | Bin 0 -> 1107 bytes vector/src/main/res/drawable-mdpi/shield.png | Bin 0 -> 733 bytes vector/src/main/res/drawable-xhdpi/shield.png | Bin 0 -> 1541 bytes .../src/main/res/drawable-xxhdpi/shield.png | Bin 0 -> 2306 bytes .../src/main/res/drawable-xxxhdpi/shield.png | Bin 0 -> 3250 bytes vector/src/main/res/layout/activity.xml | 85 + .../layout/bottom_sheet_save_recovery_key.xml | 92 + .../main/res/layout/dialog_device_delete.xml | 41 + .../main/res/layout/dialog_device_verify.xml | 76 + .../res/layout/dialog_export_e2e_keys.xml | 12 +- .../res/layout/dialog_import_e2e_keys.xml | 37 + .../fragment_keys_backup_restore_from_key.xml | 100 + ...nt_keys_backup_restore_from_passphrase.xml | 99 + .../fragment_keys_backup_restore_success.xml | 56 + .../layout/fragment_keys_backup_settings.xml | 10 + .../fragment_keys_backup_setup_step1.xml | 88 + .../fragment_keys_backup_setup_step2.xml | 151 ++ .../fragment_keys_backup_setup_step3.xml | 92 + ...fragment_sas_verification_display_code.xml | 119 + ...ment_sas_verification_incoming_request.xml | 126 + .../fragment_sas_verification_start.xml | 90 + .../fragment_sas_verification_verified.xml | 66 + .../src/main/res/layout/item_emoji_verif.xml | 28 + .../src/main/res/layout/item_generic_list.xml | 98 + ...tem_keys_backup_settings_button_footer.xml | 33 + .../res/layout/view_keys_backup_banner.xml | 104 + .../res/layout/view_password_strength_bar.xml | 43 + vector/src/main/res/values-ar/strings.xml | 4 +- vector/src/main/res/values-bg/strings.xml | 8 +- vector/src/main/res/values-bn-rIN/strings.xml | 71 +- vector/src/main/res/values-de/strings.xml | 6 +- vector/src/main/res/values-eu/strings.xml | 8 +- vector/src/main/res/values-fa/strings.xml | 119 +- vector/src/main/res/values-fi/strings.xml | 12 +- vector/src/main/res/values-fr/strings.xml | 4 +- vector/src/main/res/values-hu/strings.xml | 4 +- vector/src/main/res/values-it/strings.xml | 4 +- vector/src/main/res/values-nl/strings.xml | 4 +- vector/src/main/res/values-sq/strings.xml | 4 +- .../src/main/res/values-w480dp/integers.xml | 6 + vector/src/main/res/values-zh-rCN/strings.xml | 8 +- vector/src/main/res/values-zh-rTW/strings.xml | 4 +- vector/src/main/res/values/integers.xml | 4 + vector/src/main/res/values/strings.xml | 58 +- vector/src/main/res/values/styles_riot.xml | 8 + 303 files changed, 25671 insertions(+), 506 deletions(-) delete mode 100644 matrix-sdk-android/libs/olm-sdk.aar create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/crypto/RoomCryptoService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoAsyncHelper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoConfig.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEncryptedAttachments.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/api/RoomKeysApi.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrust.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrust.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/BackupKeysResult.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersion.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/Base58.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/RecoveryKey.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/ImportRoomKeysResult.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKey.java create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession.java create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession2.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSession.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.java create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXQueuedEncryption.kt create mode 100755 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXUsersDevicesMap.java create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptedEventContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmPayloadContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/RoomKeyContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceAuth.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceParams.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedBodyFileInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/ForwardedRoomKeyContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationAccept.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationCancel.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationKey.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationMac.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceObject.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoRoomEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeysBackupDataEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/UserEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationEmoji.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransaction.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SecretKeyAndVersion.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt create mode 100644 matrix-sdk-android/src/test/java/ModuleTest.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/platform/SimpleFragmentActivity.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/platform/WaitingViewData.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/resources/ResourceUtils.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericItemViewHolder.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericRecyclerViewItem.kt create mode 100755 vector/src/main/java/im/vector/riotredesign/core/ui/views/KeysBackupBanner.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/core/ui/views/PasswordStrengthBar.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewAdapter.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/popup/PopupAlertManager.kt create mode 100644 vector/src/main/res/anim/anim_alerter_no_anim.xml create mode 100644 vector/src/main/res/anim/enter_fade_in.xml create mode 100644 vector/src/main/res/anim/enter_from_left.xml create mode 100644 vector/src/main/res/anim/enter_from_right.xml create mode 100644 vector/src/main/res/anim/exit_fade_out.xml create mode 100644 vector/src/main/res/anim/exit_to_left.xml create mode 100644 vector/src/main/res/anim/exit_to_right.xml create mode 100644 vector/src/main/res/anim/no_anim.xml create mode 100644 vector/src/main/res/drawable-hdpi/shield.png create mode 100644 vector/src/main/res/drawable-mdpi/shield.png create mode 100644 vector/src/main/res/drawable-xhdpi/shield.png create mode 100644 vector/src/main/res/drawable-xxhdpi/shield.png create mode 100644 vector/src/main/res/drawable-xxxhdpi/shield.png create mode 100644 vector/src/main/res/layout/activity.xml create mode 100644 vector/src/main/res/layout/bottom_sheet_save_recovery_key.xml create mode 100644 vector/src/main/res/layout/dialog_device_delete.xml create mode 100644 vector/src/main/res/layout/dialog_device_verify.xml create mode 100644 vector/src/main/res/layout/dialog_import_e2e_keys.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_restore_from_passphrase.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_restore_success.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_settings.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_setup_step1.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_setup_step2.xml create mode 100644 vector/src/main/res/layout/fragment_keys_backup_setup_step3.xml create mode 100644 vector/src/main/res/layout/fragment_sas_verification_display_code.xml create mode 100644 vector/src/main/res/layout/fragment_sas_verification_incoming_request.xml create mode 100644 vector/src/main/res/layout/fragment_sas_verification_start.xml create mode 100644 vector/src/main/res/layout/fragment_sas_verification_verified.xml create mode 100644 vector/src/main/res/layout/item_emoji_verif.xml create mode 100644 vector/src/main/res/layout/item_generic_list.xml create mode 100644 vector/src/main/res/layout/item_keys_backup_settings_button_footer.xml create mode 100644 vector/src/main/res/layout/view_keys_backup_banner.xml create mode 100644 vector/src/main/res/layout/view_password_strength_bar.xml create mode 100644 vector/src/main/res/values-w480dp/integers.xml diff --git a/build.gradle b/build.gradle index c54c0f59..8c9840e9 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ buildscript { ext.kotlin_version = '1.3.21' ext.koin_version = '1.0.2' + // TODO ext.koin_version = '2.0.0-GA' repositories { google() jcenter() @@ -11,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.google.gms:google-services:4.2.0' classpath "com.airbnb.okreplay:gradle-plugin:1.4.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" @@ -29,6 +30,10 @@ allprojects { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } google() jcenter() + // For Olm SDK + maven { + url 'https://jitpack.io' + } } } diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index c04907ab..4922a78e 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -48,7 +48,7 @@ android { buildConfigField "boolean", "LOG_PRIVATE_DATA", "false" // Set to BODY instead of NONE to enable logging - buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.NONE" + buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.BODY" } release { @@ -126,6 +126,9 @@ dependencies { implementation "io.arrow-kt:arrow-effects-instances:$arrow_version" implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version" + // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm + implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2' + // DI implementation "org.koin:koin-core:$koin_version" implementation "org.koin:koin-core-ext:$koin_version" diff --git a/matrix-sdk-android/libs/olm-sdk.aar b/matrix-sdk-android/libs/olm-sdk.aar deleted file mode 100644 index 66be8a65ac3e662392cc3375902efc70365ef55c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1281913 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

y`Y;dzo@OkFcbT)l88 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

UQ_(t{B85;e?6V??5xg<=LePIPJrA{ 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

NK~YAIu)B-tX=7gHNkx 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ⅈt?!XLJ)2nP(O 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

W~+VEd-{$70+6kp1f(1i0}1c-0)M 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

  • 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

    xRD$ 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

    NoE)_K6L=^?k8 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

    T378ffrySGzhZw^9S|a>~C-EZcjVxmT`_uc<`Z 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%0DMQH6t0rG7& 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 XF^GO#^Znhez~5a!sQ1A8N=p9^YwW@( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt new file mode 100644 index 00000000..aad33ab7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/comparators/DatedObjectComparators.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.comparators + +import im.vector.matrix.android.api.interfaces.DatedObject +import java.util.* + +object DatedObjectComparators { + + /** + * Comparator to sort DatedObjects from the oldest to the latest. + */ + val ascComparator by lazy { + Comparator { datedObject1, datedObject2 -> + (datedObject1.date - datedObject2.date).toInt() + } + } + + /** + * Comparator to sort DatedObjects from the latest to the oldest. + */ + val descComparator by lazy { + Comparator { datedObject1, datedObject2 -> + (datedObject2.date - datedObject1.date).toInt() + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt new file mode 100644 index 00000000..e2e53904 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/MatrixSdkExtensions.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.extensions + +import im.vector.matrix.android.api.comparators.DatedObjectComparators +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo +import java.util.* + +/* ========================================================================================== + * MXDeviceInfo + * ========================================================================================== */ + +fun MXDeviceInfo.getFingerprintHumanReadable() = fingerprint() + ?.chunked(4) + ?.joinToString(separator = " ") + + +fun List.sortByLastSeen() { + Collections.sort(this, DatedObjectComparators.descComparator) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt index 94e3776e..8431239a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Failure.kt @@ -16,6 +16,7 @@ package im.vector.matrix.android.api.failure +import im.vector.matrix.android.api.session.crypto.MXCryptoError import java.io.IOException /** @@ -31,6 +32,7 @@ sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) { data class Unknown(val throwable: Throwable? = null) : Failure(throwable) data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException) data class ServerError(val error: MatrixError) : Failure(RuntimeException(error.toString())) + data class CryptoError(val error: MXCryptoError) : Failure(RuntimeException(error.toString())) abstract class FeatureFailure : Failure() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt index 92cba3b8..1e87cfc1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/MatrixError.kt @@ -54,5 +54,6 @@ data class MatrixError( const val TOO_LARGE = "M_TOO_LARGE" const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN" const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED" + const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION" } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt new file mode 100644 index 00000000..cd735506 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/interfaces/DatedObject.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.interfaces + +/** + * Can be implemented by any object containing a timestamp. + * This interface can be use to sort such object + */ +interface DatedObject { + val date: Long +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt new file mode 100644 index 00000000..854f0e9f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.listeners + +/** + * Interface to send a progress info + */ +interface ProgressListener { + /** + * @param progress from 0 to total by contract + * @param total + */ + fun onProgress(progress: Int, total: Int) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt new file mode 100644 index 00000000..af5b815c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/StepProgressListener.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.listeners + +/** + * Interface to send a progress info + */ +interface StepProgressListener { + + sealed class Step { + data class ComputingKey(val progress: Int, val total: Int) : Step() + object DownloadingKey : Step() + data class ImportingKey(val progress: Int, val total: Int) : Step() + } + + /** + * @param step The current step, containing progress data if available. Else you should consider progress as indeterminate + */ + fun onStepProgress(step: Step) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt index 86c8a86f..df85b2f0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt @@ -16,8 +16,107 @@ package im.vector.matrix.android.api.session.crypto +import android.content.Context +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse + interface CryptoService { - // Not supported for the moment - fun isCryptoEnabled() = false + fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) + + fun deleteDevice(deviceId: String, accountPassword: String, callback: MatrixCallback) + + fun getCryptoVersion(context: Context, longFormat: Boolean): String + + fun isCryptoEnabled(): Boolean + + fun getSasVerificationService(): SasVerificationService + + fun getKeysBackupService(): KeysBackupService + + fun isRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback?) + + fun setWarnOnUnknownDevices(warn: Boolean) + + fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String, callback: MatrixCallback) + + fun getUserDevices(userId: String): MutableList + + fun setDevicesKnown(devices: List, callback: MatrixCallback?) + + fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? + + fun getMyDevice(): MXDeviceInfo + + fun getGlobalBlacklistUnverifiedDevices(callback: MatrixCallback?) + + fun setGlobalBlacklistUnverifiedDevices(block: Boolean, callback: MatrixCallback?) + + fun setRoomUnBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) + + fun getDeviceTrackingStatus(userId: String): Int + + fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?, callback: MatrixCallback) + + fun exportRoomKeys(password: String, callback: MatrixCallback) + + fun setRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) + + fun getDeviceInfo(userId: String, deviceId: String?, callback: MatrixCallback) + + // TODO move elsewhere + fun reRequestRoomKeyForEvent(event: Event) + + fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) + + fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) + + fun getDevicesList(callback: MatrixCallback) + + fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int + + /* + fun start(isInitialSync: Boolean, aCallback: MatrixCallback?) + + fun isStarted(): Boolean + + fun isStarting(): Boolean + + fun close() + + fun encryptEventContent(eventContent: Content, + eventType: String, + room: Room, + callback: MatrixCallback) + + fun onToDeviceEvent(event: Event) + + fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean) + + fun getOlmDevice(): MXOlmDevice? + + fun checkUnknownDevices(userIds: List, callback: MatrixCallback) + + fun warnOnUnknownDevices(): Boolean + + @Throws(MXDecryptionException::class) + fun decryptEvent(event: Event, timelineId: String?): MXEventDecryptionResult? + + fun resetReplayAttackCheckInTimeline(timelineId: String) + + + @VisibleForTesting + fun ensureOlmSessionsForUsers(users: List, callback: MatrixCallback>) + */ + + fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt new file mode 100644 index 00000000..3e4f7028 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto + +import android.text.TextUtils + +/** + * Represents a crypto error response. + */ +class MXCryptoError(var code: String, + var message: String) { + + /** + * Describe the error with more details + */ + private var mDetailedErrorDescription: String? = null + + /** + * Data exception. + * Some exceptions provide some data to describe the exception + */ + var mExceptionData: Any? = null + + /** + * @return true if the current error is an olm one. + */ + val isOlmError: Boolean + get() = TextUtils.equals(OLM_ERROR_CODE, code) + + + /** + * @return the detailed error description + */ + val detailedErrorDescription: String? + get() = if (TextUtils.isEmpty(mDetailedErrorDescription)) { + message + } else mDetailedErrorDescription + + /** + * Create a crypto error + * + * @param code the error code (see XX_ERROR_CODE) + * @param shortErrorDescription the short error description + * @param detailedErrorDescription the detailed error description + */ + constructor(code: String, shortErrorDescription: String, detailedErrorDescription: String?) : this(code, shortErrorDescription) { + mDetailedErrorDescription = detailedErrorDescription + } + + /** + * Create a crypto error + * + * @param code the error code (see XX_ERROR_CODE) + * @param shortErrorDescription the short error description + * @param detailedErrorDescription the detailed error description + * @param exceptionData the exception data + */ + constructor(code: String, shortErrorDescription: String, detailedErrorDescription: String?, exceptionData: Any) : this(code, shortErrorDescription) { + mDetailedErrorDescription = detailedErrorDescription + mExceptionData = exceptionData + } + + companion object { + + /** + * Error codes + */ + const val ENCRYPTING_NOT_ENABLED_ERROR_CODE = "ENCRYPTING_NOT_ENABLED" + const val UNABLE_TO_ENCRYPT_ERROR_CODE = "UNABLE_TO_ENCRYPT" + const val UNABLE_TO_DECRYPT_ERROR_CODE = "UNABLE_TO_DECRYPT" + const val UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE = "UNKNOWN_INBOUND_SESSION_ID" + const val INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE = "INBOUND_SESSION_MISMATCH_ROOM_ID" + const val MISSING_FIELDS_ERROR_CODE = "MISSING_FIELDS" + const val MISSING_CIPHER_TEXT_ERROR_CODE = "MISSING_CIPHER_TEXT" + const val NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE = "NOT_INCLUDE_IN_RECIPIENTS" + const val BAD_RECIPIENT_ERROR_CODE = "BAD_RECIPIENT" + const val BAD_RECIPIENT_KEY_ERROR_CODE = "BAD_RECIPIENT_KEY" + const val FORWARDED_MESSAGE_ERROR_CODE = "FORWARDED_MESSAGE" + const val BAD_ROOM_ERROR_CODE = "BAD_ROOM" + const val BAD_ENCRYPTED_MESSAGE_ERROR_CODE = "BAD_ENCRYPTED_MESSAGE" + const val DUPLICATED_MESSAGE_INDEX_ERROR_CODE = "DUPLICATED_MESSAGE_INDEX" + const val MISSING_PROPERTY_ERROR_CODE = "MISSING_PROPERTY" + const val OLM_ERROR_CODE = "OLM_ERROR_CODE" + const val UNKNOWN_DEVICES_CODE = "UNKNOWN_DEVICES_CODE" + + /** + * short error reasons + */ + const val UNABLE_TO_DECRYPT = "Unable to decrypt" + const val UNABLE_TO_ENCRYPT = "Unable to encrypt" + + /** + * Detailed error reasons + */ + const val ENCRYPTING_NOT_ENABLED_REASON = "Encryption not enabled" + const val UNABLE_TO_ENCRYPT_REASON = "Unable to encrypt %s" + const val UNABLE_TO_DECRYPT_REASON = "Unable to decrypt %1\$s. Algorithm: %2\$s" + const val OLM_REASON = "OLM error: %1\$s" + const val DETAILLED_OLM_REASON = "Unable to decrypt %1\$s. OLM error: %2\$s" + const val UNKNOWN_INBOUND_SESSION_ID_REASON = "Unknown inbound session id" + const val INBOUND_SESSION_MISMATCH_ROOM_ID_REASON = "Mismatched room_id for inbound group session (expected %1\$s, was %2\$s)" + const val MISSING_FIELDS_REASON = "Missing fields in input" + const val MISSING_CIPHER_TEXT_REASON = "Missing ciphertext" + const val NOT_INCLUDED_IN_RECIPIENT_REASON = "Not included in recipients" + const val BAD_RECIPIENT_REASON = "Message was intended for %1\$s" + const val BAD_RECIPIENT_KEY_REASON = "Message not intended for this device" + const val FORWARDED_MESSAGE_REASON = "Message forwarded from %1\$s" + const val BAD_ROOM_REASON = "Message intended for room %1\$s" + const val BAD_ENCRYPTED_MESSAGE_REASON = "Bad Encrypted Message" + const val DUPLICATE_MESSAGE_INDEX_REASON = "Duplicate message index, possible replay attack %1\$s" + const val ERROR_MISSING_PROPERTY_REASON = "No '%1\$s' property. Cannot prevent unknown-key attack" + const val UNKNOWN_DEVICES_REASON = "This room contains unknown devices which have not been verified.\n" + "We strongly recommend you verify them before continuing." + const val NO_MORE_ALGORITHM_REASON = "Room was previously configured to use encryption, but is no longer." + " Perhaps the homeserver is hiding the configuration event." + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt new file mode 100644 index 00000000..85a96b5f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.keysbackup + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.listeners.StepProgressListener +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult + +// TODO Add doc from implementation +interface KeysBackupService { + fun getCurrentVersion(callback: MatrixCallback) + fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo, callback: MatrixCallback) + fun getTotalNumbersOfKeys(): Int + fun getTotalNumbersOfBackedUpKeys(): Int + fun backupAllGroupSessions(progressListener: ProgressListener?, callback: MatrixCallback?) + fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult, callback: MatrixCallback) + fun getBackupProgress(progressListener: ProgressListener) + fun maybeBackupKeys() + fun getVersion(version: String, callback: MatrixCallback) + fun forceUsingLastVersion(callback: MatrixCallback) + fun checkAndStartKeysBackup() + fun addListener(listener: KeysBackupStateListener) + fun removeListener(listener: KeysBackupStateListener) + + fun prepareKeysBackupVersion(password: String?, progressListener: ProgressListener?, callback: MatrixCallback) + fun deleteBackup(version: String, callback: MatrixCallback?) + fun canRestoreKeys(): Boolean + fun trustKeysBackupVersion(keysBackupVersion: KeysVersionResult, trust: Boolean, callback: MatrixCallback) + fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, recoveryKey: String, callback: MatrixCallback) + fun trustKeysBackupVersionWithPassphrase(keysBackupVersion: KeysVersionResult, password: String, callback: MatrixCallback) + fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult, recoveryKey: String, roomId: String?, sessionId: String?, stepProgressListener: StepProgressListener?, callback: MatrixCallback) + fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult, password: String, roomId: String?, sessionId: String?, stepProgressListener: StepProgressListener?, callback: MatrixCallback) + + val mKeysBackupVersion: KeysVersionResult? + val currentBackupVersion: String? + val isEnabled: Boolean + val isStucked: Boolean + val state: KeysBackupState + + interface KeysBackupStateListener { + fun onStateChange(newState: KeysBackupState) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt new file mode 100644 index 00000000..a1bd29e7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupState.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.keysbackup + +/** + * E2e keys backup states. + * + *

    + *                               |
    + *                               V        deleteKeyBackupVersion (on current backup)
    + *  +---------------------->  UNKNOWN  <-------------
    + *  |                            |
    + *  |                            | checkAndStartKeysBackup (at startup or on new verified device or a new detected backup)
    + *  |                            V
    + *  |                     CHECKING BACKUP
    + *  |                            |
    + *  |    Network error           |
    + *  +<----------+----------------+-------> DISABLED <----------------------+
    + *  |           |                |            |                            |
    + *  |           |                |            | createKeysBackupVersion    |
    + *  |           V                |            V                            |
    + *  +<---  WRONG VERSION         |         ENABLING                        |
    + *      |       ^                |            |                            |
    + *      |       |                V       ok   |     error                  |
    + *      |       |     +------> READY <--------+----------------------------+
    + *      V       |     |          |
    + * NOT TRUSTED  |     |          | on new key
    + *              |     |          V
    + *              |     |     WILL BACK UP (waiting a random duration)
    + *              |     |          |
    + *              |     |          |
    + *              |     | ok       V
    + *              |     +----- BACKING UP
    + *              |                |
    + *              |      Error     |
    + *              +<---------------+
    + * 
    + */ +enum class KeysBackupState { + // Need to check the current backup version on the homeserver + Unknown, + // Checking if backup is enabled on home server + CheckingBackUpOnHomeserver, + // Backup has been stopped because a new backup version has been detected on the homeserver + WrongBackUpVersion, + // Backup from this device is not enabled + Disabled, + // There is a backup available on the homeserver but it is not trusted. + // It is not trusted because the signature is invalid or the device that created it is not verified + // Use [KeysBackup.getKeysBackupTrust()] to get trust details. + // Consequently, the backup from this device is not enabled. + NotTrusted, + // Backup is being enabled: the backup version is being created on the homeserver + Enabling, + // Backup is enabled and ready to send backup to the homeserver + ReadyToBackUp, + // e2e keys are going to be sent to the homeserver + WillBackUp, + // e2e keys are being sent to the homeserver + BackingUp +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt new file mode 100644 index 00000000..5bce27e1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keyshare/RoomKeysRequestListener.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.keyshare + +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCancellation + +/** + * Room keys events listener + */ +interface RoomKeysRequestListener { + /** + * An room key request has been received. + * + * @param request the request + */ + fun onRoomKeyRequest(request: IncomingRoomKeyRequest) + + /** + * A room key request cancellation has been received. + * + * @param request the cancellation request + */ + fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt new file mode 100644 index 00000000..d999d06f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/CancelCode.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.api.session.crypto.sas + +enum class CancelCode(val value: String, val humanReadable: String) { + User("m.user", "the user cancelled the verification"), + Timeout("m.timeout", "the verification process timed out"), + UnknownTransaction("m.unknown_transaction", "the device does not know about that transaction"), + UnknownMethod("m.unknown_method", "the device can’t agree on a key agreement, hash, MAC, or SAS method"), + MismatchedCommitment("m.mismatched_commitment", "the hash commitment did not match"), + MismatchedSas("m.mismatched_sas", "the SAS did not match"), + UnexpectedMessage("m.unexpected_message", "the device received an unexpected message"), + InvalidMessage("m.invalid_message", "an invalid message was received"), + MismatchedKeys("m.key_mismatch", "Key mismatch"), + UserMismatchError("m.user_error", "User mismatch") +} + +fun safeValueOf(code: String?): CancelCode { + return CancelCode.values().firstOrNull { code == it.value } ?: CancelCode.User +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt new file mode 100644 index 00000000..0bcb96a9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/EmojiRepresentation.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +import androidx.annotation.StringRes + +data class EmojiRepresentation(val emoji: String, + @StringRes val nameResId: Int) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt new file mode 100644 index 00000000..791c63dc --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/IncomingSasVerificationTransaction.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +interface IncomingSasVerificationTransaction { + val uxState: UxState + + fun performAccept() + + enum class UxState { + UNKNOWN, + SHOW_ACCEPT, + WAIT_FOR_KEY_AGREEMENT, + SHOW_SAS, + WAIT_FOR_VERIFICATION, + VERIFIED, + CANCELLED_BY_ME, + CANCELLED_BY_OTHER + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt new file mode 100644 index 00000000..da72e98b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/Mode.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +object SasMode { + const val DECIMAL = "decimal" + const val EMOJI = "emoji" +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt new file mode 100644 index 00000000..6aeed555 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/OutgoingSasVerificationRequest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +interface OutgoingSasVerificationRequest { + val uxState: UxState + + enum class UxState { + UNKNOWN, + WAIT_FOR_START, + WAIT_FOR_KEY_AGREEMENT, + SHOW_SAS, + WAIT_FOR_VERIFICATION, + VERIFIED, + CANCELLED_BY_ME, + CANCELLED_BY_OTHER + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt new file mode 100644 index 00000000..a11c69cd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +interface SasVerificationService { + fun addListener(listener: SasVerificationListener) + + fun removeListener(listener: SasVerificationListener) + + fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) + + fun getExistingTransaction(otherUser: String, tid: String): SasVerificationTransaction? + + fun beginKeyVerificationSAS(userId: String, deviceID: String): String? + + fun beginKeyVerification(method: String, userId: String, deviceID: String): String? + + // fun transactionUpdated(tx: SasVerificationTransaction) + + interface SasVerificationListener { + fun transactionCreated(tx: SasVerificationTransaction) + fun transactionUpdated(tx: SasVerificationTransaction) + fun markedAsManuallyVerified(userId: String, deviceId: String) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt new file mode 100644 index 00000000..dc489cf6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +interface SasVerificationTransaction { + val state: SasVerificationTxState + + val cancelledReason: CancelCode? + + val transactionId: String + + val otherUserId: String + + var otherDeviceId: String? + + val isIncoming: Boolean + + fun supportsEmoji(): Boolean + + fun supportsDecimal(): Boolean + + fun getEmojiCodeRepresentation(): List + + fun getDecimalCodeRepresentation(): String + + /** + * User wants to cancel the transaction + */ + fun cancel() + + /** + * To be called by the client when the user has verified that + * both short codes do match + */ + fun userHasVerifiedShortCode() +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt new file mode 100644 index 00000000..60ce8f0c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTxState.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.crypto.sas + +enum class SasVerificationTxState { + None, + // I have started a verification request + SendingStart, + Started, + // Other user/device sent me a request + OnStarted, + // I have accepted a request started by the other user/device + SendingAccept, + Accepted, + // My request has been accepted by the other user/device + OnAccepted, + // I have sent my public key + SendingKey, + KeySent, + // The other user/device has sent me his public key + OnKeyReceived, + // Short code is ready to be displayed + ShortCodeReady, + // I have compared the code and manually said that they match + ShortCodeAccepted, + + SendingMac, + MacSent, + Verifying, + Verified, + + //Global: The verification has been cancelled (by me or other), see cancelReason for details + Cancelled, + OnCancelled +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index 7c6ca39b..cb04fa45 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -16,13 +16,20 @@ package im.vector.matrix.android.api.session.events.model +import android.text.TextUtils import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import com.squareup.moshi.Types +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.di.MoshiProvider +import timber.log.Timber import java.lang.reflect.ParameterizedType +import java.util.ArrayList +import java.util.HashMap -typealias Content = Map +typealias Content = JsonDict /** * This methods is a facility method to map a json content to a model. @@ -77,4 +84,148 @@ data class Event( companion object { internal val CONTENT_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java) } + + //============================================================================================================== + // Crypto + //============================================================================================================== + + /** + * For encrypted events, the plaintext payload for the event. + * This is a small MXEvent instance with typically value for `type` and 'content' fields. + */ + @Transient + private var mClearEvent: Event? = null + + /** + * Curve25519 key which we believe belongs to the sender of the event. + * See `senderKey` property. + */ + @Transient + private var mSenderCurve25519Key: String? = null + + /** + * Ed25519 key which the sender of this event (for olm) or the creator of the megolm session (for megolm) claims to own. + * See `claimedEd25519Key` property. + */ + @Transient + private var mClaimedEd25519Key: String? = null + + /** + * Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key. + * See `forwardingCurve25519KeyChain` property. + */ + @Transient + private var mForwardingCurve25519KeyChain: List = ArrayList() + + /** + * Decryption error + */ + @Transient + private var mCryptoError: MXCryptoError? = null + + /** + * @return true if this event is encrypted. + */ + fun isEncrypted(): Boolean { + return TextUtils.equals(type, EventType.ENCRYPTED) + } + + /** + * Update the clear data on this event. + * This is used after decrypting an event; it should not be used by applications. + * It fires kMXEventDidDecryptNotification. + * + * @param decryptionResult the decryption result, including the plaintext and some key info. + */ + fun setClearData(decryptionResult: MXEventDecryptionResult?) { + mClearEvent = null + + if (null != decryptionResult) { + if (null != decryptionResult!!.mClearEvent) { + mClearEvent = decryptionResult!!.mClearEvent + } + + if (null != mClearEvent) { + mClearEvent!!.mSenderCurve25519Key = decryptionResult!!.mSenderCurve25519Key + mClearEvent!!.mClaimedEd25519Key = decryptionResult!!.mClaimedEd25519Key + + if (null != decryptionResult!!.mForwardingCurve25519KeyChain) { + mClearEvent!!.mForwardingCurve25519KeyChain = decryptionResult!!.mForwardingCurve25519KeyChain + } else { + mClearEvent!!.mForwardingCurve25519KeyChain = ArrayList() + } + + try { + // Add "m.relates_to" data from e2e event to the unencrypted event + // TODO + //if (getWireContent().getAsJsonObject().has("m.relates_to")) { + // mClearEvent!!.getContentAsJsonObject() + // .add("m.relates_to", getWireContent().getAsJsonObject().get("m.relates_to")) + //} + } catch (e: Exception) { + Timber.e(e,"Unable to restore 'm.relates_to' the clear event") + } + + } + + mCryptoError = null + } + } + + /** + * @return The curve25519 key that sent this event. + */ + fun getSenderKey(): String? { + return if (null != mClearEvent) { + mClearEvent!!.mSenderCurve25519Key + } else { + mSenderCurve25519Key + } + } + + /** + * @return The additional keys the sender of this encrypted event claims to possess. + */ + fun getKeysClaimed(): Map { + val res = HashMap() + + val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key + + if (null != claimedEd25519Key) { + res["ed25519"] = claimedEd25519Key + } + + return res + } + + /** + * @return the event type + */ + fun getClearType(): String { + return if (null != mClearEvent) { + mClearEvent!!.type + } else { + type + } + } + + /** + * @return the linked crypto error + */ + fun getCryptoError(): MXCryptoError? { + return mCryptoError + } + + /** + * Update the linked crypto error + * + * @param error the new crypto error. + */ + fun setCryptoError(error: MXCryptoError?) { + mCryptoError = error + if (null != error) { + mClearEvent = null + } + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt index 4315fd59..b867063c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt @@ -36,8 +36,6 @@ object EventType { const val FULLY_READ = "m.fully_read" const val PLUMBING = "m.room.plumbing" const val BOT_OPTIONS = "m.room.bot.options" - const val KEY_REQUEST = "m.room_key_request" - const val FORWARDED_ROOM_KEY = "m.forwarded_room_key" const val PREVIEW_URLS = "org.matrix.room.preview_urls" // State Events @@ -65,6 +63,17 @@ object EventType { const val CALL_ANSWER = "m.call.answer" const val CALL_HANGUP = "m.call.hangup" + // Key share events + const val ROOM_KEY_REQUEST = "m.room_key_request" + const val FORWARDED_ROOM_KEY = "m.forwarded_room_key" + + // Interactive key verification + const val KEY_VERIFICATION_START = "m.key.verification.start" + const val KEY_VERIFICATION_ACCEPT = "m.key.verification.accept" + const val KEY_VERIFICATION_KEY = "m.key.verification.key" + const val KEY_VERIFICATION_MAC = "m.key.verification.mac" + const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel" + private val STATE_EVENTS = listOf( STATE_ROOM_NAME, STATE_ROOM_TOPIC, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt index c2e69d33..9cb4b846 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.room import androidx.lifecycle.LiveData +import im.vector.matrix.android.api.session.room.crypto.RoomCryptoService import im.vector.matrix.android.api.session.room.members.RoomMembersService import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.read.ReadService @@ -27,7 +28,12 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineService /** * This interface defines methods to interact within a room. */ -interface Room : TimelineService, SendService, ReadService, RoomMembersService, StateService { +interface Room : TimelineService, + SendService, + ReadService, + RoomMembersService, + StateService, + RoomCryptoService { /** * The roomId of this room diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/crypto/RoomCryptoService.kt new file mode 100644 index 00000000..52a139cf --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/crypto/RoomCryptoService.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.room.crypto + +import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM + +interface RoomCryptoService { + + // TODO + fun isEncrypted(): Boolean = false + + // TODO + fun encryptionAlgorithm(): String? = MXCRYPTO_ALGORITHM_MEGOLM + + // TODO + fun shouldEncryptForInvitedMembers(): Boolean = false +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMembersService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMembersService.kt index 930afd7a..e04cebea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMembersService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMembersService.kt @@ -54,4 +54,18 @@ interface RoomMembersService { */ fun invite(userId: String, callback: MatrixCallback) + /** + * Return all the roomMembers ids which are joined or invited to the room + * + * @return a roomMember id list of joined or invited members. + */ + fun getActiveRoomMemberIds(): List + + /** + * Return all the roomMembers ids which are joined to the room + * + * @return a roomMember id list of joined members. + */ + fun getJoinedRoomMemberIds(): List + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt new file mode 100644 index 00000000..601b3cb0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.util + +typealias JsonDict = Map diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoAsyncHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoAsyncHelper.kt new file mode 100644 index 00000000..42e97d30 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoAsyncHelper.kt @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.crypto + +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper + +private const val THREAD_ENCRYPT_NAME = "Crypto_Encrypt_Thread" +private const val THREAD_DECRYPT_NAME = "Crypto_Decrypt_Thread" + +internal object CryptoAsyncHelper { + + private var uiHandler: Handler? = null + private var decryptBackgroundHandler: Handler? = null + private var encryptBackgroundHandler: Handler? = null + + fun getUiHandler(): Handler { + return uiHandler + ?: Handler(Looper.getMainLooper()) + .also { uiHandler = it } + } + + fun getDecryptBackgroundHandler(): Handler { + return decryptBackgroundHandler + ?: createDecryptBackgroundHandler() + .also { decryptBackgroundHandler = it } + } + + fun getEncryptBackgroundHandler(): Handler { + return encryptBackgroundHandler + ?: createEncryptBackgroundHandler() + .also { encryptBackgroundHandler = it } + } + + private fun createDecryptBackgroundHandler(): Handler { + val handlerThread = HandlerThread(THREAD_DECRYPT_NAME) + handlerThread.start() + return Handler(handlerThread.looper) + } + + private fun createEncryptBackgroundHandler(): Handler { + val handlerThread = HandlerThread(THREAD_ENCRYPT_NAME) + handlerThread.start() + return Handler(handlerThread.looper) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt new file mode 100644 index 00000000..c045019c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +/** + * Matrix algorithm value for olm. + */ +const val MXCRYPTO_ALGORITHM_OLM = "m.olm.v1.curve25519-aes-sha2" + +/** + * Matrix algorithm value for megolm. + */ +const val MXCRYPTO_ALGORITHM_MEGOLM = "m.megolm.v1.aes-sha2" + +/** + * Matrix algorithm value for megolm keys backup. + */ +const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-sha2" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt new file mode 100755 index 00000000..ca437da0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -0,0 +1,2102 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.content.Context +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper +import android.text.TextUtils +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.session.crypto.CryptoService +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.Room +import im.vector.matrix.android.api.session.room.RoomService +import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup +import im.vector.matrix.android.internal.crypto.model.* +import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent +import im.vector.matrix.android.internal.crypto.model.rest.* +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.tasks.* +import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.session.sync.model.SyncResponse +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.convertToUTF8 +import org.matrix.olm.OlmAccount +import org.matrix.olm.OlmManager +import timber.log.Timber +import java.util.* +import java.util.concurrent.CountDownLatch + +/** + * A `MXCrypto` class instance manages the end-to-end crypto for a MXSession instance. + * + * + * Messages posted by the user are automatically redirected to MXCrypto in order to be encrypted + * before sending. + * In the other hand, received events goes through MXCrypto for decrypting. + * MXCrypto maintains all necessary keys and their sharing with other devices required for the crypto. + * Specially, it tracks all room membership changes events in order to do keys updates. + */ +internal class CryptoManager( + // The credentials, + private val mCredentials: Credentials, + // the crypto store + private val mCryptoStore: IMXCryptoStore, + // Olm device + private val mOlmDevice: MXOlmDevice, + cryptoConfig: MXCryptoConfig?, + // Device list manager + private val deviceListManager: DeviceListManager, + // The key backup service. + private val mKeysBackup: KeysBackup, + // + private val roomDecryptorProvider: RoomDecryptorProvider, + // The SAS verification service. + private val mSasVerificationService: DefaultSasVerificationService, + // + private val mIncomingRoomKeyRequestManager: IncomingRoomKeyRequestManager, + // + private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager, + // Room service + private val mRoomService: RoomService, + // Olm Manager + private val mOlmManager: OlmManager, + // Tasks + private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, + private val mDeleteDeviceTask: DeleteDeviceTask, + private val mGetDevicesTask: GetDevicesTask, + private val mGetKeyChangesTask: GetKeyChangesTask, + private val mSendToDeviceTask: SendToDeviceTask, + private val mSetDeviceNameTask: SetDeviceNameTask, + private val mUploadKeysTask: UploadKeysTask, + // TaskExecutor + private val mTaskExecutor: TaskExecutor +) : KeysBackup.KeysBackupCryptoListener, + DefaultSasVerificationService.SasCryptoListener, + DeviceListManager.DeviceListCryptoListener, + CryptoService { + + // MXEncrypting instance for each room. + private val mRoomEncryptors: MutableMap + + // Our device keys + /** + * @return my device info + */ + private val myDevice: MXDeviceInfo + + private var mLastPublishedOneTimeKeys: Map>? = null + + // the encryption is starting + private var mIsStarting: Boolean = false + + // tell if the crypto is started + private var mIsStarted: Boolean = false + + // the crypto background threads + private var mEncryptingHandlerThread: HandlerThread? = null + private var mEncryptingHandler: Handler? = null + + private var mDecryptingHandlerThread: HandlerThread? = null + private var mDecryptingHandler: Handler? = null + + // the UI thread + private val mUIHandler: Handler + + private var mOneTimeKeyCount: Int? = null + + // TODO + //private val mNetworkListener = object : IMXNetworkEventListener { + // override fun onNetworkConnectionUpdate(isConnected: Boolean) { + // if (isConnected && !isStarted()) { + // Timber.d("Start MXCrypto because a network connection has been retrieved ") + // start(false, null) + // } + // } + //} + + fun onLiveEvent(roomId: String, event: Event) { + if (event.type == EventType.ENCRYPTION) { + onCryptoEvent(roomId, event) + } else if (event.type == EventType.STATE_ROOM_MEMBER) { + onRoomMembershipEvent(roomId, event) + } + } + + // initialization callbacks + private val mInitializationCallbacks = ArrayList>() + + // Warn the user if some new devices are detected while encrypting a message. + private var mWarnOnUnknownDevices = true + + // tell if there is a OTK check in progress + private var mOneTimeKeyCheckInProgress = false + + // last OTK check timestamp + private var mLastOneTimeKeyCheck: Long = 0 + + // Set of parameters used to configure/customize the end-to-end crypto. + private var mCryptoConfig: MXCryptoConfig? = null + + /** + * @return the encrypting thread handler + */ + // mEncryptingHandlerThread was not yet ready + // fail to get the handler + // might happen if the thread is not yet ready + val encryptingThreadHandler: Handler + get() { + if (null == mEncryptingHandler) { + mEncryptingHandler = Handler(mEncryptingHandlerThread!!.looper) + } + return if (null == mEncryptingHandler) { + mUIHandler + } else mEncryptingHandler!! + } + + /** + * Tells whether the client should ever send encrypted messages to unverified devices. + * The default value is false. + * This function must be called in the getEncryptingThreadHandler() thread. + * + * @return true to unilaterally blacklist all unverified devices. + */ + val globalBlacklistUnverifiedDevices: Boolean + get() = mCryptoStore.getGlobalBlacklistUnverifiedDevices() + + init { + if (null != cryptoConfig) { + mCryptoConfig = cryptoConfig + } else { + // Consider the default configuration value + mCryptoConfig = MXCryptoConfig() + } + + mRoomEncryptors = HashMap() + + var deviceId = mCredentials.deviceId + // deviceId should always be defined + val refreshDevicesList = !TextUtils.isEmpty(deviceId) + + if (TextUtils.isEmpty(deviceId)) { + // use the stored one + deviceId = this.mCryptoStore.getDeviceId() + + // Should not happen anymore + TODO() + //mSession.setDeviceId(deviceId) + } + + if (TextUtils.isEmpty(deviceId)) { + deviceId = UUID.randomUUID().toString() + // Should not happen anymore + TODO() + //mSession.setDeviceId(deviceId) + Timber.d("Warning: No device id in MXCredentials. An id was created. Think of storing it") + this.mCryptoStore.storeDeviceId(deviceId) + } + + myDevice = MXDeviceInfo(deviceId!!, mCredentials.userId) + + val keys = HashMap() + + if (!TextUtils.isEmpty(mOlmDevice.deviceEd25519Key)) { + keys["ed25519:" + mCredentials.deviceId] = mOlmDevice.deviceEd25519Key!! + } + + if (!TextUtils.isEmpty(mOlmDevice.deviceCurve25519Key)) { + keys["curve25519:" + mCredentials.deviceId] = mOlmDevice.deviceCurve25519Key!! + } + + myDevice.keys = keys + + myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms() + myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + + // Add our own deviceinfo to the store + val endToEndDevicesForUser = this.mCryptoStore.getUserDevices(mCredentials.userId) + + val myDevices: MutableMap + + if (null != endToEndDevicesForUser) { + myDevices = HashMap(endToEndDevicesForUser) + } else { + myDevices = HashMap() + } + + myDevices[myDevice.deviceId] = myDevice + + this.mCryptoStore.storeUserDevices(mCredentials.userId, myDevices) + + mEncryptingHandlerThread = HandlerThread("MXCrypto_encrypting_" + mCredentials.userId, Thread.MIN_PRIORITY) + mEncryptingHandlerThread!!.start() + + mDecryptingHandlerThread = HandlerThread("MXCrypto_decrypting_" + mCredentials.userId, Thread.MIN_PRIORITY) + mDecryptingHandlerThread!!.start() + + mUIHandler = Handler(Looper.getMainLooper()) + + if (refreshDevicesList) { + // ensure to have the up-to-date devices list + // got some issues when upgrading from Riot < 0.6.4 + deviceListManager.handleDeviceListsChanges(listOf(mCredentials.userId), null) + } + + mOutgoingRoomKeyRequestManager.setWorkingHandler(encryptingThreadHandler) + mIncomingRoomKeyRequestManager.setEncryptingThreadHandler(encryptingThreadHandler) + + mKeysBackup.setCryptoInternalListener(this) + mSasVerificationService.setCryptoInternalListener(this) + deviceListManager.setCryptoInternalListener(this) + } + + override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) { + mSetDeviceNameTask + .configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + + override fun deleteDevice(deviceId: String, accountPassword: String, callback: MatrixCallback) { + mDeleteDeviceTask + .configureWith(DeleteDeviceTask.Params(deviceId, accountPassword)) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + + override fun getCryptoVersion(context: Context, longFormat: Boolean): String { + return if (longFormat) mOlmManager.getDetailedVersion(context) else mOlmManager.version + } + + override fun getMyDevice(): MXDeviceInfo { + return myDevice + } + + override fun getDevicesList(callback: MatrixCallback) { + mGetDevicesTask + .configureWith(Unit) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + + /** + * @return the decrypting thread handler + */ + fun getDecryptingThreadHandler(): Handler { + // mDecryptingHandlerThread was not yet ready + if (null == mDecryptingHandler) { + mDecryptingHandler = Handler(mDecryptingHandlerThread!!.looper) + } + + // fail to get the handler + // might happen if the thread is not yet ready + return if (null == mDecryptingHandler) { + mUIHandler + } else mDecryptingHandler!! + } + + override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int { + return mCryptoStore.inboundGroupSessionsCount(onlyBackedUp) + } + + /** + * @return true if this instance has been released + */ + override fun hasBeenReleased(): Boolean { + return null == mOlmDevice + } + + /** + * Provides the tracking status + * + * @param userId the user id + * @return the tracking status + */ + override fun getDeviceTrackingStatus(userId: String): Int { + return mCryptoStore.getDeviceTrackingStatus(userId, DeviceListManager.TRACKING_STATUS_NOT_TRACKED) + } + + /** + * Tell if the MXCrypto is started + * + * @return true if the crypto is started + */ + fun isStarted(): Boolean { + return mIsStarted + } + + /** + * Tells if the MXCrypto is starting. + * + * @return true if the crypto is starting + */ + fun isStarting(): Boolean { + return mIsStarting + } + + /** + * Start the crypto module. + * Device keys will be uploaded, then one time keys if there are not enough on the homeserver + * and, then, if this is the first time, this new device will be announced to all other users + * devices. + * + * @param isInitialSync true if it starts from an initial sync + * @param aCallback the asynchronous callback + */ + fun start(isInitialSync: Boolean, aCallback: MatrixCallback?) { + synchronized(mInitializationCallbacks) { + if (null != aCallback && mInitializationCallbacks.indexOf(aCallback) < 0) { + mInitializationCallbacks.add(aCallback) + } + } + + if (mIsStarting) { + return + } + + // do not start if there is not network connection + // TODO + //if (null != mNetworkConnectivityReceiver && !mNetworkConnectivityReceiver!!.isConnected()) { + // // wait that a valid network connection is retrieved + // mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener) + // mNetworkConnectivityReceiver!!.addEventListener(mNetworkListener) + // return + //} + + mIsStarting = true + + // Open the store + mCryptoStore.open() + + encryptingThreadHandler.post { + uploadDeviceKeys(object : MatrixCallback { + private fun onError() { + mUIHandler.postDelayed({ + if (!isStarted()) { + mIsStarting = false + start(isInitialSync, null) + } + }, 1000) + } + + override fun onSuccess(data: KeysUploadResponse) { + encryptingThreadHandler.post { + if (!hasBeenReleased()) { + Timber.d("###########################################################") + Timber.d("uploadDeviceKeys done for " + mCredentials.userId) + Timber.d(" - device id : " + mCredentials.deviceId) + Timber.d(" - ed25519 : " + mOlmDevice.deviceEd25519Key) + Timber.d(" - curve25519 : " + mOlmDevice.deviceCurve25519Key) + Timber.d(" - oneTimeKeys: " + mLastPublishedOneTimeKeys) + Timber.d("") + + encryptingThreadHandler.post { + maybeUploadOneTimeKeys(object : MatrixCallback { + override fun onSuccess(data: Unit) { + encryptingThreadHandler.post { + // TODO + //if (null != mNetworkConnectivityReceiver) { + // mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener) + //} + + mIsStarting = false + mIsStarted = true + + mOutgoingRoomKeyRequestManager.start() + + mKeysBackup.checkAndStartKeysBackup() + + synchronized(mInitializationCallbacks) { + for (callback in mInitializationCallbacks) { + mUIHandler.post { callback.onSuccess(Unit) } + } + mInitializationCallbacks.clear() + } + + if (isInitialSync) { + encryptingThreadHandler.post { + // refresh the devices list for each known room members + deviceListManager.invalidateAllDeviceLists() + deviceListManager.refreshOutdatedDeviceLists() + } + } else { + encryptingThreadHandler.post { + mIncomingRoomKeyRequestManager.processReceivedRoomKeyRequests() + + } + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## start failed") + onError() + } + }) + } + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## start failed") + onError() + } + }) + } + } + + /** + * Close the crypto + */ + fun close() { + if (null != mEncryptingHandlerThread) { + encryptingThreadHandler.post { + mOlmDevice.release() + + // Do not reset My Device + // mMyDevice = null; + + mCryptoStore.close() + // Do not reset Crypto store + // mCryptoStore = null; + + if (null != mEncryptingHandlerThread) { + mEncryptingHandlerThread!!.quit() + mEncryptingHandlerThread = null + } + + mOutgoingRoomKeyRequestManager.stop() + } + + getDecryptingThreadHandler().post { + if (null != mDecryptingHandlerThread) { + mDecryptingHandlerThread!!.quit() + mDecryptingHandlerThread = null + } + } + } + } + + override fun isCryptoEnabled(): Boolean { + // TODO Check that this test is correct + return mOlmDevice != null + } + + /** + * @return the Keys backup Service + */ + override fun getKeysBackupService(): KeysBackupService { + return mKeysBackup + } + + /** + * @return the SasVerificationService + */ + override fun getSasVerificationService(): SasVerificationService { + return mSasVerificationService + } + + /** + * A sync response has been received + * + * @param syncResponse the syncResponse + * @param fromToken the start sync token + * @param isCatchingUp true if there is a catch-up in progress. + */ + fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean) { + encryptingThreadHandler.post { + if (null != syncResponse.deviceLists) { + deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left) + } + + if (null != syncResponse.deviceOneTimeKeysCount) { + val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0 + updateOneTimeKeyCount(currentCount) + } + + if (isStarted()) { + // Make sure we process to-device messages before generating new one-time-keys #2782 + deviceListManager.refreshOutdatedDeviceLists() + } + + if (!isCatchingUp && isStarted()) { + maybeUploadOneTimeKeys() + + mIncomingRoomKeyRequestManager.processReceivedRoomKeyRequests() + } + } + } + + /** + * Get the stored device keys for a user. + * + * @param userId the user to list keys for. + * @param callback the asynchronous callback + */ + fun getUserDevices(userId: String, callback: MatrixCallback>?) { + encryptingThreadHandler.post { + val list = getUserDevices(userId) + + if (null != callback) { + mUIHandler.post { callback.onSuccess(list) } + } + } + } + + /** + * Stores the current one_time_key count which will be handled later (in a call of + * _onSyncCompleted). The count is e.g. coming from a /sync response. + * + * @param currentCount the new count + */ + private fun updateOneTimeKeyCount(currentCount: Int) { + mOneTimeKeyCount = currentCount + } + + /** + * Find a device by curve25519 identity key + * + * @param senderKey the curve25519 key to match. + * @param algorithm the encryption algorithm. + * @return the device info, or null if not found / unsupported algorithm / crypto released + */ + override fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? { + return if (!hasBeenReleased()) { + if (!TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_MEGOLM) && !TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_OLM)) { + // We only deal in olm keys + null + } else mCryptoStore.deviceWithIdentityKey(senderKey) + + // Find in the crypto store + } else null + + // The store is released + } + + /** + * Provides the device information for a device id and a user Id + * + * @param userId the user id + * @param deviceId the device id + * @param callback the asynchronous callback + */ + override fun getDeviceInfo(userId: String, deviceId: String?, callback: MatrixCallback) { + getDecryptingThreadHandler().post { + val di: MXDeviceInfo? + + if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) { + di = mCryptoStore.getUserDevice(deviceId!!, userId) + } else { + di = null + } + + mUIHandler.post { callback.onSuccess(di) } + } + } + + /** + * Set the devices as known + * + * @param devices the devices. Note that the mVerified member of the devices in this list will not be updated by this method. + * @param callback the asynchronous callback + */ + override fun setDevicesKnown(devices: List, callback: MatrixCallback?) { + if (hasBeenReleased()) { + return + } + encryptingThreadHandler.post { + // build a devices map + val devicesIdListByUserId = HashMap>() + + for (di in devices) { + var deviceIdsList: MutableList? = devicesIdListByUserId[di.userId]?.toMutableList() + + if (null == deviceIdsList) { + deviceIdsList = ArrayList() + devicesIdListByUserId[di.userId] = deviceIdsList + } + deviceIdsList.add(di.deviceId) + } + + val userIds = devicesIdListByUserId.keys + + for (userId in userIds) { + val storedDeviceIDs = mCryptoStore.getUserDevices(userId) + + // sanity checks + if (null != storedDeviceIDs) { + var isUpdated = false + val deviceIds = devicesIdListByUserId[userId] + + for (deviceId in deviceIds!!) { + val device = storedDeviceIDs[deviceId] + + // assume if the device is either verified or blocked + // it means that the device is known + if (null != device && device.isUnknown) { + device.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED + isUpdated = true + } + } + + if (isUpdated) { + mCryptoStore.storeUserDevices(userId, storedDeviceIDs) + } + } + } + + if (null != callback) { + mUIHandler.post { callback.onSuccess(Unit) } + } + } + } + + /** + * Update the blocked/verified state of the given device. + * + * @param verificationStatus the new verification status + * @param deviceId the unique identifier for the device. + * @param userId the owner of the device + * @param callback the asynchronous callback + */ + override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String, callback: MatrixCallback) { + if (hasBeenReleased()) { + return + } + + encryptingThreadHandler.post(Runnable { + val device = mCryptoStore.getUserDevice(deviceId, userId) + + // Sanity check + if (null == device) { + Timber.e("## setDeviceVerification() : Unknown device $userId:$deviceId") + mUIHandler.post { callback.onSuccess(Unit) } + return@Runnable + } + + if (device.mVerified != verificationStatus) { + device.mVerified = verificationStatus + mCryptoStore.storeUserDevice(userId, device) + + if (userId == mCredentials.userId) { + // If one of the user's own devices is being marked as verified / unverified, + // check the key backup status, since whether or not we use this depends on + // whether it has a signature from a verified device + mKeysBackup.checkAndStartKeysBackup() + } + } + + mUIHandler.post { callback.onSuccess(Unit) } + }) + } + + /** + * Configure a room to use encryption. + * This method must be called in getEncryptingThreadHandler + * + * @param roomId the room id to enable encryption in. + * @param algorithm the encryption config for the room. + * @param inhibitDeviceQuery true to suppress device list query for users in the room (for now) + * @param membersId list of members to start tracking their devices + * @return true if the operation succeeds. + */ + private fun setEncryptionInRoom(roomId: String, algorithm: String?, inhibitDeviceQuery: Boolean, membersId: List): Boolean { + if (hasBeenReleased()) { + return false + } + + // If we already have encryption in this room, we should ignore this event + // (for now at least. Maybe we should alert the user somehow?) + val existingAlgorithm = mCryptoStore.getRoomAlgorithm(roomId) + + if (!TextUtils.isEmpty(existingAlgorithm) && !TextUtils.equals(existingAlgorithm, algorithm)) { + Timber.e("## setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId") + return false + } + + val encryptingClass = MXCryptoAlgorithms.encryptorClassForAlgorithm(algorithm) + + if (null == encryptingClass) { + Timber.e("## setEncryptionInRoom() : Unable to encrypt with " + algorithm!!) + return false + } + + mCryptoStore.storeRoomAlgorithm(roomId, algorithm!!) + + val alg: IMXEncrypting + + try { + val ctor = encryptingClass.constructors[0] + alg = ctor.newInstance() as IMXEncrypting + } catch (e: Exception) { + Timber.e(e, "## setEncryptionInRoom() : fail to load the class") + return false + } + + alg.initWithMatrixSession(this, + mOlmDevice, + deviceListManager, + mCredentials, + mSendToDeviceTask, + mTaskExecutor, + roomId) + + synchronized(mRoomEncryptors) { + mRoomEncryptors.put(roomId, alg) + } + + // if encryption was not previously enabled in this room, we will have been + // ignoring new device events for these users so far. We may well have + // up-to-date lists for some users, for instance if we were sharing other + // e2e rooms with them, so there is room for optimisation here, but for now + // we just invalidate everyone in the room. + if (null == existingAlgorithm) { + Timber.d("Enabling encryption in $roomId for the first time; invalidating device lists for all users therein") + + val userIds = ArrayList(membersId) + + deviceListManager.startTrackingDeviceList(userIds) + + if (!inhibitDeviceQuery) { + deviceListManager.refreshOutdatedDeviceLists() + } + } + + return true + } + + /** + * Tells if a room is encrypted + * + * @param roomId the room id + * @return true if the room is encrypted + */ + fun isRoomEncrypted(roomId: String?): Boolean { + var res = false + + if (null != roomId) { + synchronized(mRoomEncryptors) { + res = mRoomEncryptors.containsKey(roomId) + + if (!res) { + val room = mRoomService.getRoom(roomId) + + if (null != room) { + res = room.isEncrypted() + } + } + } + } + + return res + } + + /** + * @return the stored device keys for a user. + */ + override fun getUserDevices(userId: String): MutableList { + val map = mCryptoStore.getUserDevices(userId) + return if (null != map) ArrayList(map.values) else ArrayList() + } + + /** + * Try to make sure we have established olm sessions for the given users. + * It must be called in getEncryptingThreadHandler() thread. + * The callback is called in the UI thread. + * + * @param users a list of user ids. + * @param callback the asynchronous callback + */ + fun ensureOlmSessionsForUsers(users: List, callback: MatrixCallback>) { + Timber.d("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") + + val devicesByUser = HashMap>() + + for (userId in users) { + devicesByUser[userId] = ArrayList() + + val devices = getUserDevices(userId) + + for (device in devices) { + val key = device.identityKey() + + if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue + } + + if (device.isVerified) { + // Don't bother setting up sessions with blocked users + continue + } + + devicesByUser[userId]!!.add(device) + } + } + + ensureOlmSessionsForDevices(devicesByUser, callback) + } + + /** + * Try to make sure we have established olm sessions for the given devices. + * It must be called in getCryptoHandler() thread. + * The callback is called in the UI thread. + * + * @param devicesByUser a map from userid to list of devices. + * @param callback the asynchronous callback + */ + fun ensureOlmSessionsForDevices(devicesByUser: Map>, + callback: MatrixCallback>?) { + val devicesWithoutSession = ArrayList() + + val results = MXUsersDevicesMap() + + val userIds = devicesByUser.keys + + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + + for (deviceInfo in deviceInfos!!) { + val deviceId = deviceInfo.deviceId + val key = deviceInfo.identityKey() + + val sessionId = mOlmDevice.getSessionId(key!!) + + if (TextUtils.isEmpty(sessionId)) { + devicesWithoutSession.add(deviceInfo) + } + + val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId) + results.setObject(olmSessionResult, userId, deviceId) + } + } + + if (devicesWithoutSession.size == 0) { + if (null != callback) { + mUIHandler.post { callback.onSuccess(results) } + } + return + } + + // Prepare the request for claiming one-time keys + val usersDevicesToClaim = MXUsersDevicesMap() + + val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE + + for (device in devicesWithoutSession) { + usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId) + } + + // TODO: this has a race condition - if we try to send another message + // while we are claiming a key, we will end up claiming two and setting up + // two sessions. + // + // That should eventually resolve itself, but it's poor form. + + Timber.d("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") + + mClaimOneTimeKeysForUsersDeviceTask + .configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)) + .dispatchTo(object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + encryptingThreadHandler.post { + try { + Timber.d("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data") + + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + + for (deviceInfo in deviceInfos!!) { + + var oneTimeKey: MXKey? = null + + val deviceIds = data.getUserDeviceIds(userId) + + if (null != deviceIds) { + for (deviceId in deviceIds) { + val olmSessionResult = results.getObject(deviceId, userId) + + if (null != olmSessionResult!!.mSessionId) { + // We already have a result for this device + continue + } + + val key = data.getObject(deviceId, userId) + + if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) { + oneTimeKey = key + } + + if (null == oneTimeKey) { + Timber.d("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) + continue + } + + // Update the result for this device in results + olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) + } + } + } + } + } catch (e: Exception) { + Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message) + } + + if (!hasBeenReleased()) { + if (null != callback) { + mUIHandler.post { callback.onSuccess(results) } + } + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed") + + callback?.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } + + private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { + var sessionId: String? = null + + val deviceId = deviceInfo.deviceId + val signKeyId = "ed25519:$deviceId" + val signature = oneTimeKey.signatureForUserId(userId, signKeyId) + + if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) { + var isVerified = false + var errorMessage: String? = null + + try { + mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) + isVerified = true + } catch (e: Exception) { + errorMessage = e.message + } + + // Check one-time key signature + if (isVerified) { + sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) + + if (!TextUtils.isEmpty(sessionId)) { + Timber.d("## verifyKeyAndStartSession() : Started new sessionid " + sessionId + + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") + } else { + // Possibly a bad key + Timber.e("## verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId") + } + } else { + Timber.e("## verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId + + ":" + deviceId + " Error " + errorMessage) + } + } + + return sessionId + } + + + /** + * Encrypt an event content according to the configuration of the room. + * + * @param eventContent the content of the event. + * @param eventType the type of the event. + * @param room the room the event will be sent. + * @param callback the asynchronous callback + */ + fun encryptEventContent(eventContent: Content, + eventType: String, + room: Room, + callback: MatrixCallback) { + // wait that the crypto is really started + if (!isStarted()) { + Timber.d("## encryptEventContent() : wait after e2e init") + + start(false, object : MatrixCallback { + override fun onSuccess(data: Unit) { + encryptEventContent(eventContent, eventType, room, callback) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## encryptEventContent() : onNetworkError while waiting to start e2e") + + callback.onFailure(failure) + } + }) + + return + } + + // Check whether the event content must be encrypted for the invited members. + val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() + + val userIds = if (encryptForInvitedMembers) { + room.getActiveRoomMemberIds() + } else { + room.getJoinedRoomMemberIds() + } + + // just as you are sending a secret message? + + encryptingThreadHandler.post { + var alg: IMXEncrypting? + + synchronized(mRoomEncryptors) { + alg = mRoomEncryptors[room.roomId] + } + + if (null == alg) { + val algorithm = room.encryptionAlgorithm() + + if (null != algorithm) { + if (setEncryptionInRoom(room.roomId, algorithm, false, userIds)) { + synchronized(mRoomEncryptors) { + alg = mRoomEncryptors[room.roomId] + } + } + } + } + + if (null != alg) { + val t0 = System.currentTimeMillis() + Timber.d("## encryptEventContent() starts") + + alg!!.encryptEventContent(eventContent, eventType, userIds, object : MatrixCallback { + override fun onSuccess(data: Content) { + Timber.d("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms") + + callback.onSuccess(MXEncryptEventContentResult(data, EventType.ENCRYPTED)) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + } else { + val algorithm = room.encryptionAlgorithm() + val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, + algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) + Timber.e("## encryptEventContent() : $reason") + + mUIHandler.post { + callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, + MXCryptoError.UNABLE_TO_ENCRYPT, reason))) + } + } + } + } + + /** + * Decrypt an event + * + * @param event the raw event. + * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. + * @return the MXEventDecryptionResult data, or null in case of error + */ + @Throws(MXDecryptionException::class) + override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + val eventContent = event.content //wireEventContent? + + if (null == eventContent) { + Timber.e("## decryptEvent : empty event content") + return null + } + + val results = ArrayList() + val lock = CountDownLatch(1) + val exceptions = ArrayList() + + getDecryptingThreadHandler().post { + var result: MXEventDecryptionResult? = null + val alg = roomDecryptorProvider.getRoomDecryptor(event.roomId, eventContent["algorithm"] as String) + + if (null == alg) { + val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String) + Timber.e("## decryptEvent() : $reason") + exceptions.add(MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, reason))) + } else { + try { + result = alg.decryptEvent(event, timeline) + } catch (decryptionException: MXDecryptionException) { + exceptions.add(decryptionException) + } + + if (null != result) { + results.add(result) + } + } + lock.countDown() + } + + try { + lock.await() + } catch (e: Exception) { + Timber.e(e, "## decryptEvent() : failed") + } + + if (!exceptions.isEmpty()) { + throw exceptions[0] + } + + return if (!results.isEmpty()) { + results[0] + } else null + + } + + /** + * Reset replay attack data for the given timeline. + * + * @param timelineId the timeline id + */ + fun resetReplayAttackCheckInTimeline(timelineId: String) { + getDecryptingThreadHandler().post { mOlmDevice.resetReplayAttackCheckInTimeline(timelineId) } + } + + /** + * Encrypt an event payload for a list of devices. + * This method must be called from the getCryptoHandler() thread. + * + * @param payloadFields fields to include in the encrypted payload. + * @param deviceInfos list of device infos to encrypt for. + * @return the content for an m.room.encrypted event. + */ + fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { + if (hasBeenReleased()) { + // Empty object + return EncryptedMessage() + } + + val deviceInfoParticipantKey = HashMap() + val participantKeys = ArrayList() + + for (di in deviceInfos) { + participantKeys.add(di.identityKey()!!) + deviceInfoParticipantKey[di.identityKey()!!] = di + } + + val payloadJson = HashMap(payloadFields) + + payloadJson["sender"] = mCredentials.userId + payloadJson["sender_device"] = mCredentials.deviceId + + // Include the Ed25519 key so that the recipient knows what + // device this message came from. + // We don't need to include the curve25519 key since the + // recipient will already know this from the olm headers. + // When combined with the device keys retrieved from the + // homeserver signed by the ed25519 key this proves that + // the curve25519 key and the ed25519 key are owned by + // the same device. + val keysMap = HashMap() + keysMap["ed25519"] = mOlmDevice.deviceEd25519Key!! + payloadJson["keys"] = keysMap + + val ciphertext = HashMap() + + for (deviceKey in participantKeys) { + val sessionId = mOlmDevice.getSessionId(deviceKey) + + if (!TextUtils.isEmpty(sessionId)) { + Timber.d("Using sessionid $sessionId for device $deviceKey") + val deviceInfo = deviceInfoParticipantKey[deviceKey] + + payloadJson["recipient"] = deviceInfo!!.userId + + val recipientsKeysMap = HashMap() + recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!! + payloadJson["recipient_keys"] = recipientsKeysMap + + // FIXME We have to canonicalize the JSON + //JsonUtility.canonicalize(JsonUtility.getGson(false).toJsonTree(payloadJson)).toString() + + val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) + ciphertext[deviceKey] = mOlmDevice.encryptMessage(deviceKey, sessionId!!, payloadString!!)!! + } + } + + val res = EncryptedMessage() + + res.algorithm = MXCRYPTO_ALGORITHM_OLM + res.senderKey = mOlmDevice.deviceCurve25519Key + res.cipherText = ciphertext + + return res + } + + /** + * Sign Object + * + * Example: + *
    +     *     {
    +     *         "[MY_USER_ID]": {
    +     *             "ed25519:[MY_DEVICE_ID]": "sign(str)"
    +     *         }
    +     *     }
    +     * 
    + * + * @param strToSign the String to sign and to include in the Map + * @return a Map (see example) + */ + override fun signObject(strToSign: String): Map> { + val result = HashMap>() + + val content = HashMap() + + content["ed25519:" + myDevice.deviceId] = mOlmDevice.signMessage(strToSign)!! + + result[myDevice.userId] = content + + return result + } + + /** + * Handle the 'toDevice' event + * + * @param event the event + */ + fun onToDeviceEvent(event: Event) { + mSasVerificationService.onToDeviceEvent(event) + + if (TextUtils.equals(event.type, EventType.ROOM_KEY) || TextUtils.equals(event.type, EventType.FORWARDED_ROOM_KEY)) { + getDecryptingThreadHandler().post { onRoomKeyEvent(event) } + } else if (TextUtils.equals(event.type, EventType.ROOM_KEY_REQUEST)) { + encryptingThreadHandler.post { + mIncomingRoomKeyRequestManager.onRoomKeyRequestEvent(event) + } + } + } + + /** + * Handle a key event. + * This method must be called on getDecryptingThreadHandler() thread. + * + * @param event the key event. + */ + private fun onRoomKeyEvent(event: Event?) { + // sanity check + if (null == event) { + Timber.e("## onRoomKeyEvent() : null event") + return + } + + val roomKeyContent = event.content.toModel()!! + + if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.algorithm)) { + Timber.e("## onRoomKeyEvent() : missing fields") + return + } + + val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(this, roomKeyContent.roomId, roomKeyContent.algorithm) + + if (null == alg) { + Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm!!) + return + } + + alg.onRoomKeyEvent(event) + } + + /** + * Handle an m.room.encryption event. + * + * @param event the encryption event. + */ + fun onCryptoEvent(roomId: String, event: Event) { + val eventContent = event.content // wireEventContent + + val room = mRoomService.getRoom(roomId)!! + + // Check whether the event content must be encrypted for the invited members. + val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() + + val userIds = if (encryptForInvitedMembers) { + room.getActiveRoomMemberIds() + } else { + room.getJoinedRoomMemberIds() + } + + encryptingThreadHandler.post { setEncryptionInRoom(roomId, eventContent!!["algorithm"] as String, true, userIds) } + } + + /** + * Handle a change in the membership state of a member of a room. + * + * @param event the membership event causing the change + */ + private fun onRoomMembershipEvent(roomId: String, event: Event) { + val alg: IMXEncrypting? + + synchronized(mRoomEncryptors) { + alg = mRoomEncryptors[roomId] + } + + if (null == alg) { + // No encrypting in this room + return + } + + val userId = event.stateKey!! + val room = mRoomService.getRoom(roomId) + + val roomMember = room?.getRoomMember(userId) + + if (null != roomMember) { + val membership = roomMember.membership + + encryptingThreadHandler.post { + if (membership == Membership.JOIN) { + // make sure we are tracking the deviceList for this user. + deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) + } else if (membership == Membership.INVITE + && room.shouldEncryptForInvitedMembers() + && mCryptoConfig!!.mEnableEncryptionForInvitedMembers) { + // track the deviceList for this invited user. + // Caution: there's a big edge case here in that federated servers do not + // know what other servers are in the room at the time they've been invited. + // They therefore will not send device updates if a user logs in whilst + // their state is invite. + deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) + } + } + } + } + + /** + * Upload my user's device keys. + * This method must called on getEncryptingThreadHandler() thread. + * The callback will called on UI thread. + * + * @param callback the asynchronous callback + */ + private fun uploadDeviceKeys(callback: MatrixCallback) { + // Prepare the device keys data to send + // Sign it + val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary()) + + myDevice.signatures = signObject(canonicalJson) + + // For now, we set the device id explicitly, as we may not be using the + // same one as used in login. + mUploadKeysTask + .configureWith(UploadKeysTask.Params(myDevice.toDeviceKeys(), null, myDevice.deviceId)) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + + /** + * OTK upload loop + * + * @param keyCount the number of key to generate + * @param keyLimit the limit + * @param callback the asynchronous callback + */ + private fun uploadLoop(keyCount: Int, keyLimit: Int, callback: MatrixCallback) { + if (keyLimit <= keyCount) { + // If we don't need to generate any more keys then we are done. + mUIHandler.post { callback.onSuccess(Unit) } + return + } + + val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) + + mOlmDevice.generateOneTimeKeys(keysThisLoop) + + uploadOneTimeKeys(object : MatrixCallback { + override fun onSuccess(data: KeysUploadResponse) { + encryptingThreadHandler.post { + if (data.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { + uploadLoop(data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit, callback) + } else { + Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") + mUIHandler.post { + callback.onFailure( + Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")) + } + } + } + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + } + + /** + * Check if the OTK must be uploaded. + * + * @param callback the asynchronous callback + */ + private fun maybeUploadOneTimeKeys(callback: MatrixCallback? = null) { + if (mOneTimeKeyCheckInProgress) { + mUIHandler.post { + callback?.onSuccess(Unit) + } + return + } + + if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { + // we've done a key upload recently. + mUIHandler.post { + callback?.onSuccess(Unit) + } + return + } + + mLastOneTimeKeyCheck = System.currentTimeMillis() + + mOneTimeKeyCheckInProgress = true + + // We then check how many keys we can store in the Account object. + val maxOneTimeKeys = mOlmDevice.getMaxNumberOfOneTimeKeys() + + // Try to keep at most half that number on the server. This leaves the + // rest of the slots free to hold keys that have been claimed from the + // server but we haven't recevied a message for. + // If we run out of slots when generating new keys then olm will + // discard the oldest private keys first. This will eventually clean + // out stale private keys that won't receive a message. + val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt() + + if (null != mOneTimeKeyCount) { + uploadOTK(mOneTimeKeyCount!!, keyLimit, callback) + } else { + // ask the server how many keys we have + mUploadKeysTask + .configureWith(UploadKeysTask.Params(null, null, myDevice.deviceId)) + .dispatchTo(object : MatrixCallback { + + override fun onSuccess(data: KeysUploadResponse) { + encryptingThreadHandler.post { + if (!hasBeenReleased()) { + // We need to keep a pool of one time public keys on the server so that + // other devices can start conversations with us. But we can only store + // a finite number of private keys in the olm Account object. + // To complicate things further then can be a delay between a device + // claiming a public one time key from the server and it sending us a + // message. We need to keep the corresponding private key locally until + // we receive the message. + // But that message might never arrive leaving us stuck with duff + // private keys clogging up our local storage. + // So we need some kind of enginering compromise to balance all of + // these factors. + val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) + uploadOTK(keyCount, keyLimit, callback) + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## uploadKeys() : failed") + + mOneTimeKeyCount = null + mOneTimeKeyCheckInProgress = false + + mUIHandler.post { + callback?.onFailure(failure) + } + } + }) + .executeBy(mTaskExecutor) + } + } + + /** + * Upload some the OTKs. + * + * @param keyCount the key count + * @param keyLimit the limit + * @param callback the asynchronous callback + */ + private fun uploadOTK(keyCount: Int, keyLimit: Int, callback: MatrixCallback?) { + uploadLoop(keyCount, keyLimit, object : MatrixCallback { + private fun uploadKeysDone(errorMessage: String?) { + if (null != errorMessage) { + Timber.e("## maybeUploadOneTimeKeys() : failed $errorMessage") + } + mOneTimeKeyCount = null + mOneTimeKeyCheckInProgress = false + } + + override fun onSuccess(data: Unit) { + Timber.d("## maybeUploadOneTimeKeys() : succeeded") + uploadKeysDone(null) + + mUIHandler.post { + callback?.onSuccess(Unit) + } + } + + override fun onFailure(failure: Throwable) { + uploadKeysDone(failure.message) + + mUIHandler.post { + callback?.onFailure(failure) + } + } + }) + + } + + /** + * Upload my user's one time keys. + * This method must called on getEncryptingThreadHandler() thread. + * The callback will called on UI thread. + * + * @param callback the asynchronous callback + */ + private fun uploadOneTimeKeys(callback: MatrixCallback?) { + val oneTimeKeys = mOlmDevice.getOneTimeKeys() + val oneTimeJson = HashMap() + + val curve25519Map = oneTimeKeys!![OlmAccount.JSON_KEY_ONE_TIME_KEY] + + if (null != curve25519Map) { + for (key_id in curve25519Map.keys) { + val k = HashMap() + k["key"] = curve25519Map[key_id]!! + + // the key is also signed + val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, k) + + k["signatures"] = signObject(canonicalJson) + + oneTimeJson["signed_curve25519:$key_id"] = k + } + } + + // For now, we set the device id explicitly, as we may not be using the + // same one as used in login. + mUploadKeysTask + .configureWith(UploadKeysTask.Params(null, oneTimeJson, myDevice.deviceId)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: KeysUploadResponse) { + encryptingThreadHandler.post { + if (!hasBeenReleased()) { + mLastPublishedOneTimeKeys = oneTimeKeys + mOlmDevice.markKeysAsPublished() + + if (null != callback) { + mUIHandler.post { callback.onSuccess(data) } + } + } + } + } + + override fun onFailure(failure: Throwable) { + if (null != callback) { + mUIHandler.post { callback.onFailure(failure) } + } + + } + }) + .executeBy(mTaskExecutor) + } + + + /** + * Export the crypto keys + * + * @param password the password + * @param callback the exported keys + */ + override fun exportRoomKeys(password: String, callback: MatrixCallback) { + exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT, callback) + } + + /** + * Export the crypto keys + * + * @param password the password + * @param anIterationCount the encryption iteration count (0 means no encryption) + * @param callback the exported keys + */ + fun exportRoomKeys(password: String, anIterationCount: Int, callback: MatrixCallback) { + val iterationCount = Math.max(0, anIterationCount) + + getDecryptingThreadHandler().post(Runnable { + val exportedSessions = ArrayList() + + val inboundGroupSessions = mCryptoStore.getInboundGroupSessions() + + for (session in inboundGroupSessions) { + val megolmSessionData = session.exportKeys() + + if (null != megolmSessionData) { + exportedSessions.add(megolmSessionData) + } + } + + val encryptedRoomKeys: ByteArray + + try { + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(List::class.java) + + encryptedRoomKeys = MXMegolmExportEncryption + .encryptMegolmKeyFile(adapter.toJson(exportedSessions), password, iterationCount) + } catch (e: Exception) { + callback.onFailure(e) + return@Runnable + } + + mUIHandler.post { callback.onSuccess(encryptedRoomKeys) } + }) + } + + /** + * Import the room keys + * + * @param roomKeysAsArray the room keys as array. + * @param password the password + * @param progressListener the progress listener + * @param callback the asynchronous callback. + */ + override fun importRoomKeys(roomKeysAsArray: ByteArray, + password: String, + progressListener: ProgressListener?, + callback: MatrixCallback) { + getDecryptingThreadHandler().post(Runnable { + Timber.d("## importRoomKeys starts") + + val t0 = System.currentTimeMillis() + val roomKeys: String + + try { + roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password) + } catch (e: Exception) { + mUIHandler.post { callback.onFailure(e) } + return@Runnable + } + + val importedSessions: List + + val t1 = System.currentTimeMillis() + + Timber.d("## importRoomKeys : decryptMegolmKeyFile done in " + (t1 - t0) + " ms") + + try { + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(List::class.java) + val list = adapter.fromJson(roomKeys) + importedSessions = list as List + } catch (e: Exception) { + Timber.e(e, "## importRoomKeys failed") + mUIHandler.post { callback.onFailure(e) } + return@Runnable + } + + val t2 = System.currentTimeMillis() + + Timber.d("## importRoomKeys : JSON parsing " + (t2 - t1) + " ms") + + importMegolmSessionsData(importedSessions, true, progressListener, callback) + }) + } + + /** + * Import a list of megolm session keys. + * + * @param megolmSessionsData megolm sessions. + * @param backUpKeys true to back up them to the homeserver. + * @param progressListener the progress listener + * @param callback + */ + override fun importMegolmSessionsData(megolmSessionsData: List, + backUpKeys: Boolean, + progressListener: ProgressListener?, + callback: MatrixCallback) { + getDecryptingThreadHandler().post { + val t0 = System.currentTimeMillis() + + val totalNumbersOfKeys = megolmSessionsData.size + var cpt = 0 + var lastProgress = 0 + var totalNumbersOfImportedKeys = 0 + + if (progressListener != null) { + mUIHandler.post { progressListener.onProgress(0, 100) } + } + + val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData) + + for (megolmSessionData in megolmSessionsData) { + cpt++ + + + val decrypting = roomDecryptorProvider.getRoomDecryptor(megolmSessionData.roomId, megolmSessionData.algorithm) + + if (null != decrypting) { + try { + val sessionId = megolmSessionData.sessionId + Timber.d("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId) + + totalNumbersOfImportedKeys++ + + // cancel any outstanding room key requests for this session + val roomKeyRequestBody = RoomKeyRequestBody() + + roomKeyRequestBody.algorithm = megolmSessionData.algorithm + roomKeyRequestBody.roomId = megolmSessionData.roomId + roomKeyRequestBody.senderKey = megolmSessionData.senderKey + roomKeyRequestBody.sessionId = megolmSessionData.sessionId + + cancelRoomKeyRequest(roomKeyRequestBody) + + // Have another go at decrypting events sent with this session + decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!) + } catch (e: Exception) { + Timber.e(e, "## importRoomKeys() : onNewSession failed") + } + } + + if (progressListener != null) { + val progress = 100 * cpt / totalNumbersOfKeys + + if (lastProgress != progress) { + lastProgress = progress + + mUIHandler.post { progressListener.onProgress(progress, 100) } + } + } + } + + // Do not back up the key if it comes from a backup recovery + if (backUpKeys) { + mKeysBackup.maybeBackupKeys() + } else { + mCryptoStore.markBackupDoneForInboundGroupSessions(sessions) + } + + val t1 = System.currentTimeMillis() + + Timber.d("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)") + + val finalTotalNumbersOfImportedKeys = totalNumbersOfImportedKeys + + mUIHandler.post { callback.onSuccess(ImportRoomKeysResult(totalNumbersOfKeys, finalTotalNumbersOfImportedKeys)) } + } + } + + /** + * Tells if the encryption must fail if some unknown devices are detected. + * + * @return true to warn when some unknown devices are detected. + */ + fun warnOnUnknownDevices(): Boolean { + return mWarnOnUnknownDevices + } + + /** + * Update the warn status when some unknown devices are detected. + * + * @param warn true to warn when some unknown devices are detected. + */ + override fun setWarnOnUnknownDevices(warn: Boolean) { + mWarnOnUnknownDevices = warn + } + + /** + * Check if the user ids list have some unknown devices. + * A success means there is no unknown devices. + * If there are some unknown devices, a MXCryptoError.UNKNOWN_DEVICES_CODE exception is triggered. + * + * @param userIds the user ids list + * @param callback the asynchronous callback. + */ + fun checkUnknownDevices(userIds: List, callback: MatrixCallback) { + // force the refresh to ensure that the devices list is up-to-date + deviceListManager.downloadKeys(userIds, true, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + val unknownDevices = getUnknownDevices(data) + + if (unknownDevices.map.size == 0) { + callback.onSuccess(Unit) + } else { + // trigger an an unknown devices exception + callback.onFailure( + Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, + MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) + } + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + } + + /** + * Set the global override for whether the client should ever send encrypted + * messages to unverified devices. + * If false, it can still be overridden per-room. + * If true, it overrides the per-room settings. + * + * @param block true to unilaterally blacklist all + * @param callback the asynchronous callback. + */ + override fun setGlobalBlacklistUnverifiedDevices(block: Boolean, callback: MatrixCallback?) { + encryptingThreadHandler.post { + mCryptoStore.setGlobalBlacklistUnverifiedDevices(block) + mUIHandler.post { + callback?.onSuccess(Unit) + } + } + } + + /** + * Tells whether the client should ever send encrypted messages to unverified devices. + * The default value is false. + * messages to unverified devices. + * + * @param callback the asynchronous callback + */ + override fun getGlobalBlacklistUnverifiedDevices(callback: MatrixCallback?) { + encryptingThreadHandler.post { + if (null != callback) { + val status = globalBlacklistUnverifiedDevices + + mUIHandler.post { callback.onSuccess(status) } + } + } + } + + /** + * Tells whether the client should encrypt messages only for the verified devices + * in this room. + * The default value is false. + * This function must be called in the getEncryptingThreadHandler() thread. + * + * @param roomId the room id + * @return true if the client should encrypt messages only for the verified devices. + */ + fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean { + return if (null != roomId) { + mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) + } else { + false + } + } + + /** + * Tells whether the client should encrypt messages only for the verified devices + * in this room. + * The default value is false. + * This function must be called in the getEncryptingThreadHandler() thread. + * + * @param roomId the room id + * @param callback the asynchronous callback + */ + override fun isRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback?) { + encryptingThreadHandler.post { + val status = isRoomBlacklistUnverifiedDevices(roomId) + + mUIHandler.post { + callback?.onSuccess(status) + } + } + } + + /** + * Manages the room black-listing for unverified devices. + * + * @param roomId the room id + * @param add true to add the room id to the list, false to remove it. + * @param callback the asynchronous callback + */ + private fun setRoomBlacklistUnverifiedDevices(roomId: String, add: Boolean, callback: MatrixCallback?) { + val room = mRoomService.getRoom(roomId) + + // sanity check + if (null == room) { + mUIHandler.post { callback!!.onSuccess(Unit) } + + return + } + + encryptingThreadHandler.post { + val roomIds = mCryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList() + + if (add) { + if (!roomIds.contains(roomId)) { + roomIds.add(roomId) + } + } else { + roomIds.remove(roomId) + } + + mCryptoStore.setRoomsListBlacklistUnverifiedDevices(roomIds) + + mUIHandler.post { + callback?.onSuccess(Unit) + } + } + } + + + /** + * Add this room to the ones which don't encrypt messages to unverified devices. + * + * @param roomId the room id + * @param callback the asynchronous callback + */ + override fun setRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) { + setRoomBlacklistUnverifiedDevices(roomId, true, callback) + } + + /** + * Remove this room to the ones which don't encrypt messages to unverified devices. + * + * @param roomId the room id + * @param callback the asynchronous callback + */ + override fun setRoomUnBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) { + setRoomBlacklistUnverifiedDevices(roomId, false, callback) + } + + /** + * Send a request for some room keys, if we have not already done so. + * + * @param requestBody requestBody + * @param recipients recipients + */ + fun requestRoomKey(requestBody: RoomKeyRequestBody, recipients: List>) { + encryptingThreadHandler.post { mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients) } + } + + /** + * Cancel any earlier room key request + * + * @param requestBody requestBody + */ + override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) { + encryptingThreadHandler.post { mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody) } + } + + /** + * Re request the encryption keys required to decrypt an event. + * + * @param event the event to decrypt again. + */ + override fun reRequestRoomKeyForEvent(event: Event) { + val wireContent = event.content!! // Wireeventcontent? + + val algorithm = wireContent["algorithm"].toString() + val senderKey = wireContent["sender_key"].toString() + val sessionId = wireContent["session_id"].toString() + + encryptingThreadHandler.post { + val requestBody = RoomKeyRequestBody() + + requestBody.roomId = event.roomId + requestBody.algorithm = algorithm + requestBody.senderKey = senderKey + requestBody.sessionId = sessionId + + mOutgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody) + } + } + + /** + * Add a RoomKeysRequestListener listener. + * + * @param listener listener + */ + override fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { + mIncomingRoomKeyRequestManager.addRoomKeysRequestListener(listener) + } + + /** + * Add a RoomKeysRequestListener listener. + * + * @param listener listener + */ + fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) { + mIncomingRoomKeyRequestManager.removeRoomKeysRequestListener(listener) + } + + /* ========================================================================================== + * DEBUG INFO + * ========================================================================================== */ + + override fun toString(): String { + return myDevice.userId + " (" + myDevice.deviceId + ")" + + } + + companion object { + // max number of keys to upload at once + // Creating keys can be an expensive operation so we limit the + // number we generate in one go to avoid blocking the application + // for too long. + private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5 + + // frequency with which to check & upload one-time keys + private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60 * 1000).toLong() // one minute + + /** + * Provides the list of unknown devices + * + * @param devicesInRoom the devices map + * @return the unknown devices map + */ + fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap): MXUsersDevicesMap { + val unknownDevices = MXUsersDevicesMap() + + val userIds = devicesInRoom.userIds + for (userId in userIds) { + val deviceIds = devicesInRoom.getUserDeviceIds(userId) + for (deviceId in deviceIds!!) { + val deviceInfo = devicesInRoom.getObject(deviceId, userId) + + if (deviceInfo!!.isUnknown) { + unknownDevices.setObject(deviceInfo, userId, deviceId) + } + } + } + + return unknownDevices + } + } +} +/** + * Check if the OTK must be uploaded. + */ \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt new file mode 100644 index 00000000..7dfb47a8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt @@ -0,0 +1,240 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.content.Context +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.CryptoService +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore +import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration +import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule +import im.vector.matrix.android.internal.crypto.store.db.hash +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.tasks.* +import im.vector.matrix.android.internal.crypto.tasks.* +import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService +import im.vector.matrix.android.internal.session.DefaultSession +import io.realm.RealmConfiguration +import org.koin.dsl.module.module +import org.matrix.olm.OlmManager +import retrofit2.Retrofit +import java.io.File + +internal class CryptoModule { + + val definition = module(override = true) { + + /* ========================================================================================== + * Crypto Main + * ========================================================================================== */ + + // Realm configuration, named to avoid clash with main cache realm configuration + scope(DefaultSession.SCOPE, name = "CryptoRealmConfiguration") { + val context: Context = get() + + val credentials: Credentials = get() + + RealmConfiguration.Builder() + .directory(File(context.filesDir, credentials.userId.hash())) + .name("crypto_store.realm") + .modules(RealmCryptoStoreModule()) + .schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION) + .migration(RealmCryptoStoreMigration) + .build() + } + + // CryptoStore + scope(DefaultSession.SCOPE) { + RealmCryptoStore(false /* TODO*/, + get("CryptoRealmConfiguration"), + get()) as IMXCryptoStore + } + + scope(DefaultSession.SCOPE) { + val retrofit: Retrofit = get() + retrofit.create(CryptoApi::class.java) + } + + // CryptoService + scope(DefaultSession.SCOPE) { + DefaultCryptoService(get()) as CryptoService + } + + // + scope(DefaultSession.SCOPE) { + MXOutgoingRoomKeyRequestManager(get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + IncomingRoomKeyRequestManager(get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + RoomDecryptorProvider(get(), get(), get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + // Ensure OlmManager is loaded first + get() + + MXOlmDevice(get()) + } + + // CryptoManager + scope(DefaultSession.SCOPE) { + CryptoManager( + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + // Tasks + get(), get(), get(), get(), get(), get(), get(), + // Task executor + get() + ) + } + + // Olm manager + single { + // load the crypto libs. + OlmManager() + } + + + // Crypto config + scope(DefaultSession.SCOPE) { + MXCryptoConfig() + } + + // Device list + scope(DefaultSession.SCOPE) { + DeviceListManager(get(), get(), get(), get(), get(), get()) + } + + // Crypto tasks + scope(DefaultSession.SCOPE) { + DefaultClaimOneTimeKeysForUsersDevice(get()) as ClaimOneTimeKeysForUsersDeviceTask + } + scope(DefaultSession.SCOPE) { + DefaultDeleteDeviceTask(get()) as DeleteDeviceTask + } + scope(DefaultSession.SCOPE) { + DefaultDownloadKeysForUsers(get()) as DownloadKeysForUsersTask + } + scope(DefaultSession.SCOPE) { + DefaultGetDevicesTask(get()) as GetDevicesTask + } + scope(DefaultSession.SCOPE) { + DefaultGetKeyChangesTask(get()) as GetKeyChangesTask + } + scope(DefaultSession.SCOPE) { + DefaultSendToDeviceTask(get()) as SendToDeviceTask + } + scope(DefaultSession.SCOPE) { + DefaultSetDeviceNameTask(get()) as SetDeviceNameTask + } + scope(DefaultSession.SCOPE) { + DefaultUploadKeysTask(get()) as UploadKeysTask + } + + /* ========================================================================================== + * Keys backup + * ========================================================================================== */ + + scope(DefaultSession.SCOPE) { + val retrofit: Retrofit = get() + retrofit.create(RoomKeysApi::class.java) + } + + scope(DefaultSession.SCOPE) { + KeysBackup( + // Credentials + get(), + // CryptoStore + get(), + get(), + // Task + get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), + // Task executor + get()) + } + + // Key backup tasks + scope(DefaultSession.SCOPE) { + DefaultCreateKeysBackupVersionTask(get()) as CreateKeysBackupVersionTask + } + scope(DefaultSession.SCOPE) { + DefaultDeleteBackupTask(get()) as DeleteBackupTask + } + scope(DefaultSession.SCOPE) { + DefaultDeleteRoomSessionDataTask(get()) as DeleteRoomSessionDataTask + } + scope(DefaultSession.SCOPE) { + DefaultDeleteRoomSessionsDataTask(get()) as DeleteRoomSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultDeleteSessionsDataTask(get()) as DeleteSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultGetKeysBackupLastVersionTask(get()) as GetKeysBackupLastVersionTask + } + scope(DefaultSession.SCOPE) { + DefaultGetKeysBackupVersionTask(get()) as GetKeysBackupVersionTask + } + scope(DefaultSession.SCOPE) { + DefaultGetRoomSessionDataTask(get()) as GetRoomSessionDataTask + } + scope(DefaultSession.SCOPE) { + DefaultGetRoomSessionsDataTask(get()) as GetRoomSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultGetSessionsDataTask(get()) as GetSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultStoreRoomSessionDataTask(get()) as StoreRoomSessionDataTask + } + scope(DefaultSession.SCOPE) { + DefaultStoreRoomSessionsDataTask(get()) as StoreRoomSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultStoreSessionsDataTask(get()) as StoreSessionsDataTask + } + scope(DefaultSession.SCOPE) { + DefaultUpdateKeysBackupVersionTask(get()) as UpdateKeysBackupVersionTask + } + + /* ========================================================================================== + * SAS Verification + * ========================================================================================== */ + + scope(DefaultSession.SCOPE) { + DefaultSasVerificationService(get(), get(), get(), get(), get()) + } + + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt new file mode 100644 index 00000000..00ecc525 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import im.vector.matrix.android.api.session.crypto.CryptoService + +internal class DefaultCryptoService(val cryptoManager: CryptoManager) + : CryptoService by cryptoManager \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt new file mode 100755 index 00000000..e6201217 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -0,0 +1,747 @@ +/* + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.MatrixPatterns +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask +import im.vector.matrix.android.internal.session.sync.SyncTokenStore +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import java.util.* + +// Legacy name: MXDeviceList +internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore, + private val mOlmDevice: MXOlmDevice, + private val mSyncTokenStore: SyncTokenStore, + private val mCredentials: Credentials, + private val mDownloadKeysForUsersTask: DownloadKeysForUsersTask, + private val mTaskExecutor: TaskExecutor) { + + // keys in progress + private val mUserKeyDownloadsInProgress = HashSet() + + // HS not ready for retry + private val mNotReadyToRetryHS = HashSet() + + // indexed by UserId + private val mPendingDownloadKeysRequestToken = HashMap() + + // pending queues list + private val mDownloadKeysQueues = ArrayList() + + // tells if there is a download keys request in progress + private var mIsDownloadingKeys = false + + // Internal listener + private lateinit var mCryptoListener: DeviceListCryptoListener + + /** + * Creator + * + * @param userIds the user ids list + * @param callback the asynchronous callback + */ + internal inner class DownloadKeysPromise(userIds: List, + val mCallback: MatrixCallback>?) { + // list of remain pending device keys + val mPendingUserIdsList: MutableList + + // the unfiltered user ids list + val mUserIdsList: List + + init { + mPendingUserIdsList = ArrayList(userIds) + mUserIdsList = ArrayList(userIds) + } + } + + init { + var isUpdated = false + + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + for (userId in deviceTrackingStatuses.keys) { + val status = deviceTrackingStatuses[userId]!! + + if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) { + // if a download was in progress when we got shut down, it isn't any more. + deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) + isUpdated = true + } + } + + if (isUpdated) { + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + } + } + + /** + * Tells if the key downloads should be tried + * + * @param userId the userId + * @return true if the keys download can be retrieved + */ + private fun canRetryKeysDownload(userId: String): Boolean { + var res = false + + if (!TextUtils.isEmpty(userId) && userId.contains(":")) { + try { + synchronized(mNotReadyToRetryHS) { + res = !mNotReadyToRetryHS.contains(userId.substring(userId.lastIndexOf(":") + 1)) + } + } catch (e: Exception) { + Timber.e(e, "## canRetryKeysDownload() failed") + } + + } + + return res + } + + /** + * Add a download keys promise + * + * @param userIds the user ids list + * @param callback the asynchronous callback + * @return the filtered user ids list i.e the one which require a remote request + */ + private fun addDownloadKeysPromise(userIds: MutableList?, callback: MatrixCallback>?): MutableList? { + if (null != userIds) { + val filteredUserIds = ArrayList() + val invalidUserIds = ArrayList() + + for (userId in userIds) { + if (MatrixPatterns.isUserId(userId)) { + filteredUserIds.add(userId) + } else { + Timber.e("## userId " + userId + "is not a valid user id") + invalidUserIds.add(userId) + } + } + + synchronized(mUserKeyDownloadsInProgress) { + filteredUserIds.removeAll(mUserKeyDownloadsInProgress) + mUserKeyDownloadsInProgress.addAll(userIds) + // got some email addresses instead of matrix ids + mUserKeyDownloadsInProgress.removeAll(invalidUserIds) + userIds.removeAll(invalidUserIds) + } + + mDownloadKeysQueues.add(DownloadKeysPromise(userIds, callback)) + + return filteredUserIds + } else { + return null + } + } + + /** + * Clear the unavailable server lists + */ + private fun clearUnavailableServersList() { + synchronized(mNotReadyToRetryHS) { + mNotReadyToRetryHS.clear() + } + } + + /** + * Mark the cached device list for the given user outdated + * flag the given user for device-list tracking, if they are not already. + * + * @param userIds the user ids list + */ + fun startTrackingDeviceList(userIds: List?) { + if (null != userIds) { + var isUpdated = false + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + + for (userId in userIds) { + if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) { + Timber.d("## startTrackingDeviceList() : Now tracking device list for $userId") + deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) + isUpdated = true + } + } + + if (isUpdated) { + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + } + } + } + + /** + * Update the devices list statuses + * + * @param changed the user ids list which have new devices + * @param left the user ids list which left a room + */ + fun handleDeviceListsChanges(changed: List?, left: List?) { + var isUpdated = false + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + + if (changed?.isNotEmpty() == true) { + clearUnavailableServersList() + + for (userId in changed) { + if (deviceTrackingStatuses.containsKey(userId)) { + Timber.d("## invalidateUserDeviceList() : Marking device list outdated for $userId") + deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) + isUpdated = true + } + } + } + + if (left?.isNotEmpty() == true) { + clearUnavailableServersList() + + for (userId in left) { + if (deviceTrackingStatuses.containsKey(userId)) { + Timber.d("## invalidateUserDeviceList() : No longer tracking device list for $userId") + deviceTrackingStatuses.put(userId, TRACKING_STATUS_NOT_TRACKED) + isUpdated = true + } + } + } + + if (isUpdated) { + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + } + } + + /** + * This will flag each user whose devices we are tracking as in need of an + * + update + */ + fun invalidateAllDeviceLists() { + handleDeviceListsChanges(ArrayList(mCryptoStore.getDeviceTrackingStatuses().keys), null) + } + + /** + * The keys download failed + * + * @param userIds the user ids list + */ + private fun onKeysDownloadFailed(userIds: List?) { + if (null != userIds) { + synchronized(mUserKeyDownloadsInProgress) { + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + + for (userId in userIds) { + mUserKeyDownloadsInProgress.remove(userId) + deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) + } + + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + } + } + + mIsDownloadingKeys = false + } + + /** + * The keys download succeeded. + * + * @param userIds the userIds list + * @param failures the failure map. + */ + private fun onKeysDownloadSucceed(userIds: List?, failures: Map>?) { + if (null != failures) { + val keys = failures.keys + + for (k in keys) { + val value = failures[k] + + if (value!!.containsKey("status")) { + val statusCodeAsVoid = value["status"] + var statusCode = 0 + + if (statusCodeAsVoid is Double) { + statusCode = statusCodeAsVoid.toInt() + } else if (statusCodeAsVoid is Int) { + statusCode = statusCodeAsVoid.toInt() + } + + if (statusCode == 503) { + synchronized(mNotReadyToRetryHS) { + mNotReadyToRetryHS.add(k) + } + } + } + } + } + + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + + if (null != userIds) { + if (mDownloadKeysQueues.size > 0) { + val promisesToRemove = ArrayList() + + for (promise in mDownloadKeysQueues) { + promise.mPendingUserIdsList.removeAll(userIds) + + if (promise.mPendingUserIdsList.size == 0) { + // private members + val usersDevicesInfoMap = MXUsersDevicesMap() + + for (userId in promise.mUserIdsList) { + val devices = mCryptoStore.getUserDevices(userId) + if (null == devices) { + if (canRetryKeysDownload(userId)) { + deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) + Timber.e("failed to retry the devices of $userId : retry later") + } else { + if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) { + deviceTrackingStatuses.put(userId, TRACKING_STATUS_UNREACHABLE_SERVER) + Timber.e("failed to retry the devices of $userId : the HS is not available") + } + } + } else { + if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) { + // we didn't get any new invalidations since this download started: + // this user's device list is now up to date. + deviceTrackingStatuses.put(userId, TRACKING_STATUS_UP_TO_DATE) + Timber.d("Device list for $userId now up to date") + } + + // And the response result + usersDevicesInfoMap.setObjects(devices, userId) + } + } + + if (!mCryptoListener.hasBeenReleased()) { + val callback = promise.mCallback + + if (null != callback) { + CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(usersDevicesInfoMap) } + } + } + promisesToRemove.add(promise) + } + } + mDownloadKeysQueues.removeAll(promisesToRemove) + } + + for (userId in userIds) { + mUserKeyDownloadsInProgress.remove(userId) + } + + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + } + + mIsDownloadingKeys = false + } + + /** + * Download the device keys for a list of users and stores the keys in the MXStore. + * It must be called in getEncryptingThreadHandler() thread. + * The callback is called in the UI thread. + * + * @param userIds The users to fetch. + * @param forceDownload Always download the keys even if cached. + * @param callback the asynchronous callback + */ + fun downloadKeys(userIds: List?, forceDownload: Boolean, callback: MatrixCallback>?) { + Timber.d("## downloadKeys() : forceDownload $forceDownload : $userIds") + + // Map from userid -> deviceid -> DeviceInfo + val stored = MXUsersDevicesMap() + + // List of user ids we need to download keys for + val downloadUsers = ArrayList() + + if (null != userIds) { + if (forceDownload) { + downloadUsers.addAll(userIds) + } else { + for (userId in userIds) { + val status = mCryptoStore.getDeviceTrackingStatus(userId, TRACKING_STATUS_NOT_TRACKED) + + // downloading keys ->the keys download won't be triggered twice but the callback requires the dedicated keys + // not yet retrieved + if (mUserKeyDownloadsInProgress.contains(userId) || TRACKING_STATUS_UP_TO_DATE != status && TRACKING_STATUS_UNREACHABLE_SERVER != status) { + downloadUsers.add(userId) + } else { + val devices = mCryptoStore.getUserDevices(userId) + + // should always be true + if (null != devices) { + stored.setObjects(devices, userId) + } else { + downloadUsers.add(userId) + } + } + } + } + } + + if (0 == downloadUsers.size) { + Timber.d("## downloadKeys() : no new user device") + + if (null != callback) { + CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(stored) } + } + } else { + Timber.d("## downloadKeys() : starts") + val t0 = System.currentTimeMillis() + + doKeyDownloadForUsers(downloadUsers, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + Timber.d("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") + + data.addEntriesFromMap(stored) + + callback?.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## downloadKeys() : doKeyDownloadForUsers onFailure") + callback?.onFailure(failure) + } + }) + } + } + + /** + * Download the devices keys for a set of users. + * It must be called in getEncryptingThreadHandler() thread. + * The callback is called in the UI thread. + * + * @param downloadUsers the user ids list + * @param callback the asynchronous callback + */ + private fun doKeyDownloadForUsers(downloadUsers: MutableList, callback: MatrixCallback>?) { + Timber.d("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers") + + // get the user ids which did not already trigger a keys download + val filteredUsers = addDownloadKeysPromise(downloadUsers, callback) + + // if there is no new keys request + if (0 == filteredUsers!!.size) { + // trigger nothing + return + } + + // sanity check + //if (null == mxSession.dataHandler || null == mxSession.dataHandler.store) { + // return + //} + + mIsDownloadingKeys = true + + // track the race condition while sending requests + // we defines a tag for each request + // and test if the response is the latest request one + val downloadToken = filteredUsers.hashCode().toString() + " " + System.currentTimeMillis() + + for (userId in filteredUsers) { + mPendingDownloadKeysRequestToken[userId] = downloadToken + } + + mDownloadKeysForUsersTask + .configureWith(DownloadKeysForUsersTask.Params(filteredUsers, mSyncTokenStore.getLastToken())) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: KeysQueryResponse) { + CryptoAsyncHelper.getEncryptBackgroundHandler().post { + Timber.d("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") + val userIdsList = ArrayList(filteredUsers) + + for (userId in userIdsList) { + // test if the response is the latest request one + if (!TextUtils.equals(mPendingDownloadKeysRequestToken[userId], downloadToken)) { + Timber.e("## doKeyDownloadForUsers() : Another update in the queue for " + + userId + " not marking up-to-date") + filteredUsers.remove(userId) + } else { + val devices = data.deviceKeys!![userId] + + Timber.d("## doKeyDownloadForUsers() : Got keys for $userId : $devices") + + if (null != devices) { + val mutableDevices = HashMap(devices) + val deviceIds = ArrayList(mutableDevices.keys) + + for (deviceId in deviceIds) { + // the user has been logged out + // TODO + //if (null == cryptoStore) { + // break + //} + + // Get the potential previously store device keys for this device + val previouslyStoredDeviceKeys = mCryptoStore.getUserDevice(deviceId, userId) + val deviceInfo = mutableDevices[deviceId] + + // in some race conditions (like unit tests) + // the self device must be seen as verified + if (TextUtils.equals(deviceInfo!!.deviceId, mCredentials.deviceId) && TextUtils.equals(userId, mCredentials.userId)) { + deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + } + + // Validate received keys + if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { + // New device keys are not valid. Do not store them + mutableDevices.remove(deviceId) + + if (null != previouslyStoredDeviceKeys) { + // But keep old validated ones if any + mutableDevices[deviceId] = previouslyStoredDeviceKeys + } + } else if (null != previouslyStoredDeviceKeys) { + // The verified status is not sync'ed with hs. + // This is a client side information, valid only for this client. + // So, transfer its previous value + mutableDevices[deviceId]!!.mVerified = previouslyStoredDeviceKeys.mVerified + } + } + + // Update the store + // Note that devices which aren't in the response will be removed from the stores + mCryptoStore.storeUserDevices(userId, mutableDevices) + } + + // the response is the latest request one + mPendingDownloadKeysRequestToken.remove(userId) + } + } + + onKeysDownloadSucceed(filteredUsers, data.failures) + } + } + + private fun onFailed() { + CryptoAsyncHelper.getEncryptBackgroundHandler().post { + val userIdsList = ArrayList(filteredUsers) + + // test if the response is the latest request one + for (userId in userIdsList) { + if (!TextUtils.equals(mPendingDownloadKeysRequestToken[userId], downloadToken)) { + Timber.e("## doKeyDownloadForUsers() : Another update in the queue for $userId not marking up-to-date") + filteredUsers.remove(userId) + } else { + // the response is the latest request one + mPendingDownloadKeysRequestToken.remove(userId) + } + } + + onKeysDownloadFailed(filteredUsers) + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "##doKeyDownloadForUsers() : onNetworkError") + + onFailed() + + callback?.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } + + /** + * Validate device keys. + * This method must called on getEncryptingThreadHandler() thread. + * + * @param deviceKeys the device keys to validate. + * @param userId the id of the user of the device. + * @param deviceId the id of the device. + * @param previouslyStoredDeviceKeys the device keys we received before for this device + * @return true if succeeds + */ + private fun validateDeviceKeys(deviceKeys: MXDeviceInfo?, userId: String, deviceId: String, previouslyStoredDeviceKeys: MXDeviceInfo?): Boolean { + if (null == deviceKeys) { + Timber.e("## validateDeviceKeys() : deviceKeys is null from $userId:$deviceId") + return false + } + + if (null == deviceKeys.keys) { + Timber.e("## validateDeviceKeys() : deviceKeys.keys is null from $userId:$deviceId") + return false + } + + if (null == deviceKeys.signatures) { + Timber.e("## validateDeviceKeys() : deviceKeys.signatures is null from $userId:$deviceId") + return false + } + + // Check that the user_id and device_id in the received deviceKeys are correct + if (!TextUtils.equals(deviceKeys.userId, userId)) { + Timber.e("## validateDeviceKeys() : Mismatched user_id " + deviceKeys.userId + " from " + userId + ":" + deviceId) + return false + } + + if (!TextUtils.equals(deviceKeys.deviceId, deviceId)) { + Timber.e("## validateDeviceKeys() : Mismatched device_id " + deviceKeys.deviceId + " from " + userId + ":" + deviceId) + return false + } + + val signKeyId = "ed25519:" + deviceKeys.deviceId + val signKey = deviceKeys.keys!![signKeyId] + + if (null == signKey) { + Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " has no ed25519 key") + return false + } + + val signatureMap = deviceKeys.signatures!![userId] + + if (null == signatureMap) { + Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " has no map for " + userId) + return false + } + + val signature = signatureMap[signKeyId] + + if (null == signature) { + Timber.e("## validateDeviceKeys() : Device " + userId + ":" + deviceKeys.deviceId + " is not signed") + return false + } + + var isVerified = false + var errorMessage: String? = null + + try { + mOlmDevice.verifySignature(signKey, deviceKeys.signalableJSONDictionary(), signature) + isVerified = true + } catch (e: Exception) { + errorMessage = e.message + } + + if (!isVerified) { + Timber.e("## validateDeviceKeys() : Unable to verify signature on device " + userId + ":" + + deviceKeys.deviceId + " with error " + errorMessage) + return false + } + + if (null != previouslyStoredDeviceKeys) { + if (!TextUtils.equals(previouslyStoredDeviceKeys.fingerprint(), signKey)) { + // This should only happen if the list has been MITMed; we are + // best off sticking with the original keys. + // + // Should we warn the user about it somehow? + Timber.e("## validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" + + deviceKeys.deviceId + " has changed : " + + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) + + Timber.e("## validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys") + Timber.e("## validateDeviceKeys() : " + previouslyStoredDeviceKeys.keys + " -> " + deviceKeys.keys) + + return false + } + } + + return true + } + + /** + * Start device queries for any users who sent us an m.new_device recently + * This method must be called on getEncryptingThreadHandler() thread. + */ + fun refreshOutdatedDeviceLists() { + val users = ArrayList() + + val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() + + for (userId in deviceTrackingStatuses.keys) { + if (TRACKING_STATUS_PENDING_DOWNLOAD == deviceTrackingStatuses[userId]) { + users.add(userId) + } + } + + if (users.size == 0) { + return + } + + if (mIsDownloadingKeys) { + // request already in progress - do nothing. (We will automatically + // make another request if there are more users with outdated + // device lists when the current request completes). + return + } + + // update the statuses + for (userId in users) { + val status = deviceTrackingStatuses[userId] + + if (null != status && TRACKING_STATUS_PENDING_DOWNLOAD == status) { + deviceTrackingStatuses.put(userId, TRACKING_STATUS_DOWNLOAD_IN_PROGRESS) + } + } + + mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) + + doKeyDownloadForUsers(users, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + CryptoAsyncHelper.getEncryptBackgroundHandler().post { Timber.d("## refreshOutdatedDeviceLists() : done") } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users") + } + }) + } + + fun setCryptoInternalListener(listener: DeviceListCryptoListener) { + mCryptoListener = listener + } + + + interface DeviceListCryptoListener { + fun hasBeenReleased(): Boolean + } + + companion object { + + /** + * State transition diagram for DeviceList.deviceTrackingStatus + *
    +         *
    +         *                                   |
    +         *        stopTrackingDeviceList     V
    +         *      +---------------------> NOT_TRACKED
    +         *      |                            |
    +         *      +<--------------------+      | startTrackingDeviceList
    +         *      |                     |      V
    +         *      |   +-------------> PENDING_DOWNLOAD <--------------------+-+
    +         *      |   |                      ^ |                            | |
    +         *      |   | restart     download | |  start download            | | invalidateUserDeviceList
    +         *      |   | client        failed | |                            | |
    +         *      |   |                      | V                            | |
    +         *      |   +------------ DOWNLOAD_IN_PROGRESS -------------------+ |
    +         *      |                    |       |                              |
    +         *      +<-------------------+       |  download successful         |
    +         *      ^                            V                              |
    +         *      +----------------------- UP_TO_DATE ------------------------+
    +         *
    +         * 
    + */ + + const val TRACKING_STATUS_NOT_TRACKED = -1 + const val TRACKING_STATUS_PENDING_DOWNLOAD = 1 + const val TRACKING_STATUS_DOWNLOAD_IN_PROGRESS = 2 + const val TRACKING_STATUS_UP_TO_DATE = 3 + const val TRACKING_STATUS_UNREACHABLE_SERVER = 4 + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt new file mode 100755 index 00000000..63394349 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + + +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest + +/** + * IncomingRoomKeyRequest class defines the incoming room keys request. + */ +open class IncomingRoomKeyRequest { + /** + * The user id + */ + var mUserId: String? = null + + /** + * The device id + */ + var mDeviceId: String? = null + + /** + * The request id + */ + var mRequestId: String? = null + + /** + * The request body + */ + var mRequestBody: RoomKeyRequestBody? = null + + /** + * The runnable to call to accept to share the keys + */ + @Transient + var mShare: Runnable? = null + + /** + * The runnable to call to ignore the key share request. + */ + @Transient + var mIgnore: Runnable? = null + + /** + * Constructor + * + * @param event the event + */ + constructor(event: Event) { + mUserId = event.sender + + val roomKeyShareRequest = event.content.toModel()!! + mDeviceId = roomKeyShareRequest.requestingDeviceId + mRequestId = roomKeyShareRequest.requestId + mRequestBody = if (null != roomKeyShareRequest.body) roomKeyShareRequest.body else RoomKeyRequestBody() + } + + /** + * Constructor for object creation from crypto store + */ + constructor() +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt new file mode 100755 index 00000000..765bc5cd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import im.vector.matrix.android.api.session.events.model.Event + +/** + * IncomingRoomKeyRequestCancellation describes the incoming room key cancellation. + */ +class IncomingRoomKeyRequestCancellation(event: Event) : IncomingRoomKeyRequest(event) { + + init { + mRequestBody = null + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt new file mode 100644 index 00000000..d70e103b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -0,0 +1,235 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.os.Handler +import android.text.TextUtils +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare +import timber.log.Timber +import java.util.* + +internal class IncomingRoomKeyRequestManager( + val mCredentials: Credentials, + val mCryptoStore: IMXCryptoStore, + val mRoomDecryptorProvider: RoomDecryptorProvider) { + + + // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations + // we received in the current sync. + private val mReceivedRoomKeyRequests = ArrayList() + private val mReceivedRoomKeyRequestCancellations = ArrayList() + + // the listeners + val mRoomKeysRequestListeners: MutableSet = HashSet() + + init { + mReceivedRoomKeyRequests.addAll(mCryptoStore.getPendingIncomingRoomKeyRequests()) + } + + /** + * Called when we get an m.room_key_request event + * This method must be called on getEncryptingThreadHandler() thread. + * + * @param event the announcement event. + */ + fun onRoomKeyRequestEvent(event: Event) { + val roomKeyShare = event.content.toModel()!! + + if (null != roomKeyShare.action) { + when (roomKeyShare.action) { + RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(mReceivedRoomKeyRequests) { + mReceivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) + } + RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(mReceivedRoomKeyRequestCancellations) { + mReceivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event)) + } + else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare.action!!) + } + } + } + + private lateinit var encryptingThreadHandler: Handler + + fun setEncryptingThreadHandler(encryptingThreadHandler: Handler) { + this.encryptingThreadHandler = encryptingThreadHandler + } + + /** + * Process any m.room_key_request events which were queued up during the + * current sync. + */ + fun processReceivedRoomKeyRequests() { + var receivedRoomKeyRequests: List? = null + + synchronized(mReceivedRoomKeyRequests) { + if (!mReceivedRoomKeyRequests.isEmpty()) { + receivedRoomKeyRequests = ArrayList(mReceivedRoomKeyRequests) + mReceivedRoomKeyRequests.clear() + } + } + + if (null != receivedRoomKeyRequests) { + for (request in receivedRoomKeyRequests!!) { + val userId = request.mUserId!! + val deviceId = request.mDeviceId + val body = request.mRequestBody + val roomId = body!!.roomId + val alg = body.algorithm + + Timber.d("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.mRequestId) + + if (!TextUtils.equals(mCredentials.userId, userId)) { + // TODO: determine if we sent this device the keys already: in + Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now") + return + } + + // todo: should we queue up requests we don't yet have keys for, + // in case they turn up later? + + // if we don't have a decryptor for this room/alg, we don't have + // the keys for the requested events, and can drop the requests. + + val decryptor = mRoomDecryptorProvider.getRoomDecryptor(roomId, alg) + + if (null == decryptor) { + Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId") + continue + } + + if (!decryptor.hasKeysForKeyRequest(request)) { + Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!) + mCryptoStore.deleteIncomingRoomKeyRequest(request) + continue + } + + if (TextUtils.equals(deviceId, mCredentials.deviceId) && TextUtils.equals(mCredentials.userId, userId)) { + Timber.d("## processReceivedRoomKeyRequests() : oneself device - ignored") + mCryptoStore.deleteIncomingRoomKeyRequest(request) + continue + } + + request.mShare = Runnable { + encryptingThreadHandler.post { + decryptor.shareKeysWithDevice(request) + mCryptoStore.deleteIncomingRoomKeyRequest(request) + } + } + + request.mIgnore = Runnable { encryptingThreadHandler.post { mCryptoStore.deleteIncomingRoomKeyRequest(request) } } + + // if the device is verified already, share the keys + val device = mCryptoStore.getUserDevice(deviceId!!, userId) + + if (null != device) { + if (device.isVerified) { + Timber.d("## processReceivedRoomKeyRequests() : device is already verified: sharing keys") + mCryptoStore.deleteIncomingRoomKeyRequest(request) + request.mShare!!.run() + continue + } + + if (device.isBlocked) { + Timber.d("## processReceivedRoomKeyRequests() : device is blocked -> ignored") + mCryptoStore.deleteIncomingRoomKeyRequest(request) + continue + } + } + + mCryptoStore.storeIncomingRoomKeyRequest(request) + onRoomKeyRequest(request) + } + } + + var receivedRoomKeyRequestCancellations: List? = null + + synchronized(mReceivedRoomKeyRequestCancellations) { + if (!mReceivedRoomKeyRequestCancellations.isEmpty()) { + receivedRoomKeyRequestCancellations = mReceivedRoomKeyRequestCancellations.toList() + mReceivedRoomKeyRequestCancellations.clear() + } + } + + if (null != receivedRoomKeyRequestCancellations) { + for (request in receivedRoomKeyRequestCancellations!!) { + Timber.d("## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.mUserId + + ":" + request.mDeviceId + " id " + request.mRequestId) + + // we should probably only notify the app of cancellations we told it + // about, but we don't currently have a record of that, so we just pass + // everything through. + onRoomKeyRequestCancellation(request) + mCryptoStore.deleteIncomingRoomKeyRequest(request) + } + } + } + + /** + * Dispatch onRoomKeyRequest + * + * @param request the request + */ + private fun onRoomKeyRequest(request: IncomingRoomKeyRequest) { + synchronized(mRoomKeysRequestListeners) { + for (listener in mRoomKeysRequestListeners) { + try { + listener.onRoomKeyRequest(request) + } catch (e: Exception) { + Timber.e(e, "## onRoomKeyRequest() failed") + } + + } + } + } + + + /** + * A room key request cancellation has been received. + * + * @param request the cancellation request + */ + private fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation) { + synchronized(mRoomKeysRequestListeners) { + for (listener in mRoomKeysRequestListeners) { + try { + listener.onRoomKeyRequestCancellation(request) + } catch (e: Exception) { + Timber.e(e, "## onRoomKeyRequestCancellation() failed") + } + + } + } + } + + fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { + synchronized(mRoomKeysRequestListeners) { + mRoomKeysRequestListeners.add(listener) + } + } + + fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) { + synchronized(mRoomKeysRequestListeners) { + mRoomKeysRequestListeners.remove(listener) + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt new file mode 100755 index 00000000..292839bd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting +import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting +import timber.log.Timber +import java.util.* + +internal object MXCryptoAlgorithms { + + // encryptors map + private val mEncryptors: MutableMap> + + // decryptors map + private val mDecryptors: MutableMap> + + init { + mEncryptors = HashMap() + try { + mEncryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryption") as Class + } catch (e: Exception) { + Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM") + } + + try { + mEncryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryption") as Class + } catch (e: Exception) { + Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM") + } + + mDecryptors = HashMap() + try { + mDecryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryption") as Class + } catch (e: Exception) { + Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM") + } + + try { + mDecryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryption") as Class + } catch (e: Exception) { + Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM") + } + + } + + /** + * Get the class implementing encryption for the provided algorithm. + * + * @param algorithm the algorithm tag. + * @return A class implementing 'IMXEncrypting'. + */ + fun encryptorClassForAlgorithm(algorithm: String?): Class? { + return if (!TextUtils.isEmpty(algorithm)) { + mEncryptors[algorithm] + } else { + null + } + } + + /** + * Get the class implementing decryption for the provided algorithm. + * + * @param algorithm the algorithm tag. + * @return A class implementing 'IMXDecrypting'. + */ + + fun decryptorClassForAlgorithm(algorithm: String?): Class? { + return if (!TextUtils.isEmpty(algorithm)) { + mDecryptors[algorithm] + } else { + null + } + } + + /** + * @return The list of registered algorithms. + */ + fun supportedAlgorithms(): List { + return ArrayList(mEncryptors.keys) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoConfig.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoConfig.kt new file mode 100644 index 00000000..5194928a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoConfig.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +/** + * Class to define the parameters used to customize or configure the end-to-end crypto. + */ +data class MXCryptoConfig( + // Tell whether the encryption of the event content is enabled for the invited members. + // By default, we encrypt messages only for the joined members. + // The encryption for the invited members will be blocked if the history visibility is "joined". + var mEnableEncryptionForInvitedMembers: Boolean = false +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt new file mode 100644 index 00000000..b0d91c21 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXDecryptionException.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import im.vector.matrix.android.api.session.crypto.MXCryptoError + +/** + * This class represents a decryption exception + */ +class MXDecryptionException +( + /** + * the linked crypto error + */ + val cryptoError: MXCryptoError? +) : Exception() { + + override val message: String? + get() = cryptoError?.message ?: super.message + + override fun getLocalizedMessage(): String { + return cryptoError?.message ?: super.getLocalizedMessage() + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEncryptedAttachments.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEncryptedAttachments.kt new file mode 100755 index 00000000..9d37990b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEncryptedAttachments.kt @@ -0,0 +1,269 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import android.util.Base64 +import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo +import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey +import timber.log.Timber +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.security.MessageDigest +import java.security.SecureRandom +import java.util.* +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object MXEncryptedAttachments { + private const val CRYPTO_BUFFER_SIZE = 32 * 1024 + private const val CIPHER_ALGORITHM = "AES/CTR/NoPadding" + private const val SECRET_KEY_SPEC_ALGORITHM = "AES" + private const val MESSAGE_DIGEST_ALGORITHM = "SHA-256" + + /** + * Define the result of an encryption file + */ + data class EncryptionResult( + var mEncryptedFileInfo: EncryptedFileInfo, + var mEncryptedStream: InputStream + ) + + /*** + * Encrypt an attachment stream. + * @param attachmentStream the attachment stream + * @param mimetype the mime type + * @return the encryption file info + */ + fun encryptAttachment(attachmentStream: InputStream, mimetype: String): EncryptionResult? { + val t0 = System.currentTimeMillis() + val secureRandom = SecureRandom() + + // generate a random iv key + // Half of the IV is random, the lower order bits are zeroed + // such that the counter never wraps. + // See https://github.com/matrix-org/matrix-ios-kit/blob/3dc0d8e46b4deb6669ed44f72ad79be56471354c/MatrixKit/Models/Room/MXEncryptedAttachments.m#L75 + val initVectorBytes = ByteArray(16) + Arrays.fill(initVectorBytes, 0.toByte()) + + val ivRandomPart = ByteArray(8) + secureRandom.nextBytes(ivRandomPart) + + System.arraycopy(ivRandomPart, 0, initVectorBytes, 0, ivRandomPart.size) + + val key = ByteArray(32) + secureRandom.nextBytes(key) + + val outStream = ByteArrayOutputStream() + + try { + val encryptCipher = Cipher.getInstance(CIPHER_ALGORITHM) + val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM) + val ivParameterSpec = IvParameterSpec(initVectorBytes) + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec) + + val messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM) + + val data = ByteArray(CRYPTO_BUFFER_SIZE) + var read: Int + var encodedBytes: ByteArray + + read = attachmentStream.read(data) + while (read != -1) { + encodedBytes = encryptCipher.update(data, 0, read) + messageDigest.update(encodedBytes, 0, encodedBytes.size) + outStream.write(encodedBytes) + read = attachmentStream.read(data) + } + + // encrypt the latest chunk + encodedBytes = encryptCipher.doFinal() + messageDigest.update(encodedBytes, 0, encodedBytes.size) + outStream.write(encodedBytes) + + val result = EncryptionResult( + mEncryptedFileInfo = EncryptedFileInfo( + url = null, + mimetype = mimetype, + key = EncryptedFileKey( + alg = "A256CTR", + ext = true, + key_ops = listOf("encrypt", "decrypt"), + kty = "oct", + k = base64ToBase64Url(Base64.encodeToString(key, Base64.DEFAULT))!! + ), + iv = Base64.encodeToString(initVectorBytes, Base64.DEFAULT).replace("\n", "").replace("=", ""), + hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))!!), + v = "v2" + ), + mEncryptedStream = ByteArrayInputStream(outStream.toByteArray()) + ) + + outStream.close() + + Timber.d("Encrypt in " + (System.currentTimeMillis() - t0) + " ms") + return result + } catch (oom: OutOfMemoryError) { + Timber.e(oom, "## encryptAttachment failed " + oom.message) + } catch (e: Exception) { + Timber.e(e, "## encryptAttachment failed " + e.message) + } + + try { + outStream.close() + } catch (e: Exception) { + Timber.e(e, "## encryptAttachment() : fail to close outStream") + } + + return null + } + + /** + * Decrypt an attachment + * + * @param attachmentStream the attachment stream + * @param encryptedFileInfo the encryption file info + * @return the decrypted attachment stream + */ + fun decryptAttachment(attachmentStream: InputStream?, encryptedFileInfo: EncryptedFileInfo?): InputStream? { + // sanity checks + if (null == attachmentStream || null == encryptedFileInfo) { + Timber.e("## decryptAttachment() : null parameters") + return null + } + + if (TextUtils.isEmpty(encryptedFileInfo.iv) + || null == encryptedFileInfo.key + || null == encryptedFileInfo.hashes + || !encryptedFileInfo.hashes.containsKey("sha256")) { + Timber.e("## decryptAttachment() : some fields are not defined") + return null + } + + if (!TextUtils.equals(encryptedFileInfo.key!!.alg, "A256CTR") + || !TextUtils.equals(encryptedFileInfo.key!!.kty, "oct") + || TextUtils.isEmpty(encryptedFileInfo.key!!.k)) { + Timber.e("## decryptAttachment() : invalid key fields") + return null + } + + // detect if there is no data to decrypt + try { + if (0 == attachmentStream.available()) { + return ByteArrayInputStream(ByteArray(0)) + } + } catch (e: Exception) { + Timber.e(e, "Fail to retrieve the file size") + } + + val t0 = System.currentTimeMillis() + + val outStream = ByteArrayOutputStream() + + try { + val key = Base64.decode(base64UrlToBase64(encryptedFileInfo.key!!.k), Base64.DEFAULT) + val initVectorBytes = Base64.decode(encryptedFileInfo.iv, Base64.DEFAULT) + + val decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM) + val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM) + val ivParameterSpec = IvParameterSpec(initVectorBytes) + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) + + val messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM) + + var read: Int + val data = ByteArray(CRYPTO_BUFFER_SIZE) + var decodedBytes: ByteArray + + read = attachmentStream.read(data) + while (read != -1) { + messageDigest.update(data, 0, read) + decodedBytes = decryptCipher.update(data, 0, read) + outStream.write(decodedBytes) + read = attachmentStream.read(data) + } + + // decrypt the last chunk + decodedBytes = decryptCipher.doFinal() + outStream.write(decodedBytes) + + val currentDigestValue = base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT)) + + if (!TextUtils.equals(encryptedFileInfo.hashes["sha256"], currentDigestValue)) { + Timber.e("## decryptAttachment() : Digest value mismatch") + outStream.close() + return null + } + + val decryptedStream = ByteArrayInputStream(outStream.toByteArray()) + outStream.close() + + Timber.d("Decrypt in " + (System.currentTimeMillis() - t0) + " ms") + + return decryptedStream + } catch (oom: OutOfMemoryError) { + Timber.e(oom, "## decryptAttachment() : failed " + oom.message) + } catch (e: Exception) { + Timber.e(e, "## decryptAttachment() : failed " + e.message) + } + + try { + outStream.close() + } catch (closeException: Exception) { + Timber.e(closeException, "## decryptAttachment() : fail to close the file") + } + + return null + } + + /** + * Base64 URL conversion methods + */ + + private fun base64UrlToBase64(base64Url: String?): String? { + var result = base64Url + if (null != result) { + result = result.replace("-".toRegex(), "+") + result = result.replace("_".toRegex(), "/") + } + + return result + } + + private fun base64ToBase64Url(base64: String?): String? { + var result = base64 + if (null != result) { + result = result.replace("\n".toRegex(), "") + result = result.replace("\\+".toRegex(), "-") + result = result.replace("/".toRegex(), "_") + result = result.replace("=".toRegex(), "") + } + return result + } + + private fun base64ToUnpaddedBase64(base64: String?): String? { + var result = base64 + if (null != result) { + result = result.replace("\n".toRegex(), "") + result = result.replace("=".toRegex(), "") + } + + return result + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt new file mode 100644 index 00000000..f8b5f6b6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXEventDecryptionResult.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import im.vector.matrix.android.api.session.events.model.Event +import java.util.* + +/** + * The result of a (successful) call to decryptEvent. + */ +data class MXEventDecryptionResult( + + /** + * The plaintext payload for the event (typically containing "type" and "content" fields). + */ + var mClearEvent: Event? = null, + + /** + * Key owned by the sender of this event. + * See MXEvent.senderKey. + */ + var mSenderCurve25519Key: String? = null, + + /** + * Ed25519 key claimed by the sender of this event. + * See MXEvent.claimedEd25519Key. + */ + var mClaimedEd25519Key: String? = null, + + /** + * List of curve25519 keys involved in telling us about the senderCurve25519Key and + * claimedEd25519Key. See MXEvent.forwardingCurve25519KeyChain. + */ + var mForwardingCurve25519KeyChain: List = ArrayList() +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt new file mode 100755 index 00000000..68aa5c67 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXMegolmExportEncryption.kt @@ -0,0 +1,373 @@ +/* + * Copyright 2017 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import android.util.Base64 + +import java.io.ByteArrayOutputStream +import java.security.SecureRandom +import java.util.Arrays + +import javax.crypto.Cipher +import javax.crypto.Mac +import javax.crypto.SecretKey +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +import timber.log.Timber +import java.nio.charset.Charset +import kotlin.experimental.and +import kotlin.experimental.xor + +/** + * Utility class to import/export the crypto data + */ +object MXMegolmExportEncryption { + private const val HEADER_LINE = "-----BEGIN MEGOLM SESSION DATA-----" + private const val TRAILER_LINE = "-----END MEGOLM SESSION DATA-----" + // we split into lines before base64ing, because encodeBase64 doesn't deal + // terribly well with large arrays. + private const val LINE_LENGTH = 72 * 4 / 3 + + // default iteration count to export the e2e keys + const val DEFAULT_ITERATION_COUNT = 500000 + + /** + * Convert a signed byte to a int value + * + * @param bVal the byte value to convert + * @return the matched int value + */ + private fun byteToInt(bVal: Byte): Int { + return (bVal and 0xFF.toByte()).toInt() + } + + /** + * Extract the AES key from the deriveKeys result. + * + * @param keyBits the deriveKeys result. + * @return the AES key + */ + private fun getAesKey(keyBits: ByteArray): ByteArray { + return Arrays.copyOfRange(keyBits, 0, 32) + } + + /** + * Extract the Hmac key from the deriveKeys result. + * + * @param keyBits the deriveKeys result. + * @return the Hmac key. + */ + private fun getHmacKey(keyBits: ByteArray): ByteArray { + return Arrays.copyOfRange(keyBits, 32, keyBits.size) + } + + /** + * Decrypt a megolm key file + * + * @param data the data to decrypt + * @param password the password. + * @return the decrypted output. + * @throws Exception the failure reason + */ + @Throws(Exception::class) + fun decryptMegolmKeyFile(data: ByteArray, password: String): String { + val body = unpackMegolmKeyFile(data) + + // check we have a version byte + if (null == body || body.size == 0) { + Timber.e("## decryptMegolmKeyFile() : Invalid file: too short") + throw Exception("Invalid file: too short") + } + + val version = body[0] + if (version.toInt() != 1) { + Timber.e("## decryptMegolmKeyFile() : Invalid file: too short") + throw Exception("Unsupported version") + } + + val ciphertextLength = body.size - (1 + 16 + 16 + 4 + 32) + if (ciphertextLength < 0) { + throw Exception("Invalid file: too short") + } + + if (TextUtils.isEmpty(password)) { + throw Exception("Empty password is not supported") + } + + val salt = Arrays.copyOfRange(body, 1, 1 + 16) + val iv = Arrays.copyOfRange(body, 17, 17 + 16) + val iterations = byteToInt(body[33]) shl 24 or (byteToInt(body[34]) shl 16) or (byteToInt(body[35]) shl 8) or byteToInt(body[36]) + val ciphertext = Arrays.copyOfRange(body, 37, 37 + ciphertextLength) + val hmac = Arrays.copyOfRange(body, body.size - 32, body.size) + + val deriveKey = deriveKeys(salt, iterations, password) + + val toVerify = Arrays.copyOfRange(body, 0, body.size - 32) + + val macKey = SecretKeySpec(getHmacKey(deriveKey), "HmacSHA256") + val mac = Mac.getInstance("HmacSHA256") + mac.init(macKey) + val digest = mac.doFinal(toVerify) + + if (!Arrays.equals(hmac, digest)) { + Timber.e("## decryptMegolmKeyFile() : Authentication check failed: incorrect password?") + throw Exception("Authentication check failed: incorrect password?") + } + + val decryptCipher = Cipher.getInstance("AES/CTR/NoPadding") + + val secretKeySpec = SecretKeySpec(getAesKey(deriveKey), "AES") + val ivParameterSpec = IvParameterSpec(iv) + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) + + val outStream = ByteArrayOutputStream() + outStream.write(decryptCipher.update(ciphertext)) + outStream.write(decryptCipher.doFinal()) + + val decodedString = String(outStream.toByteArray(), Charset.defaultCharset()) + outStream.close() + + return decodedString + } + + /** + * Encrypt a string into the megolm export format. + * + * @param data the data to encrypt. + * @param password the password + * @param kdf_rounds the iteration count + * @return the encrypted data + * @throws Exception the failure reason + */ + @Throws(Exception::class) + @JvmOverloads + fun encryptMegolmKeyFile(data: String, password: String, kdf_rounds: Int = DEFAULT_ITERATION_COUNT): ByteArray { + if (TextUtils.isEmpty(password)) { + throw Exception("Empty password is not supported") + } + + val secureRandom = SecureRandom() + + val salt = ByteArray(16) + secureRandom.nextBytes(salt) + + val iv = ByteArray(16) + secureRandom.nextBytes(iv) + + // clear bit 63 of the salt to stop us hitting the 64-bit counter boundary + // (which would mean we wouldn't be able to decrypt on Android). The loss + // of a single bit of salt is a price we have to pay. + iv[9] = iv[9] and 0x7f + + val deriveKey = deriveKeys(salt, kdf_rounds, password) + + val decryptCipher = Cipher.getInstance("AES/CTR/NoPadding") + + val secretKeySpec = SecretKeySpec(getAesKey(deriveKey), "AES") + val ivParameterSpec = IvParameterSpec(iv) + decryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec) + + val outStream = ByteArrayOutputStream() + outStream.write(decryptCipher.update(data.toByteArray(charset("UTF-8")))) + outStream.write(decryptCipher.doFinal()) + + val cipherArray = outStream.toByteArray() + val bodyLength = 1 + salt.size + iv.size + 4 + cipherArray.size + 32 + + val resultBuffer = ByteArray(bodyLength) + var idx = 0 + resultBuffer[idx++] = 1 // version + + System.arraycopy(salt, 0, resultBuffer, idx, salt.size) + idx += salt.size + + System.arraycopy(iv, 0, resultBuffer, idx, iv.size) + idx += iv.size + + resultBuffer[idx++] = (kdf_rounds shr 24 and 0xff).toByte() + resultBuffer[idx++] = (kdf_rounds shr 16 and 0xff).toByte() + resultBuffer[idx++] = (kdf_rounds shr 8 and 0xff).toByte() + resultBuffer[idx++] = (kdf_rounds and 0xff).toByte() + + System.arraycopy(cipherArray, 0, resultBuffer, idx, cipherArray.size) + idx += cipherArray.size + + val toSign = Arrays.copyOfRange(resultBuffer, 0, idx) + + val macKey = SecretKeySpec(getHmacKey(deriveKey), "HmacSHA256") + val mac = Mac.getInstance("HmacSHA256") + mac.init(macKey) + val digest = mac.doFinal(toSign) + System.arraycopy(digest, 0, resultBuffer, idx, digest.size) + + return packMegolmKeyFile(resultBuffer) + } + + /** + * Unbase64 an ascii-armoured megolm key file + * Strips the header and trailer lines, and unbase64s the content + * + * @param data the input data + * @return unbase64ed content + */ + @Throws(Exception::class) + private fun unpackMegolmKeyFile(data: ByteArray): ByteArray? { + val fileStr = String(data, Charset.defaultCharset()) + + // look for the start line + var lineStart = 0 + + while (true) { + val lineEnd = fileStr.indexOf('\n', lineStart) + + if (lineEnd < 0) { + Timber.e("## unpackMegolmKeyFile() : Header line not found") + throw Exception("Header line not found") + } + + val line = fileStr.substring(lineStart, lineEnd).trim { it <= ' ' } + + // start the next line after the newline + lineStart = lineEnd + 1 + + if (TextUtils.equals(line, HEADER_LINE)) { + break + } + } + + val dataStart = lineStart + + // look for the end line + while (true) { + val lineEnd = fileStr.indexOf('\n', lineStart) + val line: String + + if (lineEnd < 0) { + line = fileStr.substring(lineStart).trim { it <= ' ' } + } else { + line = fileStr.substring(lineStart, lineEnd).trim { it <= ' ' } + } + + if (TextUtils.equals(line, TRAILER_LINE)) { + break + } + + if (lineEnd < 0) { + Timber.e("## unpackMegolmKeyFile() : Trailer line not found") + throw Exception("Trailer line not found") + } + + // start the next line after the newline + lineStart = lineEnd + 1 + } + + val dataEnd = lineStart + + // Receiving side + return Base64.decode(fileStr.substring(dataStart, dataEnd), Base64.DEFAULT) + } + + /** + * Pack the megolm data. + * + * @param data the data to pack. + * @return the packed data + * @throws Exception the failure reason. + */ + @Throws(Exception::class) + private fun packMegolmKeyFile(data: ByteArray): ByteArray { + val nLines = (data.size + LINE_LENGTH - 1) / LINE_LENGTH + + val outStream = ByteArrayOutputStream() + outStream.write(HEADER_LINE.toByteArray()) + + var o = 0 + + for (i in 1..nLines) { + outStream.write("\n".toByteArray()) + + val len = Math.min(LINE_LENGTH, data.size - o) + outStream.write(Base64.encode(data, o, len, Base64.DEFAULT)) + o += LINE_LENGTH + } + + outStream.write("\n".toByteArray()) + outStream.write(TRAILER_LINE.toByteArray()) + outStream.write("\n".toByteArray()) + + return outStream.toByteArray() + } + + /** + * Derive the AES and HMAC-SHA-256 keys for the file + * + * @param salt salt for pbkdf + * @param iterations number of pbkdf iterations + * @param password password + * @return the derived keys + */ + @Throws(Exception::class) + private fun deriveKeys(salt: ByteArray, iterations: Int, password: String): ByteArray { + val t0 = System.currentTimeMillis() + + // based on https://en.wikipedia.org/wiki/PBKDF2 algorithm + // it is simpler than the generic algorithm because the expected key length is equal to the mac key length. + // noticed as dklen/hlen + val prf = Mac.getInstance("HmacSHA512") + prf.init(SecretKeySpec(password.toByteArray(charset("UTF-8")), "HmacSHA512")) + + // 512 bits key length + val key = ByteArray(64) + val Uc = ByteArray(64) + + // U1 = PRF(Password, Salt || INT_32_BE(i)) + prf.update(salt) + val int32BE = ByteArray(4) + Arrays.fill(int32BE, 0.toByte()) + int32BE[3] = 1.toByte() + prf.update(int32BE) + prf.doFinal(Uc, 0) + + // copy to the key + System.arraycopy(Uc, 0, key, 0, Uc.size) + + for (index in 2..iterations) { + // Uc = PRF(Password, Uc-1) + prf.update(Uc) + prf.doFinal(Uc, 0) + + // F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc + for (byteIndex in Uc.indices) { + key[byteIndex] = key[byteIndex] xor Uc[byteIndex] + } + } + + Timber.d("## deriveKeys() : " + iterations + " in " + (System.currentTimeMillis() - t0) + " ms") + + return key + } +} +/** + * Encrypt a string into the megolm export format. + * + * @param data the data to encrypt. + * @param password the password + * @return the encrypted data + * @throws Exception the failure reason + */ \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt new file mode 100755 index 00000000..4c26afca --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -0,0 +1,814 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult +import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 +import im.vector.matrix.android.internal.crypto.model.MXOlmSession +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.util.convertFromUTF8 +import im.vector.matrix.android.internal.util.convertToUTF8 +import org.matrix.olm.* +import timber.log.Timber +import java.net.URLEncoder +import java.util.* + +// The libolm wrapper. +internal class MXOlmDevice( + /** + * The store where crypto data is saved. + */ + private val mStore: IMXCryptoStore) { + + /** + * @return the Curve25519 key for the account. + */ + var deviceCurve25519Key: String? = null + private set + + /** + * @return the Ed25519 key for the account. + */ + var deviceEd25519Key: String? = null + private set + + // The OLMKit account instance. + private var mOlmAccount: OlmAccount? = null + + // The OLMKit utility instance. + private var mOlmUtility: OlmUtility? = null + + // The outbound group session. + // They are not stored in 'store' to avoid to remember to which devices we sent the session key. + // Plus, in cryptography, it is good to refresh sessions from time to time. + // The key is the session id, the value the outbound group session. + private val mOutboundGroupSessionStore: MutableMap + + // Store a set of decrypted message indexes for each group session. + // This partially mitigates a replay attack where a MITM resends a group + // message into the room. + // + // The Matrix SDK exposes events through MXEventTimelines. A developer can open several + // timelines from a same room so that a message can be decrypted several times but from + // a different timeline. + // So, store these message indexes per timeline id. + // + // The first level keys are timeline ids. + // The second level keys are strings of form "||" + // Values are true. + private val mInboundGroupSessionMessageIndexes: MutableMap> + + /** + * inboundGroupSessionWithId error + */ + private var mInboundGroupSessionWithIdError: MXCryptoError? = null + + init { + // Retrieve the account from the store + mOlmAccount = mStore.getAccount() + + if (null == mOlmAccount) { + Timber.d("MXOlmDevice : create a new olm account") + // Else, create it + try { + mOlmAccount = OlmAccount() + mStore.storeAccount(mOlmAccount!!) + } catch (e: Exception) { + Timber.e(e, "MXOlmDevice : cannot initialize mOlmAccount") + } + + } else { + Timber.d("MXOlmDevice : use an existing account") + } + + try { + mOlmUtility = OlmUtility() + } catch (e: Exception) { + Timber.e(e, "## MXOlmDevice : OlmUtility failed with error") + mOlmUtility = null + } + + mOutboundGroupSessionStore = HashMap() + + try { + deviceCurve25519Key = mOlmAccount!!.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY] + } catch (e: Exception) { + Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_IDENTITY_KEY + " with error") + } + + try { + deviceEd25519Key = mOlmAccount!!.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY] + } catch (e: Exception) { + Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_FINGER_PRINT_KEY + " with error") + } + + mInboundGroupSessionMessageIndexes = HashMap() + } + + /** + * @return The current (unused, unpublished) one-time keys for this account. + */ + fun getOneTimeKeys(): Map>? { + try { + return mOlmAccount!!.oneTimeKeys() + } catch (e: Exception) { + Timber.e(e, "## getOneTimeKeys() : failed") + } + + return null + } + + /** + * @return The maximum number of one-time keys the olm account can store. + */ + fun getMaxNumberOfOneTimeKeys(): Long { + return mOlmAccount?.maxOneTimeKeys() ?: -1 + } + + /** + * Release the instance + */ + fun release() { + if (null != mOlmAccount) { + mOlmAccount!!.releaseAccount() + } + } + + /** + * Signs a message with the ed25519 key for this account. + * + * @param message the message to be signed. + * @return the base64-encoded signature. + */ + fun signMessage(message: String): String? { + try { + return mOlmAccount!!.signMessage(message) + } catch (e: Exception) { + Timber.e(e, "## signMessage() : failed") + } + + return null + } + + /** + * Marks all of the one-time keys as published. + */ + fun markKeysAsPublished() { + try { + mOlmAccount!!.markOneTimeKeysAsPublished() + mStore.storeAccount(mOlmAccount!!) + } catch (e: Exception) { + Timber.e(e, "## markKeysAsPublished() : failed") + } + } + + /** + * Generate some new one-time keys + * + * @param numKeys number of keys to generate + */ + fun generateOneTimeKeys(numKeys: Int) { + try { + mOlmAccount!!.generateOneTimeKeys(numKeys) + mStore.storeAccount(mOlmAccount!!) + } catch (e: Exception) { + Timber.e(e, "## generateOneTimeKeys() : failed") + } + + } + + /** + * Generate a new outbound session. + * The new session will be stored in the MXStore. + * + * @param theirIdentityKey the remote user's Curve25519 identity key + * @param theirOneTimeKey the remote user's one-time Curve25519 key + * @return the session id for the outbound session. + */ + fun createOutboundSession(theirIdentityKey: String, theirOneTimeKey: String): String? { + Timber.d("## createOutboundSession() ; theirIdentityKey $theirIdentityKey theirOneTimeKey $theirOneTimeKey") + var olmSession: OlmSession? = null + + try { + olmSession = OlmSession() + olmSession.initOutboundSession(mOlmAccount!!, theirIdentityKey, theirOneTimeKey) + + val mxOlmSession = MXOlmSession(olmSession, 0) + + // Pretend we've received a message at this point, otherwise + // if we try to send a message to the device, it won't use + // this session + mxOlmSession.onMessageReceived() + + mStore.storeSession(mxOlmSession, theirIdentityKey) + + val sessionIdentifier = olmSession.sessionIdentifier() + + Timber.d("## createOutboundSession() ; olmSession.sessionIdentifier: $sessionIdentifier") + return sessionIdentifier + + } catch (e: Exception) { + Timber.e(e, "## createOutboundSession() failed") + + olmSession?.releaseSession() + } + + return null + } + + /** + * Generate a new inbound session, given an incoming message. + * + * @param theirDeviceIdentityKey the remote user's Curve25519 identity key. + * @param messageType the message_type field from the received message (must be 0). + * @param ciphertext base64-encoded body from the received message. + * @return {{payload: string, session_id: string}} decrypted payload, and session id of new session. + */ + fun createInboundSession(theirDeviceIdentityKey: String, messageType: Int, ciphertext: String): Map? { + + Timber.d("## createInboundSession() : theirIdentityKey: $theirDeviceIdentityKey") + + var olmSession: OlmSession? = null + + try { + try { + olmSession = OlmSession() + olmSession.initInboundSessionFrom(mOlmAccount!!, theirDeviceIdentityKey, ciphertext) + } catch (e: Exception) { + Timber.e(e, "## createInboundSession() : the session creation failed") + return null + } + + Timber.d("## createInboundSession() : sessionId: " + olmSession.sessionIdentifier()) + + try { + mOlmAccount!!.removeOneTimeKeys(olmSession) + mStore.storeAccount(mOlmAccount!!) + } catch (e: Exception) { + Timber.e(e, "## createInboundSession() : removeOneTimeKeys failed") + } + + Timber.d("## createInboundSession() : ciphertext: $ciphertext") + try { + Timber.d("## createInboundSession() :ciphertext: SHA256:" + mOlmUtility!!.sha256(URLEncoder.encode(ciphertext, "utf-8"))) + } catch (e: Exception) { + Timber.e(e, "## createInboundSession() :ciphertext: cannot encode ciphertext") + } + + val olmMessage = OlmMessage() + olmMessage.mCipherText = ciphertext + olmMessage.mType = messageType.toLong() + + var payloadString: String? = null + + try { + payloadString = olmSession.decryptMessage(olmMessage) + + val mxOlmSession = MXOlmSession(olmSession, 0) + // This counts as a received message: set last received message time to now + mxOlmSession.onMessageReceived() + + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey) + } catch (e: Exception) { + Timber.e(e, "## createInboundSession() : decryptMessage failed") + } + + val res = HashMap() + + if (!TextUtils.isEmpty(payloadString)) { + res["payload"] = payloadString!! + } + + val sessionIdentifier = olmSession.sessionIdentifier() + + if (!TextUtils.isEmpty(sessionIdentifier)) { + res["session_id"] = sessionIdentifier + } + + return res + } catch (e: Exception) { + Timber.e(e, "## createInboundSession() : OlmSession creation failed") + + olmSession?.releaseSession() + } + + return null + } + + /** + * Get a list of known session IDs for the given device. + * + * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. + * @return a list of known session ids for the device. + */ + fun getSessionIds(theirDeviceIdentityKey: String): Set? { + return mStore.getDeviceSessionIds(theirDeviceIdentityKey) + } + + /** + * Get the right olm session id for encrypting messages to the given identity key. + * + * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. + * @return the session id, or null if no established session. + */ + fun getSessionId(theirDeviceIdentityKey: String): String? { + return mStore.getLastUsedSessionId(theirDeviceIdentityKey) + } + + /** + * Encrypt an outgoing message using an existing session. + * + * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. + * @param sessionId the id of the active session + * @param payloadString the payload to be encrypted and sent + * @return the cipher text + */ + fun encryptMessage(theirDeviceIdentityKey: String, sessionId: String, payloadString: String): Map? { + var res: MutableMap? = null + val olmMessage: OlmMessage + val mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId) + + if (mxOlmSession != null) { + try { + Timber.d("## encryptMessage() : olmSession.sessionIdentifier: $sessionId") + //Timber.d("## encryptMessage() : payloadString: " + payloadString); + + olmMessage = mxOlmSession.olmSession.encryptMessage(payloadString) + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey) + res = HashMap() + + res["body"] = olmMessage.mCipherText + res["type"] = olmMessage.mType + } catch (e: Exception) { + Timber.e(e, "## encryptMessage() : failed " + e.message) + } + + } + + return res + } + + /** + * Decrypt an incoming message using an existing session. + * + * @param ciphertext the base64-encoded body from the received message. + * @param messageType message_type field from the received message. + * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. + * @param sessionId the id of the active session. + * @return the decrypted payload. + */ + fun decryptMessage(ciphertext: String, messageType: Int, sessionId: String, theirDeviceIdentityKey: String): String? { + var payloadString: String? = null + + val mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId) + + if (null != mxOlmSession) { + val olmMessage = OlmMessage() + olmMessage.mCipherText = ciphertext + olmMessage.mType = messageType.toLong() + + try { + payloadString = mxOlmSession.olmSession.decryptMessage(olmMessage) + mxOlmSession.onMessageReceived() + mStore.storeSession(mxOlmSession, theirDeviceIdentityKey) + } catch (e: Exception) { + Timber.e(e, "## decryptMessage() : decryptMessage failed " + e.message) + } + + } + + return payloadString + } + + /** + * Determine if an incoming messages is a prekey message matching an existing session. + * + * @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. + * @param sessionId the id of the active session. + * @param messageType message_type field from the received message. + * @param ciphertext the base64-encoded body from the received message. + * @return YES if the received message is a prekey message which matchesthe given session. + */ + fun matchesSession(theirDeviceIdentityKey: String, sessionId: String, messageType: Int, ciphertext: String): Boolean { + if (messageType != 0) { + return false + } + + val mxOlmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId) + return null != mxOlmSession && mxOlmSession.olmSession.matchesInboundSession(ciphertext) + } + + + // Outbound group session + + /** + * Generate a new outbound group session. + * + * @return the session id for the outbound session. + */ + fun createOutboundGroupSession(): String? { + var session: OlmOutboundGroupSession? = null + try { + session = OlmOutboundGroupSession() + mOutboundGroupSessionStore[session.sessionIdentifier()] = session + return session.sessionIdentifier() + } catch (e: Exception) { + Timber.e(e, "createOutboundGroupSession " + e.message) + + session?.releaseSession() + } + + return null + } + + /** + * Get the current session key of an outbound group session. + * + * @param sessionId the id of the outbound group session. + * @return the base64-encoded secret key. + */ + fun getSessionKey(sessionId: String): String? { + if (!TextUtils.isEmpty(sessionId)) { + try { + return mOutboundGroupSessionStore[sessionId]!!.sessionKey() + } catch (e: Exception) { + Timber.e(e, "## getSessionKey() : failed " + e.message) + } + + } + return null + } + + /** + * Get the current message index of an outbound group session. + * + * @param sessionId the id of the outbound group session. + * @return the current chain index. + */ + fun getMessageIndex(sessionId: String): Int { + return if (!TextUtils.isEmpty(sessionId)) { + mOutboundGroupSessionStore[sessionId]!!.messageIndex() + } else 0 + } + + /** + * Encrypt an outgoing message with an outbound group session. + * + * @param sessionId the id of the outbound group session. + * @param payloadString the payload to be encrypted and sent. + * @return ciphertext + */ + fun encryptGroupMessage(sessionId: String, payloadString: String): String? { + if (!TextUtils.isEmpty(sessionId) && !TextUtils.isEmpty(payloadString)) { + try { + return mOutboundGroupSessionStore[sessionId]!!.encryptMessage(payloadString) + } catch (e: Exception) { + Timber.e(e, "## encryptGroupMessage() : failed " + e.message) + } + + } + return null + } + + // Inbound group session + + /** + * Add an inbound group session to the session store. + * + * @param sessionId the session identifier. + * @param sessionKey base64-encoded secret key. + * @param roomId the id of the room in which this session will be used. + * @param senderKey the base64-encoded curve25519 key of the sender. + * @param forwardingCurve25519KeyChain Devices involved in forwarding this session to us. + * @param keysClaimed Other keys the sender claims. + * @param exportFormat true if the megolm keys are in export format + * @return true if the operation succeeds. + */ + fun addInboundGroupSession(sessionId: String, + sessionKey: String, + roomId: String, + senderKey: String, + forwardingCurve25519KeyChain: List, + keysClaimed: Map, + exportFormat: Boolean): Boolean { + val existingInboundSession = getInboundGroupSession(sessionId, senderKey, roomId) + val session = MXOlmInboundGroupSession2(sessionKey, exportFormat) + + if (null != existingInboundSession) { + // If we already have this session, consider updating it + Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + + val existingFirstKnown = existingInboundSession.firstKnownIndex!! + val newKnownFirstIndex = session.firstKnownIndex!! + + //If our existing session is better we keep it + if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { + if (session.mSession != null) { + session.mSession!!.releaseSession() + } + return false + } + } + + // sanity check + if (null == session.mSession) { + Timber.e("## addInboundGroupSession : invalid session") + return false + } + + try { + if (!TextUtils.equals(session.mSession!!.sessionIdentifier(), sessionId)) { + Timber.e("## addInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey") + session.mSession!!.releaseSession() + return false + } + } catch (e: Exception) { + session.mSession!!.releaseSession() + Timber.e(e, "## addInboundGroupSession : sessionIdentifier() failed") + return false + } + + session.mSenderKey = senderKey + session.mRoomId = roomId + session.mKeysClaimed = keysClaimed + session.mForwardingCurve25519KeyChain = forwardingCurve25519KeyChain + + mStore.storeInboundGroupSessions(listOf(session)) + + return true + } + + /** + * Import an inbound group sessions to the session store. + * + * @param megolmSessionsData the megolm sessions data + * @return the successfully imported sessions. + */ + fun importInboundGroupSessions(megolmSessionsData: List): List { + val sessions = ArrayList(megolmSessionsData.size) + + for (megolmSessionData in megolmSessionsData) { + + val sessionId = megolmSessionData.sessionId + val senderKey = megolmSessionData.senderKey + val roomId = megolmSessionData.roomId + + var session: MXOlmInboundGroupSession2? = null + + try { + session = MXOlmInboundGroupSession2(megolmSessionData) + } catch (e: Exception) { + Timber.e(e, "## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + } + + // sanity check + if (null == session || null == session.mSession) { + Timber.e("## importInboundGroupSession : invalid session") + continue + } + + try { + if (!TextUtils.equals(session.mSession!!.sessionIdentifier(), sessionId)) { + Timber.e("## importInboundGroupSession : ERROR: Mismatched group session ID from senderKey: " + senderKey!!) + if (session.mSession != null) session.mSession!!.releaseSession() + continue + } + } catch (e: Exception) { + Timber.e(e, "## importInboundGroupSession : sessionIdentifier() failed") + session.mSession!!.releaseSession() + continue + } + + val existingOlmSession = getInboundGroupSession(sessionId, senderKey, roomId) + if (null != existingOlmSession) { + // If we already have this session, consider updating it + Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + + // For now we just ignore updates. TODO: implement something here + if (existingOlmSession.firstKnownIndex!! <= session.firstKnownIndex!!) { + //Ignore this, keep existing + session.mSession!!.releaseSession() + continue + } + } + + sessions.add(session) + } + + mStore.storeInboundGroupSessions(sessions) + + return sessions + } + + /** + * Remove an inbound group session + * + * @param sessionId the session identifier. + * @param sessionKey base64-encoded secret key. + */ + fun removeInboundGroupSession(sessionId: String?, sessionKey: String?) { + if (null != sessionId && null != sessionKey) { + mStore.removeInboundGroupSession(sessionId, sessionKey) + } + } + + /** + * Decrypt a received message with an inbound group session. + * + * @param body the base64-encoded body of the encrypted message. + * @param roomId the room in which the message was received. + * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. + * @param sessionId the session identifier. + * @param senderKey the base64-encoded curve25519 key of the sender. + * @return the decrypting result. Nil if the sessionId is unknown. + */ + @Throws(MXDecryptionException::class) + fun decryptGroupMessage(body: String, + roomId: String, + timeline: String?, + sessionId: String, + senderKey: String): MXDecryptionResult? { + val result = MXDecryptionResult() + val session = getInboundGroupSession(sessionId, senderKey, roomId) + + if (null != session) { + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (TextUtils.equals(roomId, session.mRoomId)) { + var errorMessage = "" + var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null + try { + decryptResult = session.mSession!!.decryptMessage(body) + } catch (e: Exception) { + Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") + errorMessage = e.message ?: "" + } + + if (null != decryptResult) { + if (null != timeline) { + if (!mInboundGroupSessionMessageIndexes.containsKey(timeline)) { + mInboundGroupSessionMessageIndexes[timeline] = HashMap() + } + + val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + + if (null != mInboundGroupSessionMessageIndexes[timeline]!![messageIndexKey]) { + val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) + Timber.e("## decryptGroupMessage() : $reason") + throw MXDecryptionException(MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, reason)) + } + + mInboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + } + + mStore.storeInboundGroupSessions(listOf(session)) + try { + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(Map::class.java) + result.mPayload = adapter.fromJson(convertFromUTF8(decryptResult.mDecryptedMessage)) as Event? + } catch (e: Exception) { + Timber.e(e, "## decryptGroupMessage() : RLEncoder.encode failed " + e.message) + return null + } + + if (null == result.mPayload) { + Timber.e("## decryptGroupMessage() : fails to parse the payload") + return null + } + + result.mKeysClaimed = session.mKeysClaimed + result.mSenderKey = senderKey + result.mForwardingCurve25519KeyChain = session.mForwardingCurve25519KeyChain + } else { + Timber.e("## decryptGroupMessage() : failed to decode the message") + throw MXDecryptionException(MXCryptoError(MXCryptoError.OLM_ERROR_CODE, errorMessage, null)) + } + } else { + val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId) + Timber.e("## decryptGroupMessage() : $reason") + throw MXDecryptionException(MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, reason)) + } + } else { + Timber.e("## decryptGroupMessage() : Cannot retrieve inbound group session $sessionId") + throw MXDecryptionException(mInboundGroupSessionWithIdError) + } + + return result + } + + /** + * Reset replay attack data for the given timeline. + * + * @param timeline the id of the timeline. + */ + fun resetReplayAttackCheckInTimeline(timeline: String?) { + if (null != timeline) { + mInboundGroupSessionMessageIndexes.remove(timeline) + } + } + + // Utilities + + /** + * Verify an ed25519 signature on a JSON object. + * + * @param key the ed25519 key. + * @param jsonDictionary the JSON object which was signed. + * @param signature the base64-encoded signature to be checked. + * @throws Exception the exception + */ + @Throws(Exception::class) + fun verifySignature(key: String, jsonDictionary: Map, signature: String) { + // Check signature on the canonical version of the JSON + mOlmUtility!!.verifyEd25519Signature(signature, key, MoshiProvider.getCanonicalJson>(Map::class.java, jsonDictionary)) + } + + /** + * Calculate the SHA-256 hash of the input and encodes it as base64. + * + * @param message the message to hash. + * @return the base64-encoded hash value. + */ + fun sha256(message: String): String { + return mOlmUtility!!.sha256(convertToUTF8(message)) + } + + /** + * Search an OlmSession + * + * @param theirDeviceIdentityKey the device key + * @param sessionId the session Id + * @return the olm session + */ + private fun getSessionForDevice(theirDeviceIdentityKey: String, sessionId: String): MXOlmSession? { + // sanity check + return if (!TextUtils.isEmpty(theirDeviceIdentityKey) && !TextUtils.isEmpty(sessionId)) { + mStore.getDeviceSession(sessionId, theirDeviceIdentityKey) + } else null + + } + + /** + * Extract an InboundGroupSession from the session store and do some check. + * mInboundGroupSessionWithIdError describes the failure reason. + * + * @param roomId the room where the session is used. + * @param sessionId the session identifier. + * @param senderKey the base64-encoded curve25519 key of the sender. + * @return the inbound group session. + */ + fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): MXOlmInboundGroupSession2? { + mInboundGroupSessionWithIdError = null + + val session = mStore.getInboundGroupSession(sessionId!!, senderKey!!) + + if (null != session) { + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (!TextUtils.equals(roomId, session.mRoomId)) { + val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId) + Timber.e("## getInboundGroupSession() : $errorDescription") + mInboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, errorDescription) + } + } else { + Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") + mInboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, + MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON, null) + } + return session + } + + /** + * Determine if we have the keys for a given megolm session. + * + * @param roomId room in which the message was received + * @param senderKey base64-encoded curve25519 key of the sender + * @param sessionId session identifier + * @return true if the unbound session keys are known. + */ + fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean { + return null != getInboundGroupSession(sessionId, senderKey, roomId) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt new file mode 100755 index 00000000..c314fcba --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt @@ -0,0 +1,312 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.os.Handler +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareCancellation +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import java.util.* + +internal class MXOutgoingRoomKeyRequestManager( + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor) { + + // working handler (should not be the UI thread) + private lateinit var mWorkingHandler: Handler + + // running + var mClientRunning: Boolean = false + + // transaction counter + private var mTxnCtr: Int = 0 + + // sanity check to ensure that we don't end up with two concurrent runs + // of mSendOutgoingRoomKeyRequestsTimer + private var mSendOutgoingRoomKeyRequestsRunning: Boolean = false + + fun setWorkingHandler(encryptingThreadHandler: Handler) { + mWorkingHandler = encryptingThreadHandler + } + + /** + * Called when the client is started. Sets background processes running. + */ + fun start() { + mClientRunning = true + startTimer() + } + + /** + * Called when the client is stopped. Stops any running background processes. + */ + fun stop() { + mClientRunning = false + } + + /** + * Make up a new transaction id + * + * @return {string} a new, unique, transaction id + */ + private fun makeTxnId(): String { + return "m" + System.currentTimeMillis() + "." + mTxnCtr++ + } + + /** + * Send off a room key request, if we haven't already done so. + * + * + * The `requestBody` is compared (with a deep-equality check) against + * previous queued or sent requests and if it matches, no change is made. + * Otherwise, a request is added to the pending list, and a job is started + * in the background to send it. + * + * @param requestBody requestBody + * @param recipients recipients + */ + fun sendRoomKeyRequest(requestBody: RoomKeyRequestBody?, recipients: List>) { + mWorkingHandler.post { + val req = mCryptoStore.getOrAddOutgoingRoomKeyRequest( + OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT)) + + + if (req!!.mState === OutgoingRoomKeyRequest.RequestState.UNSENT) { + startTimer() + } + } + } + + /** + * Cancel room key requests, if any match the given details + * + * @param requestBody requestBody + */ + fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) { + cancelRoomKeyRequest(requestBody, false) + } + + /** + * Cancel room key requests, if any match the given details, and resend + * + * @param requestBody requestBody + */ + fun resendRoomKeyRequest(requestBody: RoomKeyRequestBody) { + cancelRoomKeyRequest(requestBody, true) + } + + /** + * Cancel room key requests, if any match the given details, and resend + * + * @param requestBody requestBody + * @param andResend true to resend the key request + */ + private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) { + val req = mCryptoStore.getOutgoingRoomKeyRequest(requestBody) + ?: // no request was made for this key + return + + Timber.d("cancelRoomKeyRequest: requestId: " + req.mRequestId + " state: " + req.mState + " andResend: " + andResend) + + if (req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING || req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND) { + // nothing to do here + } else if (req.mState === OutgoingRoomKeyRequest.RequestState.UNSENT || req.mState === OutgoingRoomKeyRequest.RequestState.FAILED) { + Timber.d("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody") + mCryptoStore.deleteOutgoingRoomKeyRequest(req.mRequestId) + } else if (req.mState === OutgoingRoomKeyRequest.RequestState.SENT) { + if (andResend) { + req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND + } else { + req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING + } + req.mCancellationTxnId = makeTxnId() + mCryptoStore.updateOutgoingRoomKeyRequest(req) + sendOutgoingRoomKeyRequestCancellation(req) + } + } + + + /** + * Start the background timer to send queued requests, if the timer isn't already running. + */ + private fun startTimer() { + mWorkingHandler.post(Runnable { + if (mSendOutgoingRoomKeyRequestsRunning) { + return@Runnable + } + + mWorkingHandler.postDelayed(Runnable { + if (mSendOutgoingRoomKeyRequestsRunning) { + Timber.d("## startTimer() : RoomKeyRequestSend already in progress!") + return@Runnable + } + + mSendOutgoingRoomKeyRequestsRunning = true + sendOutgoingRoomKeyRequests() + }, SEND_KEY_REQUESTS_DELAY_MS.toLong()) + }) + } + + // look for and send any queued requests. Runs itself recursively until + // there are no more requests, or there is an error (in which case, the + // timer will be restarted before the promise resolves). + private fun sendOutgoingRoomKeyRequests() { + if (!mClientRunning) { + mSendOutgoingRoomKeyRequestsRunning = false + return + } + + Timber.d("## sendOutgoingRoomKeyRequests() : Looking for queued outgoing room key requests") + val outgoingRoomKeyRequest = mCryptoStore.getOutgoingRoomKeyRequestByState( + HashSet(Arrays.asList(OutgoingRoomKeyRequest.RequestState.UNSENT, + OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING, + OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND))) + + if (null == outgoingRoomKeyRequest) { + Timber.e("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests") + mSendOutgoingRoomKeyRequestsRunning = false + return + } + + if (OutgoingRoomKeyRequest.RequestState.UNSENT === outgoingRoomKeyRequest.mState) { + sendOutgoingRoomKeyRequest(outgoingRoomKeyRequest) + } else { + sendOutgoingRoomKeyRequestCancellation(outgoingRoomKeyRequest) + } + } + + /** + * Send the outgoing key request. + * + * @param request the request + */ + private fun sendOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) { + Timber.d("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.mRequestBody + + " from " + request.mRecipients + " id " + request.mRequestId) + + val requestMessage = RoomKeyShareRequest() + requestMessage.requestingDeviceId = mCryptoStore.getDeviceId() + requestMessage.requestId = request.mRequestId + requestMessage.body = request.mRequestBody + + sendMessageToDevices(requestMessage, request.mRecipients, request.mRequestId, object : MatrixCallback { + private fun onDone(state: OutgoingRoomKeyRequest.RequestState) { + mWorkingHandler.post { + if (request.mState !== OutgoingRoomKeyRequest.RequestState.UNSENT) { + Timber.d("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.mState) + } else { + request.mState = state + mCryptoStore.updateOutgoingRoomKeyRequest(request) + } + + mSendOutgoingRoomKeyRequestsRunning = false + startTimer() + } + } + + override fun onSuccess(data: Unit) { + Timber.d("## sendOutgoingRoomKeyRequest succeed") + onDone(OutgoingRoomKeyRequest.RequestState.SENT) + } + + override fun onFailure(failure: Throwable) { + Timber.e("## sendOutgoingRoomKeyRequest failed") + onDone(OutgoingRoomKeyRequest.RequestState.FAILED) + } + }) + } + + /** + * Given a OutgoingRoomKeyRequest, cancel it and delete the request record + * + * @param request the request + */ + private fun sendOutgoingRoomKeyRequestCancellation(request: OutgoingRoomKeyRequest) { + Timber.d("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.mRequestBody + + " to " + request.mRecipients + + " cancellation id " + request.mCancellationTxnId) + + val roomKeyShareCancellation = RoomKeyShareCancellation() + roomKeyShareCancellation.requestingDeviceId = mCryptoStore.getDeviceId() + roomKeyShareCancellation.requestId = request.mCancellationTxnId + + sendMessageToDevices(roomKeyShareCancellation, request.mRecipients, request.mCancellationTxnId, object : MatrixCallback { + private fun onDone() { + mWorkingHandler.post { + mCryptoStore.deleteOutgoingRoomKeyRequest(request.mRequestId) + mSendOutgoingRoomKeyRequestsRunning = false + startTimer() + } + } + + + override fun onSuccess(data: Unit) { + Timber.d("## sendOutgoingRoomKeyRequestCancellation() : done") + val resend = request.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND + + onDone() + + // Resend the request with a new ID + if (resend) { + sendRoomKeyRequest(request.mRequestBody, request.mRecipients) + } + } + + override fun onFailure(failure: Throwable) { + Timber.e("## sendOutgoingRoomKeyRequestCancellation failed") + onDone() + } + }) + } + + /** + * Send a SendToDeviceObject to a list of recipients + * + * @param message the message + * @param recipients the recipients. + * @param transactionId the transaction id + * @param callback the asynchronous callback. + */ + private fun sendMessageToDevices(message: Any, + recipients: List>, + transactionId: String?, + callback: MatrixCallback) { + val contentMap = MXUsersDevicesMap() + + for (recipient in recipients) { + contentMap.setObject(message, recipient["userId"], recipient["deviceId"]) + } + + mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + + companion object { + private const val SEND_KEY_REQUESTS_DELAY_MS = 500 + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt new file mode 100644 index 00000000..04fb5eb4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.di.MoshiProvider + +/** + * The type of object we use for importing and exporting megolm session data. + */ +@JsonClass(generateAdapter = true) +data class MegolmSessionData( + /** + * The algorithm used. + */ + @Json(name = "algorithm") + var algorithm: String? = null, + + /** + * Unique id for the session. + */ + @Json(name = "session_id") + var sessionId: String? = null, + + /** + * Sender's Curve25519 device key. + */ + @Json(name = "sender_key") + var senderKey: String? = null, + + /** + * Room this session is used in. + */ + @Json(name = "room_id") + var roomId: String? = null, + + /** + * Base64'ed key data. + */ + @Json(name = "session_key") + var sessionKey: String? = null, + + /** + * Other keys the sender claims. + */ + @Json(name = "sender_claimed_keys") + var senderClaimedKeys: Map? = null, + + // This is a shortcut for sender_claimed_keys.get("ed25519") + // Keep it for compatibility reason. + @Json(name = "sender_claimed_ed25519_key") + var senderClaimedEd25519Key: String? = null, + + /** + * Devices which forwarded this session to us (normally empty). + */ + @Json(name = "forwarding_curve25519_key_chain") + var forwardingCurve25519KeyChain: List? = null +) { + + fun toJsonString(): String { + return MoshiProvider.providesMoshi().adapter(MegolmSessionData::class.java).toJson(this) + } +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt new file mode 100755 index 00000000..a439fddd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody + +/** + * Represents an outgoing room key request + */ +class OutgoingRoomKeyRequest( + // RequestBody + var mRequestBody: RoomKeyRequestBody?, // list of recipients for the request + var mRecipients: List>, // Unique id for this request. Used for both + // an id within the request for later pairing with a cancellation, and for + // the transaction id when sending the to_device messages to our local + var mRequestId: String, // current state of this request + var mState: RequestState) { + + // transaction id for the cancellation, if any + var mCancellationTxnId: String? = null + + /** + * Used only for log. + * + * @return the room id. + */ + val roomId: String? + get() = if (null != mRequestBody) { + mRequestBody!!.roomId + } else null + + /** + * Used only for log. + * + * @return the session id + */ + val sessionId: String? + get() = if (null != mRequestBody) { + mRequestBody!!.sessionId + } else null + + /** + * possible states for a room key request + * + * + * The state machine looks like: + *
    +     *
    +     *      |
    +     *      V
    +     *    UNSENT  -----------------------------+
    +     *      |                                  |
    +     *      | (send successful)                | (cancellation requested)
    +     *      V                                  |
    +     *     SENT                                |
    +     *      |--------------------------------  |  --------------+
    +     *      |                                  |                |
    +     *      |                                  |                | (cancellation requested with intent
    +     *      |                                  |                | to resend a new request)
    +     *      | (cancellation requested)         |                |
    +     *      V                                  |                V
    +     *  CANCELLATION_PENDING                   | CANCELLATION_PENDING_AND_WILL_RESEND
    +     *      |                                  |                |
    +     *      | (cancellation sent)              |                | (cancellation sent. Create new request
    +     *      |                                  |                |  in the UNSENT state)
    +     *      V                                  |                |
    +     *  (deleted)  <---------------------------+----------------+
    +     *  
    + */ + + enum class RequestState { + /** + * request not yet sent + */ + UNSENT, + /** + * request sent, awaiting reply + */ + SENT, + /** + * reply received, cancellation not yet sent + */ + CANCELLATION_PENDING, + /** + * Cancellation not yet sent, once sent, a new request will be done + */ + CANCELLATION_PENDING_AND_WILL_RESEND, + /** + * sending failed + */ + FAILED; + + companion object { + fun from(state: Int) = when (state) { + 0 -> UNSENT + 1 -> SENT + 2 -> CANCELLATION_PENDING + 3 -> CANCELLATION_PENDING_AND_WILL_RESEND + else /*4*/ -> FAILED + } + } + } +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt new file mode 100644 index 00000000..04a0f596 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import timber.log.Timber +import java.util.* + +internal class RoomDecryptorProvider( + val mCredentials: Credentials, + val olmDevice: MXOlmDevice, + val deviceListManager: DeviceListManager, + val mSendToDeviceTask: SendToDeviceTask, + val mTaskExecutor: TaskExecutor +) { + + // A map from algorithm to MXDecrypting instance, for each room + private val mRoomDecryptors: MutableMap>/* room id */ = HashMap() + + /** + * Get a decryptor for a given room and algorithm. + * If we already have a decryptor for the given room and algorithm, return + * it. Otherwise try to instantiate it. + * + * @param roomId the room id + * @param algorithm the crypto algorithm + * @return the decryptor + * TODO do not provide cryptoManager? + */ + fun getOrCreateRoomDecryptor(cryptoManager: CryptoManager, roomId: String?, algorithm: String?): IMXDecrypting? { + // sanity check + if (TextUtils.isEmpty(algorithm)) { + Timber.e("## getRoomDecryptor() : null algorithm") + return null + } + + if (null == mRoomDecryptors) { + Timber.e("## getRoomDecryptor() : null mRoomDecryptors") + return null + } + + var alg: IMXDecrypting? = null + + if (!TextUtils.isEmpty(roomId)) { + synchronized(mRoomDecryptors) { + if (!mRoomDecryptors.containsKey(roomId)) { + mRoomDecryptors[roomId!!] = HashMap() + } + + alg = mRoomDecryptors[roomId]!![algorithm] + } + + if (null != alg) { + return alg + } + } + + val decryptingClass = MXCryptoAlgorithms.decryptorClassForAlgorithm(algorithm) + + if (null != decryptingClass) { + try { + val ctor = decryptingClass.constructors[0] + alg = ctor.newInstance() as IMXDecrypting + + if (null != alg) { + alg!!.initWithMatrixSession(mCredentials, cryptoManager, olmDevice, deviceListManager, mSendToDeviceTask, mTaskExecutor) + + if (!TextUtils.isEmpty(roomId)) { + synchronized(mRoomDecryptors) { + mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!) + } + } + } + } catch (e: Exception) { + Timber.e(e, "## getRoomDecryptor() : fail to load the class") + return null + } + + } + + return alg + } + + fun getRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? { + if (roomId == null || algorithm == null) { + return null + } + + return mRoomDecryptors[roomId]?.get(algorithm) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt new file mode 100644 index 00000000..a8711a1b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor + +/** + * An interface for decrypting data + */ +internal interface IMXDecrypting { + + /** + * Init the object fields + * + * @param matrixSession the session + */ + fun initWithMatrixSession(credentials: Credentials, + crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor) + + /** + * Decrypt an event + * + * @param event the raw event. + * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. + * @return the decryption information, or null in case of error + * @throws MXDecryptionException the decryption failure reason + */ + @Throws(MXDecryptionException::class) + fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? + + /** + * Handle a key event. + * + * @param event the key event. + */ + fun onRoomKeyEvent(event: Event) + + /** + * Check if the some messages can be decrypted with a new session + * + * @param senderKey the session sender key + * @param sessionId the session id + */ + fun onNewSession(senderKey: String, sessionId: String) + + /** + * Determine if we have the keys necessary to respond to a room key request + * + * @param request keyRequest + * @return true if we have the keys and could (theoretically) share + */ + fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean + + /** + * Send the response to a room key request. + * + * @param request keyRequest + */ + fun shareKeysWithDevice(request: IncomingRoomKeyRequest) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt new file mode 100644 index 00000000..dafec264 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor + +/** + * An interface for encrypting data + */ +internal interface IMXEncrypting { + + /** + * Init + * + * @param matrixSession the related 'MXSession'. + * @param roomId the id of the room we will be sending to. + */ + fun initWithMatrixSession(crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + credentials: Credentials, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor, + roomId: String) + + /** + * Encrypt an event content according to the configuration of the room. + * + * @param eventContent the content of the event. + * @param eventType the type of the event. + * @param userIds the room members the event will be sent to. + * @param callback the asynchronous callback + */ + fun encryptEventContent(eventContent: Content, eventType: String, userIds: List, callback: MatrixCallback) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt new file mode 100755 index 00000000..186d7459 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/MXDecryptionResult.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms + +import im.vector.matrix.android.api.session.events.model.Event + +/** + * This class represents the decryption result. + */ +data class MXDecryptionResult( + /** + * The decrypted payload (with properties 'type', 'content') + */ + var mPayload: Event? = null, + + /** + * keys that the sender of the event claims ownership of: + * map from key type to base64-encoded key. + */ + var mKeysClaimed: Map? = null, + + /** + * The curve25519 key that the sender of the event is known to have ownership of. + */ + var mSenderKey: String? = null, + + /** + * Devices which forwarded this session to us (normally empty). + */ + var mForwardingCurve25519KeyChain: List? = null +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt new file mode 100644 index 00000000..92cf5786 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -0,0 +1,422 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms.megolm + +import android.text.TextUtils +import androidx.annotation.Keep +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting +import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent +import im.vector.matrix.android.internal.crypto.model.rest.ForwardedRoomKeyContent +import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import java.util.* + +@Keep +internal class MXMegolmDecryption : IMXDecrypting { + /** + * The olm device interface + */ + + // the matrix credentials + private lateinit var mCredentials: Credentials + + private lateinit var mCrypto: CryptoManager + private lateinit var mOlmDevice: MXOlmDevice + private lateinit var mDeviceListManager: DeviceListManager + private lateinit var mCryptoStore: IMXCryptoStore + private lateinit var mSendToDeviceTask: SendToDeviceTask + private lateinit var mTaskExecutor: TaskExecutor + + /** + * Events which we couldn't decrypt due to unknown sessions / indexes: map from + * senderKey|sessionId to timelines to list of MatrixEvents. + */ + private var mPendingEvents: MutableMap>>? = null/* senderKey|sessionId */ + + /** + * Init the object fields + */ + override fun initWithMatrixSession(credentials: Credentials, + crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor) { + mCredentials = credentials + mDeviceListManager = deviceListManager + mSendToDeviceTask = sendToDeviceTask + mTaskExecutor = taskExecutor + mOlmDevice = olmDevice + mPendingEvents = HashMap() + } + + @Throws(MXDecryptionException::class) + override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + return decryptEvent(event, timeline, true) + } + + @Throws(MXDecryptionException::class) + private fun decryptEvent(event: Event?, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? { + // sanity check + if (null == event) { + Timber.e("## decryptEvent() : null event") + return null + } + + val encryptedEventContent = event.content.toModel()!! + + if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { + throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) + } + + var eventDecryptionResult: MXEventDecryptionResult? = null + var cryptoError: MXCryptoError? = null + var decryptGroupMessageResult: MXDecryptionResult? = null + + try { + decryptGroupMessageResult = mOlmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) + } catch (e: MXDecryptionException) { + cryptoError = e.cryptoError + } + + // the decryption succeeds + if (null != decryptGroupMessageResult && null != decryptGroupMessageResult.mPayload && null == cryptoError) { + eventDecryptionResult = MXEventDecryptionResult() + + eventDecryptionResult.mClearEvent = decryptGroupMessageResult.mPayload + eventDecryptionResult.mSenderCurve25519Key = decryptGroupMessageResult.mSenderKey + + if (null != decryptGroupMessageResult.mKeysClaimed) { + eventDecryptionResult.mClaimedEd25519Key = decryptGroupMessageResult.mKeysClaimed!!["ed25519"] + } + + eventDecryptionResult.mForwardingCurve25519KeyChain = decryptGroupMessageResult.mForwardingCurve25519KeyChain!! + } else if (null != cryptoError) { + if (cryptoError.isOlmError) { + if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) { + addEventToPendingList(event, timeline) + + if (requestKeysOnFail) { + requestKeysForEvent(event) + } + } + + val reason = String.format(MXCryptoError.OLM_REASON, cryptoError.message) + val detailedReason = String.format(MXCryptoError.DETAILLED_OLM_REASON, encryptedEventContent.ciphertext, cryptoError.message) + + throw MXDecryptionException(MXCryptoError( + MXCryptoError.OLM_ERROR_CODE, + reason, + detailedReason)) + } else if (TextUtils.equals(cryptoError.code, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE)) { + addEventToPendingList(event, timeline) + if (requestKeysOnFail) { + requestKeysForEvent(event) + } + } + + throw MXDecryptionException(cryptoError) + } + + return eventDecryptionResult + } + + /** + * Helper for the real decryptEvent and for _retryDecryption. If + * requestKeysOnFail is true, we'll send an m.room_key_request when we fail + * to decrypt the event due to missing megolm keys. + * + * @param event the event + */ + private fun requestKeysForEvent(event: Event) { + val sender = event.sender!! + val encryptedEventContent = event.content.toModel()!! + + val recipients = ArrayList>() + + val selfMap = HashMap() + selfMap["userId"] = mCredentials.userId + selfMap["deviceId"] = "*" + recipients.add(selfMap) + + if (!TextUtils.equals(sender, mCredentials.userId)) { + val senderMap = HashMap() + senderMap["userId"] = sender + senderMap["deviceId"] = encryptedEventContent.deviceId!! + recipients.add(senderMap) + } + + val requestBody = RoomKeyRequestBody() + + requestBody.roomId = event.roomId + requestBody.algorithm = encryptedEventContent.algorithm + requestBody.senderKey = encryptedEventContent.senderKey + requestBody.sessionId = encryptedEventContent.sessionId + + mCrypto.requestRoomKey(requestBody, recipients) + } + + /** + * Add an event to the list of those we couldn't decrypt the first time we + * saw them. + * + * @param event the event to try to decrypt later + * @param timelineId the timeline identifier + */ + private fun addEventToPendingList(event: Event, timelineId: String) { + var timelineId = timelineId + val encryptedEventContent = event.content.toModel()!! + + val k = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}" + + // avoid undefined timelineId + if (TextUtils.isEmpty(timelineId)) { + timelineId = "" + } + + if (!mPendingEvents!!.containsKey(k)) { + mPendingEvents!![k] = HashMap() + } + + if (!mPendingEvents!![k]!!.containsKey(timelineId)) { + mPendingEvents!![k]!!.put(timelineId, ArrayList()) + } + + if (mPendingEvents!![k]!![timelineId]!!.indexOf(event) < 0) { + Timber.d("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId) + mPendingEvents!![k]!![timelineId]!!.add(event) + } + } + + /** + * Handle a key event. + * + * @param roomKeyEvent the key event. + */ + override fun onRoomKeyEvent(event: Event) { + var exportFormat = false + val roomKeyContent = event.content.toModel()!! + + var senderKey: String? = event.getSenderKey() + var keysClaimed: MutableMap = HashMap() + var forwarding_curve25519_key_chain: MutableList? = null + + if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.sessionId) || TextUtils.isEmpty(roomKeyContent.sessionKey)) { + Timber.e("## onRoomKeyEvent() : Key event is missing fields") + return + } + + if (TextUtils.equals(event.type, EventType.FORWARDED_ROOM_KEY)) { + Timber.d("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId + + " sessionKey " + roomKeyContent.sessionKey) // from " + event); + val forwardedRoomKeyContent = event.content.toModel()!! + + if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) { + forwarding_curve25519_key_chain = ArrayList() + } else { + forwarding_curve25519_key_chain = ArrayList(forwardedRoomKeyContent.forwardingCurve25519KeyChain!!) + } + + forwarding_curve25519_key_chain.add(senderKey!!) + + exportFormat = true + senderKey = forwardedRoomKeyContent.senderKey + if (null == senderKey) { + Timber.e("## onRoomKeyEvent() : forwarded_room_key event is missing sender_key field") + return + } + + if (null == forwardedRoomKeyContent.senderClaimedEd25519Key) { + Timber.e("## forwarded_room_key_event is missing sender_claimed_ed25519_key field") + return + } + + keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key!! + } else { + Timber.d("## onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId + + " sessionKey " + roomKeyContent.sessionKey) // from " + event); + + if (null == senderKey) { + Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)") + return + } + + // inherit the claimed ed25519 key from the setup message + keysClaimed = event.getKeysClaimed().toMutableMap() + } + + val added = mOlmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat) + + if (added) { + mCrypto.getKeysBackupService().maybeBackupKeys() + + val content = RoomKeyRequestBody() + + content.algorithm = roomKeyContent.algorithm + content.roomId = roomKeyContent.roomId + content.sessionId = roomKeyContent.sessionId + content.senderKey = senderKey + + mCrypto.cancelRoomKeyRequest(content) + + onNewSession(senderKey, roomKeyContent.sessionId!!) + } + } + + /** + * Check if the some messages can be decrypted with a new session + * + * @param senderKey the session sender key + * @param sessionId the session id + */ + override fun onNewSession(senderKey: String, sessionId: String) { + val k = "$senderKey|$sessionId" + + val pending = mPendingEvents!![k] + + if (null != pending) { + // Have another go at decrypting events sent with this session. + mPendingEvents!!.remove(k) + + val timelineIds = pending.keys + + for (timelineId in timelineIds) { + val events = pending[timelineId] + + for (event in events!!) { + var result: MXEventDecryptionResult? = null + + try { + result = decryptEvent(event, timelineId) + } catch (e: MXDecryptionException) { + Timber.e(e, "## onNewSession() : Still can't decrypt " + event.eventId + ". Error") + event.setCryptoError(e.cryptoError) + } + + if (null != result) { + val fResut = result + CryptoAsyncHelper.getUiHandler().post { + event.setClearData(fResut) + TODO() + //mSession!!.onEventDecrypted(event) + } + Timber.d("## onNewSession() : successful re-decryption of " + event.eventId) + } + } + } + } + } + + override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { + return (null != request + && null != request.mRequestBody + && mOlmDevice.hasInboundSessionKeys(request.mRequestBody!!.roomId!!, request.mRequestBody!!.senderKey!!, request.mRequestBody!!.sessionId!!)) + } + + override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) { + // sanity checks + if (null == request || null == request.mRequestBody) { + return + } + + val userId = request.mUserId!! + + mDeviceListManager.downloadKeys(listOf(userId), false, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + val deviceId = request.mDeviceId + val deviceInfo = mCryptoStore.getUserDevice(deviceId!!, userId) + + if (null != deviceInfo) { + val body = request.mRequestBody + + val devicesByUser = HashMap>() + devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) + + mCrypto.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback> { + override fun onSuccess(map: MXUsersDevicesMap) { + val olmSessionResult = map.getObject(deviceId, userId) + + if (null == olmSessionResult || null == olmSessionResult.mSessionId) { + // no session with this device, probably because there + // were no one-time keys. + // + // ensureOlmSessionsForUsers has already done the logging, + // so just skip it. + return + } + + Timber.d("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId + + " with device " + userId + ":" + deviceId) + + val inboundGroupSession = mOlmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) + + val payloadJson = HashMap() + payloadJson["type"] = EventType.FORWARDED_ROOM_KEY + payloadJson["content"] = inboundGroupSession!!.exportKeys()!! + + val encodedPayload = mCrypto.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(encodedPayload, userId, deviceId) + + Timber.d("## shareKeysWithDevice() : sending to $userId:$deviceId") + mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.d("## shareKeysWithDevice() : sent to $userId:$deviceId") + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareKeysWithDevice() : sendToDevice $userId:$deviceId failed") + } + }) + .executeBy(mTaskExecutor) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId failed") + } + }) + } else { + Timber.e("## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId not found") + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareKeysWithDevice() : downloadKeys $userId failed") + } + }) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt new file mode 100644 index 00000000..d49f9c87 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -0,0 +1,548 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms.megolm + +import android.text.TextUtils +import androidx.annotation.Keep +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toContent +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult +import im.vector.matrix.android.internal.crypto.model.MXQueuedEncryption +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.convertToUTF8 +import timber.log.Timber +import java.util.* + +@Keep +internal class MXMegolmEncryption : IMXEncrypting { + + private lateinit var mCrypto: CryptoManager + private lateinit var olmDevice: MXOlmDevice + private lateinit var mDeviceListManager: DeviceListManager + + private lateinit var mKeysBackup: KeysBackup + private lateinit var mCredentials: Credentials + private lateinit var mSendToDeviceTask: SendToDeviceTask + private lateinit var mTaskExecutor: TaskExecutor + + // The id of the room we will be sending to. + private var mRoomId: String? = null + + private var mDeviceId: String? = null + + // OutboundSessionInfo. Null if we haven't yet started setting one up. Note + // that even if this is non-null, it may not be ready for use (in which + // case outboundSession.shareOperation will be non-null.) + private var mOutboundSession: MXOutboundSessionInfo? = null + + // true when there is an HTTP operation in progress + private var mShareOperationIsProgress: Boolean = false + + private val mPendingEncryptions = ArrayList() + + // Session rotation periods + private var mSessionRotationPeriodMsgs: Int = 0 + private var mSessionRotationPeriodMs: Int = 0 + + /** + * @return a snapshot of the pending encryptions + */ + private val pendingEncryptions: List + get() { + val list = ArrayList() + + synchronized(mPendingEncryptions) { + list.addAll(mPendingEncryptions) + } + + return list + } + + override fun initWithMatrixSession(crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + credentials: Credentials, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor, + roomId: String) { + mCrypto = crypto + this.olmDevice = olmDevice + mDeviceListManager = deviceListManager + mCredentials = credentials + mSendToDeviceTask = sendToDeviceTask + mTaskExecutor = taskExecutor + + mRoomId = roomId + mDeviceId = mCredentials.deviceId + + // Default rotation periods + // TODO: Make it configurable via parameters + mSessionRotationPeriodMsgs = 100 + mSessionRotationPeriodMs = 7 * 24 * 3600 * 1000 + } + + override fun encryptEventContent(eventContent: Content, + eventType: String, + userIds: List, + callback: MatrixCallback) { + // Queue the encryption request + // It will be processed when everything is set up + val queuedEncryption = MXQueuedEncryption() + + queuedEncryption.mEventContent = eventContent + queuedEncryption.mEventType = eventType + queuedEncryption.mApiCallback = callback + + synchronized(mPendingEncryptions) { + mPendingEncryptions.add(queuedEncryption) + } + + val t0 = System.currentTimeMillis() + Timber.d("## encryptEventContent () starts") + + getDevicesInRoom(userIds, object : MatrixCallback> { + + /** + * A network error has been received while encrypting + * @param failure the exception + */ + private fun dispatchFailure(failure: Throwable) { + Timber.e(failure, "## encryptEventContent() : failure") + val queuedEncryptions = pendingEncryptions + + for (queuedEncryption in queuedEncryptions) { + queuedEncryption.mApiCallback?.onFailure(failure) + } + + synchronized(mPendingEncryptions) { + mPendingEncryptions.removeAll(queuedEncryptions) + } + } + + override fun onSuccess(devicesInRoom: MXUsersDevicesMap) { + ensureOutboundSession(devicesInRoom, object : MatrixCallback { + override fun onSuccess(data: MXOutboundSessionInfo) { + mCrypto!!.encryptingThreadHandler.post { + Timber.d("## encryptEventContent () processPendingEncryptions after " + (System.currentTimeMillis() - t0) + "ms") + processPendingEncryptions(data) + } + } + + override fun onFailure(failure: Throwable) { + dispatchFailure(failure) + } + }) + } + + override fun onFailure(failure: Throwable) { + dispatchFailure(failure) + } + }) + } + + /** + * Prepare a new session. + * + * @return the session description + */ + private fun prepareNewSessionInRoom(): MXOutboundSessionInfo { + val sessionId = olmDevice!!.createOutboundGroupSession() + + val keysClaimedMap = HashMap() + keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! + + olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, mRoomId!!, olmDevice.deviceCurve25519Key!!, + ArrayList(), keysClaimedMap, false) + + mKeysBackup.maybeBackupKeys() + + return MXOutboundSessionInfo(sessionId) + } + + /** + * Ensure the outbound session + * + * @param devicesInRoom the devices list + * @param callback the asynchronous callback. + */ + private fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap, callback: MatrixCallback?) { + var session = mOutboundSession + + if (null == session + // Need to make a brand new session? + || session.needsRotation(mSessionRotationPeriodMsgs, mSessionRotationPeriodMs) + // Determine if we have shared with anyone we shouldn't have + || session.sharedWithTooManyDevices(devicesInRoom)) { + session = prepareNewSessionInRoom() + mOutboundSession = session + } + + if (mShareOperationIsProgress) { + Timber.d("## ensureOutboundSessionInRoom() : already in progress") + // Key share already in progress + return + } + + val fSession = session + + val shareMap = HashMap>()/* userId */ + + val userIds = devicesInRoom.userIds + + for (userId in userIds) { + val deviceIds = devicesInRoom.getUserDeviceIds(userId) + + for (deviceId in deviceIds!!) { + val deviceInfo = devicesInRoom.getObject(deviceId, userId) + + if (null == fSession.mSharedWithDevices.getObject(deviceId, userId)) { + if (!shareMap.containsKey(userId)) { + shareMap[userId] = ArrayList() + } + + shareMap[userId]!!.add(deviceInfo) + } + } + } + + shareKey(fSession, shareMap, object : MatrixCallback { + override fun onSuccess(data: Unit) { + mShareOperationIsProgress = false + callback?.onSuccess(fSession) + } + + override fun onFailure(failure: Throwable) { + Timber.e("## ensureOutboundSessionInRoom() : shareKey onFailure") + + callback?.onFailure(failure) + mShareOperationIsProgress = false + } + }) + + } + + /** + * Share the device key to a list of users + * + * @param session the session info + * @param devicesByUsers the devices map + * @param callback the asynchronous callback + */ + private fun shareKey(session: MXOutboundSessionInfo, + devicesByUsers: MutableMap>, + callback: MatrixCallback?) { + // nothing to send, the task is done + if (0 == devicesByUsers.size) { + Timber.d("## shareKey() : nothing more to do") + + if (null != callback) { + CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) } + } + + return + } + + // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) + val subMap = HashMap>() + + val userIds = ArrayList() + var devicesCount = 0 + + for (userId in devicesByUsers.keys) { + val devicesList = devicesByUsers[userId] + + userIds.add(userId) + subMap[userId] = devicesList!! + + devicesCount += devicesList.size + + if (devicesCount > 100) { + break + } + } + + Timber.d("## shareKey() ; userId $userIds") + shareUserDevicesKey(session, subMap, object : MatrixCallback { + override fun onSuccess(data: Unit) { + mCrypto!!.encryptingThreadHandler.post { + for (userId in userIds) { + devicesByUsers.remove(userId) + } + shareKey(session, devicesByUsers, callback) + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareKey() ; userIds " + userIds + " failed") + callback?.onFailure(failure) + } + }) + } + + /** + * Share the device keys of a an user + * + * @param session the session info + * @param devicesByUser the devices map + * @param callback the asynchronous callback + */ + private fun shareUserDevicesKey(session: MXOutboundSessionInfo, + devicesByUser: Map>, + callback: MatrixCallback?) { + val sessionKey = olmDevice.getSessionKey(session.mSessionId) + val chainIndex = olmDevice.getMessageIndex(session.mSessionId) + + val submap = HashMap() + submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + submap["room_id"] = mRoomId!! + submap["session_id"] = session.mSessionId + submap["session_key"] = sessionKey!! + submap["chain_index"] = chainIndex + + val payload = HashMap() + payload["type"] = EventType.ROOM_KEY + payload["content"] = submap + + val t0 = System.currentTimeMillis() + Timber.d("## shareUserDevicesKey() : starts") + + mCrypto!!.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback> { + override fun onSuccess(results: MXUsersDevicesMap) { + mCrypto!!.encryptingThreadHandler.post { + Timber.d("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + val contentMap = MXUsersDevicesMap() + + var haveTargets = false + val userIds = results.userIds + + for (userId in userIds) { + val devicesToShareWith = devicesByUser[userId] + + for ((deviceID) in devicesToShareWith!!) { + + val sessionResult = results.getObject(deviceID, userId) + + if (null == sessionResult || null == sessionResult.mSessionId) { + // no session with this device, probably because there + // were no one-time keys. + // + // we could send them a to_device message anyway, as a + // signal that they have missed out on the key sharing + // message because of the lack of keys, but there's not + // much point in that really; it will mostly serve to clog + // up to_device inboxes. + // + // ensureOlmSessionsForUsers has already done the logging, + // so just skip it. + continue + } + + Timber.d("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument + contentMap.setObject(mCrypto!!.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) + haveTargets = true + } + } + + if (haveTargets && !mCrypto!!.hasBeenReleased()) { + val t0 = System.currentTimeMillis() + Timber.d("## shareUserDevicesKey() : has target") + + mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: Unit) { + mCrypto!!.encryptingThreadHandler.post { + Timber.d("## shareUserDevicesKey() : sendToDevice succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + + // Add the devices we have shared with to session.sharedWithDevices. + // we deliberately iterate over devicesByUser (ie, the devices we + // attempted to share with) rather than the contentMap (those we did + // share with), because we don't want to try to claim a one-time-key + // for dead devices on every message. + for (userId in devicesByUser.keys) { + val devicesToShareWith = devicesByUser[userId] + + for ((deviceId) in devicesToShareWith!!) { + session.mSharedWithDevices.setObject(chainIndex, userId, deviceId) + } + } + + CryptoAsyncHelper.getUiHandler().post { + callback?.onSuccess(Unit) + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareUserDevicesKey() : sendToDevice") + + callback?.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } else { + Timber.d("## shareUserDevicesKey() : no need to sharekey") + + if (null != callback) { + CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) } + } + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## shareUserDevicesKey() : ensureOlmSessionsForDevices failed") + + callback?.onFailure(failure) + } + }) + } + + /** + * process the pending encryptions + */ + private fun processPendingEncryptions(session: MXOutboundSessionInfo?) { + if (null != session) { + val queuedEncryptions = pendingEncryptions + + // Everything is in place, encrypt all pending events + for (queuedEncryption in queuedEncryptions) { + val payloadJson = HashMap() + + payloadJson["room_id"] = mRoomId!! + payloadJson["type"] = queuedEncryption.mEventType!! + payloadJson["content"] = queuedEncryption.mEventContent!! + + // Get canonical Json from + val content = payloadJson.toContent()!! + + val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, content)) + val ciphertext = olmDevice.encryptGroupMessage(session.mSessionId, payloadString!!) + + val map = HashMap() + map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + map["sender_key"] = olmDevice.deviceCurve25519Key!! + map["ciphertext"] = ciphertext!! + map["session_id"] = session.mSessionId + + // Include our device ID so that recipients can send us a + // m.new_device message if they don't have our session key. + map["device_id"] = mDeviceId!! + + CryptoAsyncHelper.getUiHandler().post { queuedEncryption.mApiCallback?.onSuccess(map.toContent()!!) } + + session.mUseCount++ + } + + synchronized(mPendingEncryptions) { + mPendingEncryptions.removeAll(queuedEncryptions) + } + } + } + + /** + * Get the list of devices which can encrypt data to. + * This method must be called in getDecryptingThreadHandler() thread. + * + * @param userIds the user ids whose devices must be checked. + * @param callback the asynchronous callback + */ + private fun getDevicesInRoom(userIds: List, callback: MatrixCallback>) { + // We are happy to use a cached version here: we assume that if we already + // have a list of the user's devices, then we already share an e2e room + // with them, which means that they will have announced any new devices via + // an m.new_device. + mDeviceListManager.downloadKeys(userIds, false, object : MatrixCallback> { + override fun onSuccess(devices: MXUsersDevicesMap) { + mCrypto!!.encryptingThreadHandler.post { + val encryptToVerifiedDevicesOnly = mCrypto!!.globalBlacklistUnverifiedDevices || mCrypto!!.isRoomBlacklistUnverifiedDevices(mRoomId) + + val devicesInRoom = MXUsersDevicesMap() + val unknownDevices = MXUsersDevicesMap() + + val userIds = devices.userIds + + for (userId in userIds) { + val deviceIds = devices.getUserDeviceIds(userId) + + for (deviceId in deviceIds!!) { + val deviceInfo = devices.getObject(deviceId, userId) + + if (mCrypto!!.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { + // The device is not yet known by the user + unknownDevices.setObject(deviceInfo, userId, deviceId) + continue + } + + if (deviceInfo!!.isBlocked) { + // Remove any blocked devices + continue + } + + if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { + continue + } + + if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { + // Don't bother sending to ourself + continue + } + + devicesInRoom.setObject(deviceInfo, userId, deviceId) + } + } + + CryptoAsyncHelper.getUiHandler().post { + // Check if any of these devices are not yet known to the user. + // if so, warn the user so they can verify or ignore. + if (0 != unknownDevices.map.size) { + callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, + MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) + } else { + callback.onSuccess(devicesInRoom) + } + } + } + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt new file mode 100644 index 00000000..b78b3444 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms.megolm + +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import timber.log.Timber + +class MXOutboundSessionInfo( + // The id of the session + val mSessionId: String) { + // When the session was created + private val mCreationTime = System.currentTimeMillis() + + // Number of times this session has been used + var mUseCount: Int = 0 + + // Devices with which we have shared the session key + // userId -> {deviceId -> msgindex} + val mSharedWithDevices: MXUsersDevicesMap = MXUsersDevicesMap() + + fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean { + var needsRotation = false + val sessionLifetime = System.currentTimeMillis() - mCreationTime + + if (mUseCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) { + Timber.d("## needsRotation() : Rotating megolm session after " + mUseCount + ", " + sessionLifetime + "ms") + needsRotation = true + } + + return needsRotation + } + + /** + * Determine if this session has been shared with devices which it shouldn't have been. + * + * @param devicesInRoom the devices map + * @return true if we have shared the session with devices which aren't in devicesInRoom. + */ + fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap): Boolean { + val userIds = mSharedWithDevices.userIds + + for (userId in userIds) { + if (null == devicesInRoom.getUserDeviceIds(userId)) { + Timber.d("## sharedWithTooManyDevices() : Starting new session because we shared with $userId") + return true + } + + val deviceIds = mSharedWithDevices.getUserDeviceIds(userId) + + for (deviceId in deviceIds!!) { + if (null == devicesInRoom.getObject(deviceId, userId)) { + Timber.d("## sharedWithTooManyDevices() : Starting new session because we shared with $userId:$deviceId") + return true + } + } + } + + return false + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt new file mode 100644 index 00000000..8a9a2c25 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -0,0 +1,276 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms.olm + +import android.text.TextUtils +import androidx.annotation.Keep +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.model.event.OlmEventContent +import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.util.convertFromUTF8 +import timber.log.Timber +import java.util.* + +/** + * An interface for encrypting data + */ +@Keep +internal class MXOlmDecryption : IMXDecrypting { + + // The olm device interface + private var mOlmDevice: MXOlmDevice? = null + + // the matrix credentials + private lateinit var mCredentials: Credentials + + private lateinit var mCrypto: CryptoManager + private lateinit var mCryptoStore: IMXCryptoStore + private lateinit var mSendToDeviceTask: SendToDeviceTask + private lateinit var mTaskExecutor: TaskExecutor + + override fun initWithMatrixSession(credentials: Credentials, + crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor) { + mCredentials = credentials + mCrypto = crypto + mSendToDeviceTask = sendToDeviceTask + mTaskExecutor = taskExecutor + mOlmDevice = olmDevice + } + + @Throws(MXDecryptionException::class) + override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + // sanity check + if (null == event) { + Timber.e("## decryptEvent() : null event") + return null + } + + val olmEventContent = event.content.toModel()!! + + if (null == olmEventContent.ciphertext) { + Timber.e("## decryptEvent() : missing cipher text") + throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_CIPHER_TEXT_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + } + + if (!olmEventContent.ciphertext!!.containsKey(mOlmDevice!!.deviceCurve25519Key)) { + Timber.e("## decryptEvent() : our device " + mOlmDevice!!.deviceCurve25519Key + + " is not included in recipients. Event") + throw MXDecryptionException(MXCryptoError(MXCryptoError.NOT_INCLUDE_IN_RECIPIENTS_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) + } + + // The message for myUser + val message = olmEventContent.ciphertext!![mOlmDevice!!.deviceCurve25519Key] as Map + val payloadString = decryptMessage(message, olmEventContent.senderKey) + + if (null == payloadString) { + Timber.e("## decryptEvent() Failed to decrypt Olm event (id= " + event.eventId + " ) from " + olmEventContent.senderKey) + throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_ENCRYPTED_MESSAGE_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + } + + val payload = convertFromUTF8(payloadString) + + if (null == payload) { + Timber.e("## decryptEvent failed : null payload") + throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + } + + val olmPayloadContent = OlmPayloadContent.fromJsonString(payload) + + if (TextUtils.isEmpty(olmPayloadContent.recipient)) { + val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") + Timber.e("## decryptEvent() : $reason") + throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, reason)) + } + + if (!TextUtils.equals(olmPayloadContent.recipient, mCredentials.userId)) { + Timber.e("## decryptEvent() : Event " + event.eventId + ": Intended recipient " + olmPayloadContent.recipient + + " does not match our id " + mCredentials.userId) + throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_RECIPIENT_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) + } + + if (null == olmPayloadContent.recipient_keys) { + Timber.e("## decryptEvent() : Olm event (id=" + event.eventId + + ") contains no " + "'recipient_keys' property; cannot prevent unknown-key attack") + throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) + } + + val ed25519 = olmPayloadContent.recipient_keys!!.get("ed25519") + + if (!TextUtils.equals(ed25519, mOlmDevice!!.deviceEd25519Key)) { + Timber.e("## decryptEvent() : Event " + event.eventId + ": Intended recipient ed25519 key " + ed25519 + " did not match ours") + throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_RECIPIENT_KEY_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.BAD_RECIPIENT_KEY_REASON)) + } + + if (TextUtils.isEmpty(olmPayloadContent.sender)) { + Timber.e("## decryptEvent() : Olm event (id=" + event.eventId + + ") contains no 'sender' property; cannot prevent unknown-key attack") + throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_PROPERTY_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) + } + + if (!TextUtils.equals(olmPayloadContent.sender, event.sender)) { + Timber.e("Event " + event.eventId + ": original sender " + olmPayloadContent.sender + + " does not match reported sender " + event.sender) + throw MXDecryptionException(MXCryptoError(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) + } + + if (!TextUtils.equals(olmPayloadContent.room_id, event.roomId)) { + Timber.e("## decryptEvent() : Event " + event.eventId + ": original room " + olmPayloadContent.room_id + + " does not match reported room " + event.roomId) + throw MXDecryptionException(MXCryptoError(MXCryptoError.BAD_ROOM_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) + } + + if (null == olmPayloadContent.keys) { + Timber.e("## decryptEvent failed : null keys") + throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + } + + val result = MXEventDecryptionResult() + // TODO result.mClearEvent = payload + result.mSenderCurve25519Key = olmEventContent.senderKey + result.mClaimedEd25519Key = olmPayloadContent.keys!!.get("ed25519") + + return result + } + + override fun onRoomKeyEvent(event: Event) { + // No impact for olm + } + + override fun onNewSession(senderKey: String, sessionId: String) { + // No impact for olm + } + + override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { + return false + } + + override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {} + + /** + * Attempt to decrypt an Olm message. + * + * @param theirDeviceIdentityKey the Curve25519 identity key of the sender. + * @param message message object, with 'type' and 'body' fields. + * @return payload, if decrypted successfully. + */ + private fun decryptMessage(message: Map, theirDeviceIdentityKey: String?): String? { + val sessionIdsSet = mOlmDevice!!.getSessionIds(theirDeviceIdentityKey!!) + + val sessionIds: List + + if (null == sessionIdsSet) { + sessionIds = ArrayList() + } else { + sessionIds = ArrayList(sessionIdsSet) + } + + val messageBody = message["body"] as String? + var messageType: Int? = null + + val typeAsVoid = message["type"] + + if (null != typeAsVoid) { + if (typeAsVoid is Double) { + messageType = typeAsVoid.toInt() + } else if (typeAsVoid is Int) { + messageType = typeAsVoid + } else if (typeAsVoid is Long) { + messageType = typeAsVoid.toInt() + } + } + + if (null == messageBody || null == messageType) { + return null + } + + // Try each session in turn + // decryptionErrors = {}; + for (sessionId in sessionIds) { + val payload = mOlmDevice!!.decryptMessage(messageBody, messageType, sessionId, theirDeviceIdentityKey) + + if (null != payload) { + Timber.d("## decryptMessage() : Decrypted Olm message from $theirDeviceIdentityKey with session $sessionId") + return payload + } else { + val foundSession = mOlmDevice!!.matchesSession(theirDeviceIdentityKey, sessionId, messageType, messageBody) + + if (foundSession) { + // Decryption failed, but it was a prekey message matching this + // session, so it should have worked. + Timber.e("## decryptMessage() : Error decrypting prekey message with existing session id $sessionId:TODO") + return null + } + } + } + + if (messageType != 0) { + // not a prekey message, so it should have matched an existing session, but it + // didn't work. + + if (sessionIds.size == 0) { + Timber.e("## decryptMessage() : No existing sessions") + } else { + Timber.e("## decryptMessage() : Error decrypting non-prekey message with existing sessions") + } + + return null + } + + // prekey message which doesn't match any existing sessions: make a new + // session. + val res = mOlmDevice!!.createInboundSession(theirDeviceIdentityKey, messageType, messageBody) + + if (null == res) { + Timber.e("## decryptMessage() : Error decrypting non-prekey message with existing sessions") + return null + } + + Timber.d("## decryptMessage() : Created new inbound Olm session get id " + res["session_id"] + " with " + theirDeviceIdentityKey) + + return res["payload"] + } + + companion object { + private val LOG_TAG = "MXOlmDecryption" + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt new file mode 100644 index 00000000..1242956b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2015 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.algorithms.olm + +import android.text.TextUtils +import androidx.annotation.Keep +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.events.model.toContent +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import java.util.* + +@Keep +internal class MXOlmEncryption : IMXEncrypting { + private lateinit var mCrypto: CryptoManager + private lateinit var mOlmDevice: MXOlmDevice + private lateinit var mDeviceListManager: DeviceListManager + + private lateinit var mCredentials: Credentials + private lateinit var mSendToDeviceTask: SendToDeviceTask + private lateinit var mTaskExecutor: TaskExecutor + + private lateinit var mRoomId: String + + override fun initWithMatrixSession(crypto: CryptoManager, + olmDevice: MXOlmDevice, + deviceListManager: DeviceListManager, + credentials: Credentials, + sendToDeviceTask: SendToDeviceTask, + taskExecutor: TaskExecutor, + roomId: String) { + mCrypto = crypto + mOlmDevice = olmDevice + mDeviceListManager = deviceListManager + + mRoomId = roomId + } + + override fun encryptEventContent(eventContent: Content, + eventType: String, + userIds: List, + callback: MatrixCallback) { + // pick the list of recipients based on the membership list. + // + // TODO: there is a race condition here! What if a new user turns up + ensureSession(userIds, object : MatrixCallback { + override fun onSuccess(info: Unit) { + val deviceInfos = ArrayList() + + for (userId in userIds) { + val devices = mCrypto.getUserDevices(userId) + + if (null != devices) { + for (device in devices) { + val key = device.identityKey() + + if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue + } + + if (device.isBlocked) { + // Don't bother setting up sessions with blocked users + continue + } + + deviceInfos.add(device) + } + } + } + + val messageMap = HashMap() + messageMap["room_id"] = mRoomId!! + messageMap["type"] = eventType + messageMap["content"] = eventContent + + mCrypto!!.encryptMessage(messageMap, deviceInfos) + + callback.onSuccess(messageMap.toContent()!!) + } + }) + } + + /** + * Ensure that the session + * + * @param users the user ids list + * @param callback the asynchronous callback + */ + private fun ensureSession(users: List, callback: MatrixCallback?) { + mDeviceListManager.downloadKeys(users, false, object : MatrixCallback> { + + override fun onSuccess(data: MXUsersDevicesMap) { + mCrypto!!.ensureOlmSessionsForUsers(users, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + callback?.onSuccess(Unit) + } + + override fun onFailure(failure: Throwable) { + callback?.onFailure(failure) + } + }) + } + + override fun onFailure(failure: Throwable) { + callback?.onFailure(failure) + } + }) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt new file mode 100644 index 00000000..23e5b466 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2014 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.api + + +import im.vector.matrix.android.internal.crypto.model.rest.* +import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody +import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody +import im.vector.matrix.android.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.* + +internal interface CryptoApi { + + /** + * Get the devices list + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-devices + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "devices") + fun getDevices(): Call + + /** + * Upload device and/or one-time keys. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-upload + * + * @param params the params. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/upload") + fun uploadKeys(@Body body: KeysUploadBody): Call + + /** + * Upload device and/or one-time keys. + * Doc: not documented + * + * @param deviceId the deviceId + * @param params the params. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/upload/{deviceId}") + fun uploadKeys(@Path("deviceId") deviceId: String, @Body body: KeysUploadBody): Call + + /** + * Download device keys. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-query + * + * @param params the params. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/query") + fun downloadKeysForUsers(@Body params: KeysQueryBody): Call + + /** + * Claim one-time keys. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-claim + * + * @param params the params. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/claim") + fun claimOneTimeKeysForUsersDevices(@Body body: KeysClaimBody): Call + + /** + * Send an event to a specific list of devices + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-sendtodevice-eventtype-txnid + * + * @param eventType the type of event to send + * @param transactionId the transaction ID for this event + * @param body the body + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sendToDevice/{eventType}/{txnId}") + fun sendToDevice(@Path("eventType") eventType: String, @Path("txnId") transactionId: String, @Body body: SendToDeviceBody): Call + + /** + * Delete a device. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#delete-matrix-client-r0-devices-deviceid + * + * @param deviceId the device id + * @param params the deletion parameters + */ + @HTTP(path = NetworkConstants.URI_API_PREFIX_PATH_R0 + "devices/{device_id}", method = "DELETE", hasBody = true) + fun deleteDevice(@Path("device_id") deviceId: String, @Body params: DeleteDeviceParams): Call + + /** + * Update the device information. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-devices-deviceid + * + * @param deviceId the device id + * @param params the params + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "devices/{device_id}") + fun updateDeviceInfo(@Path("device_id") deviceId: String, @Body params: UpdateDeviceInfoBody): Call + + /** + * Get the update devices list from two sync token. + * Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-keys-changes + * + * @param oldToken the start token. + * @param newToken the up-to token. + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/changes") + fun getKeyChanges(@Query("from") oldToken: String, @Query("to") newToken: String): Call +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt new file mode 100644 index 00000000..6fce19bf --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -0,0 +1,1503 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup + +import android.os.Handler +import android.os.Looper +import androidx.annotation.UiThread +import androidx.annotation.VisibleForTesting +import androidx.annotation.WorkerThread +import arrow.core.Try +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.failure.MatrixError +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.listeners.StepProgressListener +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust +import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.* +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 +import im.vector.matrix.android.internal.crypto.keysbackup.tasks.* +import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey +import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.task.Task +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.TaskThread +import im.vector.matrix.android.internal.task.configureWith +import org.matrix.olm.OlmException +import org.matrix.olm.OlmPkDecryption +import org.matrix.olm.OlmPkEncryption +import org.matrix.olm.OlmPkMessage +import timber.log.Timber +import java.security.InvalidParameterException +import java.util.* +import kotlin.collections.HashMap + +/** + * A KeysBackup class instance manage incremental backup of e2e keys (megolm keys) + * to the user's homeserver. + */ +internal class KeysBackup( + private val mCredentials: Credentials, + private val mCryptoStore: IMXCryptoStore, + private val mOlmDevice: MXOlmDevice, + // Tasks + private val mCreateKeysBackupVersionTask: CreateKeysBackupVersionTask, + private val mDeleteBackupTask: DeleteBackupTask, + private val mDeleteRoomSessionDataTask: DeleteRoomSessionDataTask, + private val mDeleteRoomSessionsDataTask: DeleteRoomSessionsDataTask, + private val mDeleteSessionDataTask: DeleteSessionsDataTask, + private val mGetKeysBackupLastVersionTask: GetKeysBackupLastVersionTask, + private val mGetKeysBackupVersionTask: GetKeysBackupVersionTask, + private val mGetRoomSessionDataTask: GetRoomSessionDataTask, + private val mGetRoomSessionsDataTask: GetRoomSessionsDataTask, + private val mGetSessionsDataTask: GetSessionsDataTask, + private val mStoreRoomSessionDataTask: StoreRoomSessionDataTask, + private val mStoreSessionsDataTask: StoreRoomSessionsDataTask, + private val mStoreSessionDataTask: StoreSessionsDataTask, + private val mUpdateKeysBackupVersionTask: UpdateKeysBackupVersionTask, + // Task executor + private val mTaskExecutor: TaskExecutor +) : KeysBackupService { + + private val mUIHandler = Handler(Looper.getMainLooper()) + + private val mKeysBackupStateManager = KeysBackupStateManager(mUIHandler) + + // The backup version + override var mKeysBackupVersion: KeysVersionResult? = null + private set + + // The backup key being used. + private var mBackupKey: OlmPkEncryption? = null + + private val mRandom = Random() + + private var backupAllGroupSessionsCallback: MatrixCallback? = null + + private var mKeysBackupStateListener: KeysBackupService.KeysBackupStateListener? = null + + override val isEnabled: Boolean + get() = mKeysBackupStateManager.isEnabled + + override val isStucked: Boolean + get() = mKeysBackupStateManager.isStucked + + override val state: KeysBackupState + get() = mKeysBackupStateManager.state + + override val currentBackupVersion: String? + get() = mKeysBackupVersion?.version + + // Internal listener + private lateinit var mKeysBackupCryptoListener: KeysBackupCryptoListener + + override fun addListener(listener: KeysBackupService.KeysBackupStateListener) { + mKeysBackupStateManager.addListener(listener) + } + + override fun removeListener(listener: KeysBackupService.KeysBackupStateListener) { + mKeysBackupStateManager.removeListener(listener) + } + + /** + * Set up the data required to create a new backup version. + * The backup version will not be created and enabled until [createKeysBackupVersion] + * is called. + * The returned [MegolmBackupCreationInfo] object has a `recoveryKey` member with + * the user-facing recovery key string. + * + * @param password an optional passphrase string that can be entered by the user + * when restoring the backup as an alternative to entering the recovery key. + * @param progressListener a progress listener, as generating private key from password may take a while + * @param callback Asynchronous callback + */ + override fun prepareKeysBackupVersion(password: String?, + progressListener: ProgressListener?, + callback: MatrixCallback) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + try { + val olmPkDecryption = OlmPkDecryption() + val megolmBackupAuthData = MegolmBackupAuthData() + + if (password != null) { + // Generate a private key from the password + val backgroundProgressListener = if (progressListener == null) { + null + } else { + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + mUIHandler.post { + try { + progressListener.onProgress(progress, total) + } catch (e: Exception) { + Timber.e(e, "prepareKeysBackupVersion: onProgress failure") + } + } + } + } + } + + val generatePrivateKeyResult = generatePrivateKeyWithPassword(password, backgroundProgressListener) + megolmBackupAuthData.publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey) + megolmBackupAuthData.privateKeySalt = generatePrivateKeyResult.salt + megolmBackupAuthData.privateKeyIterations = generatePrivateKeyResult.iterations + } else { + val publicKey = olmPkDecryption.generateKey() + + megolmBackupAuthData.publicKey = publicKey + } + + val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, megolmBackupAuthData.signalableJSONDictionary()) + + megolmBackupAuthData.signatures = mKeysBackupCryptoListener.signObject(canonicalJson) + + + val megolmBackupCreationInfo = MegolmBackupCreationInfo() + megolmBackupCreationInfo.algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP + megolmBackupCreationInfo.authData = megolmBackupAuthData + megolmBackupCreationInfo.recoveryKey = computeRecoveryKey(olmPkDecryption.privateKey()) + + mUIHandler.post { callback.onSuccess(megolmBackupCreationInfo) } + } catch (e: OlmException) { + Timber.e(e, "OlmException") + + mUIHandler.post { callback.onFailure(e) } + } + } + } + + /** + * Create a new keys backup version and enable it, using the information return from [prepareKeysBackupVersion]. + * + * @param keysBackupCreationInfo the info object from [prepareKeysBackupVersion]. + * @param callback Asynchronous callback + */ + override fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo, + callback: MatrixCallback) { + val createKeysBackupVersionBody = CreateKeysBackupVersionBody() + createKeysBackupVersionBody.algorithm = keysBackupCreationInfo.algorithm + createKeysBackupVersionBody.authData = MoshiProvider.providesMoshi().adapter(Map::class.java) + .fromJson(keysBackupCreationInfo.authData?.toJsonString()) as Map? + + mKeysBackupStateManager.state = KeysBackupState.Enabling + + mCreateKeysBackupVersionTask + .configureWith(createKeysBackupVersionBody) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(info: KeysVersion) { + // Reset backup markers. + mCryptoStore.resetBackupMarkers() + + val keyBackupVersion = KeysVersionResult() + keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm + keyBackupVersion.authData = createKeysBackupVersionBody.authData + keyBackupVersion.version = info.version + + // We can consider that the server does not have keys yet + keyBackupVersion.count = 0 + keyBackupVersion.hash = null + + enableKeysBackup(keyBackupVersion) + + callback.onSuccess(info) + } + + override fun onFailure(failure: Throwable) { + mKeysBackupStateManager.state = KeysBackupState.Disabled + callback.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } + + /** + * Delete a keys backup version. It will delete all backed up keys on the server, and the backup itself. + * If we are backing up to this version. Backup will be stopped. + * + * @param version the backup version to delete. + * @param callback Asynchronous callback + */ + override fun deleteBackup(version: String, callback: MatrixCallback?) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + // If we're currently backing up to this backup... stop. + // (We start using it automatically in createKeysBackupVersion so this is symmetrical). + if (mKeysBackupVersion != null && version == mKeysBackupVersion!!.version) { + resetKeysBackupData() + mKeysBackupVersion = null + mKeysBackupStateManager.state = KeysBackupState.Unknown + } + + mDeleteBackupTask.configureWith(DeleteBackupTask.Params(version)) + .dispatchTo(object : MatrixCallback { + private fun eventuallyRestartBackup() { + // Do not stay in KeysBackupState.Unknown but check what is available on the homeserver + if (state == KeysBackupState.Unknown) { + checkAndStartKeysBackup() + } + } + + override fun onSuccess(data: Unit) { + eventuallyRestartBackup() + + mUIHandler.post { callback?.onSuccess(Unit) } + } + + override fun onFailure(failure: Throwable) { + eventuallyRestartBackup() + + mUIHandler.post { callback?.onFailure(failure) } + } + }) + .executeBy(mTaskExecutor) + } + } + + /** + * Ask if the backup on the server contains keys that we may do not have locally. + * This should be called when entering in the state READY_TO_BACKUP + */ + override fun canRestoreKeys(): Boolean { + // Server contains more keys than locally + val totalNumberOfKeysLocally = getTotalNumbersOfKeys() + + val keysBackupData = mCryptoStore.getKeysBackupData() + + val totalNumberOfKeysServer = keysBackupData?.backupLastServerNumberOfKeys ?: -1 + val hashServer = keysBackupData?.backupLastServerHash + + return when { + totalNumberOfKeysLocally < totalNumberOfKeysServer -> { + // Server contains more keys than this device + true + } + totalNumberOfKeysLocally == totalNumberOfKeysServer -> { + // Same number, compare hash? + // TODO We have not found any algorithm to determine if a restore is recommended here. Return false for the moment + false + } + else -> false + } + } + + /** + * Facility method to get the total number of locally stored keys + */ + override fun getTotalNumbersOfKeys(): Int { + return mCryptoStore.inboundGroupSessionsCount(false) + + } + + /** + * Facility method to get the number of backed up keys + */ + override fun getTotalNumbersOfBackedUpKeys(): Int { + return mCryptoStore.inboundGroupSessionsCount(true) + } + + /** + * Start to back up keys immediately. + * + * @param progressListener the callback to follow the progress + * @param callback the main callback + */ + override fun backupAllGroupSessions(progressListener: ProgressListener?, + callback: MatrixCallback?) { + // Get a status right now + getBackupProgress(object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + // Reset previous listeners if any + resetBackupAllGroupSessionsListeners() + Timber.d("backupAllGroupSessions: backupProgress: $progress/$total") + try { + progressListener?.onProgress(progress, total) + } catch (e: Exception) { + Timber.e(e, "backupAllGroupSessions: onProgress failure") + } + + if (progress == total) { + Timber.d("backupAllGroupSessions: complete") + callback?.onSuccess(Unit) + return + } + + backupAllGroupSessionsCallback = callback + + // Listen to `state` change to determine when to call onBackupProgress and onComplete + mKeysBackupStateListener = object : KeysBackupService.KeysBackupStateListener { + override fun onStateChange(newState: KeysBackupState) { + getBackupProgress(object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + try { + progressListener?.onProgress(progress, total) + } catch (e: Exception) { + Timber.e(e, "backupAllGroupSessions: onProgress failure 2") + } + + // If backup is finished, notify the main listener + if (state === KeysBackupState.ReadyToBackUp) { + backupAllGroupSessionsCallback?.onSuccess(Unit) + resetBackupAllGroupSessionsListeners() + } + } + }) + } + } + + mKeysBackupStateManager.addListener(mKeysBackupStateListener!!) + + backupKeys() + } + }) + } + + /** + * Check trust on a key backup version. + * + * @param keysBackupVersion the backup version to check. + * @param callback block called when the operations completes. + */ + override fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult, + callback: MatrixCallback) { + // TODO Validate with François that this is correct + object : Task { + override fun execute(params: KeysVersionResult): Try { + return Try { + getKeysBackupTrustBg(params) + } + } + } + .configureWith(keysBackupVersion) + .dispatchTo(callback) + .executeOn(TaskThread.COMPUTATION) + .executeBy(mTaskExecutor) + } + + /** + * Check trust on a key backup version. + * This has to be called on background thread. + * + * @param keysBackupVersion the backup version to check. + * @return a KeysBackupVersionTrust object + */ + @WorkerThread + private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust { + val myUserId = mCredentials.userId + + val keysBackupVersionTrust = KeysBackupVersionTrust() + val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData() + + if (keysBackupVersion.algorithm == null + || authData == null + || authData.publicKey.isEmpty() + || authData.signatures.isNullOrEmpty()) { + Timber.d("getKeysBackupTrust: Key backup is absent or missing required data") + return keysBackupVersionTrust + } + + val mySigs: Map = authData.signatures!![myUserId] as Map + if (mySigs.isEmpty()) { + Timber.d("getKeysBackupTrust: Ignoring key backup because it lacks any signatures from this user") + return keysBackupVersionTrust + } + + for (keyId in mySigs.keys) { + // XXX: is this how we're supposed to get the device id? + var deviceId: String? = null + val components = keyId.split(":") + if (components.size == 2) { + deviceId = components[1] + } + + var device: MXDeviceInfo? = null + if (deviceId != null) { + device = mCryptoStore.getUserDevice(deviceId, myUserId) + + var isSignatureValid = false + + if (device == null) { + Timber.d("getKeysBackupTrust: Signature from unknown device $deviceId") + } else { + try { + mOlmDevice.verifySignature(device.fingerprint()!!, authData.signalableJSONDictionary(), mySigs[keyId] as String) + isSignatureValid = true + } catch (e: OlmException) { + Timber.d("getKeysBackupTrust: Bad signature from device " + device.deviceId + " " + e.localizedMessage) + } + + if (isSignatureValid && device.isVerified) { + keysBackupVersionTrust.usable = true + } + } + + val signature = KeysBackupVersionTrustSignature() + signature.device = device + signature.valid = isSignatureValid + signature.deviceId = deviceId + keysBackupVersionTrust.signatures.add(signature) + } + } + + return keysBackupVersionTrust + } + + /** + * Set trust on a keys backup version. + * It adds (or removes) the signature of the current device to the authentication part of the keys backup version. + * + * @param keysBackupVersion the backup version to check. + * @param trust the trust to set to the keys backup. + * @param callback block called when the operations completes. + */ + override fun trustKeysBackupVersion(keysBackupVersion: KeysVersionResult, + trust: Boolean, + callback: MatrixCallback) { + Timber.d("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + val myUserId = mCredentials.userId + + // Get auth data to update it + val authData = getMegolmBackupAuthData(keysBackupVersion) + + if (authData == null) { + Timber.w("trustKeyBackupVersion:trust: Key backup is missing required data") + + mUIHandler.post { + callback.onFailure(IllegalArgumentException("Missing element")) + } + + return@post + } + + // Get current signatures, or create an empty set + val myUserSignatures = (authData.signatures!![myUserId]?.toMutableMap() ?: HashMap()) + + if (trust) { + // Add current device signature + val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, authData.signalableJSONDictionary()) + + val deviceSignatures = mKeysBackupCryptoListener.signObject(canonicalJson) + + deviceSignatures[myUserId]?.forEach { entry -> + myUserSignatures[entry.key] = entry.value + } + } else { + // Remove current device signature + myUserSignatures.remove("ed25519:${mCredentials.deviceId}") + } + + // Create an updated version of KeysVersionResult + val updateKeysBackupVersionBody = UpdateKeysBackupVersionBody(keysBackupVersion.version!!) + + updateKeysBackupVersionBody.algorithm = keysBackupVersion.algorithm + + val newMegolmBackupAuthData = authData.copy() + + val newSignatures = newMegolmBackupAuthData.signatures!!.toMutableMap() + newSignatures[myUserId] = myUserSignatures + + newMegolmBackupAuthData.signatures = newSignatures + + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(Map::class.java) + + + updateKeysBackupVersionBody.authData = adapter.fromJson(newMegolmBackupAuthData.toJsonString()) as Map? + + // And send it to the homeserver + mUpdateKeysBackupVersionTask + .configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: Unit) { + // Relaunch the state machine on this updated backup version + val newKeysBackupVersion = KeysVersionResult() + + newKeysBackupVersion.version = keysBackupVersion.version + newKeysBackupVersion.algorithm = keysBackupVersion.algorithm + newKeysBackupVersion.count = keysBackupVersion.count + newKeysBackupVersion.hash = keysBackupVersion.hash + newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData + + checkAndStartWithKeysBackupVersion(newKeysBackupVersion) + + mUIHandler.post { + callback.onSuccess(data) + } + } + + override fun onFailure(failure: Throwable) { + mUIHandler.post { + callback.onFailure(failure) + } + } + }) + .executeBy(mTaskExecutor) + } + } + + /** + * Set trust on a keys backup version. + * + * @param keysBackupVersion the backup version to check. + * @param recoveryKey the recovery key to challenge with the key backup public key. + * @param callback block called when the operations completes. + */ + override fun trustKeysBackupVersionWithRecoveryKey(keysBackupVersion: KeysVersionResult, + recoveryKey: String, + callback: MatrixCallback) { + Timber.d("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { + Timber.w("trustKeyBackupVersionWithRecoveryKey: Invalid recovery key.") + + mUIHandler.post { + callback.onFailure(IllegalArgumentException("Invalid recovery key or password")) + } + return@post + } + + trustKeysBackupVersion(keysBackupVersion, true, callback) + } + } + + /** + * Set trust on a keys backup version. + * + * @param keysBackupVersion the backup version to check. + * @param password the pass phrase to challenge with the keyBackupVersion public key. + * @param callback block called when the operations completes. + */ + override fun trustKeysBackupVersionWithPassphrase(keysBackupVersion: KeysVersionResult, + password: String, + callback: MatrixCallback) { + Timber.d("trustKeysBackupVersionWithPassphrase: version ${keysBackupVersion.version}") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + val recoveryKey = recoveryKeyFromPassword(password, keysBackupVersion, null) + + if (recoveryKey == null) { + Timber.w("trustKeysBackupVersionWithPassphrase: Key backup is missing required data") + + mUIHandler.post { + callback.onFailure(IllegalArgumentException("Missing element")) + } + + return@post + } + + // Check trust using the recovery key + trustKeysBackupVersionWithRecoveryKey(keysBackupVersion, recoveryKey, callback) + } + } + + /** + * Get public key from a Recovery key + * + * @param recoveryKey the recovery key + * @return the corresponding public key, from Olm + */ + @WorkerThread + private fun pkPublicKeyFromRecoveryKey(recoveryKey: String): String? { + // Extract the primary key + val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey) + + if (privateKey == null) { + Timber.w("pkPublicKeyFromRecoveryKey: private key is null") + + return null + } + + // Built the PK decryption with it + val pkPublicKey: String + + try { + val decryption = OlmPkDecryption() + pkPublicKey = decryption.setPrivateKey(privateKey) + } catch (e: OlmException) { + return null + } + + return pkPublicKey + } + + private fun resetBackupAllGroupSessionsListeners() { + backupAllGroupSessionsCallback = null + + mKeysBackupStateListener?.let { + mKeysBackupStateManager.removeListener(it) + } + + mKeysBackupStateListener = null + } + + /** + * Return the current progress of the backup + */ + override fun getBackupProgress(progressListener: ProgressListener) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + val backedUpKeys = mCryptoStore.inboundGroupSessionsCount(true) + val total = mCryptoStore.inboundGroupSessionsCount(false) + + mUIHandler.post { progressListener.onProgress(backedUpKeys, total) } + } + } + + /** + * Restore a backup with a recovery key from a given backup version stored on the homeserver. + * + * @param keysVersionResult the backup version to restore from. + * @param recoveryKey the recovery key to decrypt the retrieved backup. + * @param roomId the id of the room to get backup data from. + * @param sessionId the id of the session to restore. + * @param stepProgressListener the step progress listener + * @param callback Callback. It provides the number of found keys and the number of successfully imported keys. + */ + override fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult, + recoveryKey: String, + roomId: String?, + sessionId: String?, + stepProgressListener: StepProgressListener?, + callback: MatrixCallback) { + Timber.d("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post(Runnable { + // Check if the recovery is valid before going any further + if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysVersionResult)) { + Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key for this keys version") + mUIHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) } + return@Runnable + } + + // Get a PK decryption instance + val decryption = pkDecryptionFromRecoveryKey(recoveryKey) + if (decryption == null) { + // This should not happen anymore + Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key. Error") + mUIHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) } + return@Runnable + } + + if (stepProgressListener != null) { + mUIHandler.post { stepProgressListener.onStepProgress(StepProgressListener.Step.DownloadingKey) } + } + + // Get backed up keys from the homeserver + getKeys(sessionId, roomId, keysVersionResult.version!!, object : MatrixCallback { + override fun onSuccess(data: KeysBackupData) { + val sessionsData = ArrayList() + // Restore that data + var sessionsFromHsCount = 0 + for (roomIdLoop in data.roomIdToRoomKeysBackupData.keys) { + for (sessionIdLoop in data.roomIdToRoomKeysBackupData[roomIdLoop]!!.sessionIdToKeyBackupData.keys) { + sessionsFromHsCount++ + + val keyBackupData = data.roomIdToRoomKeysBackupData[roomIdLoop]!!.sessionIdToKeyBackupData[sessionIdLoop]!! + + val sessionData = decryptKeyBackupData(keyBackupData, sessionIdLoop, roomIdLoop, decryption) + + sessionData?.let { + sessionsData.add(it) + } + } + } + Timber.d("restoreKeysWithRecoveryKey: Decrypted " + sessionsData.size + " keys out of " + + sessionsFromHsCount + " from the backup store on the homeserver") + + // Do not trigger a backup for them if they come from the backup version we are using + val backUp = keysVersionResult.version != mKeysBackupVersion?.version + if (backUp) { + Timber.d("restoreKeysWithRecoveryKey: Those keys will be backed up to backup version: " + mKeysBackupVersion?.version) + } + + // Import them into the crypto store + val progressListener = if (stepProgressListener != null) { + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + // Note: no need to post to UI thread, importMegolmSessionsData() will do it + stepProgressListener.onStepProgress(StepProgressListener.Step.ImportingKey(progress, total)) + } + } + } else { + null + } + + mKeysBackupCryptoListener.importMegolmSessionsData(sessionsData, backUp, progressListener, callback) + } + + override fun onFailure(failure: Throwable) { + mUIHandler.post { callback.onFailure(failure) } + } + }) + }) + } + + /** + * Restore a backup with a password from a given backup version stored on the homeserver. + * + * @param keysBackupVersion the backup version to restore from. + * @param password the password to decrypt the retrieved backup. + * @param roomId the id of the room to get backup data from. + * @param sessionId the id of the session to restore. + * @param stepProgressListener the step progress listener + * @param callback Callback. It provides the number of found keys and the number of successfully imported keys. + */ + override fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult, + password: String, + roomId: String?, + sessionId: String?, + stepProgressListener: StepProgressListener?, + callback: MatrixCallback) { + Timber.d("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + val progressListener = if (stepProgressListener != null) { + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + mUIHandler.post { + stepProgressListener.onStepProgress(StepProgressListener.Step.ComputingKey(progress, total)) + } + } + } + } else { + null + } + + val recoveryKey = recoveryKeyFromPassword(password, keysBackupVersion, progressListener) + + if (recoveryKey == null) { + mUIHandler.post { + Timber.d("backupKeys: Invalid configuration") + callback.onFailure(IllegalStateException("Invalid configuration")) + } + + return@post + } + + restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, roomId, sessionId, stepProgressListener, callback) + } + } + + /** + * Same method as [RoomKeysRestClient.getRoomKey] except that it accepts nullable + * parameters and always returns a KeysBackupData object through the Callback + */ + private fun getKeys(sessionId: String?, + roomId: String?, + version: String, + callback: MatrixCallback) { + if (roomId != null && sessionId != null) { + // Get key for the room and for the session + mGetRoomSessionDataTask + .configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: KeyBackupData) { + // Convert to KeysBackupData + val keysBackupData = KeysBackupData() + keysBackupData.roomIdToRoomKeysBackupData = HashMap() + val roomKeysBackupData = RoomKeysBackupData() + roomKeysBackupData.sessionIdToKeyBackupData = HashMap() + roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data + keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData + + callback.onSuccess(keysBackupData) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } else if (roomId != null) { + // Get all keys for the room + mGetRoomSessionsDataTask + .configureWith(GetRoomSessionsDataTask.Params(roomId, version)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: RoomKeysBackupData) { + // Convert to KeysBackupData + val keysBackupData = KeysBackupData() + keysBackupData.roomIdToRoomKeysBackupData = HashMap() + keysBackupData.roomIdToRoomKeysBackupData[roomId] = data + + callback.onSuccess(keysBackupData) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } else { + // Get all keys + mGetSessionsDataTask + .configureWith(GetSessionsDataTask.Params(version)) + .dispatchTo(callback) + .executeBy(mTaskExecutor) + } + } + + @VisibleForTesting + @WorkerThread + fun pkDecryptionFromRecoveryKey(recoveryKey: String): OlmPkDecryption? { + // Extract the primary key + val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey) + + // Built the PK decryption with it + var decryption: OlmPkDecryption? = null + if (privateKey != null) { + try { + decryption = OlmPkDecryption() + decryption.setPrivateKey(privateKey) + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + + } + + return decryption + } + + /** + * Do a backup if there are new keys, with a delay + */ + override fun maybeBackupKeys() { + when { + isStucked -> { + // If not already done, or in error case, check for a valid backup version on the homeserver. + // If there is one, maybeBackupKeys will be called again. + checkAndStartKeysBackup() + } + state == KeysBackupState.ReadyToBackUp -> { + mKeysBackupStateManager.state = KeysBackupState.WillBackUp + + // Wait between 0 and 10 seconds, to avoid backup requests from + // different clients hitting the server all at the same time when a + // new key is sent + val delayInMs = mRandom.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong() + + mUIHandler.postDelayed({ backupKeys() }, delayInMs) + } + else -> { + Timber.d("maybeBackupKeys: Skip it because state: $state") + } + } + } + + /** + * Get information about a backup version defined on the homeserver. + * + * It can be different than mKeysBackupVersion. + * @param version the backup version + * @param callback + */ + override fun getVersion(version: String, + callback: MatrixCallback) { + mGetKeysBackupVersionTask + .configureWith(version) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult) { + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError + && failure.error.code == MatrixError.NOT_FOUND) { + // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup + callback.onSuccess(null) + } else { + // Transmit the error + callback.onFailure(failure) + } + } + }) + .executeBy(mTaskExecutor) + } + + /** + * Retrieve the current version of the backup from the home server + * + * It can be different than mKeysBackupVersion. + * @param callback onSuccess(null) will be called if there is no backup on the server + */ + override fun getCurrentVersion(callback: MatrixCallback) { + mGetKeysBackupLastVersionTask + .configureWith(Unit) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult) { + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError + && failure.error.code == MatrixError.NOT_FOUND) { + // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup + callback.onSuccess(null) + } else { + // Transmit the error + callback.onFailure(failure) + } + } + }) + .executeBy(mTaskExecutor) + } + + /** + * This method fetches the last backup version on the server, then compare to the currently backup version use. + * If versions are not the same, the current backup is deleted (on server or locally), then the backup may be started again, using the last version. + * + * @param callback true if backup is already using the last version, and false if it is not the case + */ + override fun forceUsingLastVersion(callback: MatrixCallback) { + getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult?) { + val localBackupVersion = mKeysBackupVersion?.version + val serverBackupVersion = data?.version + + if (serverBackupVersion == null) { + if (localBackupVersion == null) { + // No backup on the server, and backup is not active + callback.onSuccess(true) + } else { + // No backup on the server, and we are currently backing up, so stop backing up + callback.onSuccess(false) + resetKeysBackupData() + mKeysBackupVersion = null + mKeysBackupStateManager.state = KeysBackupState.Disabled + } + } else { + if (localBackupVersion == null) { + // backup on the server, and backup is not active + callback.onSuccess(false) + // Do a check + checkAndStartWithKeysBackupVersion(data) + } else { + // Backup on the server, and we are currently backing up, compare version + if (localBackupVersion == serverBackupVersion) { + // We are already using the last version of the backup + callback.onSuccess(true) + } else { + // We are not using the last version, so delete the current version we are using on the server + callback.onSuccess(false) + + // This will automatically check for the last version then + deleteBackup(localBackupVersion, null) + } + } + } + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) + } + + /** + * Check the server for an active key backup. + * + * If one is present and has a valid signature from one of the user's verified + * devices, start backing up to it. + */ + override fun checkAndStartKeysBackup() { + if (!isStucked) { + // Try to start or restart the backup only if it is in unknown or bad state + Timber.w("checkAndStartKeysBackup: invalid state: $state") + + return + } + + mKeysBackupVersion = null + mKeysBackupStateManager.state = KeysBackupState.CheckingBackUpOnHomeserver + + getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult?) { + checkAndStartWithKeysBackupVersion(data) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "checkAndStartKeysBackup: Failed to get current version") + mKeysBackupStateManager.state = KeysBackupState.Unknown + } + }) + } + + private fun checkAndStartWithKeysBackupVersion(keyBackupVersion: KeysVersionResult?) { + Timber.d("checkAndStartWithKeyBackupVersion: ${keyBackupVersion?.version}") + + mKeysBackupVersion = keyBackupVersion + + if (keyBackupVersion == null) { + Timber.d("checkAndStartWithKeysBackupVersion: Found no key backup version on the homeserver") + resetKeysBackupData() + mKeysBackupStateManager.state = KeysBackupState.Disabled + } else { + getKeysBackupTrust(keyBackupVersion, object : MatrixCallback { + + override fun onSuccess(data: KeysBackupVersionTrust) { + val versionInStore = mCryptoStore.getKeyBackupVersion() + + if (data.usable) { + Timber.d("checkAndStartWithKeysBackupVersion: Found usable key backup. version: " + keyBackupVersion.version) + // Check the version we used at the previous app run + if (versionInStore != null && versionInStore != keyBackupVersion.version) { + Timber.d(" -> clean the previously used version $versionInStore") + resetKeysBackupData() + } + + Timber.d(" -> enabling key backups") + enableKeysBackup(keyBackupVersion) + } else { + Timber.d("checkAndStartWithKeysBackupVersion: No usable key backup. version: " + keyBackupVersion.version) + if (versionInStore != null) { + Timber.d(" -> disabling key backup") + resetKeysBackupData() + } + + mKeysBackupStateManager.state = KeysBackupState.NotTrusted + } + } + + override fun onFailure(failure: Throwable) { + // Cannot happen + } + + }) + } + } + + /* ========================================================================================== + * Private + * ========================================================================================== */ + + /** + * Extract MegolmBackupAuthData data from a backup version. + * + * @param keysBackupData the key backup data + * + * @return the authentication if found and valid, null in other case + */ + private fun getMegolmBackupAuthData(keysBackupData: KeysVersionResult): MegolmBackupAuthData? { + if (keysBackupData.version.isNullOrBlank() + || keysBackupData.algorithm != MXCRYPTO_ALGORITHM_MEGOLM_BACKUP + || keysBackupData.authData == null) { + return null + } + + val authData = keysBackupData.getAuthDataAsMegolmBackupAuthData() + + if (authData.signatures == null + || authData.publicKey.isBlank()) { + return null + } + + return authData + } + + /** + * Compute the recovery key from a password and key backup version. + * + * @param password the password. + * @param keysBackupData the backup and its auth data. + * + * @return the recovery key if successful, null in other cases + */ + @WorkerThread + private fun recoveryKeyFromPassword(password: String, keysBackupData: KeysVersionResult, progressListener: ProgressListener?): String? { + val authData = getMegolmBackupAuthData(keysBackupData) + + if (authData == null) { + Timber.w("recoveryKeyFromPassword: invalid parameter") + return null + } + + if (authData.privateKeySalt.isNullOrBlank() + || authData.privateKeyIterations == null) { + Timber.w("recoveryKeyFromPassword: Salt and/or iterations not found in key backup auth data") + + return null + } + + // Extract the recovery key from the passphrase + val data = retrievePrivateKeyWithPassword(password, authData.privateKeySalt!!, authData.privateKeyIterations!!, progressListener) + + return computeRecoveryKey(data) + } + + /** + * Check if a recovery key matches key backup authentication data. + * + * @param recoveryKey the recovery key to challenge. + * @param keysBackupData the backup and its auth data. + * + * @return true if successful. + */ + @WorkerThread + private fun isValidRecoveryKeyForKeysBackupVersion(recoveryKey: String, keysBackupData: KeysVersionResult): Boolean { + // Build PK decryption instance with the recovery key + val publicKey = pkPublicKeyFromRecoveryKey(recoveryKey) + + if (publicKey == null) { + Timber.w("isValidRecoveryKeyForKeysBackupVersion: public key is null") + + return false + } + + val authData = getMegolmBackupAuthData(keysBackupData) + + if (authData == null) { + Timber.w("isValidRecoveryKeyForKeysBackupVersion: Key backup is missing required data") + + return false + } + + // Compare both + if (publicKey != authData.publicKey) { + Timber.w("isValidRecoveryKeyForKeysBackupVersion: Public keys mismatch") + + return false + } + + // Public keys match! + return true + } + + /** + * Enable backing up of keys. + * This method will update the state and will start sending keys in nominal case + * + * @param keysVersionResult backup information object as returned by [getCurrentVersion]. + */ + private fun enableKeysBackup(keysVersionResult: KeysVersionResult) { + if (keysVersionResult.authData != null) { + val retrievedMegolmBackupAuthData = keysVersionResult.getAuthDataAsMegolmBackupAuthData() + + if (retrievedMegolmBackupAuthData != null) { + mKeysBackupVersion = keysVersionResult + mCryptoStore.setKeyBackupVersion(keysVersionResult.version) + + onServerDataRetrieved(keysVersionResult.count, keysVersionResult.hash) + + try { + mBackupKey = OlmPkEncryption().apply { + setRecipientKey(retrievedMegolmBackupAuthData.publicKey) + } + } catch (e: OlmException) { + Timber.e(e, "OlmException") + mKeysBackupStateManager.state = KeysBackupState.Disabled + return + } + + mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp + + maybeBackupKeys() + } else { + Timber.e("Invalid authentication data") + mKeysBackupStateManager.state = KeysBackupState.Disabled + } + } else { + Timber.e("Invalid authentication data") + mKeysBackupStateManager.state = KeysBackupState.Disabled + } + } + + /** + * Update the DB with data fetch from the server + */ + private fun onServerDataRetrieved(count: Int?, hash: String?) { + mCryptoStore.setKeysBackupData(KeysBackupDataEntity() + .apply { + backupLastServerNumberOfKeys = count + backupLastServerHash = hash + } + ) + } + + /** + * Reset all local key backup data. + * + * Note: This method does not update the state + */ + private fun resetKeysBackupData() { + resetBackupAllGroupSessionsListeners() + + mCryptoStore.setKeyBackupVersion(null) + mCryptoStore.setKeysBackupData(null) + mBackupKey = null + + // Reset backup markers + mCryptoStore.resetBackupMarkers() + } + + /** + * Send a chunk of keys to backup + */ + @UiThread + private fun backupKeys() { + Timber.d("backupKeys") + + // Sanity check, as this method can be called after a delay, the state may have change during the delay + if (!isEnabled || mBackupKey == null || mKeysBackupVersion == null) { + Timber.d("backupKeys: Invalid configuration") + backupAllGroupSessionsCallback?.onFailure(IllegalStateException("Invalid configuration")) + resetBackupAllGroupSessionsListeners() + + return + } + + if (state === KeysBackupState.BackingUp) { + // Do nothing if we are already backing up + Timber.d("backupKeys: Invalid state: $state") + return + } + + // Get a chunk of keys to backup + val sessions = mCryptoStore.inboundGroupSessionsToBackup(KEY_BACKUP_SEND_KEYS_MAX_COUNT) + + Timber.d("backupKeys: 1 - " + sessions.size + " sessions to back up") + + if (sessions.isEmpty()) { + // Backup is up to date + mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp + + backupAllGroupSessionsCallback?.onSuccess(Unit) + resetBackupAllGroupSessionsListeners() + return + } + + mKeysBackupStateManager.state = KeysBackupState.BackingUp + + CryptoAsyncHelper.getEncryptBackgroundHandler().post { + Timber.d("backupKeys: 2 - Encrypting keys") + + // Gather data to send to the homeserver + // roomId -> sessionId -> MXKeyBackupData + val keysBackupData = KeysBackupData() + keysBackupData.roomIdToRoomKeysBackupData = HashMap() + + for (session in sessions) { + val keyBackupData = encryptGroupSession(session) + if (keysBackupData.roomIdToRoomKeysBackupData[session.mRoomId] == null) { + val roomKeysBackupData = RoomKeysBackupData() + roomKeysBackupData.sessionIdToKeyBackupData = HashMap() + keysBackupData.roomIdToRoomKeysBackupData[session.mRoomId!!] = roomKeysBackupData + } + + try { + keysBackupData.roomIdToRoomKeysBackupData[session.mRoomId]!!.sessionIdToKeyBackupData[session.mSession!!.sessionIdentifier()] = keyBackupData + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + } + + Timber.d("backupKeys: 4 - Sending request") + + // Make the request + mStoreSessionDataTask + .configureWith(StoreSessionsDataTask.Params(mKeysBackupVersion!!.version!!, keysBackupData)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: BackupKeysResult) { + mUIHandler.post { + Timber.d("backupKeys: 5a - Request complete") + + // Mark keys as backed up + mCryptoStore.markBackupDoneForInboundGroupSessions(sessions) + + if (sessions.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) { + Timber.d("backupKeys: All keys have been backed up") + onServerDataRetrieved(data.count, data.hash) + + // Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess() + mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp + } else { + Timber.d("backupKeys: Continue to back up keys") + mKeysBackupStateManager.state = KeysBackupState.WillBackUp + + backupKeys() + } + } + } + + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError) { + mUIHandler.post { + Timber.e(failure, "backupKeys: backupKeys failed.") + + when (failure.error.code) { + MatrixError.NOT_FOUND, + MatrixError.WRONG_ROOM_KEYS_VERSION -> { + // Backup has been deleted on the server, or we are not using the last backup version + mKeysBackupStateManager.state = KeysBackupState.WrongBackUpVersion + backupAllGroupSessionsCallback?.onFailure(failure) + resetBackupAllGroupSessionsListeners() + resetKeysBackupData() + mKeysBackupVersion = null + + // Do not stay in KeysBackupState.WrongBackUpVersion but check what is available on the homeserver + checkAndStartKeysBackup() + } + else -> // Come back to the ready state so that we will retry on the next received key + mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp + } + } + } else { + mUIHandler.post { + backupAllGroupSessionsCallback?.onFailure(failure) + resetBackupAllGroupSessionsListeners() + + Timber.e("backupKeys: backupKeys failed.") + + // Retry a bit later + mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp + maybeBackupKeys() + } + } + } + }) + .executeBy(mTaskExecutor) + } + } + + @VisibleForTesting + @WorkerThread + fun encryptGroupSession(session: MXOlmInboundGroupSession2): KeyBackupData { + // Gather information for each key + val device = mKeysBackupCryptoListener.deviceWithIdentityKey(session.mSenderKey!!, MXCRYPTO_ALGORITHM_MEGOLM) + + // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at + // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format + val sessionData = session.exportKeys() + val sessionBackupData = mapOf( + "algorithm" to sessionData!!.algorithm, + "sender_key" to sessionData.senderKey, + "sender_claimed_keys" to sessionData.senderClaimedKeys, + "forwarding_curve25519_key_chain" to (sessionData.forwardingCurve25519KeyChain ?: ArrayList()), + "session_key" to sessionData.sessionKey) + + var encryptedSessionBackupData: OlmPkMessage? = null + + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(Map::class.java) + + try { + val json = adapter.toJson(sessionBackupData) + + encryptedSessionBackupData = mBackupKey?.encrypt(json) + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + + // Build backup data for that key + val keyBackupData = KeyBackupData() + try { + keyBackupData.firstMessageIndex = session.mSession!!.firstKnownIndex + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + + keyBackupData.forwardedCount = session.mForwardingCurve25519KeyChain!!.size + keyBackupData.isVerified = device?.isVerified == true + + val data = mapOf( + "ciphertext" to encryptedSessionBackupData!!.mCipherText, + "mac" to encryptedSessionBackupData.mMac, + "ephemeral" to encryptedSessionBackupData.mEphemeralKey) + + keyBackupData.sessionData = data + + return keyBackupData + } + + @VisibleForTesting + @WorkerThread + fun decryptKeyBackupData(keyBackupData: KeyBackupData, sessionId: String, roomId: String, decryption: OlmPkDecryption): MegolmSessionData? { + var sessionBackupData: MegolmSessionData? = null + + val jsonObject = keyBackupData.sessionData + + val ciphertext = jsonObject?.get("ciphertext")?.toString() + val mac = jsonObject?.get("mac")?.toString() + val ephemeralKey = jsonObject?.get("ephemeral")?.toString() + + if (ciphertext != null && mac != null && ephemeralKey != null) { + val encrypted = OlmPkMessage() + encrypted.mCipherText = ciphertext + encrypted.mMac = mac + encrypted.mEphemeralKey = ephemeralKey + + try { + val decrypted = decryption.decrypt(encrypted) + + val moshi = MoshiProvider.providesMoshi() + val adapter = moshi.adapter(MegolmSessionData::class.java) + + sessionBackupData = adapter.fromJson(decrypted) + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + + if (sessionBackupData != null) { + sessionBackupData.sessionId = sessionId + sessionBackupData.roomId = roomId + } + } + + return sessionBackupData + } + + companion object { + // Maximum delay in ms in {@link maybeBackupKeys} + private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10000 + + // Maximum number of keys to send at a time to the homeserver. + private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100 + } + + + fun setCryptoInternalListener(listener: KeysBackupCryptoListener) { + mKeysBackupCryptoListener = listener + } + + interface KeysBackupCryptoListener { + fun signObject(strToSign: String): Map> + + fun importMegolmSessionsData(megolmSessionsData: List, + backUpKeys: Boolean, + progressListener: ProgressListener?, + callback: MatrixCallback) + + fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? + } + + + /* ========================================================================================== + * DEBUG INFO + * ========================================================================================== */ + + override fun toString() = "KeysBackup for ${mCredentials.userId}" +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt new file mode 100644 index 00000000..ae4aa609 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Utility to compute a backup private key from a password and vice-versa. + */ +package im.vector.matrix.android.internal.crypto.keysbackup + +import androidx.annotation.WorkerThread +import im.vector.matrix.android.api.listeners.ProgressListener +import timber.log.Timber +import java.util.* +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec +import kotlin.experimental.xor + + +private const val SALT_LENGTH = 32 +private const val DEFAULT_ITERATION = 500_000 + +data class GeneratePrivateKeyResult( + // The private key + val privateKey: ByteArray, + // the salt used to generate the private key + val salt: String, + // number of key derivations done on the generated private key. + val iterations: Int) + +/** + * Compute a private key from a password. + * + * @param password the password to use. + * + * @return a {privateKey, salt, iterations} tuple. + */ +@WorkerThread +fun generatePrivateKeyWithPassword(password: String, progressListener: ProgressListener?): GeneratePrivateKeyResult { + val salt = generateSalt() + val iterations = DEFAULT_ITERATION + val privateKey = deriveKey(password, salt, iterations, progressListener) + + return GeneratePrivateKeyResult(privateKey, salt, iterations) +} + +/** + * Retrieve a private key from {password, salt, iterations} + * + * @param password the password used to generated the private key. + * @param salt the salt. + * @param iterations number of key derivations. + * @param progressListener the progress listener + * + * @return a private key. + */ +@WorkerThread +fun retrievePrivateKeyWithPassword(password: String, + salt: String, + iterations: Int, + progressListener: ProgressListener? = null): ByteArray { + return deriveKey(password, salt, iterations, progressListener) +} + +/** + * Compute a private key by deriving a password and a salt strings. + * + * @param password the password. + * @param salt the salt. + * @param iterations number of derivations. + * @param progressListener a listener to follow progress. + * + * @return a private key. + */ +@WorkerThread +private fun deriveKey(password: String, + salt: String, + iterations: Int, + progressListener: ProgressListener?): ByteArray { + // Note: copied and adapted from MXMegolmExportEncryption + val t0 = System.currentTimeMillis() + + // based on https://en.wikipedia.org/wiki/PBKDF2 algorithm + // it is simpler than the generic algorithm because the expected key length is equal to the mac key length. + // noticed as dklen/hlen + + // dklen = 256 + // hlen = 512 + val prf = Mac.getInstance("HmacSHA512") + + prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512")) + + // 256 bits key length + val dk = ByteArray(32) + val uc = ByteArray(64) + + // U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen + prf.update(salt.toByteArray()) + val int32BE = byteArrayOf(0, 0, 0, 1) + prf.update(int32BE) + prf.doFinal(uc, 0) + + // copy to the key + System.arraycopy(uc, 0, dk, 0, dk.size) + + var lastProgress = -1 + + for (index in 2..iterations) { + // Uc = PRF(Password, Uc-1) + prf.update(uc) + prf.doFinal(uc, 0) + + // F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc + for (byteIndex in dk.indices) { + dk[byteIndex] = dk[byteIndex] xor uc[byteIndex] + } + + val progress = (index + 1) * 100 / iterations + if (progress != lastProgress) { + lastProgress = progress + progressListener?.onProgress(lastProgress, 100) + } + } + + Timber.d("KeysBackupPassword", "## deriveKeys() : " + iterations + " in " + (System.currentTimeMillis() - t0) + " ms") + + return dk +} + +/** + * Generate a 32 chars salt + */ +private fun generateSalt(): String { + var salt = "" + + do { + salt += UUID.randomUUID().toString() + } while (salt.length < SALT_LENGTH) + + + return salt.substring(0, SALT_LENGTH) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt new file mode 100644 index 00000000..fe765712 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup + +import android.os.Handler +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import timber.log.Timber +import java.util.* + +internal class KeysBackupStateManager(val uiHandler: Handler) { + + private val mListeners = ArrayList() + + // Backup state + var state = KeysBackupState.Unknown + set(newState) { + Timber.d("KeysBackup", "setState: $field -> $newState") + + field = newState + + // Notify listeners about the state change, on the ui thread + uiHandler.post { + synchronized(mListeners) { + mListeners.forEach { + // Use newState because state may have already changed again + it.onStateChange(newState) + } + } + } + } + + val isEnabled: Boolean + get() = state == KeysBackupState.ReadyToBackUp + || state == KeysBackupState.WillBackUp + || state == KeysBackupState.BackingUp + + // True if unknown or bad state + val isStucked: Boolean + get() = state == KeysBackupState.Unknown + || state == KeysBackupState.Disabled + || state == KeysBackupState.WrongBackUpVersion + || state == KeysBackupState.NotTrusted + + fun addListener(listener: KeysBackupService.KeysBackupStateListener) { + synchronized(mListeners) { + mListeners.add(listener) + } + } + + fun removeListener(listener: KeysBackupService.KeysBackupStateListener) { + synchronized(mListeners) { + mListeners.remove(listener) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/api/RoomKeysApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/api/RoomKeysApi.kt new file mode 100644 index 00000000..73b6112f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/api/RoomKeysApi.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.api + +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.* +import im.vector.matrix.android.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.* + +/** + * Ref: https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md + */ +internal interface RoomKeysApi { + + /* ========================================================================================== + * Backup versions management + * ========================================================================================== */ + + /** + * Create a new keys backup version. + * @param createKeysBackupVersionBody the body + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/version") + fun createKeysBackupVersion(@Body createKeysBackupVersionBody: CreateKeysBackupVersionBody): Call + + /** + * Get the key backup last version + * If not supported by the server, an error is returned: {"errcode":"M_NOT_FOUND","error":"No backup found"} + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/version") + fun getKeysBackupLastVersion(): Call + + /** + * Get information about the given version. + * If not supported by the server, an error is returned: {"errcode":"M_NOT_FOUND","error":"No backup found"} + * + * @param version version + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/version/{version}") + fun getKeysBackupVersion(@Path("version") version: String): Call + + /** + * Update information about the given version. + * @param version version + * @param updateKeysBackupVersionBody the body + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/version/{version}") + fun updateKeysBackupVersion(@Path("version") version: String, + @Body keysBackupVersionBody: UpdateKeysBackupVersionBody): Call + + /* ========================================================================================== + * Storing keys + * ========================================================================================== */ + + /** + * Store the key for the given session in the given room, using the given backup version. + * + * + * If the server already has a backup in the backup version for the given session and room, then it will + * keep the "better" one. To determine which one is "better", key backups are compared first by the is_verified + * flag (true is better than false), then by the first_message_index (a lower number is better), and finally by + * forwarded_count (a lower number is better). + * + * @param roomId the room id + * @param sessionId the session id + * @param version the version of the backup + * @param keyBackupData the data to send + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}/{sessionId}") + fun storeRoomSessionData(@Path("roomId") roomId: String, + @Path("sessionId") sessionId: String, + @Query("version") version: String, + @Body keyBackupData: KeyBackupData): Call + + /** + * Store several keys for the given room, using the given backup version. + * + * @param roomId the room id + * @param version the version of the backup + * @param roomKeysBackupData the data to send + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}") + fun storeRoomSessionsData(@Path("roomId") roomId: String, + @Query("version") version: String, + @Body roomKeysBackupData: RoomKeysBackupData): Call + + /** + * Store several keys, using the given backup version. + * + * @param version the version of the backup + * @param keysBackupData the data to send + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys") + fun storeSessionsData(@Query("version") version: String, + @Body keysBackupData: KeysBackupData): Call + + /* ========================================================================================== + * Retrieving keys + * ========================================================================================== */ + + /** + * Retrieve the key for the given session in the given room from the backup. + * + * @param roomId the room id + * @param sessionId the session id + * @param version the version of the backup, or empty String to retrieve the last version + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}/{sessionId}") + fun getRoomSessionData(@Path("roomId") roomId: String, + @Path("sessionId") sessionId: String, + @Query("version") version: String): Call + + /** + * Retrieve all the keys for the given room from the backup. + * + * @param roomId the room id + * @param version the version of the backup, or empty String to retrieve the last version + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}") + fun getRoomSessionsData(@Path("roomId") roomId: String, + @Query("version") version: String): Call + + /** + * Retrieve all the keys from the backup. + * + * @param version the version of the backup, or empty String to retrieve the last version + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys") + fun getSessionsData(@Query("version") version: String): Call + + + /* ========================================================================================== + * Deleting keys + * ========================================================================================== */ + + /** + * Deletes keys from the backup. + */ + @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}/{sessionId}") + fun deleteRoomSessionData(@Path("roomId") roomId: String, + @Path("sessionId") sessionId: String, + @Query("version") version: String): Call + + /** + * Deletes keys from the backup. + */ + @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys/{roomId}") + fun deleteRoomSessionsData(@Path("roomId") roomId: String, + @Query("version") version: String): Call + + /** + * Deletes keys from the backup. + */ + @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/keys") + fun deleteSessionsData(@Query("version") version: String): Call + + /* ========================================================================================== + * Deleting backup + * ========================================================================================== */ + + /** + * Deletes a backup. + */ + @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "room_keys/version/{version}") + fun deleteBackup(@Path("version") version: String): Call +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrust.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrust.kt new file mode 100644 index 00000000..77531d28 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrust.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +import com.squareup.moshi.JsonClass + +/** + * Data model for response to [KeysBackup.isKeyBackupTrusted()]. + */ +@JsonClass(generateAdapter = true) +data class KeyBackupVersionTrust( + /** + * Flag to indicate if the backup is trusted. + * true if there is a signature that is valid & from a trusted device. + */ + var usable: Boolean = false, + + /** + * Signatures found in the backup version. + */ + var signatures: MutableList = ArrayList() +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt new file mode 100644 index 00000000..fce77b33 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeyBackupVersionTrustSignature.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo + +/** + * A signature in a the `KeyBackupVersionTrust` object. + */ +class KeyBackupVersionTrustSignature { + + /** + * The device that signed the backup version. + */ + var device: MXDeviceInfo? = null + + /** + *Flag to indicate the signature from this device is valid. + */ + var valid = false + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrust.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrust.kt new file mode 100644 index 00000000..ed765d14 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrust.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +/** + * Data model for response to [KeysBackup.getKeysBackupTrust()]. + */ +data class KeysBackupVersionTrust( + /** + * Flag to indicate if the backup is trusted. + * true if there is a signature that is valid & from a trusted device. + */ + var usable: Boolean = false, + + /** + * Signatures found in the backup version. + */ + var signatures: MutableList = ArrayList() +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt new file mode 100644 index 00000000..afa6b779 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/KeysBackupVersionTrustSignature.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo + +/** + * A signature in a `KeysBackupVersionTrust` object. + */ +class KeysBackupVersionTrustSignature { + + /** + * The id of the device that signed the backup version. + */ + var deviceId: String? = null + + /** + * The device that signed the backup version. + * Can be null if the device is not known. + */ + var device: MXDeviceInfo? = null + + /** + * Flag to indicate the signature from this device is valid. + */ + var valid = false + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt new file mode 100644 index 00000000..442b1f08 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.di.MoshiProvider + +/** + * Data model for [org.matrix.androidsdk.rest.model.keys.KeysAlgorithmAndData.authData] in case + * of [org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP]. + */ +@JsonClass(generateAdapter = true) +data class MegolmBackupAuthData( + /** + * The curve25519 public key used to encrypt the backups. + */ + @Json(name = "public_key") + var publicKey: String = "", + + /** + * In case of a backup created from a password, the salt associated with the backup + * private key. + */ + @Json(name = "private_key_salt") + var privateKeySalt: String? = null, + + /** + * In case of a backup created from a password, the number of key derivations. + */ + @Json(name = "private_key_iterations") + var privateKeyIterations: Int? = null, + + /** + * Signatures of the public key. + * userId -> (deviceSignKeyId -> signature) + */ + var signatures: Map>? = null +) { + + fun toJsonString(): String { + return MoshiProvider.providesMoshi() + .adapter(MegolmBackupAuthData::class.java) + .toJson(this) + } + + /** + * Same as the parent [MXJSONModel JSONDictionary] but return only + * data that must be signed. + */ + fun signalableJSONDictionary(): Map = HashMap().apply { + put("public_key", publicKey) + + privateKeySalt?.let { + put("private_key_salt", it) + } + privateKeyIterations?.let { + put("private_key_iterations", it) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt new file mode 100644 index 00000000..2be7806b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model + +/** + * Data retrieved from Olm library. algorithm and authData will be send to the homeserver, and recoveryKey will be displayed to the user + */ +class MegolmBackupCreationInfo { + + /** + * The algorithm used for storing backups [org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP]. + */ + var algorithm: String = "" + + /** + * Authentication data. + */ + var authData: MegolmBackupAuthData? = null + + /** + * The Base58 recovery key. + */ + var recoveryKey: String = "" + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/BackupKeysResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/BackupKeysResult.kt new file mode 100644 index 00000000..e2abc381 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/BackupKeysResult.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class BackupKeysResult( + + // The hash value which is an opaque string representing stored keys in the backup + var hash: String? = null, + + // The number of keys stored in the backup. + var count: Int? = null + +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt new file mode 100644 index 00000000..5efbc6d0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +class CreateKeysBackupVersionBody : KeysAlgorithmAndData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt new file mode 100644 index 00000000..f172d45f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.di.MoshiProvider + +/** + * Backup data for one key. + */ +@JsonClass(generateAdapter = true) +data class KeyBackupData( + /** + * Required. The index of the first message in the session that the key can decrypt. + */ + @Json(name = "first_message_index") + var firstMessageIndex: Long = 0, + + /** + * Required. The number of times this key has been forwarded. + */ + @Json(name = "forwarded_count") + var forwardedCount: Int = 0, + + /** + * Whether the device backing up the key has verified the device that the key is from. + */ + @Json(name = "is_verified") + var isVerified: Boolean = false, + + /** + * Algorithm-dependent data. + */ + @Json(name = "session_data") + var sessionData: Map? = null +) { + + fun toJsonString(): String { + return MoshiProvider.providesMoshi().adapter(KeyBackupData::class.java).toJson(this) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt new file mode 100644 index 00000000..b03dd498 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.Json +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData +import im.vector.matrix.android.internal.di.MoshiProvider + +/** + *
    + *     Example:
    + *
    + *     {
    + *         "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
    + *         "auth_data": {
    + *             "public_key": "abcdefg",
    + *             "signatures": {
    + *                 "something": {
    + *                     "ed25519:something": "hijklmnop"
    + *                 }
    + *             }
    + *         }
    + *     }
    + * 
    + */ +open class KeysAlgorithmAndData { + + /** + * The algorithm used for storing backups. Currently, only "m.megolm_backup.v1.curve25519-aes-sha2" is defined + */ + @Json(name = "algorithm") + var algorithm: String? = null + + /** + * algorithm-dependent data, for "m.megolm_backup.v1.curve25519-aes-sha2" see [im.vector.matrix.android.internal.crypto.keysbackup.MegolmBackupAuthData] + */ + @Json(name = "auth_data") + var authData: Map? = null + + /** + * Facility method to convert authData to a MegolmBackupAuthData object + */ + fun getAuthDataAsMegolmBackupAuthData(): MegolmBackupAuthData { + return MoshiProvider.providesMoshi() + .adapter(MegolmBackupAuthData::class.java) + .fromJsonValue(authData)!! + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt new file mode 100644 index 00000000..2f4165d8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Backup data for several keys in several rooms. + */ +@JsonClass(generateAdapter = true) +data class KeysBackupData( + + // the keys are the room IDs, and the values are RoomKeysBackupData + @Json(name = "rooms") + var roomIdToRoomKeysBackupData: MutableMap = HashMap() + +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersion.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersion.kt new file mode 100644 index 00000000..4e33c17b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersion.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +data class KeysVersion( + // the keys backup version + var version: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt new file mode 100644 index 00000000..bff8f138 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class KeysVersionResult( + // the backup version + var version: String? = null, + + // The hash value which is an opaque string representing stored keys in the backup + var hash: String? = null, + + // The number of keys stored in the backup. + var count: Int? = null +) : KeysAlgorithmAndData() \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt new file mode 100644 index 00000000..5d69f635 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Backup data for several keys within a room. + */ +@JsonClass(generateAdapter = true) +data class RoomKeysBackupData( + + // the keys are the session IDs, and the values are KeyBackupData + @Json(name = "sessions") + var sessionIdToKeyBackupData: MutableMap = HashMap() +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt new file mode 100644 index 00000000..e2d628c4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class UpdateKeysBackupVersionBody( + // the backup version, mandatory + val version: String +) : KeysAlgorithmAndData() \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt new file mode 100644 index 00000000..2053f56e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface CreateKeysBackupVersionTask : Task + + +internal class DefaultCreateKeysBackupVersionTask(private val roomKeysApi: RoomKeysApi) + : CreateKeysBackupVersionTask { + + + override fun execute(params: CreateKeysBackupVersionBody): Try { + return executeRequest { + apiCall = roomKeysApi.createKeysBackupVersion(params) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt new file mode 100644 index 00000000..472d8846 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + + +internal interface DeleteBackupTask : Task { + data class Params( + val version: String + ) +} + + +internal class DefaultDeleteBackupTask(private val roomKeysApi: RoomKeysApi) + : DeleteBackupTask { + + override fun execute(params: DeleteBackupTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.deleteBackup( + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt new file mode 100644 index 00000000..01c59e8f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface DeleteRoomSessionDataTask : Task { + data class Params( + val roomId: String, + val sessionId: String, + val version: String + ) +} + + +internal class DefaultDeleteRoomSessionDataTask(private val roomKeysApi: RoomKeysApi) + : DeleteRoomSessionDataTask { + + override fun execute(params: DeleteRoomSessionDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.deleteRoomSessionData( + params.roomId, + params.sessionId, + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt new file mode 100644 index 00000000..79061f49 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface DeleteRoomSessionsDataTask : Task { + data class Params( + val roomId: String, + val version: String + ) +} + + +internal class DefaultDeleteRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : DeleteRoomSessionsDataTask { + + override fun execute(params: DeleteRoomSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.deleteRoomSessionsData( + params.roomId, + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt new file mode 100644 index 00000000..8fbd9957 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface DeleteSessionsDataTask : Task { + data class Params( + val version: String + ) +} + + +internal class DefaultDeleteSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : DeleteSessionsDataTask { + + override fun execute(params: DeleteSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.deleteSessionsData( + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt new file mode 100644 index 00000000..ff09a939 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetKeysBackupLastVersionTask : Task + + +internal class DefaultGetKeysBackupLastVersionTask(private val roomKeysApi: RoomKeysApi) + : GetKeysBackupLastVersionTask { + + + override fun execute(params: Unit): Try { + return executeRequest { + apiCall = roomKeysApi.getKeysBackupLastVersion() + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt new file mode 100644 index 00000000..becdf7a4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetKeysBackupVersionTask : Task + + +internal class DefaultGetKeysBackupVersionTask(private val roomKeysApi: RoomKeysApi) + : GetKeysBackupVersionTask { + + + override fun execute(params: String): Try { + return executeRequest { + apiCall = roomKeysApi.getKeysBackupVersion(params) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt new file mode 100644 index 00000000..ef28efd4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetRoomSessionDataTask : Task { + data class Params( + val roomId: String, + val sessionId: String, + val version: String + ) +} + + +internal class DefaultGetRoomSessionDataTask(private val roomKeysApi: RoomKeysApi) + : GetRoomSessionDataTask { + + override fun execute(params: GetRoomSessionDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.getRoomSessionData( + params.roomId, + params.sessionId, + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt new file mode 100644 index 00000000..7858c687 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + + +internal interface GetRoomSessionsDataTask : Task { + data class Params( + val roomId: String, + val version: String + ) +} + + +internal class DefaultGetRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : GetRoomSessionsDataTask { + + override fun execute(params: GetRoomSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.getRoomSessionsData( + params.roomId, + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt new file mode 100644 index 00000000..67b18788 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetSessionsDataTask : Task { + data class Params( + val version: String + ) +} + + +internal class DefaultGetSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : GetSessionsDataTask { + + override fun execute(params: GetSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.getSessionsData( + params.version) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt new file mode 100644 index 00000000..1c81f691 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface StoreRoomSessionDataTask : Task { + data class Params( + val roomId: String, + val sessionId: String, + val version: String, + val keyBackupData: KeyBackupData + ) +} + + +internal class DefaultStoreRoomSessionDataTask(private val roomKeysApi: RoomKeysApi) + : StoreRoomSessionDataTask { + + override fun execute(params: StoreRoomSessionDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.storeRoomSessionData( + params.roomId, + params.sessionId, + params.version, + params.keyBackupData) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt new file mode 100644 index 00000000..09b9c0b0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface StoreRoomSessionsDataTask : Task { + data class Params( + val roomId: String, + val version: String, + val roomKeysBackupData: RoomKeysBackupData + ) +} + + +internal class DefaultStoreRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : StoreRoomSessionsDataTask { + + override fun execute(params: StoreRoomSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.storeRoomSessionsData( + params.roomId, + params.version, + params.roomKeysBackupData) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt new file mode 100644 index 00000000..736bfab0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface StoreSessionsDataTask : Task { + data class Params( + val version: String, + val keysBackupData: KeysBackupData + ) +} + + +internal class DefaultStoreSessionsDataTask(private val roomKeysApi: RoomKeysApi) + : StoreSessionsDataTask { + + override fun execute(params: StoreSessionsDataTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.storeSessionsData( + params.version, + params.keysBackupData) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt new file mode 100644 index 00000000..ffede6ab --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface UpdateKeysBackupVersionTask : Task { + data class Params( + val version: String, + val keysBackupVersionBody: UpdateKeysBackupVersionBody + ) +} + + +internal class DefaultUpdateKeysBackupVersionTask(private val roomKeysApi: RoomKeysApi) + : UpdateKeysBackupVersionTask { + + + override fun execute(params: UpdateKeysBackupVersionTask.Params): Try { + return executeRequest { + apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/Base58.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/Base58.kt new file mode 100644 index 00000000..d842fb26 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/Base58.kt @@ -0,0 +1,85 @@ +/** + * Copyright 2011 Google Inc. + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.util + +import java.math.BigInteger + +/** + * Ref: https://github.com/bitcoin-labs/bitcoin-mobile-android/blob/master/src/bitcoinj/java/com/google/bitcoin/core/Base58.java + * + * + * A custom form of base58 is used to encode BitCoin addresses. Note that this is not the same base58 as used by + * Flickr, which you may see reference to around the internet. + * + * Satoshi says: why base-58 instead of standard base-64 encoding? + * + * * Don't want 0OIl characters that look the same in some fonts and + * could be used to create visually identical looking account numbers. + * * A string with non-alphanumeric characters is not as easily accepted as an account number. + * * E-mail usually won't line-break if there's no punctuation to break at. + * * Doubleclicking selects the whole number as one word if it's all alphanumeric. + * + */ +private const val ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +private val BASE = BigInteger.valueOf(58) + +/** + * Encode a byte array to a human readable string with base58 chars + */ +fun base58encode(input: ByteArray): String { + var bi = BigInteger(1, input) + val s = StringBuffer() + while (bi >= BASE) { + val mod = bi.mod(BASE) + s.insert(0, ALPHABET[mod.toInt()]) + bi = bi.subtract(mod).divide(BASE) + } + s.insert(0, ALPHABET[bi.toInt()]) + // Convert leading zeros too. + for (anInput in input) { + if (anInput.toInt() == 0) + s.insert(0, ALPHABET[0]) + else + break + } + return s.toString() +} + +/** + * Decode a base58 String to a byte array + */ +fun base58decode(input: String): ByteArray { + var result = decodeToBigInteger(input).toByteArray() + + // Remove the first leading zero if any + if (result[0] == 0.toByte()) { + result = result.copyOfRange(1, result.size) + } + + return result +} + +private fun decodeToBigInteger(input: String): BigInteger { + var bi = BigInteger.valueOf(0) + // Work backwards through the string. + for (i in input.length - 1 downTo 0) { + val alphaIndex = ALPHABET.indexOf(input[i]) + bi = bi.add(BigInteger.valueOf(alphaIndex.toLong()).multiply(BASE.pow(input.length - 1 - i))) + } + return bi +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/RecoveryKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/RecoveryKey.kt new file mode 100644 index 00000000..86ab5cfb --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/util/RecoveryKey.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.keysbackup.util + +import kotlin.experimental.xor + +/** + * See https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md + */ + +private const val CHAR_0 = 0x8B.toByte() +private const val CHAR_1 = 0x01.toByte() + +private const val RECOVERY_KEY_LENGTH = 2 + 32 + 1 + +/** + * Tell if the format of the recovery key is correct + * + * @param recoveryKey + * @return true if the format of the recovery key is correct + */ +fun isValidRecoveryKey(recoveryKey: String?): Boolean { + return extractCurveKeyFromRecoveryKey(recoveryKey) != null +} + +/** + * Compute recovery key from curve25519 key + * + * @param curve25519Key + * @return the recovery key + */ +fun computeRecoveryKey(curve25519Key: ByteArray): String { + // Append header and parity + val data = ByteArray(curve25519Key.size + 3) + + // Header + data[0] = CHAR_0 + data[1] = CHAR_1 + + // Copy key and compute parity + var parity: Byte = CHAR_0 xor CHAR_1 + + for (i in curve25519Key.indices) { + data[i + 2] = curve25519Key[i] + parity = parity xor curve25519Key[i] + } + + // Parity + data[curve25519Key.size + 2] = parity + + // Do not add white space every 4 chars, it's up to the presenter to do it + return base58encode(data) +} + +/** + * Please call [.isValidRecoveryKey] and ensure it returns true before calling this method + * + * @param recoveryKey the recovery key + * @return curveKey, or null in case of error + */ +fun extractCurveKeyFromRecoveryKey(recoveryKey: String?): ByteArray? { + if (recoveryKey == null) { + return null + } + + // Remove any space + val spaceFreeRecoveryKey = recoveryKey.replace("""\s""".toRegex(), "") + + val b58DecodedKey = base58decode(spaceFreeRecoveryKey) + + // Check length + if (b58DecodedKey.size != RECOVERY_KEY_LENGTH) { + return null + } + + // Check first byte + if (b58DecodedKey[0] != CHAR_0) { + return null + } + + // Check second byte + if (b58DecodedKey[1] != CHAR_1) { + return null + } + + // Check parity + var parity: Byte = 0 + + for (i in 0 until RECOVERY_KEY_LENGTH) { + parity = parity xor b58DecodedKey[i] + } + + if (parity != 0.toByte()) { + return null + } + + // Remove header and parity bytes + val result = ByteArray(b58DecodedKey.size - 3) + + for (i in 2 until b58DecodedKey.size - 1) { + result[i - 2] = b58DecodedKey[i] + } + + return result +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/ImportRoomKeysResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/ImportRoomKeysResult.kt new file mode 100644 index 00000000..03def499 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/ImportRoomKeysResult.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +data class ImportRoomKeysResult(val totalNumberOfKeys: Int, + val successfullyNumberOfImportedKeys: Int) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt new file mode 100755 index 00000000..28bcba61 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +import android.text.TextUtils +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys +import java.io.Serializable +import java.util.* + +@JsonClass(generateAdapter = true) +data class MXDeviceInfo( + + /** + * The id of this device. + */ + @Json(name = "device_id") + var deviceId: String, + + /** + * the user id + */ + @Json(name = "user_id") + var userId: String, + + /** + * The list of algorithms supported by this device. + */ + @Json(name = "algorithms") + var algorithms: List? = null, + + /** + * A map from ":" to "". + */ + @Json(name = "keys") + var keys: Map? = null, + + /** + * The signature of this MXDeviceInfo. + * A map from "" to a map from ":" to "" + */ + @Json(name = "signatures") + var signatures: Map>? = null, + + /* + * Additional data from the home server. + */ + @Json(name = "unsigned") + var unsigned: JsonDict? = null, + + /** + * Verification state of this device. + */ + var mVerified: Int = DEVICE_VERIFICATION_UNKNOWN +) : Serializable { + /** + * Tells if the device is unknown + * + * @return true if the device is unknown + */ + val isUnknown: Boolean + get() = mVerified == DEVICE_VERIFICATION_UNKNOWN + + /** + * Tells if the device is verified. + * + * @return true if the device is verified + */ + val isVerified: Boolean + get() = mVerified == DEVICE_VERIFICATION_VERIFIED + + /** + * Tells if the device is unverified. + * + * @return true if the device is unverified + */ + val isUnverified: Boolean + get() = mVerified == DEVICE_VERIFICATION_UNVERIFIED + + /** + * Tells if the device is blocked. + * + * @return true if the device is blocked + */ + val isBlocked: Boolean + get() = mVerified == DEVICE_VERIFICATION_BLOCKED + + /** + * @return the fingerprint + */ + fun fingerprint(): String? { + return if (null != keys && !TextUtils.isEmpty(deviceId)) { + keys!!["ed25519:$deviceId"] + } else null + + } + + /** + * @return the identity key + */ + fun identityKey(): String? { + return if (null != keys && !TextUtils.isEmpty(deviceId)) { + keys!!["curve25519:$deviceId"] + } else null + + } + + /** + * @return the display name + */ + fun displayName(): String? { + return if (null != unsigned) { + unsigned!!["device_display_name"] as String? + } else null + + } + + /** + * @return the signed data map + */ + fun signalableJSONDictionary(): Map { + val map = HashMap() + + map["device_id"] = deviceId + + if (null != userId) { + map["user_id"] = userId!! + } + + if (null != algorithms) { + map["algorithms"] = algorithms!! + } + + if (null != keys) { + map["keys"] = keys!! + } + + return map + } + + /** + * @return a dictionary of the parameters + */ + fun toDeviceKeys(): DeviceKeys { + return DeviceKeys( + userId = userId, + deviceId = deviceId, + algorithms = algorithms!!, + keys = keys!!, + signatures = signatures!! + ) + } + + override fun toString(): String { + return "MXDeviceInfo $userId:$deviceId" + } + + companion object { + // This device is a new device and the user was not warned it has been added. + const val DEVICE_VERIFICATION_UNKNOWN = -1 + + // The user has not yet verified this device. + const val DEVICE_VERIFICATION_UNVERIFIED = 0 + + // The user has verified this device. + const val DEVICE_VERIFICATION_VERIFIED = 1 + + // The user has blocked this device. + const val DEVICE_VERIFICATION_BLOCKED = 2 + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt new file mode 100755 index 00000000..7c0addf3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXEncryptEventContentResult.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +import im.vector.matrix.android.api.session.events.model.Content + +data class MXEncryptEventContentResult( + /** + * The event content + */ + val mEventContent: Content, + /** + * the event type + */ + val mEventType: String +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKey.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKey.java new file mode 100755 index 00000000..1a96f22c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXKey.java @@ -0,0 +1,139 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model; + +import android.text.TextUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import timber.log.Timber; + +public class MXKey implements Serializable { + /** + * Key types. + */ + public static final String KEY_CURVE_25519_TYPE = "curve25519"; + public static final String KEY_SIGNED_CURVE_25519_TYPE = "signed_curve25519"; + //public static final String KEY_ED_25519_TYPE = "ed25519"; + + /** + * The type of the key. + */ + public String type; + + /** + * The id of the key. + */ + public String keyId; + + /** + * The key. + */ + public String value; + + /** + * signature user Id to [deviceid][signature] + */ + public Map> signatures; + + /** + * Default constructor + */ + public MXKey() { + } + + /** + * Convert a map to a MXKey + * + * @param map the map to convert + */ + public MXKey(Map> map) { + if ((null != map) && (map.size() > 0)) { + List mapKeys = new ArrayList<>(map.keySet()); + + String firstEntry = mapKeys.get(0); + setKeyFullId(firstEntry); + + Map params = map.get(firstEntry); + value = (String) params.get("key"); + signatures = (Map>) params.get("signatures"); + } + } + + /** + * @return the key full id + */ + public String getKeyFullId() { + return type + ":" + keyId; + } + + /** + * Update the key fields with a key full id + * + * @param keyFullId the key full id + */ + private void setKeyFullId(String keyFullId) { + if (!TextUtils.isEmpty(keyFullId)) { + try { + String[] components = keyFullId.split(":"); + + if (components.length == 2) { + type = components[0]; + keyId = components[1]; + } + } catch (Exception e) { + Timber.e(e, "## setKeyFullId() failed"); + } + } + } + + /** + * @return the signed data map + */ + public Map signalableJSONDictionary() { + Map map = new HashMap<>(); + + if (null != value) { + map.put("key", value); + } + + return map; + } + + /** + * Returns a signature for an user Id and a signkey + * + * @param userId the user id + * @param signkey the sign key + * @return the signature + */ + public String signatureForUserId(String userId, String signkey) { + // sanity checks + if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(signkey)) { + if ((null != signatures) && signatures.containsKey(userId)) { + return signatures.get(userId).get(signkey); + } + } + + return null; + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession.java new file mode 100755 index 00000000..8d6f99e5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model; + +import org.matrix.olm.OlmInboundGroupSession; + +import java.io.Serializable; +import java.util.Map; + +import timber.log.Timber; + + +/** + * This class adds more context to a OLMInboundGroupSession object. + * This allows additional checks. The class implements NSCoding so that the context can be stored. + */ +public class MXOlmInboundGroupSession implements Serializable { + // + private static final String LOG_TAG = "OlmInboundGroupSession"; + + // The associated olm inbound group session. + public OlmInboundGroupSession mSession; + + // The room in which this session is used. + public String mRoomId; + + // The base64-encoded curve25519 key of the sender. + public String mSenderKey; + + // Other keys the sender claims. + public Map mKeysClaimed; + + /** + * Constructor + * + * @param sessionKey the session key + */ + public MXOlmInboundGroupSession(String sessionKey) { + try { + mSession = new OlmInboundGroupSession(sessionKey); + } catch (Exception e) { + Timber.e(e, "Cannot create"); + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession2.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession2.kt new file mode 100755 index 00000000..ab6f5669 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmInboundGroupSession2.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +import android.text.TextUtils +import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import im.vector.matrix.android.internal.crypto.MegolmSessionData +import org.matrix.olm.OlmInboundGroupSession +import timber.log.Timber +import java.util.* + +/** + * This class adds more context to a OLMInboundGroupSession object. + * This allows additional checks. The class implements NSCoding so that the context can be stored. + */ +class MXOlmInboundGroupSession2 { + + // The associated olm inbound group session. + var mSession: OlmInboundGroupSession? = null + + // The room in which this session is used. + var mRoomId: String? = null + + // The base64-encoded curve25519 key of the sender. + var mSenderKey: String? = null + + // Other keys the sender claims. + var mKeysClaimed: Map? = null + + // Devices which forwarded this session to us (normally empty). + var mForwardingCurve25519KeyChain: List? = ArrayList() + + /** + * @return the first known message index + */ + val firstKnownIndex: Long? + get() { + if (null != mSession) { + try { + return mSession!!.firstKnownIndex + } catch (e: Exception) { + Timber.e(e, "## getFirstKnownIndex() : getFirstKnownIndex failed") + } + + } + + return null + } + + /** + * Constructor + * + * @param prevFormatSession the previous session format + */ + constructor(prevFormatSession: MXOlmInboundGroupSession) { + mSession = prevFormatSession.mSession + mRoomId = prevFormatSession.mRoomId + mSenderKey = prevFormatSession.mSenderKey + mKeysClaimed = prevFormatSession.mKeysClaimed + } + + /** + * Constructor + * + * @param sessionKey the session key + * @param isImported true if it is an imported session key + */ + constructor(sessionKey: String, isImported: Boolean) { + try { + if (!isImported) { + mSession = OlmInboundGroupSession(sessionKey) + } else { + mSession = OlmInboundGroupSession.importSession(sessionKey) + } + } catch (e: Exception) { + Timber.e(e, "Cannot create") + } + + } + + /** + * Create a new instance from the provided keys map. + * + * @param megolmSessionData the megolm session data + * @throws Exception if the data are invalid + */ + @Throws(Exception::class) + constructor(megolmSessionData: MegolmSessionData) { + try { + mSession = OlmInboundGroupSession.importSession(megolmSessionData.sessionKey!!) + + if (!TextUtils.equals(mSession!!.sessionIdentifier(), megolmSessionData.sessionId)) { + throw Exception("Mismatched group session Id") + } + + mSenderKey = megolmSessionData.senderKey + mKeysClaimed = megolmSessionData.senderClaimedKeys + mRoomId = megolmSessionData.roomId + } catch (e: Exception) { + throw Exception(e.message) + } + } + + /** + * Export the inbound group session keys + * + * @return the inbound group session as MegolmSessionData if the operation succeeds + */ + fun exportKeys(): MegolmSessionData? { + var megolmSessionData: MegolmSessionData? = MegolmSessionData() + + try { + if (null == mForwardingCurve25519KeyChain) { + mForwardingCurve25519KeyChain = ArrayList() + } + + megolmSessionData!!.senderClaimedEd25519Key = mKeysClaimed!!["ed25519"] + megolmSessionData.forwardingCurve25519KeyChain = ArrayList(mForwardingCurve25519KeyChain!!) + megolmSessionData.senderKey = mSenderKey + megolmSessionData.senderClaimedKeys = mKeysClaimed + megolmSessionData.roomId = mRoomId + megolmSessionData.sessionId = mSession!!.sessionIdentifier() + megolmSessionData.sessionKey = mSession!!.export(mSession!!.firstKnownIndex) + megolmSessionData.algorithm = MXCRYPTO_ALGORITHM_MEGOLM + } catch (e: Exception) { + megolmSessionData = null + Timber.e(e, "## export() : senderKey " + mSenderKey + " failed") + } + + return megolmSessionData + } + + /** + * Export the session for a message index. + * + * @param messageIndex the message index + * @return the exported data + */ + fun exportSession(messageIndex: Long): String? { + if (null != mSession) { + try { + return mSession!!.export(messageIndex) + } catch (e: Exception) { + Timber.e(e, "## exportSession() : export failed") + } + + } + + return null + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSession.kt new file mode 100644 index 00000000..c9af9c2c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSession.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +import org.matrix.olm.OlmSession + +/** + * Encapsulate a OlmSession and a last received message Timestamp + */ +data class MXOlmSession( + // The associated olm session. + val olmSession: OlmSession, + // Timestamp at which the session last received a message. + var lastReceivedMessageTs: Long = 0) { + + /** + * Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs` + */ + fun onMessageReceived() { + lastReceivedMessageTs = System.currentTimeMillis() + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.java new file mode 100755 index 00000000..1beb65c4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXOlmSessionResult.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model; + +import java.io.Serializable; + +public class MXOlmSessionResult implements Serializable { + /** + * the device + */ + public final MXDeviceInfo mDevice; + + /** + * Base64 olm session id. + * null if no session could be established. + */ + public String mSessionId; + + /** + * Constructor + * + * @param device the device + * @param sessionId the olm session id + */ + public MXOlmSessionResult(MXDeviceInfo device, String sessionId) { + mDevice = device; + mSessionId = sessionId; + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXQueuedEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXQueuedEncryption.kt new file mode 100755 index 00000000..b41f3b24 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXQueuedEncryption.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.events.model.Content + +class MXQueuedEncryption { + + /** + * The data to encrypt. + */ + var mEventContent: Content? = null + var mEventType: String? = null + + /** + * the asynchronous callback + */ + var mApiCallback: MatrixCallback? = null +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXUsersDevicesMap.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXUsersDevicesMap.java new file mode 100755 index 00000000..49d46e6c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXUsersDevicesMap.java @@ -0,0 +1,186 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model; + +import android.text.TextUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MXUsersDevicesMap implements Serializable { + + // The device keys as returned by the homeserver: a map of a map (userId -> deviceId -> Object). + private final Map> mMap = new HashMap<>(); + + /** + * @return the inner map + */ + public Map> getMap() { + return mMap; + } + + /** + * Default constructor constructor + */ + public MXUsersDevicesMap() { + } + + /** + * The constructor + * + * @param map the map + */ + public MXUsersDevicesMap(Map> map) { + if (null != map) { + Set keys = map.keySet(); + + for (String key : keys) { + mMap.put(key, new HashMap<>(map.get(key))); + } + } + } + + /** + * @return a deep copy + */ + public MXUsersDevicesMap deepCopy() { + MXUsersDevicesMap copy = new MXUsersDevicesMap<>(); + + Set keys = mMap.keySet(); + + for (String key : keys) { + copy.mMap.put(key, new HashMap<>(mMap.get(key))); + } + + return copy; + } + + /** + * @return the user Ids + */ + public List getUserIds() { + return new ArrayList<>(mMap.keySet()); + } + + /** + * Provides the device ids list for an user id + * + * @param userId the user id + * @return the device ids list + */ + public List getUserDeviceIds(String userId) { + if (!TextUtils.isEmpty(userId) && mMap.containsKey(userId)) { + return new ArrayList<>(mMap.get(userId).keySet()); + } + + return null; + } + + /** + * Provides the object for a device id and an user Id + * + * @param deviceId the device id + * @param userId the object id + * @return the object + */ + public E getObject(String deviceId, String userId) { + if (!TextUtils.isEmpty(userId) && mMap.containsKey(userId) && !TextUtils.isEmpty(deviceId)) { + return mMap.get(userId).get(deviceId); + } + + return null; + } + + /** + * Set an object for a dedicated user Id and device Id + * + * @param object the object to set + * @param userId the user Id + * @param deviceId the device id + */ + public void setObject(E object, String userId, String deviceId) { + if ((null != object) && !TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) { + Map subMap = mMap.get(userId); + + if (null == subMap) { + subMap = new HashMap<>(); + mMap.put(userId, subMap); + } + + subMap.put(deviceId, object); + } + } + + /** + * Defines the objects map for an user Id + * + * @param objectsPerDevices the objects maps + * @param userId the user id + */ + public void setObjects(Map objectsPerDevices, String userId) { + if (!TextUtils.isEmpty(userId)) { + if (null == objectsPerDevices) { + mMap.remove(userId); + } else { + mMap.put(userId, new HashMap<>(objectsPerDevices)); + } + } + } + + /** + * Removes objects for a dedicated user + * + * @param userId the user id. + */ + public void removeUserObjects(String userId) { + if (!TextUtils.isEmpty(userId)) { + mMap.remove(userId); + } + } + + /** + * Clear the internal dictionary + */ + public void removeAllObjects() { + mMap.clear(); + } + + /** + * Add entries from another MXUsersDevicesMap + * + * @param other the other one + */ + public void addEntriesFromMap(MXUsersDevicesMap other) { + if (null != other) { + mMap.putAll(other.getMap()); + } + } + + @Override + public String toString() { + if (null != mMap) { + return "MXUsersDevicesMap " + mMap.toString(); + } else { + return "MXDeviceInfo : null map"; + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptedEventContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptedEventContent.kt new file mode 100644 index 00000000..fc574eb1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptedEventContent.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing an encrypted event content + */ +@JsonClass(generateAdapter = true) +data class EncryptedEventContent( + + /** + * the used algorithm + */ + @Json(name = "algorithm") + var algorithm: String? = null, + + /** + * The encrypted event + */ + @Json(name = "ciphertext") + var ciphertext: String? = null, + + /** + * The device id + */ + @Json(name = "device_id") + var deviceId: String? = null, + + /** + * the sender key + */ + @Json(name = "sender_key") + var senderKey: String? = null, + + /** + * The session id + */ + @Json(name = "session_id") + var sessionId: String? = null +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt new file mode 100644 index 00000000..22df41e9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing an encrypted event content + */ +@JsonClass(generateAdapter = true) +data class EncryptionEventContent( + + /** + * Required. The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'. + */ + @Json(name = "algorithm") + var algorithm: String, + + /** + * How long the session should be used before changing it. 604800000 (a week) is the recommended default. + */ + @Json(name = "rotation_period_ms") + var rotationPeriodMs: Long? = null, + + /** + * How many messages should be sent before changing the session. 100 is the recommended default. + */ + @Json(name = "rotation_period_msgs") + var rotationPeriodMsgs: Long? = null +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt new file mode 100644 index 00000000..a6777a4f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class NewDeviceContent( + + // the device id + @Json(name = "device_id") + var deviceId: String? = null, + + // the room ids list + @Json(name = "rooms") + var rooms: List? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt new file mode 100644 index 00000000..7ac0b075 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing an encrypted event content + */ +@JsonClass(generateAdapter = true) +data class OlmEventContent( + /** + * + */ + @Json(name = "ciphertext") + var ciphertext: Map? = null, + + /** + * the sender key + */ + @Json(name = "sender_key") + var senderKey: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmPayloadContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmPayloadContent.kt new file mode 100644 index 00000000..e97ff8f6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmPayloadContent.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.di.MoshiProvider + +/** + * Class representing the OLM payload content + */ +@JsonClass(generateAdapter = true) +data class OlmPayloadContent( + /** + * The room id + */ + var room_id: String? = null, + + /** + * The sender + */ + var sender: String? = null, + + /** + * The recipient + */ + var recipient: String? = null, + + /** + * the recipient keys + */ + var recipient_keys: Map? = null, + + /** + * The keys + */ + var keys: Map? = null +) { + fun toJsonString(): String { + return MoshiProvider.providesMoshi().adapter(OlmPayloadContent::class.java).toJson(this) + } + + companion object { + fun fromJsonString(str: String): OlmPayloadContent { + return MoshiProvider.providesMoshi().adapter(OlmPayloadContent::class.java).fromJson(str)!! + } + } +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/RoomKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/RoomKeyContent.kt new file mode 100644 index 00000000..cafcb14d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/RoomKeyContent.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.event + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing an sharekey content + */ +@JsonClass(generateAdapter = true) +data class RoomKeyContent( + + @Json(name = "algorithm") + var algorithm: String? = null, + + @Json(name = "room_id") + var roomId: String? = null, + + @Json(name = "session_id") + var sessionId: String? = null, + + @Json(name = "session_key") + var sessionKey: String? = null, + + // should be a Long but it is sometimes a double + @Json(name = "chain_index") + var chainIndex: Any? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceAuth.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceAuth.kt new file mode 100644 index 00000000..f50fbaba --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceAuth.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class provides the + */ +@JsonClass(generateAdapter = true) +data class DeleteDeviceAuth( + + // device device session id + @Json(name = "session") + var session: String? = null, + + // registration information + @Json(name = "type") + var type: String? = null, + + var user: String? = null, + var password: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceParams.kt new file mode 100644 index 00000000..ba3ed903 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeleteDeviceParams.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.JsonClass + +/** + * This class provides the parameter to delete a device + */ +@JsonClass(generateAdapter = true) +data class DeleteDeviceParams( + var auth: DeleteDeviceAuth? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt new file mode 100644 index 00000000..c43e1287 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2014 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.interfaces.DatedObject + +/** + * This class describes the device information + */ +@JsonClass(generateAdapter = true) +data class DeviceInfo( + /** + * The owner user id (not documented and useless but the homeserver sent it. You should not need it) + */ + @Json(name = "user_id") + var user_id: String? = null, + + /** + * The device id + */ + @Json(name = "device_id") + var deviceId: String? = null, + + /** + * The device display name + */ + @Json(name = "display_name") + var displayName: String? = null, + + /** + * The last time this device has been seen. + */ + @Json(name = "last_seen_ts") + var lastSeenTs: Long = 0, + + /** + * The last ip address + */ + @Json(name = "last_seen_ip") + var lastSeenIp: String? = null +) : DatedObject { + + override val date: Long + get() = lastSeenTs + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt new file mode 100644 index 00000000..3486bfda --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceKeys.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class DeviceKeys( + @Json(name = "user_id") + val userId: String, + + @Json(name = "device_id") + val deviceId: String, + + @Json(name = "algorithms") + val algorithms: List, + + @Json(name = "keys") + val keys: Map, + + @Json(name = "signatures") + val signatures: JsonDict +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt new file mode 100644 index 00000000..9b50b486 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2014 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class describes the response to https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-devices + */ +@JsonClass(generateAdapter = true) +data class DevicesListResponse( + @Json(name = "devices") + var devices: List? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedBodyFileInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedBodyFileInfo.kt new file mode 100644 index 00000000..a83001fe --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedBodyFileInfo.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import org.matrix.olm.OlmPkMessage + +/** + * Build from a OlmPkMessage object + * + * @param olmPkMessage OlmPkMessage + */ +class EncryptedBodyFileInfo(olmPkMessage: OlmPkMessage) { + var ciphertext = olmPkMessage.mCipherText + var mac = olmPkMessage.mMac + var ephemeral = olmPkMessage.mEphemeralKey +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt new file mode 100644 index 00000000..3e0d912f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EncryptedFileInfo( + var url: String? = null, + var mimetype: String, + var key: EncryptedFileKey? = null, + var iv: String, + var hashes: Map, + var v: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt new file mode 100644 index 00000000..433a3619 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EncryptedFileKey( + var alg: String, + var ext: Boolean? = null, + var key_ops: List, + var kty: String, + var k: String +) + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt new file mode 100644 index 00000000..bd070e31 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EncryptedMessage( + var algorithm: String? = null, + + @Json(name = "sender_key") + var senderKey: String? = null, + + @Json(name = "ciphertext") + var cipherText: Map? = null +) : SendToDeviceObject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/ForwardedRoomKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/ForwardedRoomKeyContent.kt new file mode 100644 index 00000000..a135c2a7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/ForwardedRoomKeyContent.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing the forward room key request body content + */ +@JsonClass(generateAdapter = true) +data class ForwardedRoomKeyContent( + + @Json(name = "algorithm") + var algorithm: String? = null, + + @Json(name = "room_id") + var roomId: String? = null, + + @Json(name = "sender_key") + var senderKey: String? = null, + + @Json(name = "session_id") + var sessionId: String? = null, + + @Json(name = "session_key") + var sessionKey: String? = null, + + @Json(name = "forwarding_curve25519_key_chain") + var forwardingCurve25519KeyChain: List? = null, + + @Json(name = "sender_claimed_ed25519_key") + var senderClaimedEd25519Key: String? = null +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt new file mode 100644 index 00000000..5a48f547 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class describes the key changes response + */ +@JsonClass(generateAdapter = true) +data class KeyChangesResponse( + // list of user ids which have new devices + @Json(name = "changed") + var changed: List? = null, + + // List of user ids who are no more tracked. + @Json(name = "left") + var left: List? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationAccept.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationAccept.kt new file mode 100644 index 00000000..7be6f204 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationAccept.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import timber.log.Timber + +/** + * Sent by Bob to accept a verification from a previously sent m.key.verification.start message. + */ +@JsonClass(generateAdapter = true) +data class KeyVerificationAccept( + + /** + * string to identify the transaction. + * This string must be unique for the pair of users performing verification for the duration that the transaction is valid. + * Alice’s device should record this ID and use it in future messages in this transaction. + */ + @Json(name = "transaction_id") + var transactionID: String? = null, + + /** + * The key agreement protocol that Bob’s device has selected to use, out of the list proposed by Alice’s device + */ + @Json(name = "key_agreement_protocol") + var keyAgreementProtocol: String? = null, + + /** + * The hash algorithm that Bob’s device has selected to use, out of the list proposed by Alice’s device + */ + var hash: String? = null, + + /** + * The message authentication code that Bob’s device has selected to use, out of the list proposed by Alice’s device + */ + @Json(name = "message_authentication_code") + var messageAuthenticationCode: String? = null, + + /** + * An array of short authentication string methods that Bob’s client (and Bob) understands. Must be a subset of the list proposed by Alice’s device + */ + @Json(name = "short_authentication_string") + var shortAuthenticationStrings: List? = null, + + /** + * The hash (encoded as unpadded base64) of the concatenation of the device’s ephemeral public key (QB, encoded as unpadded base64) + * and the canonical JSON representation of the m.key.verification.start message. + */ + var commitment: String? = null +) : SendToDeviceObject { + + fun isValid(): Boolean { + if (transactionID.isNullOrBlank() + || keyAgreementProtocol.isNullOrBlank() + || hash.isNullOrBlank() + || commitment.isNullOrBlank() + || messageAuthenticationCode.isNullOrBlank() + || shortAuthenticationStrings.isNullOrEmpty()) { + Timber.e("## received invalid verification request") + return false + } + return true + } + + companion object { + fun create(tid: String, + keyAgreementProtocol: String, + hash: String, + commitment: String, + messageAuthenticationCode: String, + shortAuthenticationStrings: List): KeyVerificationAccept { + return KeyVerificationAccept().apply { + this.transactionID = tid + this.keyAgreementProtocol = keyAgreementProtocol + this.hash = hash + this.commitment = commitment + this.messageAuthenticationCode = messageAuthenticationCode + this.shortAuthenticationStrings = shortAuthenticationStrings + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationCancel.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationCancel.kt new file mode 100644 index 00000000..1d4a9b34 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationCancel.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.crypto.sas.CancelCode + +/** + * To device event sent by either party to cancel a key verification. + */ +@JsonClass(generateAdapter = true) +data class KeyVerificationCancel( + /** + * the transaction ID of the verification to cancel + */ + @Json(name = "transaction_id") + var transactionID: String? = null, + + /** + * machine-readable reason for cancelling, see #CancelCode + */ + var code: String? = null, + + /** + * human-readable reason for cancelling. This should only be used if the receiving client does not understand the code given. + */ + var reason: String? = null +) : SendToDeviceObject { + + companion object { + fun create(tid: String, cancelCode: CancelCode): KeyVerificationCancel { + return KeyVerificationCancel().apply { + this.transactionID = tid + this.code = cancelCode.value + this.reason = cancelCode.humanReadable + } + } + } + + fun isValid(): Boolean { + if (transactionID.isNullOrBlank() || code.isNullOrBlank()) { + return false + } + return true + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationKey.kt new file mode 100644 index 00000000..b5e33232 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationKey.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + + +/** + * Sent by both devices to send their ephemeral Curve25519 public key to the other device. + */ +@JsonClass(generateAdapter = true) +data class KeyVerificationKey( + /** + * the ID of the transaction that the message is part of + */ + @Json(name = "transaction_id") + @JvmField + var transactionID: String? = null, + + /** + * The device’s ephemeral public key, as an unpadded base64 string + */ + @JvmField + var key: String? = null + +) : SendToDeviceObject { + + companion object { + fun create(tid: String, key: String): KeyVerificationKey { + return KeyVerificationKey().apply { + this.transactionID = tid + this.key = key + } + } + } + + fun isValid(): Boolean { + if (transactionID.isNullOrBlank() || key.isNullOrBlank()) { + return false + } + return true + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationMac.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationMac.kt new file mode 100644 index 00000000..19dbbe5d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationMac.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Sent by both devices to send the MAC of their device key to the other device. + */ +@JsonClass(generateAdapter = true) +data class KeyVerificationMac( + /** + * the ID of the transaction that the message is part of + */ + @Json(name = "transaction_id") + var transactionID: String? = null, + + /** + * A map of key ID to the MAC of the key, as an unpadded base64 string, calculated using the MAC key + */ + @JvmField + var mac: Map? = null, + + /** + * The MAC of the comma-separated, sorted list of key IDs given in the mac property, + * as an unpadded base64 string, calculated using the MAC key. + * For example, if the mac property gives MACs for the keys ed25519:ABCDEFG and ed25519:HIJKLMN, then this property will + * give the MAC of the string “ed25519:ABCDEFG,ed25519:HIJKLMN”. + */ + @JvmField + var keys: String? = null + +) : SendToDeviceObject { + + fun isValid(): Boolean { + if (transactionID.isNullOrBlank() || keys.isNullOrBlank() || mac.isNullOrEmpty()) { + return false + } + return true + } + + companion object { + fun create(tid: String, mac: Map, keys: String): KeyVerificationMac { + return KeyVerificationMac().apply { + this.transactionID = tid + this.mac = mac + this.keys = keys + } + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt new file mode 100644 index 00000000..3c621ec6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import im.vector.matrix.android.api.session.crypto.sas.SasMode +import im.vector.matrix.android.internal.crypto.verification.SASVerificationTransaction +import timber.log.Timber + +/** + * Sent by Alice to initiate an interactive key verification. + */ +class KeyVerificationStart : SendToDeviceObject { + + /** + * Alice’s device ID + */ + @Json(name = "from_device") + var fromDevice: String? = null + + var method: String? = null + + /** + * String to identify the transaction. + * This string must be unique for the pair of users performing verification for the duration that the transaction is valid. + * Alice’s device should record this ID and use it in future messages in this transaction. + */ + @Json(name = "transaction_id") + var transactionID: String? = null + + /** + * An array of key agreement protocols that Alice’s client understands. + * Must include “curve25519”. + * Other methods may be defined in the future + */ + @Json(name = "key_agreement_protocols") + var keyAgreementProtocols: List? = null + + /** + * An array of hashes that Alice’s client understands. + * Must include “sha256”. Other methods may be defined in the future. + */ + var hashes: List? = null + + /** + * An array of message authentication codes that Alice’s client understands. + * Must include “hkdf-hmac-sha256”. + * Other methods may be defined in the future. + */ + @Json(name = "message_authentication_codes") + var messageAuthenticationCodes: List? = null + + /** + * An array of short authentication string methods that Alice’s client (and Alice) understands. + * Must include “decimal”. + * This document also describes the “emoji” method. + * Other methods may be defined in the future + */ + @Json(name = "short_authentication_string") + var shortAuthenticationStrings: List? = null + + + companion object { + const val VERIF_METHOD_SAS = "m.sas.v1" + } + + fun isValid(): Boolean { + if (transactionID.isNullOrBlank() + || fromDevice.isNullOrBlank() + || method != VERIF_METHOD_SAS + || keyAgreementProtocols.isNullOrEmpty() + || hashes.isNullOrEmpty() + || hashes?.contains("sha256") == false + || messageAuthenticationCodes.isNullOrEmpty() + || (messageAuthenticationCodes?.contains(SASVerificationTransaction.SAS_MAC_SHA256) == false + && messageAuthenticationCodes?.contains(SASVerificationTransaction.SAS_MAC_SHA256_LONGKDF) == false) + || shortAuthenticationStrings.isNullOrEmpty() + || shortAuthenticationStrings?.contains(SasMode.DECIMAL) == false) { + Timber.e("## received invalid verification request") + return false + } + return true + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt new file mode 100644 index 00000000..f92c5afd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the response to /keys/query request made by claimOneTimeKeysForUsersDevices. + */ +@JsonClass(generateAdapter = true) +data class KeysClaimBody( + + @Json(name = "timeout") + var timeout: Int? = null, + + /** + * Required. The keys to be claimed. A map from user ID, to a map from device ID to algorithm name. + */ + @Json(name = "one_time_keys") + var oneTimeKeys: Map> +) + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt new file mode 100644 index 00000000..c1fbfb1c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the response to /keys/query request made by claimOneTimeKeysForUsersDevices. + */ +@JsonClass(generateAdapter = true) +data class KeysClaimResponse( + + /** + * The requested keys ordered by device by user. + * TODO Type does not match spec, should be Map + */ + @Json(name = "one_time_keys") + var oneTimeKeys: Map>>>? = null +) + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt new file mode 100644 index 00000000..769a28f7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the body to /keys/query + */ +@JsonClass(generateAdapter = true) +data class KeysQueryBody( + + /** + * The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default. + */ + @Json(name = "timeout") + var timeout: Int? = null, + + /** + * Required. The keys to be downloaded. + * A map from user ID, to a list of device IDs, or to an empty list to indicate all devices for the corresponding user. + */ + @Json(name = "device_keys") + val deviceKeys: Map, + + /** + * If the client is fetching keys as a result of a device update received in a sync request, this should be the 'since' token + * of that sync request, or any later sync token. This allows the server to ensure its response contains the keys advertised + * by the notification in that sync. + */ + @Json(name = "token") + var token: String? = null + +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt new file mode 100644 index 00000000..d13bf995 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryResponse.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo + +/** + * This class represents the response to /keys/query request made by downloadKeysForUsers + */ +@JsonClass(generateAdapter = true) +data class KeysQueryResponse( + /** + * The device keys per devices per users. + * Map from userId to map from deviceId to MXDeviceInfo + * TODO Use MXUsersDevicesMap? + */ + @Json(name = "device_keys") + var deviceKeys: Map>? = null, + + /** + * The failures sorted by homeservers. TODO Bad comment ? + * TODO Use MXUsersDevicesMap? + */ + var failures: Map>? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt new file mode 100644 index 00000000..f67cbd47 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadBody.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class KeysUploadBody( + @Json(name = "device_keys") + var deviceKeys: DeviceKeys? = null, + + @Json(name = "one_time_keys") + var oneTimeKeys: JsonDict? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt new file mode 100644 index 00000000..72e148ba --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This class represents the response to /keys/upload request made by uploadKeys. + */ +@JsonClass(generateAdapter = true) +data class KeysUploadResponse( + + /** + * The count per algorithm as returned by the home server: a map (algorithm to count). + */ + @Json(name = "one_time_key_counts") + var oneTimeKeyCounts: Map? = null + +) { + /** + * Helper methods to extract information from 'oneTimeKeyCounts' + * + * @param algorithm the expected algorithm + * @return the time key counts + */ + fun oneTimeKeyCountsForAlgorithm(algorithm: String): Int { + return oneTimeKeyCounts?.get(algorithm) ?: 0 + } + + /** + * Tells if there is a oneTimeKeys for a dedicated algorithm. + * + * @param algorithm the algorithm + * @return true if it is found + */ + fun hasOneTimeKeyCountsForAlgorithm(algorithm: String): Boolean { + return oneTimeKeyCounts?.containsKey(algorithm) == true + } +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt new file mode 100644 index 00000000..4dc0d1d0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Class representing an room key request body content + */ +@JsonClass(generateAdapter = true) +data class RoomKeyRequestBody( + var algorithm: String? = null, + + @Json(name = "room_id") + var roomId: String? = null, + + @Json(name = "sender_key") + var senderKey: String? = null, + + @Json(name = "session_id") + var sessionId: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt new file mode 100644 index 00000000..5d7d49f5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json + +/** + * Parent class representing an room key action request + * Note: this class cannot be abstract because of [org.matrix.androidsdk.core.JsonUtils.toRoomKeyShare] + */ +open class RoomKeyShare : SendToDeviceObject { + + var action: String? = null + + @Json(name = "requesting_device_id") + var requestingDeviceId: String? = null + + @Json(name = "request_id") + var requestId: String? = null + + companion object { + const val ACTION_SHARE_REQUEST = "request" + const val ACTION_SHARE_CANCELLATION = "request_cancellation" + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt new file mode 100644 index 00000000..d763c554 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.JsonClass + +/** + * Class representing an room key request cancellation content + */ +@JsonClass(generateAdapter = true) +class RoomKeyShareCancellation : RoomKeyShare() { + init { + action = ACTION_SHARE_CANCELLATION + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt new file mode 100644 index 00000000..43743258 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.JsonClass + +/** + * Class representing an room key request content + */ +@JsonClass(generateAdapter = true) +class RoomKeyShareRequest : RoomKeyShare() { + + var body: RoomKeyRequestBody? = null + + init { + action = ACTION_SHARE_REQUEST + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceBody.kt new file mode 100644 index 00000000..cf1166df --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceBody.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +class SendToDeviceBody { + + // `Any` should implement SendToDeviceObject, but we cannot use interface here because of Gson serialization + /** + * The messages to send. A map from user ID, to a map from device ID to message body. + * The device ID may also be *, meaning all known devices for the user. + */ + var messages: Map>? = null +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceObject.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceObject.kt new file mode 100644 index 00000000..e69debbd --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/SendToDeviceObject.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +interface SendToDeviceObject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt new file mode 100644 index 00000000..3a7da69d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class UpdateDeviceInfoBody( + + /** + * The new display name for this device. If not given, the display name is unchanged. + */ + @Json(name = "display_name") + var displayName: String? = null + +) + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt new file mode 100644 index 00000000..d08ba7d9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt @@ -0,0 +1,375 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store + +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 +import im.vector.matrix.android.internal.crypto.model.MXOlmSession +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import org.matrix.olm.OlmAccount + +/** + * the crypto data store + */ +internal interface IMXCryptoStore { + + /** + * @return the device id + */ + fun getDeviceId(): String + + /** + * @return the olm account + */ + fun getAccount(): OlmAccount? + + /** + * Retrieve the known inbound group sessions. + * + * @return the list of all known group sessions, to export them. + */ + fun getInboundGroupSessions(): List + + /** + * @return true to unilaterally blacklist all unverified devices. + */ + fun getGlobalBlacklistUnverifiedDevices(): Boolean + + /** + * Set the global override for whether the client should ever send encrypted + * messages to unverified devices. + * If false, it can still be overridden per-room. + * If true, it overrides the per-room settings. + * + * @param block true to unilaterally blacklist all + */ + fun setGlobalBlacklistUnverifiedDevices(block: Boolean) + + /** + * Provides the rooms ids list in which the messages are not encrypted for the unverified devices. + * + * @return the room Ids list + */ + fun getRoomsListBlacklistUnverifiedDevices(): List + + /** + * Updates the rooms ids list in which the messages are not encrypted for the unverified devices. + * + * @param roomIds the room ids list + */ + fun setRoomsListBlacklistUnverifiedDevices(roomIds: List) + + /** + * Get the current keys backup version + */ + fun getKeyBackupVersion(): String? + + /** + * Set the current keys backup version + * + * @param keyBackupVersion the keys backup version or null to delete it + */ + fun setKeyBackupVersion(keyBackupVersion: String?) + + /** + * Get the current keys backup local data + */ + fun getKeysBackupData(): KeysBackupDataEntity? + + /** + * Set the keys backup local data + * + * @param keysBackupData the keys backup local data, or null to erase data + */ + fun setKeysBackupData(keysBackupData: KeysBackupDataEntity?) + + /** + * @return the devices statuses map (userId -> tracking status) + */ + fun getDeviceTrackingStatuses(): Map + + /** + * @return the pending IncomingRoomKeyRequest requests + */ + fun getPendingIncomingRoomKeyRequests(): List + + /** + * Indicate if the store contains data for the passed account. + * + * @return true means that the user enabled the crypto in a previous session + */ + fun hasData(): Boolean + + /** + * Delete the crypto store for the passed credentials. + */ + fun deleteStore() + + /** + * open any existing crypto store + */ + fun open() + + /** + * Close the store + */ + fun close() + + /** + * Store the device id. + * + * @param deviceId the device id + */ + fun storeDeviceId(deviceId: String) + + /** + * Store the end to end account for the logged-in user. + * + * @param account the account to save + */ + fun storeAccount(account: OlmAccount) + + /** + * Store a device for a user. + * + * @param userId the user's id. + * @param device the device to store. + */ + fun storeUserDevice(userId: String?, device: MXDeviceInfo?) + + /** + * Retrieve a device for a user. + * + * @param deviceId the device id. + * @param userId the user's id. + * @return the device + */ + fun getUserDevice(deviceId: String, userId: String): MXDeviceInfo? + + /** + * Retrieve a device by its identity key. + * + * @param identityKey the device identity key (`MXDeviceInfo.identityKey`) + * @return the device or null if not found + */ + fun deviceWithIdentityKey(identityKey: String): MXDeviceInfo? + + /** + * Store the known devices for a user. + * + * @param userId The user's id. + * @param devices A map from device id to 'MXDevice' object for the device. + */ + fun storeUserDevices(userId: String, devices: Map) + + /** + * Retrieve the known devices for a user. + * + * @param userId The user's id. + * @return The devices map if some devices are known, else null + */ + fun getUserDevices(userId: String): Map? + + /** + * Store the crypto algorithm for a room. + * + * @param roomId the id of the room. + * @param algorithm the algorithm. + */ + fun storeRoomAlgorithm(roomId: String, algorithm: String) + + /** + * Provides the algorithm used in a dedicated room. + * + * @param roomId the room id + * @return the algorithm, null is the room is not encrypted + */ + fun getRoomAlgorithm(roomId: String): String? + + /** + * Store a session between the logged-in user and another device. + * + * @param session the end-to-end session. + * @param deviceKey the public key of the other device. + */ + fun storeSession(session: MXOlmSession, deviceKey: String) + + /** + * Retrieve the end-to-end session ids between the logged-in user and another + * device. + * + * @param deviceKey the public key of the other device. + * @return A set of sessionId, or null if device is not known + */ + fun getDeviceSessionIds(deviceKey: String): Set? + + /** + * Retrieve an end-to-end session between the logged-in user and another + * device. + * + * @param sessionId the session Id. + * @param deviceKey the public key of the other device. + * @return The Base64 end-to-end session, or null if not found + */ + fun getDeviceSession(sessionId: String?, deviceKey: String?): MXOlmSession? + + /** + * Retrieve the last used sessionId, regarding `lastReceivedMessageTs`, or null if no session exist + * + * @param deviceKey the public key of the other device. + * @return last used sessionId, or null if not found + */ + fun getLastUsedSessionId(deviceKey: String): String? + + /** + * Store inbound group sessions. + * + * @param sessions the inbound group sessions to store. + */ + fun storeInboundGroupSessions(sessions: List) + + /** + * Retrieve an inbound group session. + * + * @param sessionId the session identifier. + * @param senderKey the base64-encoded curve25519 key of the sender. + * @return an inbound group session. + */ + fun getInboundGroupSession(sessionId: String, senderKey: String): MXOlmInboundGroupSession2? + + /** + * Remove an inbound group session + * + * @param sessionId the session identifier. + * @param senderKey the base64-encoded curve25519 key of the sender. + */ + fun removeInboundGroupSession(sessionId: String, senderKey: String) + + /* ========================================================================================== + * Keys backup + * ========================================================================================== */ + + /** + * Mark all inbound group sessions as not backed up. + */ + fun resetBackupMarkers() + + /** + * Mark inbound group sessions as backed up on the user homeserver. + * + * @param sessions the sessions + */ + fun markBackupDoneForInboundGroupSessions(sessions: List) + + /** + * Retrieve inbound group sessions that are not yet backed up. + * + * @param limit the maximum number of sessions to return. + * @return an array of non backed up inbound group sessions. + */ + fun inboundGroupSessionsToBackup(limit: Int): List + + /** + * Number of stored inbound group sessions. + * + * @param onlyBackedUp if true, count only session marked as backed up. + * @return a count. + */ + fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int + + /** + * Save the device statuses + * + * @param deviceTrackingStatuses the device tracking statuses + */ + fun saveDeviceTrackingStatuses(deviceTrackingStatuses: Map) + + /** + * Get the tracking status of a specified userId devices. + * + * @param userId the user id + * @param defaultValue the default value + * @return the tracking status + */ + fun getDeviceTrackingStatus(userId: String, defaultValue: Int): Int + + /** + * Look for an existing outgoing room key request, and if none is found, return null + * + * @param requestBody the request body + * @return an OutgoingRoomKeyRequest instance or null + */ + fun getOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody): OutgoingRoomKeyRequest? + + /** + * Look for an existing outgoing room key request, and if none is found, add a new one. + * + * @param request the request + * @return either the same instance as passed in, or the existing one. + */ + fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest? + + /** + * Look for room key requests by state. + * + * @param states the states + * @return an OutgoingRoomKeyRequest or null + */ + fun getOutgoingRoomKeyRequestByState(states: Set): OutgoingRoomKeyRequest? + + /** + * Update an existing outgoing request. + * + * @param request the request + */ + fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) + + /** + * Delete an outgoing room key request. + * + * @param transactionId the transaction id. + */ + fun deleteOutgoingRoomKeyRequest(transactionId: String) + + /** + * Store an incomingRoomKeyRequest instance + * + * @param incomingRoomKeyRequest the incoming key request + */ + fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?) + + /** + * Delete an incomingRoomKeyRequest instance + * + * @param incomingRoomKeyRequest the incoming key request + */ + fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) + + /** + * Search an IncomingRoomKeyRequest + * + * @param userId the user id + * @param deviceId the device id + * @param requestId the request id + * @return an IncomingRoomKeyRequest if it exists, else null + */ + fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest? +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt new file mode 100644 index 00000000..cbf941f1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db + +import android.util.Base64 +import im.vector.matrix.android.internal.util.CompatUtil +import io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.RealmObject +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import java.security.MessageDigest +import java.util.zip.GZIPInputStream + + +/** + * Compute a Hash of a String, using md5 algorithm + */ +fun String.hash() = try { + val digest = MessageDigest.getInstance("md5") + digest.update(toByteArray()) + val bytes = digest.digest() + val sb = StringBuilder() + for (i in bytes.indices) { + sb.append(String.format("%02X", bytes[i])) + } + sb.toString().toLowerCase() +} catch (exc: Exception) { + // Should not happen, but just in case + hashCode().toString() +} + +/** + * Get realm, invoke the action, close realm, and return the result of the action + */ +fun doWithRealm(realmConfiguration: RealmConfiguration, action: (Realm) -> T): T { + val realm = Realm.getInstance(realmConfiguration) + val result = action.invoke(realm) + realm.close() + return result +} + +/** + * Get realm, do the query, copy from realm, close realm, and return the copied result + */ +fun doRealmQueryAndCopy(realmConfiguration: RealmConfiguration, action: (Realm) -> T?): T? { + val realm = Realm.getInstance(realmConfiguration) + val result = action.invoke(realm) + val copiedResult = result?.let { realm.copyFromRealm(result) } + realm.close() + return copiedResult +} + +/** + * Get realm, do the list query, copy from realm, close realm, and return the copied result + */ +fun doRealmQueryAndCopyList(realmConfiguration: RealmConfiguration, action: (Realm) -> Iterable): Iterable { + val realm = Realm.getInstance(realmConfiguration) + val result = action.invoke(realm) + val copiedResult = realm.copyFromRealm(result) + realm.close() + return copiedResult +} + +/** + * Get realm instance, invoke the action in a transaction and close realm + */ +fun doRealmTransaction(realmConfiguration: RealmConfiguration, action: (Realm) -> Unit) { + val realm = Realm.getInstance(realmConfiguration) + realm.executeTransaction { action.invoke(it) } + realm.close() +} + +/** + * Serialize any Serializable object, zip it and convert to Base64 String + */ +fun serializeForRealm(o: Any?): String? { + if (o == null) { + return null + } + + val baos = ByteArrayOutputStream() + val gzis = CompatUtil.createGzipOutputStream(baos) + val out = ObjectOutputStream(gzis) + + out.writeObject(o) + out.close() + + return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT) +} + +/** + * Do the opposite of serializeForRealm. + */ +fun deserializeFromRealm(string: String?): T? { + if (string == null) { + return null + } + + val decodedB64 = Base64.decode(string.toByteArray(), Base64.DEFAULT) + + val bais = ByteArrayInputStream(decodedB64) + val gzis = GZIPInputStream(bais) + val ois = ObjectInputStream(gzis) + + val result = ois.readObject() as T + + ois.close() + + return result +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt new file mode 100644 index 00000000..c37ab767 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -0,0 +1,708 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db + +import android.text.TextUtils +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.store.db.model.* +import im.vector.matrix.android.internal.crypto.store.db.query.delete +import im.vector.matrix.android.internal.crypto.store.db.query.getById +import im.vector.matrix.android.internal.crypto.store.db.query.getOrCreate +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 +import im.vector.matrix.android.internal.crypto.model.MXOlmSession +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import io.realm.RealmConfiguration +import io.realm.Sort +import io.realm.kotlin.where +import org.matrix.olm.OlmAccount +import org.matrix.olm.OlmException +import timber.log.Timber +import kotlin.collections.set + +// enableFileEncryption is used to migrate the previous store +internal class RealmCryptoStore(private val enableFileEncryption: Boolean = false, + private val realmConfiguration: RealmConfiguration, + private val credentials: Credentials) : IMXCryptoStore { + + /* ========================================================================================== + * Memory cache, to correctly release JNI objects + * ========================================================================================== */ + + // The olm account + private var olmAccount: OlmAccount? = null + + // Cache for OlmSession, to release them properly + private val olmSessionsToRelease = HashMap() + + // Cache for InboundGroupSession, to release them properly + private val inboundGroupSessionToRelease = HashMap() + + /* ========================================================================================== + * Other data + * ========================================================================================== */ + + override fun hasData(): Boolean { + return doWithRealm(realmConfiguration) { + !it.isEmpty + // Check if there is a MetaData object + && it.where().count() > 0 + } + } + + override fun deleteStore() { + doRealmTransaction(realmConfiguration) { + it.deleteAll() + } + } + + override fun open() { + // Ensure CryptoMetadataEntity is inserted in DB + doWithRealm(realmConfiguration) { realm -> + var currentMetadata = realm.where().findFirst() + + var deleteAll = false + + if (currentMetadata != null) { + // Check credentials + // The device id may not have been provided in credentials. + // Check it only if provided, else trust the stored one. + if (!TextUtils.equals(currentMetadata.userId, credentials.userId) + || (credentials.deviceId != null && !TextUtils.equals(credentials.deviceId, currentMetadata.deviceId))) { + Timber.w("## open() : Credentials do not match, close this store and delete data") + deleteAll = true + currentMetadata = null + } + } + + if (currentMetadata == null) { + realm.executeTransaction { + if (deleteAll) { + it.deleteAll() + } + + // Metadata not found, or database cleaned, create it + it.createObject(CryptoMetadataEntity::class.java, credentials.userId).apply { + deviceId = credentials.deviceId + } + } + } + } + } + + override fun close() { + olmSessionsToRelease.forEach { + it.value.olmSession.releaseSession() + } + olmSessionsToRelease.clear() + + inboundGroupSessionToRelease.forEach { + it.value.mSession?.releaseSession() + } + inboundGroupSessionToRelease.clear() + + olmAccount?.releaseAccount() + } + + override fun storeDeviceId(deviceId: String) { + doRealmTransaction(realmConfiguration) { + it.where().findFirst()?.deviceId = deviceId + } + } + + override fun getDeviceId(): String { + return doRealmQueryAndCopy(realmConfiguration) { + it.where().findFirst() + }?.deviceId ?: "" + } + + override fun storeAccount(account: OlmAccount) { + olmAccount = account + + doRealmTransaction(realmConfiguration) { + it.where().findFirst()?.putOlmAccount(account) + } + } + + override fun getAccount(): OlmAccount? { + if (olmAccount == null) { + olmAccount = doRealmQueryAndCopy(realmConfiguration) { it.where().findFirst() }?.getOlmAccount() + } + + return olmAccount + } + + override fun storeUserDevice(userId: String?, deviceInfo: MXDeviceInfo?) { + if (userId == null || deviceInfo == null) { + return + } + + doRealmTransaction(realmConfiguration) { + val user = UserEntity.getOrCreate(it, userId) + + // Create device info + val deviceInfoEntity = DeviceInfoEntity.getOrCreate(it, userId, deviceInfo.deviceId).apply { + deviceId = deviceInfo.deviceId + identityKey = deviceInfo.identityKey() + putDeviceInfo(deviceInfo) + } + + if (!user.devices.contains(deviceInfoEntity)) { + user.devices.add(deviceInfoEntity) + } + } + } + + override fun getUserDevice(deviceId: String, userId: String): MXDeviceInfo? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId)) + .findFirst() + } + ?.getDeviceInfo() + } + + override fun deviceWithIdentityKey(identityKey: String): MXDeviceInfo? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey) + .findFirst() + } + ?.getDeviceInfo() + } + + override fun storeUserDevices(userId: String, devices: Map) { + if (userId == null) { + return + } + doRealmTransaction(realmConfiguration) { realm -> + if (devices == null) { + // Remove the user + UserEntity.delete(realm, userId) + } else { + UserEntity.getOrCreate(realm, userId) + .let { u -> + // Add the devices + // Ensure all other devices are deleted + u.devices.deleteAllFromRealm() + + u.devices.addAll( + devices.map { + DeviceInfoEntity.getOrCreate(realm, userId, it.value.deviceId).apply { + deviceId = it.value.deviceId + identityKey = it.value.identityKey() + putDeviceInfo(it.value) + } + } + ) + } + } + } + } + + override fun getUserDevices(userId: String): Map? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(UserEntityFields.USER_ID, userId) + .findFirst() + } + ?.devices + ?.mapNotNull { it.getDeviceInfo() } + ?.associateBy { it.deviceId } + } + + override fun storeRoomAlgorithm(roomId: String, algorithm: String) { + doRealmTransaction(realmConfiguration) { + CryptoRoomEntity.getOrCreate(it, roomId).algorithm = algorithm + } + } + + override fun getRoomAlgorithm(roomId: String): String? { + return doRealmQueryAndCopy(realmConfiguration) { + CryptoRoomEntity.getById(it, roomId) + } + ?.algorithm + } + + override fun storeSession(session: MXOlmSession, deviceKey: String) { + var sessionIdentifier: String? = null + + try { + sessionIdentifier = session.olmSession.sessionIdentifier() + } catch (e: OlmException) { + Timber.e(e, "## storeSession() : sessionIdentifier failed " + e.message) + } + + if (sessionIdentifier != null) { + val key = OlmSessionEntity.createPrimaryKey(sessionIdentifier, deviceKey) + + // Release memory of previously known session, if it is not the same one + if (olmSessionsToRelease[key]?.olmSession != session.olmSession) { + olmSessionsToRelease[key]?.olmSession?.releaseSession() + } + + olmSessionsToRelease[key] = session + + doRealmTransaction(realmConfiguration) { + val realmOlmSession = OlmSessionEntity().apply { + primaryKey = key + sessionId = sessionIdentifier + this.deviceKey = deviceKey + putOlmSession(session.olmSession) + lastReceivedMessageTs = session.lastReceivedMessageTs + } + + it.insertOrUpdate(realmOlmSession) + } + } + } + + override fun getDeviceSession(sessionId: String?, deviceKey: String?): MXOlmSession? { + if (sessionId == null || deviceKey == null) { + return null + } + + val key = OlmSessionEntity.createPrimaryKey(sessionId, deviceKey) + + // If not in cache (or not found), try to read it from realm + if (olmSessionsToRelease[key] == null) { + doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(OlmSessionEntityFields.PRIMARY_KEY, key) + .findFirst() + } + ?.let { + val olmSession = it.getOlmSession() + if (olmSession != null && it.sessionId != null) { + olmSessionsToRelease[key] = MXOlmSession(olmSession, it.lastReceivedMessageTs) + } + } + } + + return olmSessionsToRelease[key] + } + + override fun getLastUsedSessionId(deviceKey: String): String? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) + .sort(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Sort.DESCENDING) + .findFirst() + } + ?.sessionId + } + + override fun getDeviceSessionIds(deviceKey: String): MutableSet { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .equalTo(OlmSessionEntityFields.DEVICE_KEY, deviceKey) + .findAll() + } + .mapNotNull { + it.sessionId + } + .toMutableSet() + } + + override fun storeInboundGroupSessions(sessions: List) { + if (sessions.isEmpty()) { + return + } + + doRealmTransaction(realmConfiguration) { + sessions.forEach { session -> + var sessionIdentifier: String? = null + + try { + sessionIdentifier = session.mSession?.sessionIdentifier() + } catch (e: OlmException) { + Timber.e(e, "## storeInboundGroupSession() : sessionIdentifier failed " + e.message) + } + + if (sessionIdentifier != null) { + val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, session.mSenderKey) + + // Release memory of previously known session, if it is not the same one + if (inboundGroupSessionToRelease[key] != session) { + inboundGroupSessionToRelease[key]?.mSession?.releaseSession() + } + + inboundGroupSessionToRelease[key] = session + + val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { + primaryKey = key + sessionId = sessionIdentifier + senderKey = session.mSenderKey + putInboundGroupSession(session) + } + + it.insertOrUpdate(realmOlmInboundGroupSession) + } + } + } + } + + override fun getInboundGroupSession(sessionId: String, senderKey: String): MXOlmInboundGroupSession2? { + val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) + + // If not in cache (or not found), try to read it from realm + if (inboundGroupSessionToRelease[key] == null) { + doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) + .findFirst() + } + ?.getInboundGroupSession() + ?.let { + inboundGroupSessionToRelease[key] = it + } + } + + return inboundGroupSessionToRelease[key] + } + + /** + * Note: the result will be only use to export all the keys and not to use the MXOlmInboundGroupSession2, + * so there is no need to use or update `inboundGroupSessionToRelease` for native memory management + */ + override fun getInboundGroupSessions(): MutableList { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .findAll() + } + .mapNotNull { + it.getInboundGroupSession() + } + .toMutableList() + } + + override fun removeInboundGroupSession(sessionId: String, senderKey: String) { + val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) + + // Release memory of previously known session + inboundGroupSessionToRelease[key]?.mSession?.releaseSession() + inboundGroupSessionToRelease.remove(key) + + doRealmTransaction(realmConfiguration) { + it.where() + .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) + .findAll() + .deleteAllFromRealm() + } + } + + /* ========================================================================================== + * Keys backup + * ========================================================================================== */ + + override fun getKeyBackupVersion(): String? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where().findFirst() + }?.backupVersion + } + + override fun setKeyBackupVersion(keyBackupVersion: String?) { + doRealmTransaction(realmConfiguration) { + it.where().findFirst()?.backupVersion = keyBackupVersion + } + } + + override fun getKeysBackupData(): KeysBackupDataEntity? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where().findFirst() + } + } + + override fun setKeysBackupData(keysBackupData: KeysBackupDataEntity?) { + doRealmTransaction(realmConfiguration) { + if (keysBackupData == null) { + // Clear the table + it.where() + .findAll() + .deleteAllFromRealm() + } else { + // Only one object + it.copyToRealmOrUpdate(keysBackupData) + } + } + } + + override fun resetBackupMarkers() { + doRealmTransaction(realmConfiguration) { + it.where() + .findAll() + .map { inboundGroupSession -> + inboundGroupSession.backedUp = false + } + } + } + + override fun markBackupDoneForInboundGroupSessions(sessions: List) { + if (sessions.isEmpty()) { + return + } + + doRealmTransaction(realmConfiguration) { + sessions.forEach { session -> + try { + val key = OlmInboundGroupSessionEntity.createPrimaryKey(session.mSession?.sessionIdentifier(), session.mSenderKey) + + it.where() + .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) + .findFirst() + ?.backedUp = true + } catch (e: OlmException) { + Timber.e(e, "OlmException") + } + } + } + } + + override fun inboundGroupSessionsToBackup(limit: Int): List { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, false) + .limit(limit.toLong()) + .findAll() + }.mapNotNull { inboundGroupSession -> + inboundGroupSession.getInboundGroupSession() + } + } + + override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int { + return doWithRealm(realmConfiguration) { + it.where() + .apply { + if (onlyBackedUp) { + equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true) + } + } + .count() + .toInt() + } + } + + override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) { + doRealmTransaction(realmConfiguration) { + it.where().findFirst()?.globalBlacklistUnverifiedDevices = block + } + } + + override fun getGlobalBlacklistUnverifiedDevices(): Boolean { + return doRealmQueryAndCopy(realmConfiguration) { + it.where().findFirst() + }?.globalBlacklistUnverifiedDevices + ?: false + } + + override fun setRoomsListBlacklistUnverifiedDevices(roomIds: List) { + doRealmTransaction(realmConfiguration) { + // Reset all + it.where() + .findAll() + .forEach { room -> + room.blacklistUnverifiedDevices = false + } + + // Enable those in the list + it.where() + .`in`(CryptoRoomEntityFields.ROOM_ID, roomIds.toTypedArray()) + .findAll() + .forEach { room -> + room.blacklistUnverifiedDevices = true + } + } + } + + override fun getRoomsListBlacklistUnverifiedDevices(): MutableList { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .equalTo(CryptoRoomEntityFields.BLACKLIST_UNVERIFIED_DEVICES, true) + .findAll() + } + .mapNotNull { + it.roomId + } + .toMutableList() + } + + override fun getDeviceTrackingStatuses(): MutableMap { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .findAll() + } + .associateBy { + it.userId!! + } + .mapValues { + it.value.deviceTrackingStatus + } + .toMutableMap() + } + + override fun saveDeviceTrackingStatuses(deviceTrackingStatuses: Map) { + doRealmTransaction(realmConfiguration) { + deviceTrackingStatuses + .map { entry -> + UserEntity.getOrCreate(it, entry.key) + .deviceTrackingStatus = entry.value + } + } + } + + override fun getDeviceTrackingStatus(userId: String, defaultValue: Int): Int { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(UserEntityFields.USER_ID, userId) + .findFirst() + } + ?.deviceTrackingStatus + ?: defaultValue + } + + override fun getOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody): OutgoingRoomKeyRequest? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_ALGORITHM, requestBody.algorithm) + .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_ROOM_ID, requestBody.roomId) + .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_SENDER_KEY, requestBody.senderKey) + .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_SESSION_ID, requestBody.sessionId) + .findFirst() + } + ?.toOutgoingRoomKeyRequest() + } + + override fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest? { + if (request.mRequestBody == null) { + return null + } + + val existingOne = getOutgoingRoomKeyRequest(request.mRequestBody!!) + + if (existingOne != null) { + return existingOne + } + + // Insert the request and return the one passed in parameter + doRealmTransaction(realmConfiguration) { + it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.mRequestId).apply { + putRequestBody(request.mRequestBody) + putRecipients(request.mRecipients) + cancellationTxnId = request.mCancellationTxnId + state = request.mState.ordinal + } + } + + return request + } + + override fun getOutgoingRoomKeyRequestByState(states: Set): OutgoingRoomKeyRequest? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .`in`(OutgoingRoomKeyRequestEntityFields.STATE, states.map { it.ordinal }.toTypedArray()) + .findFirst() + } + ?.toOutgoingRoomKeyRequest() + } + + override fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) { + doRealmTransaction(realmConfiguration) { + val obj = OutgoingRoomKeyRequestEntity().apply { + requestId = request.mRequestId + cancellationTxnId = request.mCancellationTxnId + state = request.mState.ordinal + putRecipients(request.mRecipients) + putRequestBody(request.mRequestBody) + } + + it.insertOrUpdate(obj) + } + } + + override fun deleteOutgoingRoomKeyRequest(transactionId: String) { + doRealmTransaction(realmConfiguration) { + it.where() + .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_ID, transactionId) + .findFirst() + ?.deleteFromRealm() + } + } + + override fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?) { + if (incomingRoomKeyRequest == null) { + return + } + + doRealmTransaction(realmConfiguration) { + // Delete any previous store request with the same parameters + it.where() + .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.mUserId) + .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.mDeviceId) + .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.mRequestId) + .findAll() + .deleteAllFromRealm() + + // Then store it + it.createObject(IncomingRoomKeyRequestEntity::class.java).apply { + userId = incomingRoomKeyRequest.mUserId + deviceId = incomingRoomKeyRequest.mDeviceId + requestId = incomingRoomKeyRequest.mRequestId + putRequestBody(incomingRoomKeyRequest.mRequestBody) + } + } + } + + override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) { + doRealmTransaction(realmConfiguration) { + it.where() + .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.mUserId) + .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.mDeviceId) + .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.mRequestId) + .findAll() + .deleteAllFromRealm() + } + } + + override fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest? { + return doRealmQueryAndCopy(realmConfiguration) { + it.where() + .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, userId) + .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, deviceId) + .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, requestId) + .findFirst() + } + ?.toIncomingRoomKeyRequest() + } + + override fun getPendingIncomingRoomKeyRequests(): MutableList { + return doRealmQueryAndCopyList(realmConfiguration) { + it.where() + .findAll() + } + .map { + it.toIncomingRoomKeyRequest() + } + .toMutableList() + } + + companion object { + private const val LOG_TAG = "RealmCryptoStore" + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt new file mode 100644 index 00000000..00af9dab --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db + +import io.realm.DynamicRealm +import io.realm.RealmMigration +import timber.log.Timber + +internal object RealmCryptoStoreMigration : RealmMigration { + + const val CRYPTO_STORE_SCHEMA_VERSION = 0L + + override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { + Timber.d("Migrating Realm Crypto from $oldVersion to $newVersion") + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt new file mode 100644 index 00000000..96d69b7b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db + +import io.realm.annotations.RealmModule +import im.vector.matrix.android.internal.crypto.store.db.model.* + +/** + * Realm module for Crypto store classes + */ +@RealmModule(library = true, + classes = [ + CryptoMetadataEntity::class, + CryptoRoomEntity::class, + DeviceInfoEntity::class, + IncomingRoomKeyRequestEntity::class, + KeysBackupDataEntity::class, + OlmInboundGroupSessionEntity::class, + OlmSessionEntity::class, + OutgoingRoomKeyRequestEntity::class, + UserEntity::class + ]) +internal class RealmCryptoStoreModule \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt new file mode 100644 index 00000000..19cc06fa --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm +import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import org.matrix.olm.OlmAccount + +internal open class CryptoMetadataEntity( + // The current user id. + @PrimaryKey var userId: String? = null, + // The current device id. + var deviceId: String? = null, + // Serialized OlmAccount + var olmAccountData: String? = null, + // The sync token corresponding to the device list. // TODO? + var deviceSyncToken: String? = null, + // Settings for blacklisting unverified devices. + var globalBlacklistUnverifiedDevices: Boolean = false, + // The keys backup version currently used. Null means no backup. + var backupVersion: String? = null +) : RealmObject() { + + + // Deserialize data + fun getOlmAccount(): OlmAccount? { + return deserializeFromRealm(olmAccountData) + } + + // Serialize data + fun putOlmAccount(olmAccount: OlmAccount?) { + olmAccountData = serializeForRealm(olmAccount) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoRoomEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoRoomEntity.kt new file mode 100644 index 00000000..09394051 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoRoomEntity.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +internal open class CryptoRoomEntity( + @PrimaryKey var roomId: String? = null, + var algorithm: String? = null, + var blacklistUnverifiedDevices: Boolean = false) + : RealmObject() { + + companion object + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt new file mode 100644 index 00000000..d690073a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm +import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import io.realm.RealmObject +import io.realm.RealmResults +import io.realm.annotations.LinkingObjects +import io.realm.annotations.PrimaryKey + +internal fun DeviceInfoEntity.Companion.createPrimaryKey(userId: String, deviceId: String) = "$userId|$deviceId" + +// deviceInfoData contains serialized data +internal open class DeviceInfoEntity(@PrimaryKey var primaryKey: String = "", + var deviceId: String? = null, + var identityKey: String? = null, + var deviceInfoData: String? = null) + : RealmObject() { + + // Deserialize data + fun getDeviceInfo(): MXDeviceInfo? { + return deserializeFromRealm(deviceInfoData) + } + + // Serialize data + fun putDeviceInfo(deviceInfo: MXDeviceInfo?) { + deviceInfoData = serializeForRealm(deviceInfo) + } + + @LinkingObjects("devices") + val users: RealmResults? = null + + companion object +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt new file mode 100644 index 00000000..45a0c115 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody + +internal open class IncomingRoomKeyRequestEntity( + var requestId: String? = null, + var userId: String? = null, + var deviceId: String? = null, + // RoomKeyRequestBody fields + var requestBodyAlgorithm: String? = null, + var requestBodyRoomId: String? = null, + var requestBodySenderKey: String? = null, + var requestBodySessionId: String? = null +) : RealmObject() { + + fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest { + return IncomingRoomKeyRequest().apply { + mRequestId = requestId + mUserId = userId + mDeviceId = deviceId + mRequestBody = RoomKeyRequestBody().apply { + algorithm = requestBodyAlgorithm + roomId = requestBodyRoomId + senderKey = requestBodySenderKey + sessionId = requestBodySessionId + } + } + } + + fun putRequestBody(requestBody: RoomKeyRequestBody?) { + requestBody?.let { + requestBodyAlgorithm = it.algorithm + requestBodyRoomId = it.roomId + requestBodySenderKey = it.senderKey + requestBodySessionId = it.sessionId + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeysBackupDataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeysBackupDataEntity.kt new file mode 100644 index 00000000..53ebf146 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/KeysBackupDataEntity.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +internal open class KeysBackupDataEntity( + // Primary key to update this object. There is only one object, so it's a constant, please do not set it + @PrimaryKey + var primaryKey: Int = 0, + // The last known hash of the backed up keys on the server + var backupLastServerHash: String? = null, + // The last known number of backed up keys on the server + var backupLastServerNumberOfKeys: Int? = null +) : RealmObject() \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt new file mode 100644 index 00000000..53747a04 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm +import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 + +internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey" + +internal open class OlmInboundGroupSessionEntity( + // Combined value to build a primary key + @PrimaryKey var primaryKey: String? = null, + var sessionId: String? = null, + var senderKey: String? = null, + // olmInboundGroupSessionData contains Json + var olmInboundGroupSessionData: String? = null, + // Indicate if the key has been backed up to the homeserver + var backedUp: Boolean = false) + : RealmObject() { + + fun getInboundGroupSession(): MXOlmInboundGroupSession2? { + return deserializeFromRealm(olmInboundGroupSessionData) + } + + fun putInboundGroupSession(mxOlmInboundGroupSession2: MXOlmInboundGroupSession2?) { + olmInboundGroupSessionData = serializeForRealm(mxOlmInboundGroupSession2) + } + + companion object +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt new file mode 100644 index 00000000..4425cf33 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm +import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import org.matrix.olm.OlmSession + +internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey" + +// olmSessionData is a serialized OlmSession +internal open class OlmSessionEntity(@PrimaryKey var primaryKey: String = "", + var sessionId: String? = null, + var deviceKey: String? = null, + var olmSessionData: String? = null, + var lastReceivedMessageTs: Long = 0) + : RealmObject() { + + fun getOlmSession(): OlmSession? { + return deserializeFromRealm(olmSessionData) + } + + fun putOlmSession(olmSession: OlmSession?) { + olmSessionData = serializeForRealm(olmSession) + } + + companion object +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt new file mode 100644 index 00000000..f4cc732e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm +import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +internal open class OutgoingRoomKeyRequestEntity( + @PrimaryKey var requestId: String? = null, + var cancellationTxnId: String? = null, + // Serialized Json + var recipientsData: String? = null, + // RoomKeyRequestBody fields + var requestBodyAlgorithm: String? = null, + var requestBodyRoomId: String? = null, + var requestBodySenderKey: String? = null, + var requestBodySessionId: String? = null, + // State + var state: Int = 0 +) : RealmObject() { + + /** + * Convert to OutgoingRoomKeyRequest + */ + fun toOutgoingRoomKeyRequest(): OutgoingRoomKeyRequest { + return OutgoingRoomKeyRequest( + RoomKeyRequestBody().apply { + algorithm = requestBodyAlgorithm + roomId = requestBodyRoomId + senderKey = requestBodySenderKey + sessionId = requestBodySessionId + }, + getRecipients()!!, + requestId!!, + OutgoingRoomKeyRequest.RequestState.from(state) + ).apply { + this.mCancellationTxnId = cancellationTxnId + } + } + + private fun getRecipients(): List>? { + return deserializeFromRealm(recipientsData) + } + + fun putRecipients(recipients: List>?) { + recipientsData = serializeForRealm(recipients) + } + + fun putRequestBody(requestBody: RoomKeyRequestBody?) { + requestBody?.let { + requestBodyAlgorithm = it.algorithm + requestBodyRoomId = it.roomId + requestBodySenderKey = it.senderKey + requestBodySessionId = it.sessionId + } + } +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/UserEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/UserEntity.kt new file mode 100644 index 00000000..27cd7fe2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/UserEntity.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.model + +import io.realm.RealmList +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +internal open class UserEntity(@PrimaryKey var userId: String? = null, + var devices: RealmList = RealmList(), + var deviceTrackingStatus: Int = 0) + : RealmObject() { + + companion object +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt new file mode 100644 index 00000000..d7ab87a4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.query + +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntity +import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntityFields +import io.realm.Realm +import io.realm.kotlin.where + +/** + * Get or create a room + */ +internal fun CryptoRoomEntity.Companion.getOrCreate(realm: Realm, roomId: String): CryptoRoomEntity { + return getById(realm, roomId) + ?: let { + realm.createObject(CryptoRoomEntity::class.java, roomId) + } +} + +/** + * Get a room + */ +internal fun CryptoRoomEntity.Companion.getById(realm: Realm, roomId: String): CryptoRoomEntity? { + return realm.where() + .equalTo(CryptoRoomEntityFields.ROOM_ID, roomId) + .findFirst() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt new file mode 100644 index 00000000..2aea3cd2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.query + +import io.realm.Realm +import io.realm.kotlin.where +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity +import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields +import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey + +/** + * Get or create a device info + */ +internal fun DeviceInfoEntity.Companion.getOrCreate(realm: Realm, userId: String, deviceId: String): DeviceInfoEntity { + return realm.where() + .equalTo(DeviceInfoEntityFields.PRIMARY_KEY, DeviceInfoEntity.createPrimaryKey(userId, deviceId)) + .findFirst() + ?: let { + realm.createObject(DeviceInfoEntity::class.java, DeviceInfoEntity.createPrimaryKey(userId, deviceId)).apply { + this.deviceId = deviceId + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt new file mode 100644 index 00000000..dd5278ce --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.store.db.query + +import io.realm.Realm +import io.realm.kotlin.where +import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity +import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields + +/** + * Get or create a user + */ +internal fun UserEntity.Companion.getOrCreate(realm: Realm, userId: String): UserEntity { + return realm.where() + .equalTo(UserEntityFields.USER_ID, userId) + .findFirst() + ?: let { + realm.createObject(UserEntity::class.java, userId) + } +} + +/** + * Delete a user + */ +internal fun UserEntity.Companion.delete(realm: Realm, userId: String) { + realm.where() + .equalTo(UserEntityFields.USER_ID, userId) + .findFirst() + ?.deleteFromRealm() +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt new file mode 100644 index 00000000..06e979b4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.MXKey +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimBody +import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimResponse +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import timber.log.Timber +import java.util.* + +internal interface ClaimOneTimeKeysForUsersDeviceTask : Task> { + data class Params( + // a list of users, devices and key types to retrieve keys for. + val usersDevicesKeyTypesMap: MXUsersDevicesMap + ) +} + +internal class DefaultClaimOneTimeKeysForUsersDevice(private val cryptoApi: CryptoApi) + : ClaimOneTimeKeysForUsersDeviceTask { + + override fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): Try> { + val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map) + + return executeRequest { + apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body) + }.flatMap { keysClaimResponse -> + Try { + val map = HashMap>() + + if (null != keysClaimResponse.oneTimeKeys) { + for (userId in keysClaimResponse.oneTimeKeys!!.keys) { + val mapByUserId = keysClaimResponse.oneTimeKeys!![userId] + + val keysMap = HashMap() + + for (deviceId in mapByUserId!!.keys) { + try { + keysMap[deviceId] = MXKey(mapByUserId[deviceId]) + } catch (e: Exception) { + Timber.e(e, "## claimOneTimeKeysForUsersDevices : fail to create a MXKey ") + } + + } + + if (keysMap.size != 0) { + map[userId] = keysMap + } + } + } + + MXUsersDevicesMap(map) + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt new file mode 100644 index 00000000..4630bd93 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface DeleteDeviceTask : Task { + data class Params( + val deviceId: String, + val accountPassword: String + ) +} + +internal class DefaultDeleteDeviceTask(private val cryptoApi: CryptoApi) + : DeleteDeviceTask { + + override fun execute(params: DeleteDeviceTask.Params): Try { + return executeRequest { + apiCall = cryptoApi.deleteDevice(params.deviceId, + DeleteDeviceParams()) + } + + // TODO Recover error, see legacy code MXSession.deleteDevice() + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt new file mode 100644 index 00000000..22c552b4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import android.text.TextUtils +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody +import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import java.util.* + +internal interface DownloadKeysForUsersTask : Task { + data class Params( + // the list of users to get keys for. + val userIds: List?, + // the up-to token + val token: String?) +} + +internal class DefaultDownloadKeysForUsers(private val cryptoApi: CryptoApi) + : DownloadKeysForUsersTask { + + override fun execute(params: DownloadKeysForUsersTask.Params): Try { + val downloadQuery = HashMap>() + + if (null != params.userIds) { + for (userId in params.userIds) { + downloadQuery[userId] = HashMap() + } + } + + val body = KeysQueryBody( + deviceKeys = downloadQuery + ) + + if (!TextUtils.isEmpty(params.token)) { + body.token = params.token + } + + return executeRequest { + apiCall = cryptoApi.downloadKeysForUsers(body) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt new file mode 100644 index 00000000..b7c706d2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetDevicesTask : Task + +internal class DefaultGetDevicesTask(private val cryptoApi: CryptoApi) + : GetDevicesTask { + + override fun execute(params: Unit): Try { + return executeRequest { + apiCall = cryptoApi.getDevices() + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt new file mode 100644 index 00000000..e4f9f7ae --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface GetKeyChangesTask : Task { + data class Params( + // the start token. + val from: String, + // the up-to token. + val to: String + ) +} + +internal class DefaultGetKeyChangesTask(private val cryptoApi: CryptoApi) + : GetKeyChangesTask { + + override fun execute(params: GetKeyChangesTask.Params): Try { + return executeRequest { + apiCall = cryptoApi.getKeyChanges(params.from, + params.to) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt new file mode 100644 index 00000000..c9a99f8a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import java.util.* + +internal interface SendToDeviceTask : Task { + data class Params( + // the type of event to send + val eventType: String, + // the content to send. Map from user_id to device_id to content dictionary. + val contentMap: MXUsersDevicesMap, + // the transactionId + val transactionId: String? = null + ) +} + +internal class DefaultSendToDeviceTask(private val cryptoApi: CryptoApi) + : SendToDeviceTask { + + override fun execute(params: SendToDeviceTask.Params): Try { + val sendToDeviceBody = SendToDeviceBody() + sendToDeviceBody.messages = params.contentMap.map + + return executeRequest { + apiCall = cryptoApi.sendToDevice( + params.eventType, + params.transactionId ?: Random().nextInt(Integer.MAX_VALUE).toString(), + sendToDeviceBody + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt new file mode 100644 index 00000000..aba5b5c7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import android.text.TextUtils +import arrow.core.Try +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task + +internal interface SetDeviceNameTask : Task { + data class Params( + // the device id + val deviceId: String, + // the device name + val deviceName: String + ) +} + +internal class DefaultSetDeviceNameTask(private val cryptoApi: CryptoApi) + : SetDeviceNameTask { + + override fun execute(params: SetDeviceNameTask.Params): Try { + val body = UpdateDeviceInfoBody( + displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName + ) + + return executeRequest { + apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt new file mode 100644 index 00000000..e985032a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.tasks + +import arrow.core.Try +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.crypto.api.CryptoApi +import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse +import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys +import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import im.vector.matrix.android.internal.util.convertToUTF8 + +internal interface UploadKeysTask : Task { + data class Params( + // the device keys to send. + val deviceKeys: DeviceKeys?, + // the one-time keys to send. + val oneTimeKeys: JsonDict?, + // the explicit device_id to use for upload (default is to use the same as that used during auth). + val deviceId: String) +} + +internal class DefaultUploadKeysTask(private val cryptoApi: CryptoApi) + : UploadKeysTask { + + override fun execute(params: UploadKeysTask.Params): Try { + val encodedDeviceId = convertToUTF8(params.deviceId) + + val body = KeysUploadBody() + + if (null != params.deviceKeys) { + body.deviceKeys = params.deviceKeys + } + + if (null != params.oneTimeKeys) { + body.oneTimeKeys = params.oneTimeKeys + } + + return executeRequest { + if (encodedDeviceId.isNullOrBlank()) { + apiCall = cryptoApi.uploadKeys(body) + } else { + apiCall = cryptoApi.uploadKeys(encodedDeviceId, body) + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt new file mode 100644 index 00000000..441d7a41 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -0,0 +1,446 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.verification + +import android.os.Handler +import android.os.Looper +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.matrix.android.api.session.crypto.sas.safeValueOf +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.* +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import java.util.* +import kotlin.collections.HashMap + +/** + * Manages all current verifications transactions with short codes. + * Short codes interactive verification is a more user friendly way of verifying devices + * that is still maintaining a good level of security (alternative to the 43-character strings compare method). + */ +internal class DefaultSasVerificationService(private val mCredentials: Credentials, + private val mCryptoStore: IMXCryptoStore, + private val deviceListManager: DeviceListManager, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor) + : VerificationTransaction.Listener, SasVerificationService { + + private val uiHandler = Handler(Looper.getMainLooper()) + + // map [sender : [transaction]] + private val txMap = HashMap>() + + // Event received from the sync + fun onToDeviceEvent(event: Event) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + when (event.type) { + EventType.KEY_VERIFICATION_START -> { + onStartRequestReceived(event) + } + EventType.KEY_VERIFICATION_CANCEL -> { + onCancelReceived(event) + } + EventType.KEY_VERIFICATION_ACCEPT -> { + onAcceptReceived(event) + } + EventType.KEY_VERIFICATION_KEY -> { + onKeyReceived(event) + } + EventType.KEY_VERIFICATION_MAC -> { + onMacReceived(event) + } + else -> { + //ignore + } + } + } + } + + // Internal listener + private lateinit var mCryptoListener: SasCryptoListener + + + private var listeners = ArrayList() + + override fun addListener(listener: SasVerificationService.SasVerificationListener) { + uiHandler.post { + if (!listeners.contains(listener)) { + listeners.add(listener) + } + } + } + + override fun removeListener(listener: SasVerificationService.SasVerificationListener) { + uiHandler.post { + listeners.remove(listener) + } + } + + private fun dispatchTxAdded(tx: VerificationTransaction) { + uiHandler.post { + listeners.forEach { + try { + it.transactionCreated(tx) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + + } + } + } + + private fun dispatchTxUpdated(tx: VerificationTransaction) { + uiHandler.post { + listeners.forEach { + try { + it.transactionUpdated(tx) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + } + } + + override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { + mCryptoListener.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + deviceID, + userId, + object : MatrixCallback { + override fun onSuccess(data: Unit) { + uiHandler.post { + listeners.forEach { + try { + it.markedAsManuallyVerified(userId, deviceID) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## Manual verification failed in state") + } + }) + } + + private fun onStartRequestReceived(event: Event) { + val startReq = event.content.toModel()!! + + val otherUserId = event.sender + if (!startReq.isValid()) { + Timber.e("## received invalid verification request") + if (startReq.transactionID != null) { + cancelTransaction( + startReq.transactionID!!, + otherUserId!!, + startReq?.fromDevice ?: event.getSenderKey()!!, + CancelCode.UnknownMethod + ) + } + return + } + //Download device keys prior to everything + checkKeysAreDownloaded( + otherUserId!!, + startReq, + success = { + Timber.d("## SAS onStartRequestReceived ${startReq.transactionID!!}") + val tid = startReq.transactionID!! + val existing = getExistingTransaction(otherUserId, tid) + val existingTxs = getExistingTransactionsForUser(otherUserId) + if (existing != null) { + //should cancel both! + Timber.d("## SAS onStartRequestReceived - Request exist with same if ${startReq.transactionID!!}") + existing.cancel(CancelCode.UnexpectedMessage) + cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage) + } else if (existingTxs?.isEmpty() == false) { + Timber.d("## SAS onStartRequestReceived - There is already a transaction with this user ${startReq.transactionID!!}") + //Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time. + existingTxs.forEach { + it.cancel(CancelCode.UnexpectedMessage) + } + cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage) + } else { + //Ok we can create + if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) { + Timber.d("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}") + val tx = IncomingSASVerificationTransaction( + this, + mCredentials, + mCryptoStore, + mSendToDeviceTask, + mTaskExecutor, + mCryptoListener.getMyDevice().fingerprint()!!, + startReq.transactionID!!, + otherUserId) + addTransaction(tx) + tx.acceptToDeviceEvent(otherUserId, startReq) + } else { + Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}") + cancelTransaction(tid, otherUserId, startReq.fromDevice + ?: event.getSenderKey()!!, CancelCode.UnknownMethod) + } + } + }, + error = { + cancelTransaction(startReq.transactionID!!, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage) + }) + } + + private fun checkKeysAreDownloaded(otherUserId: String, + startReq: KeyVerificationStart, + success: (MXUsersDevicesMap) -> Unit, + error: () -> Unit) { + deviceListManager.downloadKeys(listOf(otherUserId), true, object : MatrixCallback> { + override fun onFailure(failure: Throwable) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + error() + } + } + + override fun onSuccess(info: MXUsersDevicesMap) { + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + if (info.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) { + success(info) + } else { + error() + } + } + } + }) + } + + private fun onCancelReceived(event: Event) { + Timber.d("## SAS onCancelReceived") + val cancelReq = event.content.toModel()!! + + if (!cancelReq.isValid()) { + //ignore + Timber.e("## Received invalid accept request") + return + } + val otherUserId = event.sender!! + + Timber.d("## SAS onCancelReceived otherUser:$otherUserId reason:${cancelReq.reason}") + val existing = getExistingTransaction(otherUserId, cancelReq.transactionID!!) + if (existing == null) { + Timber.e("## Received invalid cancel request") + return + } + if (existing is SASVerificationTransaction) { + existing.cancelledReason = safeValueOf(cancelReq.code) + existing.state = SasVerificationTxState.OnCancelled + } + } + + private fun onAcceptReceived(event: Event) { + val acceptReq = event.content.toModel()!! + + if (!acceptReq.isValid()) { + //ignore + Timber.e("## Received invalid accept request") + return + } + val otherUserId = event.sender!! + val existing = getExistingTransaction(otherUserId, acceptReq.transactionID!!) + if (existing == null) { + Timber.e("## Received invalid accept request") + return + + } + + if (existing is SASVerificationTransaction) { + existing.acceptToDeviceEvent(otherUserId, acceptReq) + } else { + //not other types now + } + } + + + private fun onKeyReceived(event: Event) { + val keyReq = event.content.toModel()!! + + if (!keyReq.isValid()) { + //ignore + Timber.e("## Received invalid key request") + return + } + val otherUserId = event.sender!! + val existing = getExistingTransaction(otherUserId, keyReq.transactionID!!) + if (existing == null) { + Timber.e("## Received invalid accept request") + return + } + if (existing is SASVerificationTransaction) { + existing.acceptToDeviceEvent(otherUserId, keyReq) + } else { + //not other types now + } + } + + private fun onMacReceived(event: Event) { + val macReq = event.content.toModel()!! + + if (!macReq.isValid()) { + //ignore + Timber.e("## Received invalid key request") + return + } + val otherUserId = event.sender!! + val existing = getExistingTransaction(otherUserId, macReq.transactionID!!) + if (existing == null) { + Timber.e("## Received invalid accept request") + return + } + if (existing is SASVerificationTransaction) { + existing.acceptToDeviceEvent(otherUserId, macReq) + } else { + //not other types known for now + } + } + + override fun getExistingTransaction(otherUser: String, tid: String): VerificationTransaction? { + synchronized(lock = txMap) { + return txMap[otherUser]?.get(tid) + } + } + + private fun getExistingTransactionsForUser(otherUser: String): Collection? { + synchronized(txMap) { + return txMap[otherUser]?.values + } + } + + private fun removeTransaction(otherUser: String, tid: String) { + synchronized(txMap) { + txMap[otherUser]?.remove(tid)?.removeListener(this) + } + } + + private fun addTransaction(tx: VerificationTransaction) { + tx.otherUserId.let { otherUserId -> + synchronized(txMap) { + if (txMap[otherUserId] == null) { + txMap[otherUserId] = HashMap() + } + txMap[otherUserId]?.set(tx.transactionId, tx) + dispatchTxAdded(tx) + tx.addListener(this) + } + } + } + + override fun beginKeyVerificationSAS(userId: String, deviceID: String): String? { + return beginKeyVerification(KeyVerificationStart.VERIF_METHOD_SAS, userId, deviceID) + } + + override fun beginKeyVerification(method: String, userId: String, deviceID: String): String? { + val txID = createUniqueIDForTransaction(userId, deviceID) + //should check if already one (and cancel it) + if (KeyVerificationStart.VERIF_METHOD_SAS == method) { + val tx = OutgoingSASVerificationRequest( + this, + mCredentials, + mCryptoStore, + mSendToDeviceTask, + mTaskExecutor, + mCryptoListener.getMyDevice().fingerprint()!!, + txID, + userId, + deviceID) + addTransaction(tx) + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + tx.start() + } + return txID + } else { + throw IllegalArgumentException("Unknown verification method") + } + } + + /** + * This string must be unique for the pair of users performing verification for the duration that the transaction is valid + */ + private fun createUniqueIDForTransaction(userId: String, deviceID: String): String { + val buff = StringBuffer() + buff + .append(mCredentials.userId).append("|") + .append(mCredentials.deviceId).append("|") + .append(userId).append("|") + .append(deviceID).append("|") + .append(UUID.randomUUID().toString()) + return buff.toString() + } + + + override fun transactionUpdated(tx: VerificationTransaction) { + dispatchTxUpdated(tx) + if (tx is SASVerificationTransaction + && (tx.state == SasVerificationTxState.Cancelled + || tx.state == SasVerificationTxState.OnCancelled + || tx.state == SasVerificationTxState.Verified) + ) { + //remove + this.removeTransaction(tx.otherUserId, tx.transactionId) + } + } + + fun cancelTransaction(transactionId: String, userId: String, userDevice: String, code: CancelCode) { + val cancelMessage = KeyVerificationCancel.create(transactionId, code) + val contentMap = MXUsersDevicesMap() + contentMap.setObject(cancelMessage, userId, userDevice) + + mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.d("## SAS verification [$transactionId] canceled for reason ${code.value}") + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.") + } + }) + .executeBy(mTaskExecutor) + } + + fun setCryptoInternalListener(listener: SasCryptoListener) { + mCryptoListener = listener + } + + fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String, callback: MatrixCallback) { + mCryptoListener.setDeviceVerification(verificationStatus, deviceId, userId, callback) + } + + interface SasCryptoListener { + fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String, callback: MatrixCallback) + fun getMyDevice(): MXDeviceInfo + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt new file mode 100644 index 00000000..e9bc6788 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt @@ -0,0 +1,242 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.verification + +import android.util.Base64 +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.SasMode +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.task.TaskExecutor +import timber.log.Timber + +internal class IncomingSASVerificationTransaction( + private val mSasVerificationService: DefaultSasVerificationService, + private val mCredentials: Credentials, + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor, transactionId: String, + deviceFingerprint: String, + otherUserID: String) + : SASVerificationTransaction( + mSasVerificationService, + mCredentials, + mCryptoStore, + mSendToDeviceTask, + mTaskExecutor, + deviceFingerprint, + transactionId, + otherUserID, + null, + true), + IncomingSasVerificationTransaction { + + override val uxState: IncomingSasVerificationTransaction.UxState + get() { + return when (state) { + SasVerificationTxState.OnStarted -> IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT + SasVerificationTxState.SendingAccept, + SasVerificationTxState.Accepted, + SasVerificationTxState.OnKeyReceived, + SasVerificationTxState.SendingKey, + SasVerificationTxState.KeySent -> IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT + SasVerificationTxState.ShortCodeReady -> IncomingSasVerificationTransaction.UxState.SHOW_SAS + SasVerificationTxState.ShortCodeAccepted, + SasVerificationTxState.SendingMac, + SasVerificationTxState.MacSent, + SasVerificationTxState.Verifying -> IncomingSasVerificationTransaction.UxState.WAIT_FOR_VERIFICATION + SasVerificationTxState.Verified -> IncomingSasVerificationTransaction.UxState.VERIFIED + SasVerificationTxState.Cancelled -> IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME + SasVerificationTxState.OnCancelled -> IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER + else -> IncomingSasVerificationTransaction.UxState.UNKNOWN + } + } + + override fun onVerificationStart(startReq: KeyVerificationStart) { + Timber.d("## SAS received verification request from state $state") + if (state != SasVerificationTxState.None) { + Timber.e("## received verification request from invalid state") + //should I cancel?? + throw IllegalStateException("Interactive Key verification already started") + } + this.startReq = startReq + state = SasVerificationTxState.OnStarted + this.otherDeviceId = startReq.fromDevice + + } + + + override fun performAccept() { + if (state != SasVerificationTxState.OnStarted) { + Timber.e("## Cannot perform accept from state $state") + return + } + + // Select a key agreement protocol, a hash algorithm, a message authentication code, + // and short authentication string methods out of the lists given in requester's message. + val agreedProtocol = startReq!!.keyAgreementProtocols?.firstOrNull { KNOWN_AGREEMENT_PROTOCOLS.contains(it) } + val agreedHash = startReq!!.hashes?.firstOrNull { KNOWN_HASHES.contains(it) } + val agreedMac = startReq!!.messageAuthenticationCodes?.firstOrNull { KNOWN_MACS.contains(it) } + val agreedShortCode = startReq!!.shortAuthenticationStrings?.filter { KNOWN_SHORT_CODES.contains(it) } + + //No common key sharing/hashing/hmac/SAS methods. + //If a device is unable to complete the verification because the devices are unable to find a common key sharing, + // hashing, hmac, or SAS method, then it should send a m.key.verification.cancel message + if (listOf(agreedProtocol, agreedHash, agreedMac).any { it.isNullOrBlank() } + || agreedShortCode.isNullOrEmpty()) { + //Failed to find agreement + Timber.e("## Failed to find agreement ") + cancel(CancelCode.UnknownMethod) + return + } + + //Bob’s device ensures that it has a copy of Alice’s device key. + val mxDeviceInfo = mCryptoStore.getUserDevice(this.otherUserId, otherDeviceId!!) + + if (mxDeviceInfo?.fingerprint() == null) { + Timber.e("## Failed to find device key ") + //TODO force download keys!! + //would be probably better to download the keys + //for now I cancel + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + cancel(CancelCode.User) + } + } else { + // val otherKey = info.identityKey() + //need to jump back to correct thread + val accept = KeyVerificationAccept.create( + tid = transactionId, + keyAgreementProtocol = agreedProtocol!!, + hash = agreedHash!!, + messageAuthenticationCode = agreedMac!!, + shortAuthenticationStrings = agreedShortCode, + commitment = Base64.encodeToString("temporary commitment".toByteArray(), Base64.DEFAULT) + ) + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + doAccept(accept) + } + } + } + + + private fun doAccept(accept: KeyVerificationAccept) { + this.accepted = accept + Timber.d("## SAS accept request id:$transactionId") + + //The hash commitment is the hash (using the selected hash algorithm) of the unpadded base64 representation of QB, + // concatenated with the canonical JSON representation of the content of the m.key.verification.start message + val concat = getSAS().publicKey + MoshiProvider.getCanonicalJson(KeyVerificationStart::class.java, startReq!!) + accept.commitment = hashUsingAgreedHashMethod(concat) ?: "" + //we need to send this to other device now + state = SasVerificationTxState.SendingAccept + sendToOther(EventType.KEY_VERIFICATION_ACCEPT, accept, SasVerificationTxState.Accepted, CancelCode.User) { + if (state == SasVerificationTxState.SendingAccept) { + //It is possible that we receive the next event before this one :/, in this case we should keep state + state = SasVerificationTxState.Accepted + } + } + } + + + override fun onVerificationAccept(accept: KeyVerificationAccept) { + Timber.d("## SAS invalid message for incoming request id:$transactionId") + cancel(CancelCode.UnexpectedMessage) + } + + override fun onKeyVerificationKey(userId: String, vKey: KeyVerificationKey) { + Timber.d("## SAS received key for request id:$transactionId") + if (state != SasVerificationTxState.SendingAccept && state != SasVerificationTxState.Accepted) { + Timber.e("## received key from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + + otherKey = vKey.key + // Upon receipt of the m.key.verification.key message from Alice’s device, + // Bob’s device replies with a to_device message with type set to m.key.verification.key, + // sending Bob’s public key QB + val pubKey = getSAS().publicKey + + val keyToDevice = KeyVerificationKey.create(transactionId, pubKey) + //we need to send this to other device now + state = SasVerificationTxState.SendingKey + this.sendToOther(EventType.KEY_VERIFICATION_KEY, keyToDevice, SasVerificationTxState.KeySent, CancelCode.User) { + if (state == SasVerificationTxState.SendingKey) { + //It is possible that we receive the next event before this one :/, in this case we should keep state + state = SasVerificationTxState.KeySent + } + } + + // Alice’s and Bob’s devices perform an Elliptic-curve Diffie-Hellman + // (calculate the point (x,y)=dAQB=dBQA and use x as the result of the ECDH), + // using the result as the shared secret. + + getSAS().setTheirPublicKey(otherKey) + //(Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function, + // the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of: + // - the string “MATRIX_KEY_VERIFICATION_SAS”, + // - the Matrix ID of the user who sent the m.key.verification.start message, + // - the device ID of the device that sent the m.key.verification.start message, + // - the Matrix ID of the user who sent the m.key.verification.accept message, + // - he device ID of the device that sent the m.key.verification.accept message + // - the transaction ID. + val sasInfo = "MATRIX_KEY_VERIFICATION_SAS" + + "$otherUserId$otherDeviceId" + + "${mCredentials.userId}${mCredentials.deviceId}" + + transactionId + //decimal: generate five bytes by using HKDF. + //emoji: generate six bytes by using HKDF. + shortCodeBytes = getSAS().generateShortCode(sasInfo, 6) + + Timber.e("************ BOB CODE ${getDecimalCodeRepresentation(shortCodeBytes!!)}") + Timber.e("************ BOB EMOJI CODE ${getShortCodeRepresentation(SasMode.EMOJI)}") + + state = SasVerificationTxState.ShortCodeReady + } + + override fun onKeyVerificationMac(vKey: KeyVerificationMac) { + Timber.d("## SAS received mac for request id:$transactionId") + //Check for state? + if (state != SasVerificationTxState.SendingKey + && state != SasVerificationTxState.KeySent + && state != SasVerificationTxState.ShortCodeReady + && state != SasVerificationTxState.ShortCodeAccepted + && state != SasVerificationTxState.SendingMac + && state != SasVerificationTxState.MacSent) { + Timber.e("## received key from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + theirMac = vKey + + //Do I have my Mac? + if (myMac != null) { + //I can check + verifyMacs() + } + //Wait for ShortCode Accepted + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt new file mode 100644 index 00000000..e2cc5d3a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt @@ -0,0 +1,216 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.verification + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac +import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.task.TaskExecutor +import timber.log.Timber + +internal class OutgoingSASVerificationRequest( + private val mSasVerificationService: DefaultSasVerificationService, + private val mCredentials: Credentials, + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor, + deviceFingerprint: String, + transactionId: String, + otherUserId: String, + otherDeviceId: String) + : SASVerificationTransaction( + mSasVerificationService, + mCredentials, + mCryptoStore, + mSendToDeviceTask, + mTaskExecutor, + deviceFingerprint, + transactionId, + otherUserId, + otherDeviceId, + isIncoming = false), + OutgoingSasVerificationRequest { + + + override val uxState: OutgoingSasVerificationRequest.UxState + get() { + return when (state) { + SasVerificationTxState.None -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_START + SasVerificationTxState.SendingStart, + SasVerificationTxState.Started, + SasVerificationTxState.OnAccepted, + SasVerificationTxState.SendingKey, + SasVerificationTxState.KeySent, + SasVerificationTxState.OnKeyReceived -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT + SasVerificationTxState.ShortCodeReady -> OutgoingSasVerificationRequest.UxState.SHOW_SAS + SasVerificationTxState.ShortCodeAccepted, + SasVerificationTxState.SendingMac, + SasVerificationTxState.MacSent, + SasVerificationTxState.Verifying -> OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION + SasVerificationTxState.Verified -> OutgoingSasVerificationRequest.UxState.VERIFIED + SasVerificationTxState.OnCancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME + SasVerificationTxState.Cancelled -> OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER + else -> OutgoingSasVerificationRequest.UxState.UNKNOWN + } + } + + override fun onVerificationStart(startReq: KeyVerificationStart) { + Timber.e("## onVerificationStart - unexpected id:$transactionId") + cancel(CancelCode.UnexpectedMessage) + } + + fun start() { + + if (state != SasVerificationTxState.None) { + Timber.e("## start verification from invalid state") + //should I cancel?? + throw IllegalStateException("Interactive Key verification already started") + } + + val startMessage = KeyVerificationStart() + startMessage.fromDevice = mCredentials.deviceId + startMessage.method = KeyVerificationStart.VERIF_METHOD_SAS + startMessage.transactionID = transactionId + startMessage.keyAgreementProtocols = KNOWN_AGREEMENT_PROTOCOLS + startMessage.hashes = KNOWN_HASHES + startMessage.messageAuthenticationCodes = KNOWN_MACS + startMessage.shortAuthenticationStrings = KNOWN_SHORT_CODES + + startReq = startMessage + val contentMap = MXUsersDevicesMap() + contentMap.setObject(startMessage, otherUserId, otherDeviceId) + state = SasVerificationTxState.SendingStart + + sendToOther( + EventType.KEY_VERIFICATION_START, + startMessage, + SasVerificationTxState.Started, + CancelCode.User, + null + ) + } + + override fun onVerificationAccept(accept: KeyVerificationAccept) { + Timber.d("## onVerificationAccept id:$transactionId") + if (state != SasVerificationTxState.Started) { + Timber.e("## received accept request from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + //Check that the agreement is correct + if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol) + || !KNOWN_HASHES.contains(accept.hash) + || !KNOWN_MACS.contains(accept.messageAuthenticationCode) + || accept.shortAuthenticationStrings!!.intersect(KNOWN_SHORT_CODES).isEmpty()) { + Timber.e("## received accept request from invalid state") + cancel(CancelCode.UnknownMethod) + return + } + + //Upon receipt of the m.key.verification.accept message from Bob’s device, + // Alice’s device stores the commitment value for later use. + accepted = accept + state = SasVerificationTxState.OnAccepted + + // Alice’s device creates an ephemeral Curve25519 key pair (dA,QA), + // and replies with a to_device message with type set to “m.key.verification.key”, sending Alice’s public key QA + val pubKey = getSAS().publicKey + + val keyToDevice = KeyVerificationKey.create(transactionId, pubKey) + //we need to send this to other device now + state = SasVerificationTxState.SendingKey + sendToOther(EventType.KEY_VERIFICATION_KEY, keyToDevice, SasVerificationTxState.KeySent, CancelCode.User) { + //It is possible that we receive the next event before this one :/, in this case we should keep state + if (state == SasVerificationTxState.SendingKey) { + state = SasVerificationTxState.KeySent + } + } + } + + override fun onKeyVerificationKey(userId: String, vKey: KeyVerificationKey) { + Timber.d("## onKeyVerificationKey id:$transactionId") + if (state != SasVerificationTxState.SendingKey && state != SasVerificationTxState.KeySent) { + Timber.e("## received key from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + + otherKey = vKey.key + // Upon receipt of the m.key.verification.key message from Bob’s device, + // Alice’s device checks that the commitment property from the Bob’s m.key.verification.accept + // message is the same as the expected value based on the value of the key property received + // in Bob’s m.key.verification.key and the content of Alice’s m.key.verification.start message. + + //check commitment + val concat = vKey.key + MoshiProvider.getCanonicalJson(KeyVerificationStart::class.java, startReq!!) + val otherCommitment = hashUsingAgreedHashMethod(concat) ?: "" + + if (accepted!!.commitment.equals(otherCommitment)) { + getSAS().setTheirPublicKey(otherKey) + //(Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function, + // the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of: + // - the string “MATRIX_KEY_VERIFICATION_SAS”, + // - the Matrix ID of the user who sent the m.key.verification.start message, + // - the device ID of the device that sent the m.key.verification.start message, + // - the Matrix ID of the user who sent the m.key.verification.accept message, + // - he device ID of the device that sent the m.key.verification.accept message + // - the transaction ID. + val sasInfo = "MATRIX_KEY_VERIFICATION_SAS" + + "${mCredentials.userId}${mCredentials.deviceId}" + + "$otherUserId$otherDeviceId" + + transactionId + //decimal: generate five bytes by using HKDF. + //emoji: generate six bytes by using HKDF. + shortCodeBytes = getSAS().generateShortCode(sasInfo, 6) + state = SasVerificationTxState.ShortCodeReady + } else { + //bad commitement + cancel(CancelCode.MismatchedCommitment) + } + } + + override fun onKeyVerificationMac(vKey: KeyVerificationMac) { + Timber.d("## onKeyVerificationMac id:$transactionId") + if (state != SasVerificationTxState.OnKeyReceived + && state != SasVerificationTxState.ShortCodeReady + && state != SasVerificationTxState.ShortCodeAccepted + && state != SasVerificationTxState.SendingMac + && state != SasVerificationTxState.MacSent) { + Timber.e("## received key from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + + theirMac = vKey + + //Do I have my Mac? + if (myMac != null) { + //I can check + verifyMacs() + } + //Wait for ShortCode Accepted + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt new file mode 100644 index 00000000..583029a1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt @@ -0,0 +1,420 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.verification + +import android.os.Build +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation +import im.vector.matrix.android.api.session.crypto.sas.SasMode +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXKey +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.model.rest.* +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import org.matrix.olm.OlmSAS +import org.matrix.olm.OlmUtility +import timber.log.Timber +import kotlin.properties.Delegates + +/** + * Represents an ongoing short code interactive key verification between two devices. + */ +internal abstract class SASVerificationTransaction( + private val mSasVerificationService: DefaultSasVerificationService, + private val mCredentials: Credentials, + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor, + private val deviceFingerprint: String, + transactionId: String, + otherUserId: String, + otherDevice: String?, + isIncoming: Boolean) : + VerificationTransaction(transactionId, otherUserId, otherDevice, isIncoming) { + + companion object { + const val SAS_MAC_SHA256_LONGKDF = "hmac-sha256" + const val SAS_MAC_SHA256 = "hkdf-hmac-sha256" + + //ordered by preferred order + val KNOWN_AGREEMENT_PROTOCOLS = listOf(MXKey.KEY_CURVE_25519_TYPE) + //ordered by preferred order + val KNOWN_HASHES = listOf("sha256") + //ordered by preferred order + val KNOWN_MACS = listOf(SAS_MAC_SHA256, SAS_MAC_SHA256_LONGKDF) + + //older devices have limited support of emoji, so reply with decimal + val KNOWN_SHORT_CODES = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + listOf(SasMode.EMOJI, SasMode.DECIMAL) + else + listOf(SasMode.DECIMAL) + + } + + override var state by Delegates.observable(SasVerificationTxState.None) { _, _, new -> + // println("$property has changed from $old to $new") + listeners.forEach { + try { + it.transactionUpdated(this) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + if (new == SasVerificationTxState.Cancelled + || new == SasVerificationTxState.OnCancelled + || new == SasVerificationTxState.Verified) { + releaseSAS() + } + } + + override var cancelledReason: CancelCode? = null + + private var olmSas: OlmSAS? = null + + var startReq: KeyVerificationStart? = null + var accepted: KeyVerificationAccept? = null + var otherKey: String? = null + var shortCodeBytes: ByteArray? = null + + var myMac: KeyVerificationMac? = null + var theirMac: KeyVerificationMac? = null + + fun getSAS(): OlmSAS { + if (olmSas == null) olmSas = OlmSAS() + return olmSas!! + } + + //To override finalize(), all you need to do is simply declare it, without using the override keyword: + protected fun finalize() { + releaseSAS() + } + + private fun releaseSAS() { + // finalization logic + olmSas?.releaseSas() + olmSas = null + } + + + /** + * To be called by the client when the user has verified that + * both short codes do match + */ + override fun userHasVerifiedShortCode() { + Timber.d("## SAS short code verified by user for id:$transactionId") + if (state != SasVerificationTxState.ShortCodeReady) { + //ignore and cancel? + Timber.e("## Accepted short code from invalid state $state") + cancel(CancelCode.UnexpectedMessage) + return + } + + state = SasVerificationTxState.ShortCodeAccepted + //Alice and Bob’ devices calculate the HMAC of their own device keys and a comma-separated, + // sorted list of the key IDs that they wish the other user to verify, + //the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of: + // - the string “MATRIX_KEY_VERIFICATION_MAC”, + // - the Matrix ID of the user whose key is being MAC-ed, + // - the device ID of the device sending the MAC, + // - the Matrix ID of the other user, + // - the device ID of the device receiving the MAC, + // - the transaction ID, and + // - the key ID of the key being MAC-ed, or the string “KEY_IDS” if the item being MAC-ed is the list of key IDs. + + val baseInfo = "MATRIX_KEY_VERIFICATION_MAC" + + mCredentials.userId + mCredentials.deviceId + + otherUserId + otherDeviceId + + transactionId + + val keyId = "ed25519:${mCredentials.deviceId}" + val macString = macUsingAgreedMethod(deviceFingerprint, baseInfo + keyId) + val keyStrings = macUsingAgreedMethod(keyId, baseInfo + "KEY_IDS") + + if (macString.isNullOrBlank() || keyStrings.isNullOrBlank()) { + //Should not happen + Timber.e("## SAS verification [$transactionId] failed to send KeyMac, empty key hashes.") + cancel(CancelCode.UnexpectedMessage) + return + } + + val macMsg = KeyVerificationMac.create(transactionId, mapOf(keyId to macString), keyStrings) + myMac = macMsg + state = SasVerificationTxState.SendingMac + sendToOther(EventType.KEY_VERIFICATION_MAC, macMsg, SasVerificationTxState.MacSent, CancelCode.User) { + if (state == SasVerificationTxState.SendingMac) { + //It is possible that we receive the next event before this one :/, in this case we should keep state + state = SasVerificationTxState.MacSent + } + } + + //Do I already have their Mac? + if (theirMac != null) { + verifyMacs() + } //if not wait for it + + } + + override fun acceptToDeviceEvent(senderId: String, event: SendToDeviceObject) { + when (event) { + is KeyVerificationStart -> onVerificationStart(event) + is KeyVerificationAccept -> onVerificationAccept(event) + is KeyVerificationKey -> onKeyVerificationKey(senderId, event) + is KeyVerificationMac -> onKeyVerificationMac(event) + else -> { + //nop + } + } + } + + abstract fun onVerificationStart(startReq: KeyVerificationStart) + + abstract fun onVerificationAccept(accept: KeyVerificationAccept) + + abstract fun onKeyVerificationKey(userId: String, vKey: KeyVerificationKey) + + abstract fun onKeyVerificationMac(vKey: KeyVerificationMac) + + protected fun verifyMacs() { + Timber.d("## SAS verifying macs for id:$transactionId") + state = SasVerificationTxState.Verifying + + //Keys have been downloaded earlier in process + val otherUserKnownDevices = mCryptoStore.getUserDevices(otherUserId) + + // Bob’s device calculates the HMAC (as above) of its copies of Alice’s keys given in the message (as identified by their key ID), + // as well as the HMAC of the comma-separated, sorted list of the key IDs given in the message. + // Bob’s device compares these with the HMAC values given in the m.key.verification.mac message. + // If everything matches, then consider Alice’s device keys as verified. + + val baseInfo = "MATRIX_KEY_VERIFICATION_MAC" + + otherUserId + otherDeviceId + + mCredentials.userId + mCredentials.deviceId + + transactionId + + val commaSeparatedListOfKeyIds = theirMac!!.mac!!.keys.sorted().joinToString(",") + + val keyStrings = macUsingAgreedMethod(commaSeparatedListOfKeyIds, baseInfo + "KEY_IDS") + if (theirMac!!.keys != keyStrings) { + //WRONG! + cancel(CancelCode.MismatchedKeys) + return + } + //cannot be empty because it has been validated + theirMac!!.mac!!.keys.forEach { + val keyIDNoPrefix = if (it.startsWith("ed25519:")) it.substring("ed25519:".length) else it + val otherDeviceKey = otherUserKnownDevices?.get(keyIDNoPrefix)?.fingerprint() + if (otherDeviceKey == null) { + cancel(CancelCode.MismatchedKeys) + return + } + val mac = macUsingAgreedMethod(otherDeviceKey, baseInfo + it) + if (mac != theirMac?.mac?.get(it)) { + //WRONG! + cancel(CancelCode.MismatchedKeys) + return + } + } + + setDeviceVerified( + otherDeviceId ?: "", + otherUserId, + success = { + state = SasVerificationTxState.Verified + }, + error = { + //mmm what to do?, looks like this is never called + } + ) + } + + private fun setDeviceVerified(deviceId: String, userId: String, success: () -> Unit, error: () -> Unit) { + mSasVerificationService.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + deviceId, + userId, + object : MatrixCallback { + + override fun onSuccess(data: Unit) { + //We good + Timber.d("## SAS verification complete and device status updated for id:$transactionId") + success() + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## SAS verification [$transactionId] failed in state : $state") + error() + } + }) + } + + override fun cancel() { + cancel(CancelCode.User) + } + + override fun cancel(code: CancelCode) { + cancelledReason = code + state = SasVerificationTxState.Cancelled + mSasVerificationService.cancelTransaction( + transactionId, + otherUserId, + otherDeviceId ?: "", + code) + } + + protected fun sendToOther(type: String, + keyToDevice: Any, + nextState: SasVerificationTxState, + onErrorReason: CancelCode, + onDone: (() -> Unit)?) { + val contentMap = MXUsersDevicesMap() + contentMap.setObject(keyToDevice, otherUserId, otherDeviceId) + + mSendToDeviceTask.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId)) + .dispatchTo(object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.d("## SAS verification [$transactionId] toDevice type '$type' success.") + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + if (onDone != null) { + onDone() + } else { + state = nextState + } + } + } + + override fun onFailure(failure: Throwable) { + Timber.e("## SAS verification [$transactionId] failed to send toDevice in state : $state") + + CryptoAsyncHelper.getDecryptBackgroundHandler().post { + cancel(onErrorReason) + } + } + }) + .executeBy(mTaskExecutor) + } + + fun getShortCodeRepresentation(shortAuthenticationStringMode: String): String? { + if (shortCodeBytes == null) { + return null + } + when (shortAuthenticationStringMode) { + SasMode.DECIMAL -> { + if (shortCodeBytes!!.size < 5) return null + return getDecimalCodeRepresentation(shortCodeBytes!!) + } + SasMode.EMOJI -> { + if (shortCodeBytes!!.size < 6) return null + return getEmojiCodeRepresentation(shortCodeBytes!!).joinToString(" ") { it.emoji } + } + else -> return null + } + } + + override fun supportsEmoji(): Boolean { + return accepted?.shortAuthenticationStrings?.contains(SasMode.EMOJI) == true + } + + override fun supportsDecimal(): Boolean { + return accepted?.shortAuthenticationStrings?.contains(SasMode.DECIMAL) == true + } + + protected fun hashUsingAgreedHashMethod(toHash: String): String? { + if ("sha256".toLowerCase() == accepted?.hash?.toLowerCase()) { + val olmUtil = OlmUtility() + val hashBytes = olmUtil.sha256(toHash) + olmUtil.releaseUtility() + return hashBytes + } + return null + } + + protected fun macUsingAgreedMethod(message: String, info: String): String? { + if (SAS_MAC_SHA256_LONGKDF.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { + return getSAS().calculateMacLongKdf(message, info) + } else if (SAS_MAC_SHA256.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { + return getSAS().calculateMac(message, info) + } + return null + } + + override fun getDecimalCodeRepresentation(): String { + return getDecimalCodeRepresentation(shortCodeBytes!!) + } + + /** + * decimal: generate five bytes by using HKDF. + * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive), + * and add 1000 (resulting in a number between 1000 and 9191 inclusive). + * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers. + * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000, + * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000. + * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic, + * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.) + * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers, + * or with the three numbers on separate lines. + */ + fun getDecimalCodeRepresentation(byteArray: ByteArray): String { + val b0 = byteArray[0].toInt().and(0xff) //need unsigned byte + val b1 = byteArray[1].toInt().and(0xff) //need unsigned byte + val b2 = byteArray[2].toInt().and(0xff) //need unsigned byte + val b3 = byteArray[3].toInt().and(0xff) //need unsigned byte + val b4 = byteArray[4].toInt().and(0xff) //need unsigned byte + //(B0 << 5 | B1 >> 3) + 1000 + val first = (b0.shl(5) or b1.shr(3)) + 1000 + //((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 + val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 + //((B3 & 0x3f) << 7 | B4 >> 1) + 1000 + val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 + return "$first $second $third" + } + + override fun getEmojiCodeRepresentation(): List { + return getEmojiCodeRepresentation(shortCodeBytes!!) + } + + /** + * emoji: generate six bytes by using HKDF. + * Split the first 42 bits into 7 groups of 6 bits, as one would do when creating a base64 encoding. + * For each group of 6 bits, look up the emoji from Appendix A corresponding + * to that number 7 emoji are selected from a list of 64 emoji (see Appendix A) + */ + fun getEmojiCodeRepresentation(byteArray: ByteArray): List { + val b0 = byteArray[0].toInt().and(0xff) + val b1 = byteArray[1].toInt().and(0xff) + val b2 = byteArray[2].toInt().and(0xff) + val b3 = byteArray[3].toInt().and(0xff) + val b4 = byteArray[4].toInt().and(0xff) + val b5 = byteArray[5].toInt().and(0xff) + return listOf( + getEmojiForCode((b0 and 0xFC).shr(2)), + getEmojiForCode((b0 and 0x3).shl(4) or (b1 and 0xF0).shr(4)), + getEmojiForCode((b1 and 0xF).shl(2) or (b2 and 0xC0).shr(6)), + getEmojiForCode((b2 and 0x3F)), + getEmojiForCode((b3 and 0xFC).shr(2)), + getEmojiForCode((b3 and 0x3).shl(4) or (b4 and 0xF0).shr(4)), + getEmojiForCode((b4 and 0xF).shl(2) or (b5 and 0xC0).shr(6)) + ) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationEmoji.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationEmoji.kt new file mode 100644 index 00000000..0e814083 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationEmoji.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.verification + +import im.vector.matrix.android.R +import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation + +internal fun getEmojiForCode(code: Int): EmojiRepresentation { + return when (code % 64) { + 0 -> EmojiRepresentation("🐶", R.string.verification_emoji_dog) + 1 -> EmojiRepresentation("🐱", R.string.verification_emoji_cat) + 2 -> EmojiRepresentation("🦁", R.string.verification_emoji_lion) + 3 -> EmojiRepresentation("🐎", R.string.verification_emoji_horse) + 4 -> EmojiRepresentation("🦄", R.string.verification_emoji_unicorn) + 5 -> EmojiRepresentation("🐷", R.string.verification_emoji_pig) + 6 -> EmojiRepresentation("🐘", R.string.verification_emoji_elephant) + 7 -> EmojiRepresentation("🐰", R.string.verification_emoji_rabbit) + 8 -> EmojiRepresentation("🐼", R.string.verification_emoji_panda) + 9 -> EmojiRepresentation("🐓", R.string.verification_emoji_rooster) + 10 -> EmojiRepresentation("🐧", R.string.verification_emoji_penguin) + 11 -> EmojiRepresentation("🐢", R.string.verification_emoji_turtle) + 12 -> EmojiRepresentation("🐟", R.string.verification_emoji_fish) + 13 -> EmojiRepresentation("🐙", R.string.verification_emoji_octopus) + 14 -> EmojiRepresentation("🦋", R.string.verification_emoji_butterfly) + 15 -> EmojiRepresentation("🌷", R.string.verification_emoji_flower) + 16 -> EmojiRepresentation("🌳", R.string.verification_emoji_tree) + 17 -> EmojiRepresentation("🌵", R.string.verification_emoji_cactus) + 18 -> EmojiRepresentation("🍄", R.string.verification_emoji_mushroom) + 19 -> EmojiRepresentation("🌏", R.string.verification_emoji_globe) + 20 -> EmojiRepresentation("🌙", R.string.verification_emoji_moon) + 21 -> EmojiRepresentation("☁️", R.string.verification_emoji_cloud) + 22 -> EmojiRepresentation("🔥", R.string.verification_emoji_fire) + 23 -> EmojiRepresentation("🍌", R.string.verification_emoji_banana) + 24 -> EmojiRepresentation("🍎", R.string.verification_emoji_apple) + 25 -> EmojiRepresentation("🍓", R.string.verification_emoji_strawberry) + 26 -> EmojiRepresentation("🌽", R.string.verification_emoji_corn) + 27 -> EmojiRepresentation("🍕", R.string.verification_emoji_pizza) + 28 -> EmojiRepresentation("🎂", R.string.verification_emoji_cake) + 29 -> EmojiRepresentation("❤️", R.string.verification_emoji_heart) + 30 -> EmojiRepresentation("😀", R.string.verification_emoji_smiley) + 31 -> EmojiRepresentation("🤖", R.string.verification_emoji_robot) + 32 -> EmojiRepresentation("🎩", R.string.verification_emoji_hat) + 33 -> EmojiRepresentation("👓", R.string.verification_emoji_glasses) + 34 -> EmojiRepresentation("🔧", R.string.verification_emoji_wrench) + 35 -> EmojiRepresentation("🎅", R.string.verification_emoji_santa) + 36 -> EmojiRepresentation("👍", R.string.verification_emoji_thumbsup) + 37 -> EmojiRepresentation("☂️", R.string.verification_emoji_umbrella) + 38 -> EmojiRepresentation("⌛", R.string.verification_emoji_hourglass) + 39 -> EmojiRepresentation("⏰", R.string.verification_emoji_clock) + 40 -> EmojiRepresentation("🎁", R.string.verification_emoji_gift) + 41 -> EmojiRepresentation("💡", R.string.verification_emoji_lightbulb) + 42 -> EmojiRepresentation("📕", R.string.verification_emoji_book) + 43 -> EmojiRepresentation("✏️", R.string.verification_emoji_pencil) + 44 -> EmojiRepresentation("📎", R.string.verification_emoji_paperclip) + 45 -> EmojiRepresentation("✂️", R.string.verification_emoji_scissors) + 46 -> EmojiRepresentation("🔒", R.string.verification_emoji_lock) + 47 -> EmojiRepresentation("🔑", R.string.verification_emoji_key) + 48 -> EmojiRepresentation("🔨", R.string.verification_emoji_hammer) + 49 -> EmojiRepresentation("☎️", R.string.verification_emoji_telephone) + 50 -> EmojiRepresentation("🏁", R.string.verification_emoji_flag) + 51 -> EmojiRepresentation("🚂", R.string.verification_emoji_train) + 52 -> EmojiRepresentation("🚲", R.string.verification_emoji_bicycle) + 53 -> EmojiRepresentation("✈️", R.string.verification_emoji_airplane) + 54 -> EmojiRepresentation("🚀", R.string.verification_emoji_rocket) + 55 -> EmojiRepresentation("🏆", R.string.verification_emoji_trophy) + 56 -> EmojiRepresentation("⚽", R.string.verification_emoji_ball) + 57 -> EmojiRepresentation("🎸", R.string.verification_emoji_guitar) + 58 -> EmojiRepresentation("🎺", R.string.verification_emoji_trumpet) + 59 -> EmojiRepresentation("🔔", R.string.verification_emoji_bell) + 60 -> EmojiRepresentation("⚓", R.string.verification_emoji_anchor) + 61 -> EmojiRepresentation("🎧", R.string.verification_emoji_headphone) + 62 -> EmojiRepresentation("📁", R.string.verification_emoji_folder) + /* 63 */ else -> EmojiRepresentation("📌", R.string.verification_emoji_pin) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransaction.kt new file mode 100644 index 00000000..be3f4c78 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransaction.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.internal.crypto.verification + +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction +import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceObject + +/** + * Generic interactive key verification transaction + */ +internal abstract class VerificationTransaction( + override val transactionId: String, + override val otherUserId: String, + override var otherDeviceId: String? = null, + override val isIncoming: Boolean) : SasVerificationTransaction { + + interface Listener { + fun transactionUpdated(tx: VerificationTransaction) + } + + protected var listeners = ArrayList() + + fun addListener(listener: Listener) { + if (!listeners.contains(listener)) listeners.add(listener) + } + + fun removeListener(listener: Listener) { + listeners.remove(listener) + } + + abstract fun acceptToDeviceEvent(senderId: String, event: SendToDeviceObject) + + abstract fun cancel(code: CancelCode) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt index becc0cfe..eb2af1ad 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt @@ -17,17 +17,7 @@ package im.vector.matrix.android.internal.di import com.squareup.moshi.Moshi -import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent -import im.vector.matrix.android.api.session.room.model.message.MessageContent -import im.vector.matrix.android.api.session.room.model.message.MessageDefaultContent -import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent -import im.vector.matrix.android.api.session.room.model.message.MessageFileContent -import im.vector.matrix.android.api.session.room.model.message.MessageImageContent -import im.vector.matrix.android.api.session.room.model.message.MessageLocationContent -import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent -import im.vector.matrix.android.api.session.room.model.message.MessageTextContent -import im.vector.matrix.android.api.session.room.model.message.MessageType -import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent +import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter import im.vector.matrix.android.internal.session.sync.model.UserAccountData @@ -39,17 +29,17 @@ object MoshiProvider { private val moshi: Moshi = Moshi.Builder() .add(UriMoshiAdapter()) .add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java) - .registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES) + .registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES) ) .add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java) - .registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT) - .registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE) - .registerSubtype(MessageEmoteContent::class.java, MessageType.MSGTYPE_EMOTE) - .registerSubtype(MessageAudioContent::class.java, MessageType.MSGTYPE_AUDIO) - .registerSubtype(MessageImageContent::class.java, MessageType.MSGTYPE_IMAGE) - .registerSubtype(MessageVideoContent::class.java, MessageType.MSGTYPE_VIDEO) - .registerSubtype(MessageLocationContent::class.java, MessageType.MSGTYPE_LOCATION) - .registerSubtype(MessageFileContent::class.java, MessageType.MSGTYPE_FILE) + .registerSubtype(MessageTextContent::class.java, MessageType.MSGTYPE_TEXT) + .registerSubtype(MessageNoticeContent::class.java, MessageType.MSGTYPE_NOTICE) + .registerSubtype(MessageEmoteContent::class.java, MessageType.MSGTYPE_EMOTE) + .registerSubtype(MessageAudioContent::class.java, MessageType.MSGTYPE_AUDIO) + .registerSubtype(MessageImageContent::class.java, MessageType.MSGTYPE_IMAGE) + .registerSubtype(MessageVideoContent::class.java, MessageType.MSGTYPE_VIDEO) + .registerSubtype(MessageLocationContent::class.java, MessageType.MSGTYPE_LOCATION) + .registerSubtype(MessageFileContent::class.java, MessageType.MSGTYPE_FILE) ) .build() @@ -57,4 +47,11 @@ object MoshiProvider { return moshi } + fun getCanonicalJson(type: Class, o: T): String { + val adadpter = moshi.adapter(type) + + // FIXME It is not canonical... + return adadpter.toJson(o) + } + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 42e5d8ed..3ebc200a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -16,16 +16,22 @@ package im.vector.matrix.android.internal.session +import android.content.Context import android.os.Looper import androidx.annotation.MainThread import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.api.session.content.ContentUrlResolver +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.group.Group import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.model.GroupSummary @@ -38,6 +44,13 @@ import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.MatrixCallbackDelegate +import im.vector.matrix.android.internal.crypto.CryptoModule +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinHolder @@ -53,7 +66,6 @@ import org.koin.standalone.inject internal class DefaultSession(override val sessionParams: SessionParams) : Session, MatrixKoinComponent { - companion object { const val SCOPE: String = "session" } @@ -69,6 +81,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi private val filterService by inject() private val cacheService by inject() private val signOutService by inject() + private val cryptoService by inject() private val syncThread by inject() private val contentUrlResolver by inject() private val contentUploadProgressTracker by inject() @@ -86,11 +99,20 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi val signOutModule = SignOutModule().definition val userModule = UserModule().definition val contentModule = ContentModule().definition - MatrixKoinHolder.instance.loadModules(listOf(sessionModule, syncModule, roomModule, groupModule, userModule, signOutModule, contentModule)) + val cryptoModule = CryptoModule().definition + MatrixKoinHolder.instance.loadModules(listOf(sessionModule, + syncModule, + roomModule, + groupModule, + userModule, + signOutModule, + contentModule, + cryptoModule)) scope = getKoin().getOrCreateScope(SCOPE) if (!monarchy.isMonarchyThreadOpen) { monarchy.openManually() } + cryptoService.start(false, null) liveEntityUpdaters.forEach { it.start() } } @@ -111,6 +133,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi assertMainThread() assert(isOpen) liveEntityUpdaters.forEach { it.dispose() } + cryptoService.close() if (monarchy.isMonarchyThreadOpen) { monarchy.closeManually() } @@ -208,6 +231,116 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi return userService.getUser(userId) } + // CRYPTO SERVICE + + override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) { + cryptoService.setDeviceName(deviceId, deviceName, callback) + } + + override fun deleteDevice(deviceId: String, accountPassword: String, callback: MatrixCallback) { + cryptoService.deleteDevice(deviceId, accountPassword, callback) + } + + override fun getCryptoVersion(context: Context, longFormat: Boolean): String { + return cryptoService.getCryptoVersion(context, longFormat) + } + + override fun isCryptoEnabled(): Boolean { + return cryptoService.isCryptoEnabled() + } + + override fun getSasVerificationService(): SasVerificationService { + return cryptoService.getSasVerificationService() + } + + override fun getKeysBackupService(): KeysBackupService { + return cryptoService.getKeysBackupService() + } + + override fun isRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback?) { + cryptoService.isRoomBlacklistUnverifiedDevices(roomId, callback) + } + + override fun setWarnOnUnknownDevices(warn: Boolean) { + cryptoService.setWarnOnUnknownDevices(warn) + } + + override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String, callback: MatrixCallback) { + cryptoService.setDeviceVerification(verificationStatus, deviceId, userId, callback) + } + + override fun getUserDevices(userId: String): MutableList { + return cryptoService.getUserDevices(userId) + } + + override fun setDevicesKnown(devices: List, callback: MatrixCallback?) { + cryptoService.setDevicesKnown(devices, callback) + } + + override fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? { + return cryptoService.deviceWithIdentityKey(senderKey, algorithm) + } + + override fun getMyDevice(): MXDeviceInfo { + return cryptoService.getMyDevice() + } + + override fun getDevicesList(callback: MatrixCallback) { + cryptoService.getDevicesList(callback) + } + + override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int { + return cryptoService.inboundGroupSessionsCount(onlyBackedUp) + } + + override fun getGlobalBlacklistUnverifiedDevices(callback: MatrixCallback?) { + cryptoService.getGlobalBlacklistUnverifiedDevices(callback) + } + + override fun setGlobalBlacklistUnverifiedDevices(block: Boolean, callback: MatrixCallback?) { + cryptoService.setGlobalBlacklistUnverifiedDevices(block, callback) + } + + override fun setRoomUnBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) { + cryptoService.setRoomUnBlacklistUnverifiedDevices(roomId, callback) + } + + override fun getDeviceTrackingStatus(userId: String): Int { + return cryptoService.getDeviceTrackingStatus(userId) + } + + override fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?, callback: MatrixCallback) { + cryptoService.importRoomKeys(roomKeysAsArray, password, progressListener, callback) + } + + override fun exportRoomKeys(password: String, callback: MatrixCallback) { + cryptoService.exportRoomKeys(password, callback) + } + + override fun setRoomBlacklistUnverifiedDevices(roomId: String, callback: MatrixCallback) { + cryptoService.setRoomBlacklistUnverifiedDevices(roomId, callback) + } + + override fun getDeviceInfo(userId: String, deviceId: String?, callback: MatrixCallback) { + cryptoService.getDeviceInfo(userId, deviceId, callback) + } + + override fun reRequestRoomKeyForEvent(event: Event) { + cryptoService.reRequestRoomKeyForEvent(event) + } + + override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) { + cryptoService.cancelRoomKeyRequest(requestBody) + } + + override fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { + cryptoService.addRoomKeysRequestListener(listener) + } + + override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + return cryptoService.decryptEvent(event, timeline) + } + // Private methods ***************************************************************************** private fun assertMainThread() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index accb91ec..d877bd3f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -60,7 +60,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { sessionParams.credentials } - scope(DefaultSession.SCOPE) { + scope(DefaultSession.SCOPE, name = "SessionRealmConfiguration") { val context = get() val childPath = sessionParams.credentials.userId.md5() val directory = File(context.filesDir, childPath) @@ -75,7 +75,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { scope(DefaultSession.SCOPE) { Monarchy.Builder() - .setRealmConfiguration(get()) + .setRealmConfiguration(get("SessionRealmConfiguration")) .build() } @@ -119,7 +119,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { } scope(DefaultSession.SCOPE) { - RealmClearCacheTask(get()) as ClearCacheTask + RealmClearCacheTask(get("SessionRealmConfiguration")) as ClearCacheTask } scope(DefaultSession.SCOPE) { @@ -131,7 +131,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { } scope(DefaultSession.SCOPE) { - DefaultFilterRepository(get()) as FilterRepository + DefaultFilterRepository(get("SessionRealmConfiguration")) as FilterRepository } scope(DefaultSession.SCOPE) { @@ -142,7 +142,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { DefaultFilterService(get(), get(), get()) as FilterService } - scope(DefaultSession.SCOPE) { + scope(DefaultSession.SCOPE) { val retrofit: Retrofit = get() retrofit.create(FilterApi::class.java) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 973b7381..192bbac0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -75,7 +75,7 @@ class RoomModule { } scope(DefaultSession.SCOPE) { - DefaultCreateRoomTask(get(), get()) as CreateRoomTask + DefaultCreateRoomTask(get(), get("SessionRealmConfiguration")) as CreateRoomTask } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/DefaultRoomMembersService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/DefaultRoomMembersService.kt index a007a1eb..1e1c898a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/DefaultRoomMembersService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/DefaultRoomMembersService.kt @@ -68,4 +68,26 @@ internal class DefaultRoomMembersService(private val roomId: String, .dispatchTo(callback) .executeBy(taskExecutor) } + + override fun getActiveRoomMemberIds(): List { + return getRoomMemberIdsFiltered { it.membership == Membership.JOIN || it.membership == Membership.INVITE } + } + + override fun getJoinedRoomMemberIds(): List { + return getRoomMemberIdsFiltered { it.membership == Membership.JOIN } + } + + /* ========================================================================================== + * Private + * ========================================================================================== */ + + private fun getRoomMemberIdsFiltered(predicate: (RoomMember) -> Boolean): List { + return monarchy.fetchAllCopiedSync { RoomMembers(it, roomId).queryRoomMembersEvent() } + .map { it.asDomain() } + .associateBy { it.stateKey!! } + .mapValues { it.value.content.toModel()!! } + .filterValues { predicate(it) } + .keys + .toList() + } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMembers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMembers.kt index 40266cb7..98d33f12 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMembers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMembers.kt @@ -49,7 +49,7 @@ internal class RoomMembers(private val realm: Realm, } fun isUniqueDisplayName(displayName: String?): Boolean { - if(displayName.isNullOrEmpty()){ + if (displayName.isNullOrEmpty()) { return true } return EventEntity @@ -83,12 +83,12 @@ internal class RoomMembers(private val realm: Realm, fun getNumberOfJoinedMembers(): Int { return roomSummary?.joinedMembersCount - ?: getLoaded().filterValues { it.membership == Membership.JOIN }.size + ?: getLoaded().filterValues { it.membership == Membership.JOIN }.size } fun getNumberOfInvitedMembers(): Int { return roomSummary?.invitedMembersCount - ?: getLoaded().filterValues { it.membership == Membership.INVITE }.size + ?: getLoaded().filterValues { it.membership == Membership.INVITE }.size } fun getNumberOfMembers(): Int { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt new file mode 100644 index 00000000..992ad747 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.session.sync + +import im.vector.matrix.android.internal.crypto.CryptoManager +import im.vector.matrix.android.internal.session.sync.model.SyncResponse +import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse + + +internal class CryptoSyncHandler(private val crypto: CryptoManager) { + + fun handleToDevice(toDevice: ToDeviceSyncResponse) { + toDevice.events?.forEach { + crypto.onToDeviceEvent(it) + } + + } + + fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, catchingUp: Boolean) { + crypto.onSyncCompleted(syncResponse, fromToken, catchingUp) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 24dff339..3ed252d5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.MyMembership import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent +import im.vector.matrix.android.internal.crypto.CryptoManager import im.vector.matrix.android.internal.database.helper.addAll import im.vector.matrix.android.internal.database.helper.addOrUpdate import im.vector.matrix.android.internal.database.helper.addStateEvents @@ -33,11 +34,7 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection -import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync -import im.vector.matrix.android.internal.session.sync.model.RoomSync -import im.vector.matrix.android.internal.session.sync.model.RoomSyncAccountData -import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral -import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse +import im.vector.matrix.android.internal.session.sync.model.* import io.realm.Realm import io.realm.kotlin.createObject import timber.log.Timber @@ -45,7 +42,8 @@ import timber.log.Timber internal class RoomSyncHandler(private val monarchy: Monarchy, private val readReceiptHandler: ReadReceiptHandler, private val roomSummaryUpdater: RoomSummaryUpdater, - private val roomTagHandler: RoomTagHandler) { + private val roomTagHandler: RoomTagHandler, + private val mCrypto: CryptoManager) { sealed class HandlingStrategy { data class JOINED(val data: Map) : HandlingStrategy() @@ -65,9 +63,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) { val rooms = when (handlingStrategy) { - is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) } + is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) } is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) } - is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) } + is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) } } realm.insertOrUpdate(rooms) } @@ -79,7 +77,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, Timber.v("Handle join sync for room $roomId") val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) if (roomEntity.membership == MyMembership.INVITED) { roomEntity.chunks.deleteAllFromRealm() @@ -110,6 +108,11 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, ) roomEntity.addOrUpdate(chunkEntity) + // Give info to crypto module + roomSync.timeline.events.forEach { + mCrypto.onLiveEvent(roomId, it) + } + // Try to remove local echo val transactionIds = roomSync.timeline.events.mapNotNull { it.unsignedData?.transactionId } transactionIds.forEach { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt index d0f60405..d954667c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt @@ -40,19 +40,23 @@ internal class SyncModule { } scope(DefaultSession.SCOPE) { - RoomSyncHandler(get(), get(), get(), get()) + RoomSyncHandler(get(), get(), get(), get(), get()) } scope(DefaultSession.SCOPE) { GroupSyncHandler(get()) } + scope(DefaultSession.SCOPE) { + CryptoSyncHandler(get()) + } + scope(DefaultSession.SCOPE) { UserAccountDataSyncHandler(get()) } scope(DefaultSession.SCOPE) { - SyncResponseHandler(get(), get(), get()) + SyncResponseHandler(get(), get(), get(), get()) } scope(DefaultSession.SCOPE) { @@ -60,7 +64,7 @@ internal class SyncModule { } scope(DefaultSession.SCOPE) { - SyncTokenStore(get()) + SyncTokenStore(get("SessionRealmConfiguration")) } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt index d9859526..011e2ddb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt @@ -23,12 +23,18 @@ import kotlin.system.measureTimeMillis internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, private val userAccountDataSyncHandler: UserAccountDataSyncHandler, - private val groupSyncHandler: GroupSyncHandler) { + private val groupSyncHandler: GroupSyncHandler, + private val cryptoSyncHandler: CryptoSyncHandler) { fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try { return Try { Timber.v("Start handling sync") val measure = measureTimeMillis { + // Handle the to device events before the room ones + // to ensure to decrypt them properly + if (syncResponse.toDevice != null) { + cryptoSyncHandler.handleToDevice(syncResponse.toDevice) + } if (syncResponse.rooms != null) { roomSyncHandler.handle(syncResponse.rooms) } @@ -38,6 +44,8 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, if (syncResponse.accountData != null) { userAccountDataSyncHandler.handle(syncResponse.accountData) } + + cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp) } Timber.v("Finish handling sync in $measure ms") syncResponse diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt index ed0be6eb..c1d9e0a1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt @@ -22,5 +22,4 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class DeviceOneTimeKeysCountSyncResponse( @Json(name = "signed_curve25519") val signedCurve25519: Int? = null - ) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt new file mode 100644 index 00000000..76797ac7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CompatUtil.kt @@ -0,0 +1,325 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.util + +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import android.preference.PreferenceManager +import android.security.KeyPairGeneratorSpec +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import android.util.Base64 +import androidx.annotation.RequiresApi +import timber.log.Timber +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.math.BigInteger +import java.security.* +import java.security.cert.CertificateException +import java.security.spec.AlgorithmParameterSpec +import java.security.spec.RSAKeyGenParameterSpec +import java.util.* +import java.util.zip.GZIPOutputStream +import javax.crypto.* +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import javax.security.auth.x500.X500Principal + +object CompatUtil { + private val TAG = CompatUtil::class.java.simpleName + private const val ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore" + private const val AES_GCM_CIPHER_TYPE = "AES/GCM/NoPadding" + private const val AES_GCM_KEY_SIZE_IN_BITS = 128 + private const val AES_GCM_IV_LENGTH = 12 + private const val AES_LOCAL_PROTECTION_KEY_ALIAS = "aes_local_protection" + + private const val RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS = "rsa_wrap_local_protection" + private const val RSA_WRAP_CIPHER_TYPE = "RSA/NONE/PKCS1Padding" + private const val AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE = "aes_wrapped_local_protection" + + private const val SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED = "android_version_when_key_has_been_generated" + + private var sSecretKeyAndVersion: SecretKeyAndVersion? = null + private var sPrng: SecureRandom? = null + + /** + * Returns the unique SecureRandom instance shared for all local storage encryption operations. + */ + private val prng: SecureRandom + get() { + if (sPrng == null) { + sPrng = SecureRandom() + } + + return sPrng!! + } + + /** + * Create a GZIPOutputStream instance + * Special treatment on KitKat device, force the syncFlush param to false + * Before Kitkat, this param does not exist and after Kitkat it is set to false by default + * + * @param outputStream the output stream + */ + @Throws(IOException::class) + fun createGzipOutputStream(outputStream: OutputStream): GZIPOutputStream { + return if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + GZIPOutputStream(outputStream, false) + } else { + GZIPOutputStream(outputStream) + } + } + + /** + * Returns the AES key used for local storage encryption/decryption with AES/GCM. + * The key is created if it does not exist already in the keystore. + * From Marshmallow, this key is generated and operated directly from the android keystore. + * From KitKat and before Marshmallow, this key is stored in the application shared preferences + * wrapped by a RSA key generated and operated directly from the android keystore. + * + * @param context the context holding the application shared preferences + */ + @RequiresApi(Build.VERSION_CODES.KITKAT) + @Synchronized + @Throws(KeyStoreException::class, + CertificateException::class, + NoSuchAlgorithmException::class, + IOException::class, + NoSuchProviderException::class, + InvalidAlgorithmParameterException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + IllegalBlockSizeException::class, + UnrecoverableKeyException::class) + private fun getAesGcmLocalProtectionKey(context: Context): SecretKeyAndVersion { + if (sSecretKeyAndVersion == null) { + val keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER) + keyStore.load(null) + + Timber.i(TAG, "Loading local protection key") + + var key: SecretKey? + + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + // Get the version of Android when the key has been generated, default to the current version of the system. In this case, the + // key will be generated + val androidVersionWhenTheKeyHasBeenGenerated = sharedPreferences.getInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (keyStore.containsAlias(AES_LOCAL_PROTECTION_KEY_ALIAS)) { + Timber.i(TAG, "AES local protection key found in keystore") + key = keyStore.getKey(AES_LOCAL_PROTECTION_KEY_ALIAS, null) as SecretKey + } else { + // Check if a key has been created on version < M (in case of OS upgrade) + key = readKeyApiL(sharedPreferences, keyStore) + + if (key == null) { + Timber.i(TAG, "Generating AES key with keystore") + val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER) + generator.init( + KeyGenParameterSpec.Builder(AES_LOCAL_PROTECTION_KEY_ALIAS, + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setKeySize(AES_GCM_KEY_SIZE_IN_BITS) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()) + key = generator.generateKey() + + sharedPreferences.edit() + .putInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT) + .apply() + } + } + } else { + key = readKeyApiL(sharedPreferences, keyStore) + + if (key == null) { + Timber.i(TAG, "Generating RSA key pair with keystore") + val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE_PROVIDER) + val start = Calendar.getInstance() + val end = Calendar.getInstance() + end.add(Calendar.YEAR, 10) + + generator.initialize( + KeyPairGeneratorSpec.Builder(context) + .setAlgorithmParameterSpec(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)) + .setAlias(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS) + .setSubject(X500Principal("CN=matrix-android-sdk")) + .setStartDate(start.time) + .setEndDate(end.time) + .setSerialNumber(BigInteger.ONE) + .build()) + val keyPair = generator.generateKeyPair() + + Timber.i(TAG, "Generating wrapped AES key") + + val aesKeyRaw = ByteArray(AES_GCM_KEY_SIZE_IN_BITS / java.lang.Byte.SIZE) + prng.nextBytes(aesKeyRaw) + key = SecretKeySpec(aesKeyRaw, "AES") + + val cipher = Cipher.getInstance(RSA_WRAP_CIPHER_TYPE) + cipher.init(Cipher.WRAP_MODE, keyPair.public) + val wrappedAesKey = cipher.wrap(key) + + sharedPreferences.edit() + .putString(AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE, Base64.encodeToString(wrappedAesKey, 0)) + .putInt(SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT) + .apply() + } + } + + sSecretKeyAndVersion = SecretKeyAndVersion(key!!, androidVersionWhenTheKeyHasBeenGenerated) + } + + return sSecretKeyAndVersion!! + } + + /** + * Read the key, which may have been stored when the OS was < M + * + * @param sharedPreferences shared pref + * @param keyStore key store + * @return the key if it exists or null + */ + @Throws(KeyStoreException::class, + NoSuchPaddingException::class, + NoSuchAlgorithmException::class, + InvalidKeyException::class, + UnrecoverableKeyException::class) + private fun readKeyApiL(sharedPreferences: SharedPreferences, keyStore: KeyStore): SecretKey? { + val wrappedAesKeyString = sharedPreferences.getString(AES_WRAPPED_PROTECTION_KEY_SHARED_PREFERENCE, null) + if (wrappedAesKeyString != null && keyStore.containsAlias(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS)) { + Timber.i(TAG, "RSA + wrapped AES local protection keys found in keystore") + val privateKey = keyStore.getKey(RSA_WRAP_LOCAL_PROTECTION_KEY_ALIAS, null) as PrivateKey + val wrappedAesKey = Base64.decode(wrappedAesKeyString, 0) + val cipher = Cipher.getInstance(RSA_WRAP_CIPHER_TYPE) + cipher.init(Cipher.UNWRAP_MODE, privateKey) + return cipher.unwrap(wrappedAesKey, "AES", Cipher.SECRET_KEY) as SecretKey + } + + // Key does not exist + return null + } + + /** + * Create a CipherOutputStream instance. + * Before Kitkat, this method will return out as local storage encryption is not implemented for + * devices before KitKat. + * + * @param out the output stream + * @param context the context holding the application shared preferences + */ + @Throws(IOException::class, + CertificateException::class, + NoSuchAlgorithmException::class, + UnrecoverableKeyException::class, + InvalidKeyException::class, + InvalidAlgorithmParameterException::class, + NoSuchPaddingException::class, + NoSuchProviderException::class, + KeyStoreException::class, + IllegalBlockSizeException::class) + fun createCipherOutputStream(out: OutputStream, context: Context): OutputStream? { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return out + } + + val keyAndVersion = getAesGcmLocalProtectionKey(context) + if (keyAndVersion == null || keyAndVersion.secretKey == null) { + throw KeyStoreException() + } + + val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE) + val iv: ByteArray + + if (keyAndVersion.androidVersionWhenTheKeyHasBeenGenerated >= Build.VERSION_CODES.M) { + cipher.init(Cipher.ENCRYPT_MODE, keyAndVersion.secretKey) + iv = cipher.iv + } else { + iv = ByteArray(AES_GCM_IV_LENGTH) + prng.nextBytes(iv) + cipher.init(Cipher.ENCRYPT_MODE, keyAndVersion.secretKey, IvParameterSpec(iv)) + } + + if (iv.size != AES_GCM_IV_LENGTH) { + Timber.e(TAG, "Invalid IV length " + iv.size) + return null + } + + out.write(iv.size) + out.write(iv) + + return CipherOutputStream(out, cipher) + } + + /** + * Create a CipherInputStream instance. + * Before Kitkat, this method will return `in` because local storage encryption is not implemented for devices before KitKat. + * Warning, if `in` is not an encrypted stream, it's up to the caller to close and reopen `in`, because the stream has been read. + * + * @param in the input stream + * @param context the context holding the application shared preferences + * @return in, or the created InputStream, or null if the InputStream `in` does not contain encrypted data + */ + @Throws(NoSuchPaddingException::class, + NoSuchAlgorithmException::class, + CertificateException::class, + InvalidKeyException::class, + KeyStoreException::class, + UnrecoverableKeyException::class, + IllegalBlockSizeException::class, + NoSuchProviderException::class, + InvalidAlgorithmParameterException::class, + IOException::class) + fun createCipherInputStream(`in`: InputStream, context: Context): InputStream? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return `in` + } + + val iv_len = `in`.read() + if (iv_len != AES_GCM_IV_LENGTH) { + Timber.e(TAG, "Invalid IV length $iv_len") + return null + } + + val iv = ByteArray(AES_GCM_IV_LENGTH) + `in`.read(iv) + + val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE) + + val keyAndVersion = getAesGcmLocalProtectionKey(context) + if (keyAndVersion == null || keyAndVersion.secretKey == null) { + throw KeyStoreException() + } + + val spec: AlgorithmParameterSpec + + if (keyAndVersion.androidVersionWhenTheKeyHasBeenGenerated >= Build.VERSION_CODES.M) { + spec = GCMParameterSpec(AES_GCM_KEY_SIZE_IN_BITS, iv) + } else { + spec = IvParameterSpec(iv) + } + + cipher.init(Cipher.DECRYPT_MODE, keyAndVersion.secretKey, spec) + + return CipherInputStream(`in`, cipher) + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SecretKeyAndVersion.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SecretKeyAndVersion.kt new file mode 100644 index 00000000..6accb520 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SecretKeyAndVersion.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.util + +import javax.crypto.SecretKey + +/** + * Tuple which contains the secret key and the version of Android when the key has been generated + */ +internal data class SecretKeyAndVersion( + /** + * the key + */ + val secretKey: SecretKey, + /** + * The android version when the key has been generated + */ + val androidVersionWhenTheKeyHasBeenGenerated: Int) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt new file mode 100644 index 00000000..4e7f0c7a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.util + +import timber.log.Timber + +/** + * Convert a string to an UTF8 String + * + * @param s the string to convert + * @return the utf-8 string + */ +fun convertToUTF8(s: String): String? { + var out: String? = s + + if (null != out) { + try { + val bytes = out.toByteArray(charset("UTF-8")) + out = String(bytes) + } catch (e: Exception) { + Timber.e(e, "## convertToUTF8() failed") + } + + } + + return out +} + +/** + * Convert a string from an UTF8 String + * + * @param s the string to convert + * @return the utf-16 string + */ +fun convertFromUTF8(s: String): String? { + var out: String? = s + + if (null != out) { + try { + val bytes = out.toByteArray() + out = String(bytes, charset("UTF-8")) + } catch (e: Exception) { + Timber.e(e, "## convertFromUTF8() failed") + } + + } + + return out +} diff --git a/matrix-sdk-android/src/main/res/values-bg/strings.xml b/matrix-sdk-android/src/main/res/values-bg/strings.xml index 16f17103..897be232 100644 --- a/matrix-sdk-android/src/main/res/values-bg/strings.xml +++ b/matrix-sdk-android/src/main/res/values-bg/strings.xml @@ -81,4 +81,8 @@ Празна стая + Премахнато съобщение + Съобщение премахнато от %1$s + Премахнато съобщение [причина: %1$s] + Съобщение премахнато от %1$s [причина: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-eo/strings.xml b/matrix-sdk-android/src/main/res/values-eo/strings.xml index a24f0973..c88d96d6 100644 --- a/matrix-sdk-android/src/main/res/values-eo/strings.xml +++ b/matrix-sdk-android/src/main/res/values-eo/strings.xml @@ -14,9 +14,9 @@ %1$s forbaris %2$s %1$s malinvitis %2$s %1$s ŝanĝis sian profilbildon -** Ne eblas malĉifri: %s ** + ** Ne eblas malĉifri: %s ** La aparato de la sendanto ne sendis al ni la ŝlosilojn por tiu mesaĝo. Respondanta al - + diff --git a/matrix-sdk-android/src/main/res/values-eu/strings.xml b/matrix-sdk-android/src/main/res/values-eu/strings.xml index c123936a..4dfab9f4 100644 --- a/matrix-sdk-android/src/main/res/values-eu/strings.xml +++ b/matrix-sdk-android/src/main/res/values-eu/strings.xml @@ -81,4 +81,8 @@ + Mezua kendu da + %1$s erabiltzaileak mezua kendu du + Mezua kendu da [arrazoia: %1$s] + %1$s erabiltzaileak mezua kendu du [arrazoia: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-fi/strings.xml b/matrix-sdk-android/src/main/res/values-fi/strings.xml index cee8489c..d76293b2 100644 --- a/matrix-sdk-android/src/main/res/values-fi/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fi/strings.xml @@ -3,7 +3,7 @@ %1$s lähetti kuvan. %s:n kutsu - %1$s kutsui %2$s + %1$s kutsui käyttäjän %2$s %1$s kutsui sinut %1$s liittyi %1$s poistui @@ -14,25 +14,25 @@ %1$s veti takaisin kutsun käyttäjälle %2$s %1$s vaihtoi profiilikuvaa %1$s asetti näyttönimekseen %2$s - %1$s muutti näyttönimensä %2$s -> %3$s + %1$s muutti näyttönimensä nimestä %2$s nimeen %3$s %1$s poisti näyttönimensä (%2$s) %1$s muutti aiheeksi %2$s %1$s muutti huoneen nimeksi %2$s %s soitti videopuhelun. - %s soitti puhelun. + %s soitti äänipuhelun. %s vastasi puheluun. %s lopetti puhelun. %1$s muutti tulevan huonehistorian näkyväksi käyttäjälle %2$s - kaikki jäsenet, heidän kutsuistaan asti. - kaikki jäsenet, heidän liittymisestään asti. + kaikki huoneen jäsenet, heidän kutsumisestaan asti. + kaikki huoneen jäsenet, heidän liittymisestään asti. kaikki huoneen jäsenet. kaikki. tuntematon (%s). %1$s otti käyttöön osapuolten välisen salauksen (%2$s) - %1$s lähetti VoIP konferenssi-pyynnön - VoIP konferenssi alkoi - VoIP konferenssi loppui + %1$s lähetti VoIP-konferenssipyynnön + VoIP-konferenssi alkoi + VoIP-konferenssi päättyi (profiilikuva muuttui myös) %1$s poisti huoneen nimen @@ -48,7 +48,7 @@ Kuvan lataaminen epäonnistui Verkkovirhe - Matrix virhe + Matrix-virhe Tällä hetkellä ei ole mahdollista liittyä uudelleen tyhjään huoneeseen. @@ -68,7 +68,7 @@ Tyhjä huone -%1$s lähetti tarran. + %1$s lähetti tarran. Vastauksena käyttäjälle @@ -78,8 +78,12 @@ lähetti tiedoston. - %1$s ja yksi muu - %1$s ja %2$d muuta - + %1$s ja yksi muu + %1$s ja %2$d muuta + - + Viesti poistettu + %1$s poisti viestin + Viesti poistettu [syy: %1$s] + %1$s poisti viestin [syy: %2$s] + diff --git a/matrix-sdk-android/src/main/res/values-fr/strings.xml b/matrix-sdk-android/src/main/res/values-fr/strings.xml index f6a00e36..c54e1b7c 100644 --- a/matrix-sdk-android/src/main/res/values-fr/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fr/strings.xml @@ -81,4 +81,8 @@ + Message supprimé + Message supprimé par %1$s + Message supprimé [motif : %1$s] + Message supprimé par %1$s [motif : %2$s] diff --git a/matrix-sdk-android/src/main/res/values-hu/strings.xml b/matrix-sdk-android/src/main/res/values-hu/strings.xml index a235f131..5e4881be 100644 --- a/matrix-sdk-android/src/main/res/values-hu/strings.xml +++ b/matrix-sdk-android/src/main/res/values-hu/strings.xml @@ -80,4 +80,8 @@ + Üzenet eltávolítva + Üzenetet eltávolította: %1$s + Üzenet eltávolítva [ok: %1$s] + Üzenetet eltávolította: %1$s [ok: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-it/strings.xml b/matrix-sdk-android/src/main/res/values-it/strings.xml index df881bfc..bfa3d559 100644 --- a/matrix-sdk-android/src/main/res/values-it/strings.xml +++ b/matrix-sdk-android/src/main/res/values-it/strings.xml @@ -81,4 +81,8 @@ + Messaggio rimosso + Messaggio rimosso da %1$s + Messaggio rimosso [motivo: %1$s] + Messaggio rimosso da %1$s [motivo: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-nl/strings.xml b/matrix-sdk-android/src/main/res/values-nl/strings.xml index 81357f94..cd04670c 100644 --- a/matrix-sdk-android/src/main/res/values-nl/strings.xml +++ b/matrix-sdk-android/src/main/res/values-nl/strings.xml @@ -90,4 +90,8 @@ + Bericht verwijderd + Bericht verwijderd door %1$s + Bericht verwijderd [reden: %1$s] + Bericht verwijderd door %1$s [reden: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-pl/strings.xml b/matrix-sdk-android/src/main/res/values-pl/strings.xml index ec8774b9..1bb8aef3 100644 --- a/matrix-sdk-android/src/main/res/values-pl/strings.xml +++ b/matrix-sdk-android/src/main/res/values-pl/strings.xml @@ -53,7 +53,7 @@ %1$s i jeden inny %1$s i kilku innych %1$s i %2$d innych - + ** Nie można odszyfrować: %s ** diff --git a/matrix-sdk-android/src/main/res/values-sk/strings.xml b/matrix-sdk-android/src/main/res/values-sk/strings.xml index b1a0a4f8..b7e9bb41 100644 --- a/matrix-sdk-android/src/main/res/values-sk/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sk/strings.xml @@ -78,7 +78,7 @@ %1$s a 1 ďalší %1$s a %2$d ďalší %1$s a %2$d ďalších - + diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml index 31c1a408..0203f3fa 100644 --- a/matrix-sdk-android/src/main/res/values-sq/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sq/strings.xml @@ -80,4 +80,8 @@ %1$s dhe %2$d të tjerë + Mesazhi u hoq + Mesazhi u hoq nga %1$s + Mesazh i hequr [arsye: %1$s] + Mesazh i hequr nga %1$s [arsye: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml index 563fdded..b92c72e5 100644 --- a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml +++ b/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml @@ -78,4 +78,8 @@ %1$s 与其他 %2$d 位 + 消息已被移除 + 消息已被 %1$s 移除 + 消息已被移除 [原因: %1$s] + 消息已被 %1$s 移除 [原因: %2$s] diff --git a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml index 3ab114b6..5b3d5a7e 100644 --- a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml +++ b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml @@ -79,4 +79,8 @@ + 訊息已移除 + 訊息已被 %1$s 移除 + 訊息已移除 [理由:%1$s] + 訊息已被 %1$s 移除 [理由:%2$s] diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index 2172c095..1414c723 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -98,4 +98,134 @@ Empty room + + + Dog + + Cat + + Lion + + Horse + + Unicorn + + Pig + + Elephant + + Rabbit + + Panda + + Rooster + + Penguin + + Turtle + + Fish + + Octopus + + Butterfly + + Flower + + Tree + + Cactus + + Mushroom + + Globe + + Moon + + Cloud + + Fire + + Banana + + Apple + + Strawberry + + Corn + + Pizza + + Cake + + Heart + + Smiley + + Robot + + Hat + + Glasses + + Wrench + + Santa + + Thumbs Up + + Umbrella + + Hourglass + + Clock + + Gift + + Light Bulb + + Book + + Pencil + + Paperclip + + Scissors + + Lock + + Key + + Hammer + + Telephone + + Flag + + Train + + Bicycle + + Airplane + + Rocket + + Trophy + + Ball + + Guitar + + Trumpet + + Bell + + Anchor + + Headphone + + Folder + + Pin + diff --git a/matrix-sdk-android/src/test/java/ModuleTest.kt b/matrix-sdk-android/src/test/java/ModuleTest.kt new file mode 100644 index 00000000..11735fd3 --- /dev/null +++ b/matrix-sdk-android/src/test/java/ModuleTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO When upgrading to koin 2.0 +/* +class ModuleTest : KoinTest { + + @Test + fun checkModules() { + startKoin { + listOf(CryptoModule().definition) + }.checkModules() + } +} + */ \ No newline at end of file diff --git a/vector/build.gradle b/vector/build.gradle index e75bbd32..5e18183a 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -174,6 +174,12 @@ dependencies { implementation "ru.noties.markwon:core:$markwon_version" implementation "ru.noties.markwon:html:$markwon_version" + // Passphrase strength helper + implementation 'com.nulab-inc:zxcvbn:1.2.5' + + //Alerter + implementation 'com.tapadoo.android:alerter:3.0.2' + implementation 'com.otaliastudios:autocomplete:1.1.0' // Butterknife diff --git a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/troubleshoot/TestTokenRegistration.kt index 7a8c319e..912597ec 100644 --- a/vector/src/gplay/java/im/vector/riotredesign/push/fcm/troubleshoot/TestTokenRegistration.kt +++ b/vector/src/gplay/java/im/vector/riotredesign/push/fcm/troubleshoot/TestTokenRegistration.kt @@ -27,7 +27,7 @@ class TestTokenRegistration(val fragment: Fragment) : TroubleshootTest(R.string. override fun perform() { /* TODO - Matrix.getInstance(VectorApp.getInstance().baseContext).pushManager.forceSessionsRegistration(object : ApiCallback { + Matrix.getInstance(VectorApp.getInstance().baseContext).pushManager.forceSessionsRegistration(object : MatrixCallback { override fun onSuccess(info: Void?) { description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_success) status = TestStatus.SUCCESS diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 2860cf9a..a65ef719 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -38,6 +38,18 @@ android:label="@string/title_activity_settings" android:windowSoftInputMode="adjustResize" /> + + + + () + + override fun initUiAndData() { + configureToolbar() + waitingView = findViewById(R.id.waiting_view) + } + + /** + * Displays a progress indicator with a message to the user. + * Blocks user interactions. + */ + fun updateWaitingView(data: WaitingViewData?) { + data?.let { + waitingStatusText.text = data.message + + if (data.progress != null && data.progressTotal != null) { + waitingHorizontalProgress.isIndeterminate = false + waitingHorizontalProgress.progress = data.progress + waitingHorizontalProgress.max = data.progressTotal + waitingHorizontalProgress.isVisible = true + waitingCircularProgress.isVisible = false + } else if (data.isIndeterminate) { + waitingHorizontalProgress.isIndeterminate = true + waitingHorizontalProgress.isVisible = true + waitingCircularProgress.isVisible = false + } else { + waitingHorizontalProgress.isVisible = false + waitingCircularProgress.isVisible = true + } + + showWaitingView() + } ?: run { + hideWaitingView() + } + } + + override fun showWaitingView() { + hideKeyboard() + waitingStatusText.isGone = waitingStatusText.text.isNullOrBlank() + super.showWaitingView() + } + + override fun hideWaitingView() { + waitingStatusText.text = null + waitingStatusText.isGone = true + waitingHorizontalProgress.progress = 0 + waitingHorizontalProgress.isVisible = false + super.hideWaitingView() + } + + override fun onBackPressed() { + if (waitingView!!.isVisible) { + // ignore + return + } + super.onBackPressed() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt index 758b78be..0424528e 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/platform/VectorBaseActivity.kt @@ -24,6 +24,7 @@ import android.view.View import androidx.annotation.* import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.isVisible import butterknife.BindView import butterknife.ButterKnife import butterknife.Unbinder @@ -254,6 +255,39 @@ abstract class VectorBaseActivity : BaseMvRxActivity() { } } + //============================================================================================== + // Handle loading view (also called waiting view or spinner view) + //============================================================================================== + + var waitingView: View? = null + set(value) { + field = value + + // Ensure this view is clickable to catch UI events + value?.isClickable = true + } + + /** + * Tells if the waiting view is currently displayed + * + * @return true if the waiting view is displayed + */ + fun isWaitingViewVisible() = waitingView?.isVisible == true + + /** + * Show the waiting view + */ + open fun showWaitingView() { + waitingView?.isVisible = true + } + + /** + * Hide the waiting view + */ + open fun hideWaitingView() { + waitingView?.isVisible = false + } + /* ========================================================================================== * OPEN METHODS * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/riotredesign/core/platform/WaitingViewData.kt b/vector/src/main/java/im/vector/riotredesign/core/platform/WaitingViewData.kt new file mode 100644 index 00000000..00191007 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/platform/WaitingViewData.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.core.platform + +/** + * Model to display a Waiting View + */ +data class WaitingViewData( + val message: String, + val progress: Int? = null, + val progressTotal: Int? = null, + val isIndeterminate: Boolean = false +) diff --git a/vector/src/main/java/im/vector/riotredesign/core/preference/VectorPreference.kt b/vector/src/main/java/im/vector/riotredesign/core/preference/VectorPreference.kt index 6b46a633..5ba29341 100755 --- a/vector/src/main/java/im/vector/riotredesign/core/preference/VectorPreference.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/preference/VectorPreference.kt @@ -133,7 +133,7 @@ open class VectorPreference : Preference { } } catch (e: Exception) { - Timber.e(LOG_TAG, "onBindView " + e.message, e) + Timber.e("onBindView " + e.message, e) } super.onBindViewHolder(holder) diff --git a/vector/src/main/java/im/vector/riotredesign/core/resources/ResourceUtils.kt b/vector/src/main/java/im/vector/riotredesign/core/resources/ResourceUtils.kt new file mode 100644 index 00000000..1f1fbd9b --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/resources/ResourceUtils.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.core.resources + +import android.content.Context +import android.net.Uri +import android.text.TextUtils +import android.webkit.MimeTypeMap +import im.vector.riotredesign.core.utils.getFileExtension +import timber.log.Timber +import java.io.InputStream + +/** + * Mime types + */ +const val MIME_TYPE_JPEG = "image/jpeg" +const val MIME_TYPE_JPG = "image/jpg" +const val MIME_TYPE_IMAGE_ALL = "image/*" +const val MIME_TYPE_ALL_CONTENT = "*/*" + +data class Resource( + var mContentStream: InputStream? = null, + var mMimeType: String? = null +) { + /** + * Close the content stream. + */ + fun close() { + try { + mMimeType = null + + mContentStream?.close() + mContentStream = null + } catch (e: Exception) { + Timber.e(e, "Resource.close failed") + } + + } + + /** + * Tells if the opened resource is a jpeg one. + * + * @return true if the opened resource is a jpeg one. + */ + fun isJpegResource(): Boolean { + return MIME_TYPE_JPEG == mMimeType || MIME_TYPE_JPG == mMimeType + } +} + +/** + * Get a resource stream and metadata about it given its URI returned from onActivityResult. + * + * @param context the context. + * @param uri the URI + * @param mimetype the mimetype + * @return a [Resource] encapsulating the opened resource stream and associated metadata + * or `null` if opening the resource stream failed. + */ +fun openResource(context: Context, uri: Uri, mimetype: String?): Resource? { + var mimetype = mimetype + try { + // if the mime type is not provided, try to find it out + if (TextUtils.isEmpty(mimetype)) { + mimetype = context.contentResolver.getType(uri) + + // try to find the mimetype from the filename + if (null == mimetype) { + val extension = getFileExtension(uri.toString()) + if (extension != null) { + mimetype = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + } + } + } + + return Resource( + context.contentResolver.openInputStream(uri), + mimetype) + + } catch (e: Exception) { + Timber.e(e, "Failed to open resource input stream") + } + + return null +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericItemViewHolder.kt b/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericItemViewHolder.kt new file mode 100644 index 00000000..ca8f1c10 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericItemViewHolder.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.core.ui.list + +import android.view.View +import android.widget.Button +import android.widget.ImageView +import android.widget.ProgressBar +import android.widget.TextView +import androidx.annotation.LayoutRes +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import butterknife.ButterKnife +import im.vector.riotredesign.R + +/** + * View Holder for generic list items. + * Displays an item with a title, and optional description. + * Can display an accessory on the right, that can be an image or an indeterminate progress. + * If provided with an action, will display a button at the bottom of the list item. + */ +class GenericItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + companion object { + @LayoutRes + const val resId = R.layout.item_generic_list + } + + @BindView(R.id.item_generic_title_text) + lateinit var titleText: TextView + + @BindView(R.id.item_generic_description_text) + lateinit var descriptionText: TextView + + @BindView(R.id.item_generic_accessory_image) + lateinit var accessoryImage: ImageView + + @BindView(R.id.item_generic_progress_bar) + lateinit var progressBar: ProgressBar + + @BindView(R.id.item_generic_action_button) + lateinit var actionButton: Button + + init { + ButterKnife.bind(this, itemView) + } + + fun bind(item: GenericRecyclerViewItem) { + titleText.text = item.title + + when (item.style) { + GenericRecyclerViewItem.STYLE.BIG_TEXT -> titleText.textSize = 18f + GenericRecyclerViewItem.STYLE.NORMAL_TEXT -> titleText.textSize = 14f + } + + item.description?.let { + descriptionText.isVisible = true + descriptionText.text = it + } ?: run { descriptionText.isVisible = false } + + if (item.hasIndeterminateProcess) { + progressBar.isVisible = true + accessoryImage.isVisible = false + } else { + progressBar.isVisible = false + if (item.endIconResourceId != -1) { + accessoryImage.setImageResource(item.endIconResourceId) + accessoryImage.isVisible = true + } else { + accessoryImage.isVisible = false + } + } + + val buttonAction = item.buttonAction + + if (buttonAction == null) { + actionButton.isVisible = false + } else { + actionButton.text = buttonAction.title + actionButton.setOnClickListener { + buttonAction.perform?.run() + } + actionButton.isVisible = true + } + + itemView?.setOnClickListener { + item.itemClickAction?.perform?.run() + } + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericRecyclerViewItem.kt b/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericRecyclerViewItem.kt new file mode 100644 index 00000000..7b936ddd --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/ui/list/GenericRecyclerViewItem.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.core.ui.list + +import androidx.annotation.DrawableRes + + +/** + * A generic list item. + * Displays an item with a title, and optional description. + * Can display an accessory on the right, that can be an image or an indeterminate progress. + * If provided with an action, will display a button at the bottom of the list item. + */ +class GenericRecyclerViewItem(val title: String, + var description: String? = null, + val style: STYLE = STYLE.NORMAL_TEXT) { + + enum class STYLE { + BIG_TEXT, + NORMAL_TEXT + } + + @DrawableRes + var endIconResourceId: Int = -1 + + var hasIndeterminateProcess = false + + var buttonAction: Action? = null + + var itemClickAction: Action? = null + + class Action(var title: String) { + var perform: Runnable? = null + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/ui/views/KeysBackupBanner.kt b/vector/src/main/java/im/vector/riotredesign/core/ui/views/KeysBackupBanner.kt new file mode 100755 index 00000000..5cc88971 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/ui/views/KeysBackupBanner.kt @@ -0,0 +1,293 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.core.ui.views + +import android.content.Context +import android.preference.PreferenceManager +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.AbsListView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.edit +import androidx.core.view.isVisible +import androidx.transition.TransitionManager +import butterknife.BindView +import butterknife.ButterKnife +import butterknife.OnClick +import im.vector.riotredesign.R +import timber.log.Timber + +/** + * The view used in VectorHomeActivity to show some information about the keys backup state + * It does have a unique render method + */ +class KeysBackupBanner @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener { + + @BindView(R.id.view_keys_backup_banner_text_1) + lateinit var textView1: TextView + + @BindView(R.id.view_keys_backup_banner_text_2) + lateinit var textView2: TextView + + @BindView(R.id.view_keys_backup_banner_close_group) + lateinit var close: View + + @BindView(R.id.view_keys_backup_banner_loading) + lateinit var loading: View + + var delegate: Delegate? = null + private var state: State = State.Initial + + private var scrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE + set(value) { + field = value + + val pendingV = pendingVisibility + + if (pendingV != null) { + pendingVisibility = null + visibility = pendingV + } + } + + private var pendingVisibility: Int? = null + + init { + setupView() + } + + /** + * This methods is responsible for rendering the view according to the newState + * + * @param newState the newState representing the view + */ + fun render(newState: State, force: Boolean = false) { + if (newState == state && !force) { + Timber.d("State unchanged") + return + } + Timber.d("Rendering $newState") + + state = newState + + hideAll() + + when (newState) { + State.Initial -> renderInitial() + State.Hidden -> renderHidden() + is State.Setup -> renderSetup(newState.numberOfKeys) + is State.Recover -> renderRecover(newState.version) + is State.Update -> renderUpdate(newState.version) + State.BackingUp -> renderBackingUp() + } + } + + override fun setVisibility(visibility: Int) { + if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { + // Wait for scroll state to be idle + pendingVisibility = visibility + return + } + + if (visibility != getVisibility()) { + // Schedule animation + val parent = parent as ViewGroup + TransitionManager.beginDelayedTransition(parent) + } + + super.setVisibility(visibility) + } + + override fun onClick(v: View?) { + when (state) { + is State.Setup -> { + delegate?.setupKeysBackup() + } + is State.Recover -> { + delegate?.recoverKeysBackup() + } + } + } + + @OnClick(R.id.view_keys_backup_banner_close) + internal fun onCloseClicked() { + state.let { + when (it) { + is State.Setup -> { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, true) + } + } + is State.Recover -> { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, it.version) + } + } + is State.Update -> { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, it.version) + } + } + else -> { + // Should not happen, close button is not displayed in other cases + } + } + } + + // Force refresh + render(state, true) + } + + // PRIVATE METHODS ***************************************************************************************************************************************** + + private fun setupView() { + inflate(context, R.layout.view_keys_backup_banner, this) + ButterKnife.bind(this) + + setOnClickListener(this) + } + + private fun renderInitial() { + isVisible = false + } + + private fun renderHidden() { + isVisible = false + } + + private fun renderSetup(nbOfKeys: Int) { + if (nbOfKeys == 0 + || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) { + // Do not display the setup banner if there is no keys to backup, or if the user has already closed it + isVisible = false + } else { + isVisible = true + + textView1.setText(R.string.keys_backup_banner_setup_line1) + textView2.isVisible = true + textView2.setText(R.string.keys_backup_banner_setup_line2) + close.isVisible = true + } + } + + private fun renderRecover(version: String) { + if (version == PreferenceManager.getDefaultSharedPreferences(context).getString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, null)) { + isVisible = false + } else { + isVisible = true + + textView1.setText(R.string.keys_backup_banner_recover_line1) + textView2.isVisible = true + textView2.setText(R.string.keys_backup_banner_recover_line2) + close.isVisible = true + } + } + + private fun renderUpdate(version: String) { + if (version == PreferenceManager.getDefaultSharedPreferences(context).getString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, null)) { + isVisible = false + } else { + isVisible = true + + textView1.setText(R.string.keys_backup_banner_update_line1) + textView2.isVisible = true + textView2.setText(R.string.keys_backup_banner_update_line2) + close.isVisible = true + } + } + + private fun renderBackingUp() { + isVisible = true + + textView1.setText(R.string.keys_backup_banner_in_progress) + loading.isVisible = true + } + + /** + * Hide all views that are not visible in all state + */ + private fun hideAll() { + textView2.isVisible = false + close.isVisible = false + loading.isVisible = false + } + + /** + * The state representing the view + * It can take one state at a time + */ + sealed class State { + // Not yet rendered + object Initial : State() + + // View will be Gone + object Hidden : State() + + // Keys backup is not setup, numberOfKeys is the number of locally stored keys + data class Setup(val numberOfKeys: Int) : State() + + // Keys backup can be recovered, with version from the server + data class Recover(val version: String) : State() + + // Keys backup can be updated + data class Update(val version: String) : State() + + // Keys are backing up + object BackingUp : State() + } + + /** + * An interface to delegate some actions to another object + */ + interface Delegate { + fun setupKeysBackup() + fun recoverKeysBackup() + } + + companion object { + /** + * Preference key for setup. Value is a boolean. + */ + private const val BANNER_SETUP_DO_NOT_SHOW_AGAIN = "BANNER_SETUP_DO_NOT_SHOW_AGAIN" + + /** + * Preference key for recover. Value is a backup version (String). + */ + private const val BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION = "BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION" + + /** + * Preference key for update. Value is a backup version (String). + */ + private const val BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION = "BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION" + + /** + * Inform the banner that a Recover has been done for this version, so do not show the Recover banner for this version + */ + fun onRecoverDoneForVersion(context: Context, version: String) { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, version) + } + } + } +} + diff --git a/vector/src/main/java/im/vector/riotredesign/core/ui/views/PasswordStrengthBar.kt b/vector/src/main/java/im/vector/riotredesign/core/ui/views/PasswordStrengthBar.kt new file mode 100644 index 00000000..1d53593d --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/ui/views/PasswordStrengthBar.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.core.ui.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import androidx.annotation.IntRange +import butterknife.BindColor +import butterknife.BindView +import butterknife.ButterKnife +import im.vector.riotredesign.R + +/** + * A password strength bar custom widget + * Strength is an Integer + * -> 0 No strength + * -> 1 Weak + * -> 2 Fair + * -> 3 Good + * -> 4 Strong + */ +class PasswordStrengthBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0) + : LinearLayout(context, attrs, defStyleAttr) { + + @BindView(R.id.password_strength_bar_1) + lateinit var bar1: View + + @BindView(R.id.password_strength_bar_2) + lateinit var bar2: View + + @BindView(R.id.password_strength_bar_3) + lateinit var bar3: View + + @BindView(R.id.password_strength_bar_4) + lateinit var bar4: View + + + @BindColor(R.color.password_strength_bar_undefined) + @JvmField + var colorBackground: Int = 0 + @BindColor(R.color.password_strength_bar_weak) + @JvmField + var colorWeak: Int = 0 + @BindColor(R.color.password_strength_bar_low) + @JvmField + var colorLow: Int = 0 + @BindColor(R.color.password_strength_bar_ok) + @JvmField + var colorOk: Int = 0 + @BindColor(R.color.password_strength_bar_strong) + @JvmField + var colorStrong: Int = 0 + + @IntRange(from = 0, to = 4) + var strength = 0 + set(newValue) { + field = newValue.coerceIn(0, 4) + + when (newValue) { + 0 -> { + bar1.setBackgroundColor(colorBackground) + bar2.setBackgroundColor(colorBackground) + bar3.setBackgroundColor(colorBackground) + bar4.setBackgroundColor(colorBackground) + } + 1 -> { + bar1.setBackgroundColor(colorWeak) + bar2.setBackgroundColor(colorBackground) + bar3.setBackgroundColor(colorBackground) + bar4.setBackgroundColor(colorBackground) + } + 2 -> { + bar1.setBackgroundColor(colorLow) + bar2.setBackgroundColor(colorLow) + bar3.setBackgroundColor(colorBackground) + bar4.setBackgroundColor(colorBackground) + } + 3 -> { + bar1.setBackgroundColor(colorOk) + bar2.setBackgroundColor(colorOk) + bar3.setBackgroundColor(colorOk) + bar4.setBackgroundColor(colorBackground) + } + 4 -> { + bar1.setBackgroundColor(colorStrong) + bar2.setBackgroundColor(colorStrong) + bar3.setBackgroundColor(colorStrong) + bar4.setBackgroundColor(colorStrong) + } + } + } + + init { + LayoutInflater.from(context) + .inflate(R.layout.view_password_strength_bar, this, true) + orientation = HORIZONTAL + ButterKnife.bind(this) + strength = 0 + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/utils/FileUtils.kt b/vector/src/main/java/im/vector/riotredesign/core/utils/FileUtils.kt index c0a75970..e1fae0df 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/utils/FileUtils.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/utils/FileUtils.kt @@ -17,6 +17,7 @@ package im.vector.riotredesign.core.utils import android.content.Context +import android.text.TextUtils import timber.log.Timber import java.io.File @@ -87,3 +88,46 @@ private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean { return action.invoke(file) } + +/** + * Get the file extension of a fileUri or a filename + * + * @param fileUri the fileUri (can be a simple filename) + * @return the file extension, in lower case, or null is extension is not available or empty + */ +fun getFileExtension(fileUri: String): String? { + var reducedStr = fileUri + + if (!TextUtils.isEmpty(reducedStr)) { + // Remove fragment + val fragment = fileUri.lastIndexOf('#') + if (fragment > 0) { + reducedStr = fileUri.substring(0, fragment) + } + + // Remove query + val query = reducedStr.lastIndexOf('?') + if (query > 0) { + reducedStr = reducedStr.substring(0, query) + } + + // Remove path + val filenamePos = reducedStr.lastIndexOf('/') + val filename = if (0 <= filenamePos) reducedStr.substring(filenamePos + 1) else reducedStr + + // Contrary to method MimeTypeMap.getFileExtensionFromUrl, we do not check the pattern + // See https://stackoverflow.com/questions/14320527/android-should-i-use-mimetypemap-getfileextensionfromurl-bugs + if (!filename.isEmpty()) { + val dotPos = filename.lastIndexOf('.') + if (0 <= dotPos) { + val ext = filename.substring(dotPos + 1) + + if (ext.isNotBlank()) { + return ext.toLowerCase() + } + } + } + } + + return null +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/riotredesign/core/utils/PermissionsTools.kt index f9438d61..96202766 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/utils/PermissionsTools.kt @@ -269,9 +269,10 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, val permissionsArrayToBeGranted = permissionsListToBeGranted.toTypedArray() // for android < M, we use a custom dialog to request the contacts book access. - /* if (permissionsListToBeGranted.contains(Manifest.permission.READ_CONTACTS) && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + TODO() + /* AlertDialog.Builder(activity) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.permissions_rationale_popup_title) @@ -293,13 +294,13 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, } } .show() + */ } else { fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode) ?: run { ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode) } } - */ } else { // permissions were granted, start now. isPermissionGranted = true diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt new file mode 100644 index 00000000..20fb0bd8 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.restore + +import android.app.Activity +import android.content.Context +import android.content.Intent +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.SimpleFragmentActivity + +class KeysBackupRestoreActivity : SimpleFragmentActivity() { + + companion object { + + fun intent(context: Context): Intent { + return Intent(context, KeysBackupRestoreActivity::class.java) + } + } + + override fun getTitleRes() = R.string.title_activity_keys_backup_restore + + private lateinit var viewModel: KeysBackupRestoreSharedViewModel + + override fun initUiAndData() { + super.initUiAndData() + viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java) + viewModel.initSession(mSession) + + viewModel.keyVersionResult.observe(this, Observer { keyVersion -> + + if (keyVersion != null && supportFragmentManager.fragments.isEmpty()) { + val isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData().privateKeySalt != null + if (isBackupCreatedFromPassphrase) { + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupRestoreFromPassphraseFragment.newInstance()) + .commitNow() + } else { + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupRestoreFromKeyFragment.newInstance()) + .commitNow() + } + } + }) + + viewModel.keyVersionResultError.observe(this, Observer { uxStateEvent -> + uxStateEvent?.getContentIfNotHandled()?.let { + AlertDialog.Builder(this) + .setTitle(R.string.unknown_error) + .setMessage(it) + .setCancelable(false) + .setPositiveButton(R.string.ok) { _, _ -> + //nop + finish() + } + .show() + } + }) + + if (viewModel.keyVersionResult.value == null) { + //We need to fetch from API + viewModel.getLatestVersion(this) + } + + viewModel.navigateEvent.observe(this, Observer { uxStateEvent -> + when (uxStateEvent?.getContentIfNotHandled()) { + KeysBackupRestoreSharedViewModel.NAVIGATE_TO_RECOVER_WITH_KEY -> { + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupRestoreFromKeyFragment.newInstance()) + .addToBackStack(null) + .commit() + } + KeysBackupRestoreSharedViewModel.NAVIGATE_TO_SUCCESS -> { + supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupRestoreSuccessFragment.newInstance()) + .commit() + } + } + }) + + viewModel.loadingEvent.observe(this, Observer { + updateWaitingView(it) + }) + + viewModel.importRoomKeysFinishWithResult.observe(this, Observer { + it?.getContentIfNotHandled()?.let { + //set data? + setResult(Activity.RESULT_OK) + finish() + } + }) + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt new file mode 100644 index 00000000..31736e33 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.restore + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.text.Editable +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import butterknife.OnTextChanged +import com.google.android.material.textfield.TextInputLayout +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.utils.startImportTextFromFileIntent +import timber.log.Timber + +class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() { + + companion object { + fun newInstance() = KeysBackupRestoreFromKeyFragment() + + private const val REQUEST_TEXT_FILE_GET = 1 + } + + override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key + + private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel + private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel + + @BindView(R.id.keys_backup_key_enter_til) + lateinit var mKeyInputLayout: TextInputLayout + @BindView(R.id.keys_restore_key_enter_edittext) + lateinit var mKeyTextEdit: EditText + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromKeyViewModel::class.java) + sharedViewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + mKeyTextEdit.setText(viewModel.recoveryCode.value) + mKeyTextEdit.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + onRestoreFromKey() + return@setOnEditorActionListener true + } + return@setOnEditorActionListener false + } + + mKeyInputLayout.error = viewModel.recoveryCodeErrorText.value + viewModel.recoveryCodeErrorText.observe(this, Observer { newValue -> + mKeyInputLayout.error = newValue + }) + + } + + @OnTextChanged(R.id.keys_restore_key_enter_edittext) + fun onRestoreKeyTextEditChange(s: Editable?) { + s?.toString()?.let { + viewModel.updateCode(it) + } + } + + @OnClick(R.id.keys_restore_button) + fun onRestoreFromKey() { + val value = viewModel.recoveryCode.value + if (value.isNullOrBlank()) { + viewModel.recoveryCodeErrorText.value = context?.getString(R.string.keys_backup_recovery_code_empty_error_message) + } else { + viewModel.recoverKeys(context!!, sharedViewModel) + } + } + + @OnClick(R.id.keys_backup_import) + fun onImport() { + startImportTextFromFileIntent(this, REQUEST_TEXT_FILE_GET) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_TEXT_FILE_GET && resultCode == Activity.RESULT_OK) { + val dataURI = data?.data + if (dataURI != null) { + try { + activity + ?.contentResolver + ?.openInputStream(dataURI) + ?.bufferedReader() + ?.use { it.readText() } + ?.let { + mKeyTextEdit.setText(it) + mKeyTextEdit.setSelection(it.length) + } + } catch (e: Exception) { + Timber.e(e, "Failed to read recovery kay from text") + } + } + return + } + super.onActivityResult(requestCode, resultCode, data) + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt new file mode 100644 index 00000000..358b7c6f --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.restore + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.StepProgressListener +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.core.ui.views.KeysBackupBanner +import timber.log.Timber + +class KeysBackupRestoreFromKeyViewModel : ViewModel() { + + var recoveryCode: MutableLiveData = MutableLiveData() + var recoveryCodeErrorText: MutableLiveData = MutableLiveData() + + init { + recoveryCode.value = null + recoveryCodeErrorText.value = null + } + + //========= Actions ========= + fun updateCode(newValue: String) { + recoveryCode.value = newValue + recoveryCodeErrorText.value = null + } + + fun recoverKeys(context: Context, sharedViewModel: KeysBackupRestoreSharedViewModel) { + val session = sharedViewModel.session + val keysBackup = session.getKeysBackupService() + + recoveryCodeErrorText.value = null + val recoveryKey = recoveryCode.value!! + + val keysVersionResult = sharedViewModel.keyVersionResult.value!! + + keysBackup.restoreKeysWithRecoveryKey(keysVersionResult, + recoveryKey, + null, + session.sessionParams.credentials.userId, + object : StepProgressListener { + override fun onStepProgress(step: StepProgressListener.Step) { + when (step) { + is StepProgressListener.Step.DownloadingKey -> { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message), + isIndeterminate = true) + } + is StepProgressListener.Step.ImportingKey -> { + // Progress 0 can take a while, display an indeterminate progress in this case + if (step.progress == 0) { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + isIndeterminate = true) + + } else { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + step.progress, + step.total) + } + } + } + } + }, + object : MatrixCallback { + override fun onSuccess(info: ImportRoomKeysResult) { + sharedViewModel.loadingEvent.value = null + sharedViewModel.didRecoverSucceed(info) + + KeysBackupBanner.onRecoverDoneForVersion(context, keysVersionResult.version!!) + trustOnDecrypt(keysBackup, keysVersionResult) + } + + override fun onFailure(failure: Throwable) { + sharedViewModel.loadingEvent.value = null + recoveryCodeErrorText.value = context.getString(R.string.keys_backup_recovery_code_error_decrypt) + Timber.e(failure, "## onUnexpectedError") + } + }) + } + + private fun trustOnDecrypt(keysBackup: KeysBackupService, keysVersionResult: KeysVersionResult) { + keysBackup.trustKeysBackupVersion(keysVersionResult, true, + object : MatrixCallback { + + override fun onSuccess(data: Unit) { + Timber.d("##### trustKeysBackupVersion onSuccess") + } + + }) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt new file mode 100644 index 00000000..41d0a4e5 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.fragments.keysbackup.restore + +import android.content.Context +import android.os.Bundle +import android.text.Editable +import android.text.SpannableString +import android.text.style.ClickableSpan +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.ImageView +import android.widget.TextView +import androidx.core.text.set +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import butterknife.OnTextChanged +import com.google.android.material.textfield.TextInputLayout +import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.showPassword +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel + +class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_passphrase + + private lateinit var viewModel: KeysBackupRestoreFromPassphraseViewModel + private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel + + @BindView(R.id.keys_backup_passphrase_enter_til) + lateinit var mPassphraseInputLayout: TextInputLayout + + @BindView(R.id.keys_backup_passphrase_enter_edittext) + lateinit var mPassphraseTextEdit: EditText + + @BindView(R.id.keys_backup_view_show_password) + lateinit var mPassphraseReveal: ImageView + + @BindView(R.id.keys_backup_passphrase_help_with_link) + lateinit var helperTextWithLink: TextView + + @OnClick(R.id.keys_backup_view_show_password) + fun toggleVisibilityMode() { + viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false) + } + + companion object { + fun newInstance() = KeysBackupRestoreFromPassphraseFragment() + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromPassphraseViewModel::class.java) + sharedViewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + + viewModel.passphraseErrorText.observe(this, Observer { newValue -> + mPassphraseInputLayout.error = newValue + }) + + helperTextWithLink.text = spannableStringForHelperText(context!!) + + viewModel.showPasswordMode.observe(this, Observer { + val shouldBeVisible = it ?: false + mPassphraseTextEdit.showPassword(shouldBeVisible) + mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) + }) + + mPassphraseTextEdit.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + onRestoreBackup() + return@setOnEditorActionListener true + } + return@setOnEditorActionListener false + } + + } + + private fun spannableStringForHelperText(context: Context): SpannableString { + val clickableText = context.getString(R.string.keys_backup_restore_use_recovery_key) + val helperText = context.getString(R.string.keys_backup_restore_with_passphrase_helper_with_link, clickableText) + + val spanString = SpannableString(helperText) + + // used just to have default link representation + val clickableSpan = object : ClickableSpan() { + override fun onClick(widget: View?) {} + } + val start = helperText.indexOf(clickableText) + val end = start + clickableText.length + spanString[start, end] = clickableSpan + return spanString + } + + @OnTextChanged(R.id.keys_backup_passphrase_enter_edittext) + fun onPassphraseTextEditChange(s: Editable?) { + s?.toString()?.let { viewModel.updatePassphrase(it) } + } + + @OnClick(R.id.keys_backup_passphrase_help_with_link) + fun onUseRecoveryKey() { + sharedViewModel.moveToRecoverWithKey() + } + + @OnClick(R.id.keys_backup_restore_with_passphrase_submit) + fun onRestoreBackup() { + val value = viewModel.passphrase.value + if (value.isNullOrBlank()) { + viewModel.passphraseErrorText.value = context?.getString(R.string.passphrase_empty_error_message) + } else { + viewModel.recoverKeys(context!!, sharedViewModel) + } + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt new file mode 100644 index 00000000..f58ce7c8 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.restore + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.StepProgressListener +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.core.ui.views.KeysBackupBanner +import timber.log.Timber + +class KeysBackupRestoreFromPassphraseViewModel : ViewModel() { + + var passphrase: MutableLiveData = MutableLiveData() + var passphraseErrorText: MutableLiveData = MutableLiveData() + var showPasswordMode: MutableLiveData = MutableLiveData() + + init { + passphrase.value = null + passphraseErrorText.value = null + showPasswordMode.value = false + } + + //========= Actions ========= + + fun updatePassphrase(newValue: String) { + passphrase.value = newValue + passphraseErrorText.value = null + } + + fun recoverKeys(context: Context, sharedViewModel: KeysBackupRestoreSharedViewModel) { + val keysBackup = sharedViewModel.session.getKeysBackupService() + + passphraseErrorText.value = null + + val keysVersionResult = sharedViewModel.keyVersionResult.value!! + + keysBackup.restoreKeyBackupWithPassword(keysVersionResult, + passphrase.value!!, + null, + sharedViewModel.session.sessionParams.credentials.userId, + object : StepProgressListener { + override fun onStepProgress(step: StepProgressListener.Step) { + when (step) { + is StepProgressListener.Step.ComputingKey -> { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_computing_key_waiting_message), + step.progress, + step.total) + } + is StepProgressListener.Step.DownloadingKey -> { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message), + isIndeterminate = true) + } + is StepProgressListener.Step.ImportingKey -> { + // Progress 0 can take a while, display an indeterminate progress in this case + if (step.progress == 0) { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + isIndeterminate = true) + + } else { + sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + step.progress, + step.total) + } + } + } + } + }, + object : MatrixCallback { + override fun onSuccess(data: ImportRoomKeysResult) { + sharedViewModel.loadingEvent.value = null + sharedViewModel.didRecoverSucceed(data) + + KeysBackupBanner.onRecoverDoneForVersion(context, keysVersionResult.version!!) + trustOnDecrypt(keysBackup, keysVersionResult) + } + + override fun onFailure(failure: Throwable) { + sharedViewModel.loadingEvent.value = null + passphraseErrorText.value = context.getString(R.string.keys_backup_passphrase_error_decrypt) + Timber.e(failure, "## onUnexpectedError") + } + }) + } + + private fun trustOnDecrypt(keysBackup: KeysBackupService, keysVersionResult: KeysVersionResult) { + keysBackup.trustKeysBackupVersion(keysVersionResult, true, + object : MatrixCallback { + + override fun onSuccess(data: Unit) { + Timber.d("##### trustKeysBackupVersion onSuccess") + } + + }) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt new file mode 100644 index 00000000..3db0191e --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.fragments.keysbackup.restore + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.core.utils.LiveEvent + +class KeysBackupRestoreSharedViewModel : ViewModel() { + + companion object { + const val NAVIGATE_TO_RECOVER_WITH_KEY = "NAVIGATE_TO_RECOVER_WITH_KEY" + const val NAVIGATE_TO_SUCCESS = "NAVIGATE_TO_SUCCESS" + } + + lateinit var session: Session + + var keyVersionResult: MutableLiveData = MutableLiveData() + + private var _keyVersionResultError: MutableLiveData> = MutableLiveData() + val keyVersionResultError: LiveData> + get() = _keyVersionResultError + + + private var _navigateEvent: MutableLiveData> = MutableLiveData() + val navigateEvent: LiveData> + get() = _navigateEvent + + var loadingEvent: MutableLiveData = MutableLiveData() + + + var importKeyResult: ImportRoomKeysResult? = null + var importRoomKeysFinishWithResult: MutableLiveData> = MutableLiveData() + + + init { + keyVersionResult.value = null + _keyVersionResultError.value = null + loadingEvent.value = null + } + + fun initSession(session: Session) { + this.session = session + } + + + fun getLatestVersion(context: Context) { + val keysBackup = session.getKeysBackupService() + + loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restore_is_getting_backup_version)) + + keysBackup.getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult?) { + loadingEvent.value = null + if (data?.version.isNullOrBlank()) { + //should not happen + _keyVersionResultError.value = LiveEvent(context.getString(R.string.keys_backup_get_version_error, "")) + } else { + keyVersionResult.value = data + } + } + + override fun onFailure(failure: Throwable) { + loadingEvent.value = null + _keyVersionResultError.value = LiveEvent(context.getString(R.string.keys_backup_get_version_error, failure.localizedMessage)) + + // TODO For network error + // _keyVersionResultError.value = LiveEvent(context.getString(R.string.network_error_please_check_and_retry)) + } + }) + } + + fun moveToRecoverWithKey() { + _navigateEvent.value = LiveEvent(NAVIGATE_TO_RECOVER_WITH_KEY) + } + + fun didRecoverSucceed(result: ImportRoomKeysResult) { + importKeyResult = result + _navigateEvent.value = LiveEvent(NAVIGATE_TO_SUCCESS) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt new file mode 100644 index 00000000..106ed764 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.restore + +import android.os.Bundle +import android.widget.TextView +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.utils.LiveEvent + +class KeysBackupRestoreSuccessFragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_success + + @BindView(R.id.keys_backup_restore_success) + lateinit var mSuccessText: TextView + @BindView(R.id.keys_backup_restore_success_info) + lateinit var mSuccessDetailsText: TextView + + + private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + sharedViewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + sharedViewModel.importKeyResult?.let { + val part1 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part1, + it.totalNumberOfKeys, it.totalNumberOfKeys) + val part2 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part2, + it.successfullyNumberOfImportedKeys, it.successfullyNumberOfImportedKeys) + mSuccessDetailsText.text = String.format("%s\n%s", part1, part2) + } + + //We don't put emoji in string xml as it will crash on old devices + mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title, "🎉") + } + + @OnClick(R.id.keys_backup_setup_done_button) + fun onDone() { + sharedViewModel.importRoomKeysFinishWithResult.value = LiveEvent(sharedViewModel.importKeyResult!!) + } + + companion object { + fun newInstance() = KeysBackupRestoreSuccessFragment() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt new file mode 100644 index 00000000..e313064f --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.settings + +import android.content.Context +import android.content.Intent +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import im.vector.fragments.keysbackup.settings.KeysBackupSettingsFragment +import im.vector.matrix.android.api.MatrixCallback +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.SimpleFragmentActivity + + +class KeysBackupManageActivity : SimpleFragmentActivity() { + + companion object { + + fun intent(context: Context): Intent { + val intent = Intent(context, KeysBackupManageActivity::class.java) + return intent + } + } + + override fun getTitleRes() = R.string.encryption_message_recovery + + + private lateinit var viewModel: KeysBackupSettingsViewModel + + override fun initUiAndData() { + super.initUiAndData() + viewModel = ViewModelProviders.of(this).get(KeysBackupSettingsViewModel::class.java) + viewModel.initSession(mSession) + + + if (supportFragmentManager.fragments.isEmpty()) { + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupSettingsFragment.newInstance()) + .commitNow() + + mSession.getKeysBackupService() + .forceUsingLastVersion(object : MatrixCallback {}) + } + + viewModel.loadingEvent.observe(this, Observer { + updateWaitingView(it) + }) + + + viewModel.apiResultError.observe(this, Observer { uxStateEvent -> + uxStateEvent?.getContentIfNotHandled()?.let { + AlertDialog.Builder(this) + .setTitle(R.string.unknown_error) + .setMessage(it) + .setCancelable(false) + .setPositiveButton(R.string.ok, null) + .show() + } + }) + + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt new file mode 100644 index 00000000..72dc66c2 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.fragments.keysbackup.settings + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreActivity +import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel +import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupActivity + +class KeysBackupSettingsFragment : VectorBaseFragment(), + KeysBackupSettingsRecyclerViewAdapter.AdapterListener { + + + companion object { + fun newInstance() = KeysBackupSettingsFragment() + } + + override fun getLayoutResId() = R.layout.fragment_keys_backup_settings + + private lateinit var viewModel: KeysBackupSettingsViewModel + + @BindView(R.id.keys_backup_settings_recycler_view) + lateinit var recyclerView: RecyclerView + + private var recyclerViewAdapter: KeysBackupSettingsRecyclerViewAdapter? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val layoutManager = LinearLayoutManager(context) + recyclerView.layoutManager = layoutManager + + recyclerViewAdapter = KeysBackupSettingsRecyclerViewAdapter(activity!!) + recyclerView.adapter = recyclerViewAdapter + recyclerViewAdapter?.adapterListener = this + + + viewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupSettingsViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + + viewModel.keyBackupState.observe(this, Observer { keysBackupState -> + if (keysBackupState == null) { + //Cannot happen? + viewModel.keyVersionTrust.value = null + } else { + when (keysBackupState) { + KeysBackupState.Unknown, + KeysBackupState.CheckingBackUpOnHomeserver -> { + viewModel.loadingEvent.value = WaitingViewData("") + } + else -> { + viewModel.loadingEvent.value = null + //All this cases will be manage by looking at the backup trust object + viewModel.session?.getKeysBackupService()?.mKeysBackupVersion?.let { + viewModel.getKeysBackupTrust(it) + } ?: run { + viewModel.keyVersionTrust.value = null + } + } + } + } + + // Update the adapter for each state change + viewModel.session?.let { session -> + recyclerViewAdapter?.updateWithTrust(session, viewModel.keyVersionTrust.value) + } + }) + + viewModel.keyVersionTrust.observe(this, Observer { + viewModel.session?.let { session -> + recyclerViewAdapter?.updateWithTrust(session, it) + } + }) + + } + + override fun didSelectSetupMessageRecovery() { + context?.let { + startActivity(KeysBackupSetupActivity.intent(it, false)) + } + } + + override fun didSelectRestoreMessageRecovery() { + context?.let { + startActivity(KeysBackupRestoreActivity.intent(it)) + } + } + + override fun didSelectDeleteSetupMessageRecovery() { + activity?.let { + AlertDialog.Builder(it) + .setTitle(R.string.keys_backup_settings_delete_confirm_title) + .setMessage(R.string.keys_backup_settings_delete_confirm_message) + .setCancelable(false) + .setPositiveButton(R.string.keys_backup_settings_delete_confirm_title) { _, _ -> + viewModel.deleteCurrentBackup(it) + } + .setNegativeButton(R.string.cancel, null) + .setCancelable(true) + .show() + } + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewAdapter.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewAdapter.kt new file mode 100644 index 00000000..aed605b0 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewAdapter.kt @@ -0,0 +1,233 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.fragments.keysbackup.settings + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import butterknife.ButterKnife +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust +import im.vector.riotredesign.R +import im.vector.riotredesign.core.ui.list.GenericItemViewHolder +import im.vector.riotredesign.core.ui.list.GenericRecyclerViewItem + +class KeysBackupSettingsRecyclerViewAdapter(val context: Context) : RecyclerView.Adapter() { + + val inflater: LayoutInflater = LayoutInflater.from(context) + + private var infoList: List = ArrayList() + + private var isBackupAlreadySetup = false + + var adapterListener: AdapterListener? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + GenericItemViewHolder.resId -> GenericItemViewHolder(inflater.inflate(viewType, parent, false)) + else -> FooterViewHolder(inflater.inflate(viewType, parent, false)) + } + } + + override fun getItemViewType(position: Int): Int { + return if (position < infoList.size) { + GenericItemViewHolder.resId + } else { + R.layout.item_keys_backup_settings_button_footer + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is GenericItemViewHolder) { + holder.bind(infoList[position]) + } else if (holder is FooterViewHolder) { + if (isBackupAlreadySetup) { + holder.button1.setText(R.string.keys_backup_settings_restore_backup_button) + holder.button1.isVisible = true + holder.button1.setOnClickListener { + adapterListener?.didSelectRestoreMessageRecovery() + } + + holder.button2.setText(R.string.keys_backup_settings_delete_backup_button) + holder.button2.isVisible = true + holder.button2.setOnClickListener { + adapterListener?.didSelectDeleteSetupMessageRecovery() + } + } else { + holder.button1.setText(R.string.keys_backup_setup) + holder.button1.isVisible = true + holder.button1.setOnClickListener { + adapterListener?.didSelectSetupMessageRecovery() + } + + holder.button2.isVisible = false + } + } + } + + override fun getItemCount(): Int { + return infoList.size + 1 /*footer*/ + } + + + fun updateWithTrust(session: Session, keyBackupVersionTrust: KeysBackupVersionTrust?) { + val keyBackupState = session.getKeysBackupService().state + val keyVersionResult = session.getKeysBackupService().mKeysBackupVersion + + val infos = ArrayList() + var itemSummary: GenericRecyclerViewItem? = null + + when (keyBackupState) { + KeysBackupState.Unknown, + KeysBackupState.CheckingBackUpOnHomeserver -> { + //In this cases recycler view is hidden any way + //so do nothing + } + KeysBackupState.Disabled -> { + itemSummary = GenericRecyclerViewItem(context.getString(R.string.keys_backup_settings_status_not_setup), + style = GenericRecyclerViewItem.STYLE.BIG_TEXT) + + isBackupAlreadySetup = false + } + KeysBackupState.WrongBackUpVersion, + KeysBackupState.NotTrusted, + KeysBackupState.Enabling -> { + itemSummary = GenericRecyclerViewItem(context.getString(R.string.keys_backup_settings_status_ko), + style = GenericRecyclerViewItem.STYLE.BIG_TEXT).apply { + description = keyBackupState.toString() + endIconResourceId = R.drawable.unit_test_ko + } + + isBackupAlreadySetup = true + } + KeysBackupState.ReadyToBackUp -> { + itemSummary = GenericRecyclerViewItem(context.getString(R.string.keys_backup_settings_status_ok), + style = GenericRecyclerViewItem.STYLE.BIG_TEXT).apply { + endIconResourceId = R.drawable.unit_test_ok + description = context.getString(R.string.keys_backup_info_keys_all_backup_up) + } + + isBackupAlreadySetup = true + } + KeysBackupState.WillBackUp, + KeysBackupState.BackingUp -> { + itemSummary = GenericRecyclerViewItem(context.getString(R.string.keys_backup_settings_status_ok), + style = GenericRecyclerViewItem.STYLE.BIG_TEXT).apply { + hasIndeterminateProcess = true + + val totalKeys = session.inboundGroupSessionsCount(false) + ?: 0 + val backedUpKeys = session.inboundGroupSessionsCount(true) + ?: 0 + + val remainingKeysToBackup = totalKeys - backedUpKeys + + description = context.resources.getQuantityString(R.plurals.keys_backup_info_keys_backing_up, remainingKeysToBackup, remainingKeysToBackup) + } + + isBackupAlreadySetup = true + } + } + + itemSummary?.let { + infos.add(it) + } + + if (keyBackupVersionTrust != null) { + + if (!keyBackupVersionTrust.usable) { + itemSummary?.description = context.getString(R.string.keys_backup_settings_untrusted_backup) + } + + //Add infos + infos.add(GenericRecyclerViewItem(context.getString(R.string.keys_backup_info_title_version), keyVersionResult?.version + ?: "")) + infos.add(GenericRecyclerViewItem(context.getString(R.string.keys_backup_info_title_algorithm), keyVersionResult?.algorithm + ?: "")) + + keyBackupVersionTrust.signatures.forEach { + val signatureInfo = GenericRecyclerViewItem(context.getString(R.string.keys_backup_info_title_signature)) + val isDeviceKnown = it.device != null + val isDeviceVerified = it.device?.isVerified ?: false + val isSignatureValid = it.valid + val deviceId: String = it.deviceId ?: "" + + if (!isDeviceKnown) { + signatureInfo.description = context.getString(R.string.keys_backup_settings_signature_from_unknown_device, deviceId) + signatureInfo.endIconResourceId = R.drawable.e2e_warning + } else { + if (isSignatureValid) { + if (session.sessionParams.credentials.deviceId == it.deviceId) { + signatureInfo.description = context.getString(R.string.keys_backup_settings_valid_signature_from_this_device) + signatureInfo.endIconResourceId = R.drawable.e2e_verified + } else { + if (isDeviceVerified) { + signatureInfo.description = context.getString(R.string.keys_backup_settings_valid_signature_from_verified_device, deviceId) + signatureInfo.endIconResourceId = R.drawable.e2e_verified + } else { + signatureInfo.description = context.getString(R.string.keys_backup_settings_valid_signature_from_unverified_device, deviceId) + signatureInfo.endIconResourceId = R.drawable.e2e_warning + } + } + } else { + //Invalid signature + signatureInfo.endIconResourceId = R.drawable.e2e_warning + if (isDeviceVerified) { + signatureInfo.description = context.getString(R.string.keys_backup_settings_invalid_signature_from_verified_device, deviceId) + } else { + signatureInfo.description = context.getString(R.string.keys_backup_settings_invalid_signature_from_unverified_device, deviceId) + } + } + } + + infos.add(signatureInfo) + } //end for each + } + + infoList = infos + + notifyDataSetChanged() + } + + class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + init { + ButterKnife.bind(this, itemView) + } + + @BindView(R.id.keys_backup_settings_footer_button1) + lateinit var button1: Button + + @BindView(R.id.keys_backup_settings_footer_button2) + lateinit var button2: Button + + fun bind() { + + } + } + + interface AdapterListener { + fun didSelectSetupMessageRecovery() + fun didSelectRestoreMessageRecovery() + fun didSelectDeleteSetupMessageRecovery() + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt new file mode 100644 index 00000000..b8e17977 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.settings + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState +import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.core.utils.LiveEvent + + +class KeysBackupSettingsViewModel : ViewModel(), + KeysBackupService.KeysBackupStateListener { + + var session: Session? = null + + var keyVersionTrust: MutableLiveData = MutableLiveData() + var keyBackupState: MutableLiveData = MutableLiveData() + + private var _apiResultError: MutableLiveData> = MutableLiveData() + val apiResultError: LiveData> + get() = _apiResultError + + var loadingEvent: MutableLiveData = MutableLiveData() + + fun initSession(session: Session) { + keyBackupState.value = session.getKeysBackupService().state + if (this.session == null) { + this.session = session + session.getKeysBackupService().addListener(this) + } + } + + fun getKeysBackupTrust(versionResult: KeysVersionResult) { + val keysBackup = session?.getKeysBackupService() + keysBackup?.getKeysBackupTrust(versionResult, object : MatrixCallback { + override fun onSuccess(data: KeysBackupVersionTrust) { + keyVersionTrust.value = data + } + }) + } + + override fun onCleared() { + super.onCleared() + session?.getKeysBackupService()?.removeListener(this) + } + + override fun onStateChange(newState: KeysBackupState) { + keyBackupState.value = newState + } + + fun deleteCurrentBackup(context: Context) { + session?.getKeysBackupService()?.run { + loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_settings_deleting_backup)) + if (currentBackupVersion != null) { + deleteBackup(currentBackupVersion!!, object : MatrixCallback { + override fun onSuccess(info: Unit) { + //mmmm if state is stil unknown/checking.. + loadingEvent.value = null + } + + override fun onFailure(failure: Throwable) { + loadingEvent.value = null + _apiResultError.value = LiveEvent(context.getString(R.string.keys_backup_get_version_error, failure.localizedMessage)) + } + }) + } + } + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt new file mode 100644 index 00000000..e3d1de92 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -0,0 +1,206 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.setup + +import android.content.Context +import android.content.Intent +import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.dialogs.ExportKeysDialog +import im.vector.riotredesign.core.platform.SimpleFragmentActivity + +class KeysBackupSetupActivity : SimpleFragmentActivity() { + + override fun getTitleRes() = R.string.title_activity_keys_backup_setup + + private lateinit var viewModel: KeysBackupSetupSharedViewModel + + override fun initUiAndData() { + super.initUiAndData() + if (isFirstCreation()) { + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupSetupStep1Fragment.newInstance()) + .commitNow() + } + + viewModel = ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java) + viewModel.showManualExport.value = intent.getBooleanExtra(EXTRA_SHOW_MANUAL_EXPORT, false) + viewModel.initSession(mSession) + + + viewModel.isCreatingBackupVersion.observe(this, Observer { + val isCreating = it ?: false + if (isCreating) { + showWaitingView() + } else { + hideWaitingView() + } + }) + + viewModel.loadingStatus.observe(this, Observer { + it?.let { + updateWaitingView(it) + } + }) + + viewModel.navigateEvent.observe(this, Observer { uxStateEvent -> + when (uxStateEvent?.getContentIfNotHandled()) { + KeysBackupSetupSharedViewModel.NAVIGATE_TO_STEP_2 -> { + supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupSetupStep2Fragment.newInstance()) + .commit() + } + KeysBackupSetupSharedViewModel.NAVIGATE_TO_STEP_3 -> { + supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + supportFragmentManager.beginTransaction() + .replace(R.id.container, KeysBackupSetupStep3Fragment.newInstance()) + .commit() + } + KeysBackupSetupSharedViewModel.NAVIGATE_FINISH -> { + val resultIntent = Intent() + viewModel.keysVersion.value?.version?.let { + resultIntent.putExtra(KEYS_VERSION, it) + } + setResult(RESULT_OK, resultIntent) + finish() + } + KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT -> { + exportKeysManually() + } + } + }) + + + viewModel.prepareRecoverFailError.observe(this, Observer { error -> + if (error != null) { + AlertDialog.Builder(this) + .setTitle(R.string.unknown_error) + .setMessage(error.localizedMessage) + .setPositiveButton(R.string.ok) { _, _ -> + //nop + viewModel.prepareRecoverFailError.value = null + } + .show() + } + }) + + viewModel.creatingBackupError.observe(this, Observer { error -> + if (error != null) { + AlertDialog.Builder(this) + .setTitle(R.string.unexpected_error) + .setMessage(error.localizedMessage) + .setPositiveButton(R.string.ok) { _, _ -> + //nop + viewModel.creatingBackupError.value = null + } + .show() + } + }) + } + + fun exportKeysManually() { + ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener { + override fun onPassphrase(passphrase: String) { + notImplemented() + /* + showWaitingView() + + CommonActivityUtils.exportKeys(mSession, passphrase, object : SimpleApiCallback(this@KeysBackupSetupActivity) { + override fun onSuccess(filename: String) { + hideWaitingView() + + AlertDialog.Builder(this@KeysBackupSetupActivity) + .setMessage(getString(R.string.encryption_export_saved_as, filename)) + .setCancelable(false) + .setPositiveButton(R.string.ok) { dialog, which -> + val resultIntent = Intent() + resultIntent.putExtra(MANUAL_EXPORT, true) + setResult(RESULT_OK, resultIntent) + finish() + } + .show() + } + + override fun onNetworkError(e: Exception) { + super.onNetworkError(e) + hideWaitingView() + } + + override fun onMatrixError(e: MatrixError) { + super.onMatrixError(e) + hideWaitingView() + } + + override fun onUnexpectedError(e: Exception) { + super.onUnexpectedError(e) + hideWaitingView() + } + }) + */ + } + }) + + + } + + + override fun onBackPressed() { + if (viewModel.shouldPromptOnBack) { + if (waitingView?.isVisible == true) { + return + } + AlertDialog.Builder(this) + .setTitle(R.string.keys_backup_setup_skip_title) + .setMessage(R.string.keys_backup_setup_skip_msg) + .setNegativeButton(R.string.stay, null) + .setPositiveButton(R.string.abort) { _, _ -> + finish() + } + .show() + } else { + super.onBackPressed() + } + } + +// I think this code is useful, but it violates the code quality rules +// override fun onOptionsItemSelected(item: MenuItem): Boolean { +// if (item.itemId == android .R. id. home) { +// onBackPressed() +// return true +// } +// +// return super.onOptionsItemSelected(item) +// } + + + companion object { + const val KEYS_VERSION = "KEYS_VERSION" + const val MANUAL_EXPORT = "MANUAL_EXPORT" + const val EXTRA_SHOW_MANUAL_EXPORT = "SHOW_MANUAL_EXPORT" + + fun intent(context: Context, showManualExport: Boolean): Intent { + val intent = Intent(context, KeysBackupSetupActivity::class.java) + intent.putExtra(EXTRA_SHOW_MANUAL_EXPORT, showManualExport) + return intent + } + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt new file mode 100644 index 00000000..c9d11d16 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.fragments.keysbackup.setup + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nulabinc.zxcvbn.Strength +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.WaitingViewData +import im.vector.riotredesign.core.utils.LiveEvent +import timber.log.Timber + +/** + * The shared view model between all fragments. + */ +class KeysBackupSetupSharedViewModel : ViewModel() { + + companion object { + const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2" + const val NAVIGATE_TO_STEP_3 = "NAVIGATE_TO_STEP_3" + const val NAVIGATE_FINISH = "NAVIGATE_FINISH" + const val NAVIGATE_MANUAL_EXPORT = "NAVIGATE_MANUAL_EXPORT" + } + + lateinit var session: Session + + var showManualExport: MutableLiveData = MutableLiveData() + + var navigateEvent: MutableLiveData> = MutableLiveData() + var shouldPromptOnBack = true + + // Step 2 + var passphrase: MutableLiveData = MutableLiveData() + var passphraseError: MutableLiveData = MutableLiveData() + + var confirmPassphrase: MutableLiveData = MutableLiveData() + var confirmPassphraseError: MutableLiveData = MutableLiveData() + + var passwordStrength: MutableLiveData = MutableLiveData() + var showPasswordMode: MutableLiveData = MutableLiveData() + + // Step 3 + // Var to ignore events from previous request(s) to generate a recovery key + private var currentRequestId: MutableLiveData = MutableLiveData() + var recoveryKey: MutableLiveData = MutableLiveData() + var prepareRecoverFailError: MutableLiveData = MutableLiveData() + var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null + var copyHasBeenMade = false + var isCreatingBackupVersion: MutableLiveData = MutableLiveData() + var creatingBackupError: MutableLiveData = MutableLiveData() + var keysVersion: MutableLiveData = MutableLiveData() + + + var loadingStatus: MutableLiveData = MutableLiveData() + + init { + showPasswordMode.value = false + recoveryKey.value = null + isCreatingBackupVersion.value = false + prepareRecoverFailError.value = null + creatingBackupError.value = null + loadingStatus.value = null + } + + fun initSession(session: Session) { + this.session = session + } + + fun prepareRecoveryKey(context: Context, withPassphrase: String?) { + // Update requestId + currentRequestId.value = System.currentTimeMillis() + isCreatingBackupVersion.value = true + + // Ensure passphrase is hidden during the process + showPasswordMode.value = false + + recoveryKey.value = null + prepareRecoverFailError.value = null + session.let { mxSession -> + val requestedId = currentRequestId.value!! + + mxSession.getKeysBackupService().prepareKeysBackupVersion(withPassphrase, + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + if (requestedId != currentRequestId.value) { + //this is an old request, we can't cancel but we can ignore + return + } + + loadingStatus.value = WaitingViewData(context.getString(R.string.keys_backup_setup_step3_generating_key_status), + progress, + total) + } + }, + object : MatrixCallback { + override fun onSuccess(data: MegolmBackupCreationInfo) { + if (requestedId != currentRequestId.value) { + //this is an old request, we can't cancel but we can ignore + return + } + recoveryKey.value = data.recoveryKey + megolmBackupCreationInfo = data + copyHasBeenMade = false + + val keyBackup = session?.getKeysBackupService() + if (keyBackup != null) { + createKeysBackup(context, keyBackup) + } else { + loadingStatus.value = null + + isCreatingBackupVersion.value = false + prepareRecoverFailError.value = Exception() + } + } + + override fun onFailure(failure: Throwable) { + if (requestedId != currentRequestId.value) { + //this is an old request, we can't cancel but we can ignore + return + } + + loadingStatus.value = null + + isCreatingBackupVersion.value = false + prepareRecoverFailError.value = failure ?: Exception() + } + }) + } + } + + private fun createKeysBackup(context: Context, keysBackup: KeysBackupService) { + loadingStatus.value = WaitingViewData(context.getString(R.string.keys_backup_setup_creating_backup), isIndeterminate = true) + + creatingBackupError.value = null + keysBackup.createKeysBackupVersion(megolmBackupCreationInfo!!, object : MatrixCallback { + + override fun onSuccess(data: KeysVersion) { + loadingStatus.value = null + + isCreatingBackupVersion.value = false + keysVersion.value = data + navigateEvent.value = LiveEvent(NAVIGATE_TO_STEP_3) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## createKeyBackupVersion") + loadingStatus.value = null + + isCreatingBackupVersion.value = false + creatingBackupError.value = failure + } + }) + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt new file mode 100644 index 00000000..0fccd510 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.crypto.keysbackup.setup + +import android.os.Bundle +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.utils.LiveEvent + +class KeysBackupSetupStep1Fragment : VectorBaseFragment() { + + companion object { + fun newInstance() = KeysBackupSetupStep1Fragment() + } + + override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step1 + + private lateinit var viewModel: KeysBackupSetupSharedViewModel + + @BindView(R.id.keys_backup_setup_step1_advanced) + lateinit var advancedOptionText: TextView + + + @BindView(R.id.keys_backup_setup_step1_manualExport) + lateinit var manualExportButton: Button + + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + viewModel.showManualExport.observe(this, Observer { + val showOption = it ?: false + //Can't use isVisible because the kotlin compiler will crash with Back-end (JVM) Internal error: wrong code generated + advancedOptionText.visibility = if (showOption) View.VISIBLE else View.GONE + manualExportButton.visibility = if (showOption) View.VISIBLE else View.GONE + }) + + } + + @OnClick(R.id.keys_backup_setup_step1_button) + fun onButtonClick() { + viewModel.navigateEvent.value = LiveEvent(KeysBackupSetupSharedViewModel.NAVIGATE_TO_STEP_2) + } + + @OnClick(R.id.keys_backup_setup_step1_manualExport) + fun onManualExportClick() { + viewModel.navigateEvent.value = LiveEvent(KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT) + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt new file mode 100644 index 00000000..47ade276 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt @@ -0,0 +1,212 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.setup + +import android.os.AsyncTask +import android.os.Bundle +import android.text.TextUtils +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.ImageView +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.transition.TransitionManager +import butterknife.BindView +import butterknife.OnClick +import butterknife.OnTextChanged +import com.google.android.material.textfield.TextInputLayout +import com.nulabinc.zxcvbn.Zxcvbn +import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.showPassword +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.ui.views.PasswordStrengthBar +import im.vector.riotredesign.features.settings.VectorLocale + + +class KeysBackupSetupStep2Fragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step2 + + @BindView(R.id.keys_backup_root) + lateinit var rootGroup: ViewGroup + + @BindView(R.id.keys_backup_passphrase_enter_edittext) + lateinit var mPassphraseTextEdit: EditText + + @BindView(R.id.keys_backup_passphrase_enter_til) + lateinit var mPassphraseInputLayout: TextInputLayout + + @BindView(R.id.keys_backup_view_show_password) + lateinit var mPassphraseReveal: ImageView + + @BindView(R.id.keys_backup_passphrase_confirm_edittext) + lateinit var mPassphraseConfirmTextEdit: EditText + + @BindView(R.id.keys_backup_passphrase_confirm_til) + lateinit var mPassphraseConfirmInputLayout: TextInputLayout + + @BindView(R.id.keys_backup_passphrase_security_progress) + lateinit var mPassphraseProgressLevel: PasswordStrengthBar + + private val zxcvbn = Zxcvbn() + + @OnTextChanged(R.id.keys_backup_passphrase_enter_edittext) + fun onPassphraseChanged() { + viewModel.passphrase.value = mPassphraseTextEdit.text.toString() + viewModel.confirmPassphraseError.value = null + } + + @OnTextChanged(R.id.keys_backup_passphrase_confirm_edittext) + fun onConfirmPassphraseChanged() { + viewModel.confirmPassphrase.value = mPassphraseConfirmTextEdit.text.toString() + } + + private lateinit var viewModel: KeysBackupSetupSharedViewModel + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + viewModel.shouldPromptOnBack = true + bindViewToViewModel() + } + + /* ========================================================================================== + * MENU + * ========================================================================================== */ + + private fun bindViewToViewModel() { + viewModel.passwordStrength.observe(this, Observer { strength -> + if (strength == null) { + mPassphraseProgressLevel.strength = 0 + mPassphraseInputLayout.error = null + } else { + val score = strength.score + mPassphraseProgressLevel.strength = score + + if (score in 1..3) { + val warning = strength.feedback?.getWarning(VectorLocale.applicationLocale) + if (warning != null) { + mPassphraseInputLayout.error = warning + } + + val suggestions = strength.feedback?.getSuggestions(VectorLocale.applicationLocale) + if (suggestions != null) { + mPassphraseInputLayout.error = suggestions.firstOrNull() + } + + } else { + mPassphraseInputLayout.error = null + } + + } + }) + + viewModel.passphrase.observe(this, Observer { newValue -> + if (TextUtils.isEmpty(newValue)) { + viewModel.passwordStrength.value = null + } else { + AsyncTask.execute { + val strength = zxcvbn.measure(newValue) + activity?.runOnUiThread { + viewModel.passwordStrength.value = strength + } + } + } + + }) + + mPassphraseTextEdit.setText(viewModel.passphrase.value) + + viewModel.passphraseError.observe(this, Observer { + TransitionManager.beginDelayedTransition(rootGroup) + mPassphraseInputLayout.error = it + }) + + mPassphraseConfirmTextEdit.setText(viewModel.confirmPassphrase.value) + + viewModel.showPasswordMode.observe(this, Observer { + val shouldBeVisible = it ?: false + mPassphraseTextEdit.showPassword(shouldBeVisible) + mPassphraseConfirmTextEdit.showPassword(shouldBeVisible) + mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) + }) + + viewModel.confirmPassphraseError.observe(this, Observer { + TransitionManager.beginDelayedTransition(rootGroup) + mPassphraseConfirmInputLayout.error = it + }) + + mPassphraseConfirmTextEdit.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + doNext() + return@setOnEditorActionListener true + } + return@setOnEditorActionListener false + } + + } + + @OnClick(R.id.keys_backup_view_show_password) + fun toggleVisibilityMode() { + viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false) + } + + @OnClick(R.id.keys_backup_setup_step2_button) + fun doNext() { + when { + TextUtils.isEmpty(viewModel.passphrase.value) -> { + viewModel.passphraseError.value = context?.getString(R.string.passphrase_empty_error_message) + } + viewModel.passphrase.value != viewModel.confirmPassphrase.value -> { + viewModel.confirmPassphraseError.value = context?.getString(R.string.passphrase_passphrase_does_not_match) + } + viewModel.passwordStrength.value?.score ?: 0 < 4 -> { + viewModel.passphraseError.value = context?.getString(R.string.passphrase_passphrase_too_weak) + } + else -> { + viewModel.megolmBackupCreationInfo = null + + viewModel.prepareRecoveryKey(activity!!, viewModel.passphrase.value) + } + } + } + + @OnClick(R.id.keys_backup_setup_step2_skip_button) + fun skipPassphrase() { + when { + TextUtils.isEmpty(viewModel.passphrase.value) -> { + // Generate a recovery key for the user + viewModel.megolmBackupCreationInfo = null + + viewModel.prepareRecoveryKey(activity!!, null) + } + else -> { + // User has entered a passphrase but want to skip this step. + viewModel.passphraseError.value = context?.getString(R.string.keys_backup_passphrase_not_empty_error_message) + } + } + } + + companion object { + fun newInstance() = KeysBackupSetupStep2Fragment() + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt new file mode 100644 index 00000000..d2739d16 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.keysbackup.setup + +import android.os.Bundle +import android.view.View +import android.widget.Button +import android.widget.TextView +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import com.google.android.material.bottomsheet.BottomSheetDialog +import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.core.utils.* +import java.io.ByteArrayInputStream + +class KeysBackupSetupStep3Fragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3 + + @BindView(R.id.keys_backup_setup_step3_button) + lateinit var mFinishButton: Button + + @BindView(R.id.keys_backup_recovery_key_text) + lateinit var mRecoveryKeyTextView: TextView + + @BindView(R.id.keys_backup_setup_step3_line2_text) + lateinit var mRecoveryKeyLabel2TextView: TextView + + companion object { + fun newInstance() = KeysBackupSetupStep3Fragment() + } + + private lateinit var viewModel: KeysBackupSetupSharedViewModel + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + viewModel = activity?.run { + ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + + viewModel.shouldPromptOnBack = false + + viewModel.passphrase.observe(this, Observer { + if (it.isNullOrBlank()) { + //Recovery was generated, so show key and options to save + mRecoveryKeyLabel2TextView.text = getString(R.string.keys_backup_setup_step3_text_line2_no_passphrase) + mFinishButton.text = getString(R.string.keys_backup_setup_step3_button_title_no_passphrase) + + mRecoveryKeyTextView.text = viewModel.recoveryKey.value!! + .replace(" ", "") + .chunked(16) + .joinToString("\n") { + it + .chunked(4) + .joinToString(" ") + } + mRecoveryKeyTextView.isVisible = true + + } else { + mRecoveryKeyLabel2TextView.text = getString(R.string.keys_backup_setup_step3_text_line2) + mFinishButton.text = getString(R.string.keys_backup_setup_step3_button_title) + mRecoveryKeyTextView.isVisible = false + } + }) + + } + + @OnClick(R.id.keys_backup_setup_step3_button) + fun onFinishButtonClicked() { + if (viewModel.megolmBackupCreationInfo == null) { + //nothing + } else { + if (viewModel.passphrase.value.isNullOrBlank() && !viewModel.copyHasBeenMade) { + Toast.makeText(context, R.string.keys_backup_setup_step3_please_make_copy, Toast.LENGTH_LONG).show() + } else { + viewModel.navigateEvent.value = LiveEvent(KeysBackupSetupSharedViewModel.NAVIGATE_FINISH) + } + } + } + + @OnClick(R.id.keys_backup_setup_step3_copy_button) + fun onCopyButtonClicked() { + val dialog = BottomSheetDialog(activity!!) + dialog.setContentView(R.layout.bottom_sheet_save_recovery_key) + dialog.setCanceledOnTouchOutside(true) + val recoveryKey = viewModel.recoveryKey.value!! + + if (viewModel.passphrase.value.isNullOrBlank()) { + dialog.findViewById(R.id.keys_backup_recovery_key_text)?.isVisible = false + } else { + dialog.findViewById(R.id.keys_backup_recovery_key_text)?.let { + it.isVisible = true + it.text = recoveryKey.replace(" ", "") + .chunked(16) + .joinToString("\n") { + it + .chunked(4) + .joinToString(" ") + } + } + } + + dialog.findViewById(R.id.keys_backup_setup_save)?.setOnClickListener { + if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_EXPORT_KEYS)) { + exportRecoveryKeyToFile(recoveryKey) + } + dialog.dismiss() + } + + dialog.findViewById(R.id.keys_backup_setup_share)?.setOnClickListener { + startSharePlainTextIntent(this, + context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), + recoveryKey, + context?.getString(R.string.recovery_key)) + viewModel.copyHasBeenMade = true + dialog.dismiss() + } + + dialog.show() + } + + @OnClick(R.id.keys_backup_recovery_key_text) + fun onRecoveryKeyClicked() { + viewModel.recoveryKey.value?.let { + viewModel.copyHasBeenMade = true + + copyToClipboard(activity!!, it) + } + } + + fun exportRecoveryKeyToFile(it: String) { + val stream = ByteArrayInputStream(it.toByteArray()) + + TODO() + /* + val url = viewModel.session.mediaCache.saveMedia(stream, "recovery-key" + System.currentTimeMillis() + ".txt", "text/plain") + stream.close() + CommonActivityUtils.saveMediaIntoDownloads(context, + File(Uri.parse(url).path!!), "recovery-key.txt", "text/plain", object : SimpleApiCallback() { + override fun onSuccess(path: String) { + context?.let { + AlertDialog.Builder(it) + .setMessage(getString(R.string.recovery_key_export_saved_as_warning, path)) + .setCancelable(false) + .setPositiveButton(R.string.ok, null) + .show() + } + + viewModel.copyHasBeenMade = true + } + }) + */ + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + if (allGranted(grantResults)) { + if (requestCode == PERMISSION_REQUEST_CODE_EXPORT_KEYS) { + viewModel.recoveryKey.value?.let { + exportRecoveryKeyToFile(it) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt new file mode 100644 index 00000000..a3ca69ce --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.content.Context +import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.riotredesign.R +import im.vector.riotredesign.features.popup.PopupAlertManager + +/** + * Listens to the VerificationManager and add a new notification when an incoming request is detected. + */ +class IncomingVerificationRequestHandler(val context: Context, + private val credentials: Credentials, + verificationService: SasVerificationService) : SasVerificationService.SasVerificationListener { + + init { + verificationService.addListener(this) + } + + override fun transactionCreated(tx: SasVerificationTransaction) {} + + override fun transactionUpdated(tx: SasVerificationTransaction) { + when (tx.state) { + SasVerificationTxState.OnStarted -> { + //Add a notification for every incoming request + val session = Matrix.getInstance().currentSession!! + val name = session.getUser(tx.otherUserId)?.displayName + ?: tx.otherUserId + + val alert = PopupAlertManager.VectorAlert( + "kvr_${tx.transactionId}", + context.getString(R.string.sas_incoming_request_notif_title), + context.getString(R.string.sas_incoming_request_notif_content, name), + R.drawable.shield + ).apply { + contentAction = Runnable { + val intent = SASVerificationActivity.incomingIntent(context, + credentials.userId, + tx.otherUserId, + tx.transactionId) + weakCurrentActivity?.get()?.startActivity(intent) + } + dismissedAction = Runnable { + tx.cancel() + } + addButton( + context.getString(R.string.ignore), + Runnable { + tx.cancel() + } + ) + addButton( + context.getString(R.string.action_open), + Runnable { + val intent = SASVerificationActivity.incomingIntent(context, + credentials.userId, + tx.otherUserId, + tx.transactionId) + weakCurrentActivity?.get()?.startActivity(intent) + } + ) + //10mn expiration + expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L) + + } + PopupAlertManager.postVectorAlert(alert) + } + SasVerificationTxState.Cancelled, + SasVerificationTxState.OnCancelled, + SasVerificationTxState.Verified -> { + //cancel related notification + PopupAlertManager.cancelAlert("kvr_${tx.transactionId}") + } + else -> Unit + } + } + + override fun markedAsManuallyVerified(userId: String, deviceId: String) { + + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt new file mode 100644 index 00000000..4db3315a --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationActivity.kt @@ -0,0 +1,245 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest +import im.vector.matrix.android.api.session.crypto.sas.CancelCode +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.SimpleFragmentActivity +import im.vector.riotredesign.core.platform.WaitingViewData + +class SASVerificationActivity : SimpleFragmentActivity() { + + + companion object { + + private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID" + private const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID" + private const val EXTRA_OTHER_USER_ID = "EXTRA_OTHER_USER_ID" + private const val EXTRA_OTHER_DEVICE_ID = "EXTRA_OTHER_DEVICE_ID" + private const val EXTRA_IS_INCOMING = "EXTRA_IS_INCOMING" + + /* ========================================================================================== + * INPUT + * ========================================================================================== */ + + fun incomingIntent(context: Context, matrixID: String, otherUserId: String, transactionID: String): Intent { + val intent = Intent(context, SASVerificationActivity::class.java) + intent.putExtra(EXTRA_MATRIX_ID, matrixID) + intent.putExtra(EXTRA_TRANSACTION_ID, transactionID) + intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId) + intent.putExtra(EXTRA_IS_INCOMING, true) + return intent + } + + fun outgoingIntent(context: Context, matrixID: String, otherUserId: String, otherDeviceId: String): Intent { + val intent = Intent(context, SASVerificationActivity::class.java) + intent.putExtra(EXTRA_MATRIX_ID, matrixID) + intent.putExtra(EXTRA_OTHER_DEVICE_ID, otherDeviceId) + intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId) + intent.putExtra(EXTRA_IS_INCOMING, false) + return intent + } + + /* ========================================================================================== + * OUTPUT + * ========================================================================================== */ + + fun getOtherUserId(intent: Intent?): String? { + return intent?.getStringExtra(EXTRA_OTHER_USER_ID) + } + + fun getOtherDeviceId(intent: Intent?): String? { + return intent?.getStringExtra(EXTRA_OTHER_DEVICE_ID) + } + } + + override fun getTitleRes() = R.string.title_activity_verify_device + + + private lateinit var viewModel: SasVerificationViewModel + + override fun initUiAndData() { + super.initUiAndData() + viewModel = ViewModelProviders.of(this).get(SasVerificationViewModel::class.java) + val transactionID: String? = intent.getStringExtra(EXTRA_TRANSACTION_ID) + + if (isFirstCreation()) { + val isIncoming = intent.getBooleanExtra(EXTRA_IS_INCOMING, false) + if (isIncoming) { + //incoming always have a transaction id + viewModel.initIncoming(mSession, intent.getStringExtra(EXTRA_OTHER_USER_ID), transactionID) + } else { + viewModel.initOutgoing(mSession, intent.getStringExtra(EXTRA_OTHER_USER_ID), intent.getStringExtra(EXTRA_OTHER_DEVICE_ID)) + } + + if (isIncoming) { + val incoming = viewModel.transaction as IncomingSasVerificationTransaction + when (incoming.uxState) { + IncomingSasVerificationTransaction.UxState.UNKNOWN, + IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT, + IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> { + supportActionBar?.setTitle(R.string.sas_incoming_request_title) + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationIncomingFragment.newInstance()) + .commitNow() + } + IncomingSasVerificationTransaction.UxState.WAIT_FOR_VERIFICATION, + IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationShortCodeFragment.newInstance()) + .commitNow() + } + IncomingSasVerificationTransaction.UxState.VERIFIED -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationVerifiedFragment.newInstance()) + .commitNow() + } + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME, + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> { + viewModel.navigateCancel() + } + } + } else { + val outgoing = viewModel.transaction as? OutgoingSasVerificationRequest + //transaction can be null, as not yet created + when (outgoing?.uxState) { + null, + OutgoingSasVerificationRequest.UxState.UNKNOWN, + OutgoingSasVerificationRequest.UxState.WAIT_FOR_START, + OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationStartFragment.newInstance()) + .commitNow() + } + OutgoingSasVerificationRequest.UxState.SHOW_SAS, + OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationShortCodeFragment.newInstance()) + .commitNow() + } + OutgoingSasVerificationRequest.UxState.VERIFIED -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationVerifiedFragment.newInstance()) + .commitNow() + } + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME, + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> { + viewModel.navigateCancel() + } + } + } + } + + viewModel.navigateEvent.observe(this, Observer { uxStateEvent -> + when (uxStateEvent?.getContentIfNotHandled()) { + SasVerificationViewModel.NAVIGATE_FINISH -> { + finish() + } + SasVerificationViewModel.NAVIGATE_FINISH_SUCCESS -> { + val dataResult = Intent() + dataResult.putExtra(EXTRA_OTHER_DEVICE_ID, viewModel.otherDeviceId) + dataResult.putExtra(EXTRA_OTHER_USER_ID, viewModel.otherUserId) + setResult(Activity.RESULT_OK, dataResult) + finish() + } + SasVerificationViewModel.NAVIGATE_SAS_DISPLAY -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationShortCodeFragment.newInstance()) + .commitNow() + } + SasVerificationViewModel.NAVIGATE_SUCCESS -> { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out) + .replace(R.id.container, SASVerificationVerifiedFragment.newInstance()) + .commitNow() + } + SasVerificationViewModel.NAVIGATE_CANCELLED -> { + val isCancelledByMe = viewModel.transaction?.state == SasVerificationTxState.Cancelled + val humanReadableReason = when (viewModel.transaction?.cancelledReason) { + CancelCode.User -> getString(R.string.sas_error_m_user) + CancelCode.Timeout -> getString(R.string.sas_error_m_timeout) + CancelCode.UnknownTransaction -> getString(R.string.sas_error_m_unknown_transaction) + CancelCode.UnknownMethod -> getString(R.string.sas_error_m_unknown_method) + CancelCode.MismatchedCommitment -> getString(R.string.sas_error_m_mismatched_commitment) + CancelCode.MismatchedSas -> getString(R.string.sas_error_m_mismatched_sas) + CancelCode.UnexpectedMessage -> getString(R.string.sas_error_m_unexpected_message) + CancelCode.InvalidMessage -> getString(R.string.sas_error_m_invalid_message) + CancelCode.MismatchedKeys -> getString(R.string.sas_error_m_key_mismatch) + // Use user error + CancelCode.UserMismatchError -> getString(R.string.sas_error_m_user_error) + null -> getString(R.string.sas_error_unknown) + } + val message = + if (isCancelledByMe) getString(R.string.sas_cancelled_by_me, humanReadableReason) + else getString(R.string.sas_cancelled_by_other, humanReadableReason) + //Show a dialog + if (!this.isFinishing) { + AlertDialog.Builder(this) + .setTitle(R.string.sas_cancelled_dialog_title) + .setMessage(message) + .setCancelable(false) + .setPositiveButton(R.string.ok) { _, _ -> + //nop + finish() + } + .show() + } + } + } + }) + + viewModel.loadingLiveEvent.observe(this, Observer { + if (it == null) { + hideWaitingView() + } else { + val status = if (it == -1) "" else getString(it) + updateWaitingView(WaitingViewData(status, isIndeterminate = true)) + } + }) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + //we want to cancel the transaction + viewModel.cancelTransaction() + } + + return super.onOptionsItemSelected(item) + } + + + override fun onBackPressed() { + //we want to cancel the transaction + viewModel.cancelTransaction() + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt new file mode 100644 index 00000000..9b479eb9 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationIncomingFragment.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.os.Bundle +import android.widget.ImageView +import android.widget.TextView +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.features.home.AvatarRenderer + +class SASVerificationIncomingFragment : VectorBaseFragment() { + + companion object { + fun newInstance() = SASVerificationIncomingFragment() + } + + @BindView(R.id.sas_incoming_request_user_display_name) + lateinit var otherUserDisplayNameTextView: TextView + + @BindView(R.id.sas_incoming_request_user_id) + lateinit var otherUserIdTextView: TextView + + @BindView(R.id.sas_incoming_request_user_device) + lateinit var otherDeviceTextView: TextView + + @BindView(R.id.sas_incoming_request_user_avatar) + lateinit var avatarImageView: ImageView + + override fun getLayoutResId() = R.layout.fragment_sas_verification_incoming_request + + private lateinit var viewModel: SasVerificationViewModel + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = activity?.run { + ViewModelProviders.of(this).get(SasVerificationViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + otherUserDisplayNameTextView.text = viewModel.otherUser?.displayName ?: viewModel.otherUserId + otherUserIdTextView.text = viewModel.otherUserId + otherDeviceTextView.text = viewModel.otherDeviceId + + viewModel.otherUser?.let { + AvatarRenderer.render(it, avatarImageView) + } + + viewModel.transactionState.observe(this, Observer { + val uxState = (viewModel.transaction as? IncomingSasVerificationTransaction)?.uxState + when (uxState) { + IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> { + viewModel.loadingLiveEvent.value = null + } + IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> { + viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner + } + IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { + viewModel.shortCodeReady() + } + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME, + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> { + viewModel.loadingLiveEvent.value = null + viewModel.navigateCancel() + } + else -> Unit + } + }) + + } + + @OnClick(R.id.sas_request_continue_button) + fun didAccept() { + viewModel.acceptTransaction() + } + + @OnClick(R.id.sas_request_cancel_button) + fun didCancel() { + viewModel.cancelTransaction() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt new file mode 100644 index 00000000..b0363fae --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationShortCodeFragment.kt @@ -0,0 +1,183 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.os.Bundle +import android.view.ViewGroup +import android.widget.TextView +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.BindView +import butterknife.OnClick +import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment + +class SASVerificationShortCodeFragment : VectorBaseFragment() { + + private lateinit var viewModel: SasVerificationViewModel + + companion object { + fun newInstance() = SASVerificationShortCodeFragment() + } + + + @BindView(R.id.sas_decimal_code) + lateinit var decimalTextView: TextView + + @BindView(R.id.sas_emoji_description) + lateinit var descriptionTextView: TextView + + @BindView(R.id.sas_emoji_grid) + lateinit var emojiGrid: ViewGroup + + + @BindView(R.id.emoji0) + lateinit var emoji0View: ViewGroup + @BindView(R.id.emoji1) + lateinit var emoji1View: ViewGroup + @BindView(R.id.emoji2) + lateinit var emoji2View: ViewGroup + @BindView(R.id.emoji3) + lateinit var emoji3View: ViewGroup + @BindView(R.id.emoji4) + lateinit var emoji4View: ViewGroup + @BindView(R.id.emoji5) + lateinit var emoji5View: ViewGroup + @BindView(R.id.emoji6) + lateinit var emoji6View: ViewGroup + + + override fun getLayoutResId() = R.layout.fragment_sas_verification_display_code + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + viewModel = activity?.run { + ViewModelProviders.of(this).get(SasVerificationViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + + viewModel.transaction?.let { + if (it.supportsEmoji()) { + val emojicodes = it.getEmojiCodeRepresentation() + emojicodes.forEachIndexed { index, emojiRepresentation -> + when (index) { + 0 -> { + emoji0View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji0View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + 1 -> { + emoji1View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji1View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + 2 -> { + emoji2View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji2View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + 3 -> { + emoji3View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji3View.findViewById(R.id.item_emoji_name_tv)?.setText(emojiRepresentation.nameResId) + } + 4 -> { + emoji4View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji4View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + 5 -> { + emoji5View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji5View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + 6 -> { + emoji6View.findViewById(R.id.item_emoji_tv).text = emojiRepresentation.emoji + emoji6View.findViewById(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId) + } + } + } + } + + //decimal is at least supported + decimalTextView.text = it.getDecimalCodeRepresentation() + + + if (it.supportsEmoji()) { + descriptionTextView.text = getString(R.string.sas_emoji_description) + decimalTextView.isVisible = false + emojiGrid.isVisible = true + } else { + descriptionTextView.text = getString(R.string.sas_decimal_description) + decimalTextView.isVisible = true + emojiGrid.isInvisible = true + } + } + + + viewModel.transactionState.observe(this, Observer { + if (viewModel.transaction is IncomingSasVerificationTransaction) { + val uxState = (viewModel.transaction as IncomingSasVerificationTransaction).uxState + when (uxState) { + IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { + viewModel.loadingLiveEvent.value = null + } + IncomingSasVerificationTransaction.UxState.VERIFIED -> { + viewModel.loadingLiveEvent.value = null + viewModel.deviceIsVerified() + } + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME, + IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> { + viewModel.loadingLiveEvent.value = null + viewModel.navigateCancel() + } + else -> { + viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner + } + } + + } else if (viewModel.transaction is OutgoingSasVerificationRequest) { + val uxState = (viewModel.transaction as OutgoingSasVerificationRequest).uxState + when (uxState) { + OutgoingSasVerificationRequest.UxState.SHOW_SAS -> { + viewModel.loadingLiveEvent.value = null + } + OutgoingSasVerificationRequest.UxState.VERIFIED -> { + viewModel.loadingLiveEvent.value = null + viewModel.deviceIsVerified() + } + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME, + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> { + viewModel.loadingLiveEvent.value = null + viewModel.navigateCancel() + } + else -> { + viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner + } + } + } + }) + } + + + @OnClick(R.id.sas_request_continue_button) + fun didAccept() { + viewModel.confirmEmojiSame() + } + + @OnClick(R.id.sas_request_cancel_button) + fun didCancel() { + viewModel.cancelTransaction() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt new file mode 100644 index 00000000..e41d0c73 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationStartFragment.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.os.Bundle +import android.view.ViewGroup +import android.widget.Button +import android.widget.ProgressBar +import android.widget.TextView +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.transition.TransitionManager +import butterknife.BindView +import butterknife.OnClick +import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseActivity +import im.vector.riotredesign.core.platform.VectorBaseFragment + +class SASVerificationStartFragment : VectorBaseFragment() { + + companion object { + fun newInstance() = SASVerificationStartFragment() + } + + override fun getLayoutResId() = R.layout.fragment_sas_verification_start + + private lateinit var viewModel: SasVerificationViewModel + + + @BindView(R.id.rootLayout) + lateinit var rootLayout: ViewGroup + + @BindView(R.id.sas_start_button) + lateinit var startButton: Button + + @BindView(R.id.sas_start_button_loading) + lateinit var startButtonLoading: ProgressBar + + @BindView(R.id.sas_verifying_keys) + lateinit var loadingText: TextView + + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = activity?.run { + ViewModelProviders.of(this).get(SasVerificationViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + viewModel.transactionState.observe(this, Observer { + val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState + when (uxState) { + OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> { + //display loading + TransitionManager.beginDelayedTransition(this.rootLayout) + this.loadingText.isVisible = true + this.startButton.isInvisible = true + this.startButtonLoading.isVisible = true + this.startButtonLoading.animate() + + } + OutgoingSasVerificationRequest.UxState.SHOW_SAS -> { + viewModel.shortCodeReady() + } + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME, + OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> { + viewModel.navigateCancel() + } + else -> { + TransitionManager.beginDelayedTransition(this.rootLayout) + this.loadingText.isVisible = false + this.startButton.isVisible = true + this.startButtonLoading.isVisible = false + } + } + }) + + } + + @OnClick(R.id.sas_start_button) + fun doStart() { + viewModel.beginSasKeyVerification() + } + + @OnClick(R.id.sas_legacy_verification) + fun doLegacy() { + (requireActivity() as VectorBaseActivity).notImplemented() + + /* + viewModel.session.crypto?.getDeviceInfo(viewModel.otherUserId ?: "", viewModel.otherDeviceId + ?: "", object : SimpleApiCallback() { + override fun onSuccess(info: MXDeviceInfo?) { + info?.let { + + CommonActivityUtils.displayDeviceVerificationDialogLegacy(it, it.userId, viewModel.session, activity, object : YesNoListener { + override fun yes() { + viewModel.manuallyVerified() + } + + override fun no() { + + } + }) + } + } + }) + */ + } + + @OnClick(R.id.sas_cancel_button) + fun doCancel() { + // Transaction may be started, or not + viewModel.cancelTransaction() + } + + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt new file mode 100644 index 00000000..a14dc3ce --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SASVerificationVerifiedFragment.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import android.os.Bundle +import androidx.lifecycle.ViewModelProviders +import butterknife.OnClick +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment + +class SASVerificationVerifiedFragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_sas_verification_verified + + companion object { + fun newInstance() = SASVerificationVerifiedFragment() + } + + private lateinit var viewModel: SasVerificationViewModel + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + viewModel = activity?.run { + ViewModelProviders.of(this).get(SasVerificationViewModel::class.java) + } ?: throw Exception("Invalid Activity") + + } + + @OnClick(R.id.sas_verification_verified_done_button) + fun onDone() { + viewModel.finishSuccess() + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt new file mode 100644 index 00000000..7f06bba8 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/crypto/verification/SasVerificationViewModel.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.crypto.verification + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState +import im.vector.matrix.android.api.session.user.model.User +import im.vector.riotredesign.core.utils.LiveEvent + + +class SasVerificationViewModel : ViewModel(), + SasVerificationService.SasVerificationListener { + + + companion object { + const val NAVIGATE_FINISH = "NAVIGATE_FINISH" + const val NAVIGATE_FINISH_SUCCESS = "NAVIGATE_FINISH_SUCCESS" + const val NAVIGATE_SAS_DISPLAY = "NAVIGATE_SAS_DISPLAY" + const val NAVIGATE_SUCCESS = "NAVIGATE_SUCCESS" + const val NAVIGATE_CANCELLED = "NAVIGATE_CANCELLED" + } + + lateinit var sasVerificationService: SasVerificationService + + var otherUserId: String? = null + var otherDeviceId: String? = null + var otherUser: User? = null + var transaction: SasVerificationTransaction? = null + + + var transactionState: MutableLiveData = MutableLiveData() + + init { + //Force a first observe + transactionState.value = null + } + + private var _navigateEvent: MutableLiveData> = MutableLiveData() + val navigateEvent: LiveData> + get() = _navigateEvent + + + var loadingLiveEvent: MutableLiveData = MutableLiveData() + + var transactionID: String? = null + set(value) { + if (value != null) { + transaction = sasVerificationService.getExistingTransaction(otherUserId!!, value) + transactionState.value = transaction?.state + otherDeviceId = transaction?.otherDeviceId + } + field = value + } + + + fun initIncoming(session: Session, otherUserId: String, transactionID: String?) { + this.sasVerificationService = session.getSasVerificationService() + this.otherUserId = otherUserId + this.transactionID = transactionID + this.sasVerificationService.addListener(this) + this.otherUser = session.getUser(otherUserId) + if (transactionID == null || transaction == null) { + //sanity, this transaction is not known anymore + _navigateEvent.value = LiveEvent(NAVIGATE_FINISH) + } + } + + fun initOutgoing(session: Session, otherUserId: String, otherDeviceId: String) { + this.sasVerificationService = session.getSasVerificationService() + this.otherUserId = otherUserId + this.otherDeviceId = otherDeviceId + this.sasVerificationService.addListener(this) + this.otherUser = session.getUser(otherUserId) + } + + fun beginSasKeyVerification() { + val verificationSAS = sasVerificationService.beginKeyVerificationSAS(otherUserId!!, otherDeviceId!!) + this.transactionID = verificationSAS + } + + + override fun transactionCreated(tx: SasVerificationTransaction) { + + } + + override fun transactionUpdated(tx: SasVerificationTransaction) { + if (transactionID == tx.transactionId) { + transactionState.value = tx.state + } + } + + override fun markedAsManuallyVerified(userId: String, deviceId: String) { + + } + + fun cancelTransaction() { + transaction?.cancel() + _navigateEvent.value = LiveEvent(NAVIGATE_FINISH) + } + + fun finishSuccess() { + _navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS) + } + + fun manuallyVerified() { + if (otherUserId != null && otherDeviceId != null) { + sasVerificationService.markedLocallyAsManuallyVerified(otherUserId!!, otherDeviceId!!) + } + _navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS) + } + + + fun acceptTransaction() { + (transaction as? IncomingSasVerificationTransaction)?.performAccept() + } + + fun confirmEmojiSame() { + transaction?.userHasVerifiedShortCode() + } + + fun shortCodeReady() { + loadingLiveEvent.value = null + _navigateEvent.value = LiveEvent(NAVIGATE_SAS_DISPLAY) + } + + fun deviceIsVerified() { + loadingLiveEvent.value = null + _navigateEvent.value = LiveEvent(NAVIGATE_SUCCESS) + } + + fun navigateCancel() { + _navigateEvent.value = LiveEvent(NAVIGATE_CANCELLED) + } + + override fun onCleared() { + super.onCleared() + sasVerificationService.removeListener(this) + } + + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt index 012bdf83..f7305439 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.user.model.User import im.vector.riotredesign.R import im.vector.riotredesign.core.glide.GlideApp import im.vector.riotredesign.core.glide.GlideRequest @@ -55,6 +56,11 @@ object AvatarRenderer { render(roomSummary.avatarUrl, roomSummary.roomId, roomSummary.displayName, imageView) } + @UiThread + fun render(user: User, imageView: ImageView) { + render(imageView.context, GlideApp.with(imageView), user.avatarUrl, user.userId, user.displayName, DrawableImageViewTarget(imageView)) + } + @UiThread fun render(avatarUrl: String?, identifier: String, name: String?, imageView: ImageView) { render(imageView.context, GlideApp.with(imageView), avatarUrl, identifier, name, DrawableImageViewTarget(imageView)) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 98666053..cffd20f1 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -66,6 +66,8 @@ class HomeModule { roomMemberItemFactory = RoomMemberItemFactory(get()), roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()), callItemFactory = CallItemFactory(get()), + encryptionItemFactory = EncryptionItemFactory(get()), + encryptedItemFactory = EncryptedItemFactory(get(), get(), messageItemFactory), defaultItemFactory = DefaultItemFactory() ) TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt new file mode 100644 index 00000000..25bb8d74 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.home.room.detail.timeline.factory + +import android.graphics.Typeface +import android.text.Spannable +import android.text.SpannableString +import android.text.style.StyleSpan +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.internal.crypto.MXDecryptionException +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult +import im.vector.riotredesign.R +import im.vector.riotredesign.core.epoxy.VectorEpoxyModel +import im.vector.riotredesign.core.resources.StringProvider +import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController +import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_ + +class EncryptedItemFactory( + private val session: Session, + private val stringProvider: StringProvider, + private val messageItemFactory: MessageItemFactory) { + + fun create(timelineEvent: TimelineEvent, + nextEvent: TimelineEvent?, + callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? { + + return when { + EventType.ENCRYPTED == timelineEvent.root.type -> { + val decrypted: MXEventDecryptionResult? + try { + decrypted = session.decryptEvent(timelineEvent.root, "TODO") + } catch (e: MXDecryptionException) { + val errorDescription = + if (e.cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) + } else { + e.localizedMessage + } + + val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription) + + val spannableStr = SpannableString(message) + spannableStr.setSpan(StyleSpan(Typeface.ITALIC), 0, message.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + // TODO This is not correct format for error, change it + return NoticeItem_() + .noticeText(spannableStr) + .avatarUrl(timelineEvent.senderAvatar) + .memberName(timelineEvent.senderName) + } + + if (decrypted == null) { + return null + } + + if (decrypted.mClearEvent == null) { + return null + } + + val decryptedTimelineEvent = timelineEvent.copy(root = decrypted.mClearEvent!!) + + // Success + return messageItemFactory.create(decryptedTimelineEvent, nextEvent, callback) + } + else -> null + } + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt new file mode 100644 index 00000000..55ab6527 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.home.room.detail.timeline.factory + +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent +import im.vector.riotredesign.R +import im.vector.riotredesign.core.resources.StringProvider +import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem +import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_ + +class EncryptionItemFactory(private val stringProvider: StringProvider) { + + fun create(event: TimelineEvent): NoticeItem? { + val text = buildNoticeText(event.root, event.senderName) ?: return null + return NoticeItem_() + .noticeText(text) + .avatarUrl(event.senderAvatar) + .memberName(event.senderName) + } + + private fun buildNoticeText(event: Event, senderName: String?): CharSequence? { + return when { + EventType.ENCRYPTION == event.type -> { + val content = event.content.toModel() ?: return null + stringProvider.getString(R.string.notice_end_to_end, senderName, content.algorithm) + } + else -> null + } + + } + + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index d5354434..21759e20 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -28,6 +28,8 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, private val roomMemberItemFactory: RoomMemberItemFactory, private val roomHistoryVisibilityItemFactory: RoomHistoryVisibilityItemFactory, private val callItemFactory: CallItemFactory, + private val encryptionItemFactory: EncryptionItemFactory, + private val encryptedItemFactory: EncryptedItemFactory, private val defaultItemFactory: DefaultItemFactory) { fun create(event: TimelineEvent, @@ -46,8 +48,10 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, EventType.CALL_HANGUP, EventType.CALL_ANSWER -> callItemFactory.create(event) - EventType.ENCRYPTED, - EventType.ENCRYPTION, + EventType.ENCRYPTION -> encryptionItemFactory.create(event) + + EventType.ENCRYPTED -> encryptedItemFactory.create(event, nextEvent, callback) + EventType.STATE_ROOM_THIRD_PARTY_INVITE, EventType.STICKER, EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event) diff --git a/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt b/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt new file mode 100644 index 00000000..9a2a5e1e --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/lifecycle/VectorActivityLifecycleCallbacks.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.lifecycle + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import im.vector.riotredesign.features.popup.PopupAlertManager + +class VectorActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks { + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityResumed(activity: Activity) { + PopupAlertManager.onNewActivityDisplayed(activity) + } + + override fun onActivityStarted(activity: Activity) { + } + + override fun onActivityDestroyed(activity: Activity) { + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityStopped(activity: Activity) { + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt index d7f7aadd..b3464b52 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/notifications/NotificationBroadcastReceiver.kt @@ -61,7 +61,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent { Matrix.getInstance(context)?.defaultSession?.let { session -> session.dataHandler ?.getRoom(roomId) - ?.markAllAsRead(object : SimpleApiCallback() { + ?.markAllAsRead(object : SimpleApiCallback() { override fun onSuccess(void: Void?) { // Ignore } @@ -100,7 +100,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver(), KoinComponent { val event = Event(mxMessage, session.credentials.userId, roomId) room.storeOutgoingEvent(event) - room.sendEvent(event, object : ApiCallback { + room.sendEvent(event, object : MatrixCallback { override fun onSuccess(info: Void?) { Timber.d("Send message : onSuccess ") val notifiableMessageEvent = NotifiableMessageEvent( diff --git a/vector/src/main/java/im/vector/riotredesign/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/riotredesign/features/popup/PopupAlertManager.kt new file mode 100644 index 00000000..f658b496 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/popup/PopupAlertManager.kt @@ -0,0 +1,229 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotredesign.features.popup + +import android.app.Activity +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import com.tapadoo.alerter.Alerter +import com.tapadoo.alerter.OnHideAlertListener +import im.vector.riotredesign.R +import im.vector.riotredesign.features.crypto.verification.SASVerificationActivity +import timber.log.Timber +import java.lang.ref.WeakReference + +/** + * Responsible of displaying important popup alerts on top of the screen. + * Alerts are stacked and will be displayed sequentially + */ +object PopupAlertManager { + + private var weakCurrentActivity: WeakReference? = null + private var currentAlerter: VectorAlert? = null + + private val alertFiFo = ArrayList() + + fun postVectorAlert(alert: VectorAlert) { + synchronized(alertFiFo) { + alertFiFo.add(alert) + } + displayNextIfPossible() + } + + fun cancelAlert(uid: String) { + synchronized(alertFiFo) { + alertFiFo.listIterator().apply { + while (this.hasNext()) { + val next = this.next() + if (next.uid == uid) { + this.remove() + } + } + } + } + + //it could also be the current one + if (currentAlerter?.uid == uid) { + Alerter.hide() + currentIsDismissed() + } + } + + + fun onNewActivityDisplayed(activity: Activity) { + //we want to remove existing popup on previous activity and display it on new one + if (currentAlerter != null) { + weakCurrentActivity?.get()?.let { + Alerter.clearCurrent(it) + } + } + + if (shouldIgnoreActivity(activity)) { + return + } + + weakCurrentActivity = WeakReference(activity) + + if (currentAlerter != null) { + if (currentAlerter!!.expirationTimestamp != null && System.currentTimeMillis() > currentAlerter!!.expirationTimestamp!!) { + //this alert has expired, remove it + //perform dismiss + try { + currentAlerter?.dismissedAction?.run() + } catch (e: Exception) { + Timber.e("## failed to perform action") + } + currentAlerter = null + Handler(Looper.getMainLooper()).postDelayed({ + displayNextIfPossible() + }, 2000) + } else { + showAlert(currentAlerter!!, activity, animate = false) + } + } else { + Handler(Looper.getMainLooper()).postDelayed({ + displayNextIfPossible() + }, 2000) + } + } + + private fun shouldIgnoreActivity(activity: Activity) = activity is SASVerificationActivity + + + private fun displayNextIfPossible() { + val currentActivity = weakCurrentActivity?.get() + if (Alerter.isShowing || currentActivity == null) { + //will retry later + return + } + val next: VectorAlert? + synchronized(alertFiFo) { + next = alertFiFo.firstOrNull() + if (next != null) alertFiFo.remove(next) + } + currentAlerter = next + next?.let { + val currentTime = System.currentTimeMillis() + if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) { + //skip + try { + next.dismissedAction?.run() + } catch (e: java.lang.Exception) { + Timber.e("## failed to perform action") + } + displayNextIfPossible() + } else { + showAlert(it, currentActivity) + } + } + } + + private fun showAlert(alert: VectorAlert, activity: Activity, animate: Boolean = true) { + alert.weakCurrentActivity = WeakReference(activity) + Alerter.create(activity) + .setTitle(alert.title) + .setText(alert.description) + .apply { + + if (!animate) { + setEnterAnimation(R.anim.anim_alerter_no_anim) + } + + alert.iconId?.let { + setIcon(it) + } + alert.actions.forEach { action -> + addButton(action.title, R.style.AlerterButton, View.OnClickListener { + if (action.autoClose) { + currentIsDismissed() + Alerter.hide() + } + try { + action.action.run() + } catch (e: java.lang.Exception) { + Timber.e("## failed to perform action") + } + + }) + } + setOnClickListener(View.OnClickListener { _ -> + alert.contentAction?.let { + currentIsDismissed() + Alerter.hide() + try { + it.run() + } catch (e: java.lang.Exception) { + Timber.e("## failed to perform action") + } + } + }) + + } + .setOnHideListener(OnHideAlertListener { + //called when dismissed on swipe + try { + alert.dismissedAction?.run() + } catch (e: java.lang.Exception) { + Timber.e("## failed to perform action") + } + currentIsDismissed() + }) + .enableSwipeToDismiss() + .enableInfiniteDuration(true) + .setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color) + .show() + } + + private fun currentIsDismissed() { + //current alert has been hidden + currentAlerter = null + Handler(Looper.getMainLooper()).postDelayed({ + displayNextIfPossible() + }, 500) + } + + /** + * Dataclass to describe an important alert with actions. + */ + data class VectorAlert(val uid: String, + val title: String, + val description: String, + @DrawableRes val iconId: Int?) { + + data class Button(val title: String, val action: Runnable, val autoClose: Boolean) + + //will be set by manager, and accessible by actions at runtime + var weakCurrentActivity: WeakReference? = null + + val actions = ArrayList

    hx3?D(t93Nm1}uRb8n9cIBX(towz`Zjq^4tI)Y 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