Merge branch 'develop' into element_3776

This commit is contained in:
SBiOSoftWhare 2021-02-23 18:51:19 +01:00 committed by GitHub
commit a8f7713a16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 262 additions and 138 deletions

View file

@ -2,9 +2,10 @@ Changes to be released in next version
=================================================
✨ Features
*
* Enable encryption for accounts, contacts and keys in the crypto database (#3867).
🙌 Improvements
* Home: Show room directory on join room action (#3775).
* RoomVC: Add quick actions in timeline on room creation (#3776).
🐛 Bugfix

View file

@ -72,6 +72,9 @@ class CommonConfiguration: NSObject, Configurable {
// Disable key backup on common
sdkOptions.enableKeyBackupWhenStartingMXCrypto = false
// Configure key provider delegate
MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared
}

View file

@ -74,16 +74,16 @@ PODS:
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
- OLMKit (~> 3.1.0)
- OLMKit (~> 3.2.2)
- Realm (= 10.1.4)
- MatrixSDK/JingleCallStack (0.18.1):
- JitsiMeetSDK (= 3.1.0)
- MatrixSDK/Core
- OLMKit (3.1.0):
- OLMKit/olmc (= 3.1.0)
- OLMKit/olmcpp (= 3.1.0)
- OLMKit/olmc (3.1.0)
- OLMKit/olmcpp (3.1.0)
- OLMKit (3.2.2):
- OLMKit/olmc (= 3.2.2)
- OLMKit/olmcpp (= 3.2.2)
- OLMKit/olmc (3.2.2)
- OLMKit/olmcpp (3.2.2)
- ReadMoreTextView (3.0.1)
- Realm (10.1.4):
- Realm/Headers (= 10.1.4)
@ -188,7 +188,7 @@ SPEC CHECKSUMS:
MatomoTracker: a59ec4da0f580be57bdc6baa708a71a86532a832
MatrixKit: 267c3235abae2e3878e41a57bda32ec4899118e6
MatrixSDK: 7d5faf810eab02a189df64aef28583c8bed81f5c
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
OLMKit: 20d1c564033a1ae7148f8f599378d4c798363905
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 80f4fb2971ccb9adc27a47d0955ae8e533a7030b
Reusable: 53a9acf5c536f229b31b5865782414b508252ddb

View file

@ -0,0 +1,128 @@
//
// Copyright 2020 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.
//
import UIKit
import KeychainAccess
import MatrixKit
import CommonCrypto
@objcMembers
class EncryptionKeyManager: NSObject, MXKeyProviderDelegate {
static let shared = EncryptionKeyManager()
private static let keychainService: String = BuildSettings.baseBundleIdentifier + ".encryption-manager-service"
private static let contactsIv: KeyValueStoreKey = "contactsIv"
private static let contactsAesKey: KeyValueStoreKey = "contactsAesKey"
private static let accountIv: KeyValueStoreKey = "accountIv"
private static let accountAesKey: KeyValueStoreKey = "accountAesKey"
private static let cryptoOlmPickleKey: KeyValueStoreKey = "cryptoOlmPickleKey"
private let keychainStore: KeyValueStore = KeychainStore(withKeychain: Keychain(service: keychainService, accessGroup: BuildSettings.keychainAccessGroup))
private override init() {
super.init()
initKeys()
}
private func initKeys() {
generateIvIfNotExists(forKey: EncryptionKeyManager.accountIv)
generateAesKeyIfNotExists(forKey: EncryptionKeyManager.accountAesKey)
generateIvIfNotExists(forKey: EncryptionKeyManager.contactsIv)
generateAesKeyIfNotExists(forKey: EncryptionKeyManager.contactsAesKey)
generateKeyIfNotExists(forKey: EncryptionKeyManager.cryptoOlmPickleKey, size: 32)
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv), "[EncryptionKeyManager] initKeys: Failed to generate IV for acount")
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey), "[EncryptionKeyManager] initKeys: Failed to generate AES Key for acount")
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv), "[EncryptionKeyManager] initKeys: Failed to generate IV for contacts")
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey), "[EncryptionKeyManager] initKeys: Failed to generate AES Key for contacts")
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.cryptoOlmPickleKey), "[EncryptionKeyManager] initKeys: Failed to generate Key for olm pickle key")
}
// MARK: - MXKeyProviderDelegate
func isEncryptionAvailableForData(ofType dataType: String) -> Bool {
return dataType == MXKContactManagerDataType
|| dataType == MXKAccountManagerDataType
|| dataType == MXCryptoOlmPickleKeyDataType
}
func hasKeyForData(ofType dataType: String) -> Bool {
switch dataType {
case MXKContactManagerDataType:
return keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv) && keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey)
case MXKAccountManagerDataType:
return keychainStore.containsObject(forKey: EncryptionKeyManager.accountIv) && keychainStore.containsObject(forKey: EncryptionKeyManager.accountAesKey)
case MXCryptoOlmPickleKeyDataType:
return keychainStore.containsObject(forKey: EncryptionKeyManager.cryptoOlmPickleKey)
default:
return false
}
}
func keyDataForData(ofType dataType: String) -> MXKeyData? {
switch dataType {
case MXKContactManagerDataType:
if let ivKey = try? keychainStore.data(forKey: EncryptionKeyManager.contactsIv),
let aesKey = try? keychainStore.data(forKey: EncryptionKeyManager.contactsAesKey) {
return MXAesKeyData(iv: ivKey, key: aesKey)
}
case MXKAccountManagerDataType:
if let ivKey = try? keychainStore.data(forKey: EncryptionKeyManager.accountIv),
let aesKey = try? keychainStore.data(forKey: EncryptionKeyManager.accountAesKey) {
return MXAesKeyData(iv: ivKey, key: aesKey)
}
case MXCryptoOlmPickleKeyDataType:
if let key = try? keychainStore.data(forKey: EncryptionKeyManager.cryptoOlmPickleKey) {
return MXRawDataKey(key: key)
}
default:
return nil
}
return nil
}
// MARK: - Private methods
private func generateIvIfNotExists(forKey key: String) {
guard !keychainStore.containsObject(forKey: key) else {
return
}
do {
try keychainStore.set(MXAes.iv(), forKey: key)
} catch {
NSLog("[EncryptionKeyManager] initKeys: Failed to generate IV: %@", error.localizedDescription)
}
}
private func generateAesKeyIfNotExists(forKey key: String) {
generateKeyIfNotExists(forKey: key, size: kCCKeySizeAES256)
}
private func generateKeyIfNotExists(forKey key: String, size: Int) {
guard !keychainStore.containsObject(forKey: key) else {
return
}
do {
var keyBytes = [UInt8](repeating: 0, count: size)
_ = SecRandomCopyBytes(kSecRandomDefault, size, &keyBytes)
try keychainStore.set(Data(bytes: keyBytes, count: size), forKey: key)
} catch {
NSLog("[EncryptionKeyManager] initKeys: Failed to generate Key[%@]: %@", key, error.localizedDescription)
}
}
}

View file

@ -165,6 +165,16 @@
*/
- (void)muteEditedRoomNotifications:(BOOL)mute;
/**
Show room directory.
*/
- (void)showRoomDirectory;
/**
Show a public room.
*/
- (void)openPublicRoom:(MXPublicRoom *)publicRoom;
#pragma mark - Scrolling
/**

View file

@ -34,7 +34,7 @@
#import "Riot-Swift.h"
@interface RecentsViewController () <CreateRoomCoordinatorBridgePresenterDelegate>
@interface RecentsViewController () <CreateRoomCoordinatorBridgePresenterDelegate, RoomsDirectoryCoordinatorBridgePresenterDelegate>
{
// Tell whether a recents refresh is pending (suspended during editing mode).
BOOL isRefreshPending;
@ -68,6 +68,8 @@
@property (nonatomic, strong) CreateRoomCoordinatorBridgePresenter *createRoomCoordinatorBridgePresenter;
@property (nonatomic, strong) RoomsDirectoryCoordinatorBridgePresenter *roomsDirectoryCoordinatorBridgePresenter;
@end
@implementation RecentsViewController
@ -856,6 +858,18 @@
self.view.userInteractionEnabled = userInteractionEnabled;
}
- (RecentsDataSource*)recentsDataSource
{
RecentsDataSource* recentsDataSource = nil;
if ([self.dataSource isKindOfClass:[RecentsDataSource class]])
{
recentsDataSource = (RecentsDataSource*)self.dataSource;
}
return recentsDataSource;
}
#pragma mark - MXKDataSourceDelegate
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
@ -1783,73 +1797,58 @@
- (void)joinARoom
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
// Prompt the user to type a room id or room alias
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_recents_join_room_title", @"Vector", nil)
message:NSLocalizedStringFromTable(@"room_recents_join_room_prompt", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
}];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
NSString *roomAliasOrId = [self->currentAlert textFields].firstObject.text;
self->currentAlert = nil;
[self.activityIndicator startAnimating];
[self showRoomDirectory];
}
// TODO
self->currentRequest = [self.mainSession joinRoom:roomAliasOrId viaServers:nil success:^(MXRoom *room) {
self->currentRequest = nil;
[self.activityIndicator stopAnimating];
// Show the room
[[AppDelegate theDelegate] showRoom:room.roomId andEventId:nil withMatrixSession:self.mainSession];
} failure:^(NSError *error) {
NSLog(@"[RecentsViewController] Join joinARoom (%@) failed", roomAliasOrId);
self->currentRequest = nil;
[self.activityIndicator stopAnimating];
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}]];
- (void)showRoomDirectory
{
if (!self.self.mainSession)
{
NSLog(@"[RecentsViewController] Fail to show room directory, session is nil");
return;
}
[currentAlert mxk_setAccessibilityIdentifier:@"RecentsVCJoinARoomAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
self.roomsDirectoryCoordinatorBridgePresenter = [[RoomsDirectoryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession dataSource:[self.recentsDataSource.publicRoomsDirectoryDataSource copy]];
self.roomsDirectoryCoordinatorBridgePresenter.delegate = self;
[self.roomsDirectoryCoordinatorBridgePresenter presentFrom:self animated:YES];
}
- (void)openPublicRoom:(MXPublicRoom *)publicRoom
{
if (!self.recentsDataSource)
{
NSLog(@"[RecentsViewController] Fail to open public room, dataSource is not kind of class MXKRecentsDataSource");
return;
}
// Check whether the user has already joined the selected public room
if ([self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession roomWithRoomId:publicRoom.roomId])
{
// Open the public room
[[AppDelegate theDelegate] showRoom:publicRoom.roomId andEventId:nil withMatrixSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession restoreInitialDisplay:NO];
}
else
{
// Preview the public room
if (publicRoom.worldReadable)
{
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
[self startActivityIndicator];
// Try to get more information about the room before opening its preview
[roomPreviewData peekInRoom:^(BOOL succeeded) {
[self stopActivityIndicator];
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
}];
}
else
{
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
}
}
}
#pragma mark - Table view scrolling
@ -2052,4 +2051,28 @@
return NO;
}
#pragma mark - RoomsDirectoryCoordinatorBridgePresenterDelegate
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidComplete:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
- (void)roomsDirectoryCoordinatorBridgePresenterDelegate:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectRoom:(MXPublicRoom *)room
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self openPublicRoom:room];
}];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidTapCreateNewRoom:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self createNewRoom];
}];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
@end

View file

@ -22,7 +22,7 @@
#import "Riot-Swift.h"
@interface RoomsViewController ()<RoomsDirectoryCoordinatorBridgePresenterDelegate>
@interface RoomsViewController ()
{
RecentsDataSource *recentsDataSource;
@ -30,8 +30,6 @@
UIView* footerSpinnerView;
}
@property (nonatomic, strong) RoomsDirectoryCoordinatorBridgePresenter *roomsDirectoryCoordinatorBridgePresenter;
@end
@implementation RoomsViewController
@ -128,9 +126,7 @@
- (void)onPlusButtonPressed
{
self.roomsDirectoryCoordinatorBridgePresenter = [[RoomsDirectoryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession dataSource:[recentsDataSource.publicRoomsDirectoryDataSource copy]];
self.roomsDirectoryCoordinatorBridgePresenter.delegate = self;
[self.roomsDirectoryCoordinatorBridgePresenter presentFrom:self animated:YES];
[self showRoomDirectory];
}
#pragma mark -
@ -263,38 +259,6 @@
[self openPublicRoom:publicRoom];
}
- (void)openPublicRoom:(MXPublicRoom *)publicRoom
{
// Check whether the user has already joined the selected public room
if ([recentsDataSource.publicRoomsDirectoryDataSource.mxSession roomWithRoomId:publicRoom.roomId])
{
// Open the public room
[[AppDelegate theDelegate] showRoom:publicRoom.roomId andEventId:nil withMatrixSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession restoreInitialDisplay:NO];
}
else
{
// Preview the public room
if (publicRoom.worldReadable)
{
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
[self startActivityIndicator];
// Try to get more information about the room before opening its preview
[roomPreviewData peekInRoom:^(BOOL succeeded) {
[self stopActivityIndicator];
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
}];
}
else
{
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
}
}
}
- (void)triggerDirectoryPagination
{
if (!recentsDataSource
@ -351,30 +315,6 @@
}
}
#pragma mark - RoomsDirectoryCoordinatorBridgePresenterDelegate
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidComplete:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
- (void)roomsDirectoryCoordinatorBridgePresenterDelegate:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectRoom:(MXPublicRoom *)room
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self openPublicRoom:room];
}];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidTapCreateNewRoom:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self createNewRoom];
}];
self.roomsDirectoryCoordinatorBridgePresenter = nil;
}
#pragma mark - Empty view management
- (void)updateEmptyView

View file

@ -48,6 +48,7 @@ targets:
- path: ../Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
- path: ../Riot/Categories/UNUserNotificationCenter.swift
- path: ../Riot/Managers/KeyValueStorage/KeyValueStore.swift
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
- path: ../Riot/Categories/Bundle.swift
- path: ../Riot/Generated/Strings.swift
- path: ../Riot/Generated/Images.swift

View file

@ -49,6 +49,8 @@ targets:
- path: ../Config/CommonConfiguration.swift
- path: ../Riot/Utils/UserNameColorGenerator.swift
- path: ../Riot/Categories/MXRoomSummary+Riot.m
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
- path: ../Riot/Managers/KeyValueStorage
- path: ../Riot/Managers/Settings/RiotSettings.swift
- path: ../Riot/Categories/UIColor.swift
- path: ../Riot/Categories/UISearchBar.swift

View file

@ -46,3 +46,5 @@ targets:
- path: ../Config/BuildSettings.swift
- path: ../Config/Configurable.swift
- path: ../Riot/Managers/Settings/RiotSettings.swift
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
- path: ../Riot/Managers/KeyValueStorage

View file

@ -274,8 +274,9 @@ platform :ios do
private_lane :update_build_number do |options|
build_number = options[:build_number]
increment_build_number_in_xcodeproj(
build_number: build_number,
update_file_content(
"../Config/AppIdentifiers.xcconfig",
/(CURRENT_PROJECT_VERSION\s*=)\s*.*/ => "\\1 #{build_number}"
)
end
@ -336,3 +337,16 @@ platform :ios do
.last # Latest ref found, in "version:refname" semantic order
end
end
# Update an arbitrary file by applying some RegExp replacements to its content
#
# @param [String] file The path to the file that needs replacing
# @param [Hash<RegExp, String>] replacements A list of replacements to apply
#
def update_file_content(file, replacements)
content = File.read(file)
replacements.each do |pattern, replacement|
content.gsub!(pattern, replacement)
end
File.write(file, content)
end