Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encrypt rooms' last messages #1718

Merged
merged 14 commits into from
Feb 21, 2023
9 changes: 9 additions & 0 deletions MatrixSDK/Data/MXRoomLastMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ FOUNDATION_EXPORT NSString *const MXRoomLastMessageDataType;

- (instancetype)initWithEvent:(MXEvent *)event;

/**
Returns an archived (possibly encrypted) version of MXRoomLastMessage sensible data.
alfogrillo marked this conversation as resolved.
Show resolved Hide resolved
These include:
- `text`
- `attributedText`
- `others`
*/
- (nullable NSData*)sensitiveData;

#pragma mark - CoreData Model

- (instancetype)initWithManagedObject:(MXRoomLastMessageMO *)model;
Expand Down
85 changes: 66 additions & 19 deletions MatrixSDK/Data/MXRoomLastMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#import <Security/Security.h>
#import <CommonCrypto/CommonCryptor.h>
#import <Foundation/Foundation.h>

NSString *const MXRoomLastMessageDataType = @"org.matrix.sdk.keychain.MXRoomLastMessage";

Expand Down Expand Up @@ -58,6 +59,22 @@ - (instancetype)initWithEvent:(MXEvent *)event
return self;
}

- (nullable NSData*)sensitiveData;
{
NSData* archived = [NSKeyedArchiver archivedDataWithRootObject:[self sensitiveDataDictionary]
requiringSecureCoding:NO
error:nil];
pixlwave marked this conversation as resolved.
Show resolved Hide resolved

if (archived && self.isEncrypted)
{
return [self encrypt:archived];
}
else
{
return archived;
}
}

- (NSComparisonResult)compareOriginServerTs:(MXRoomLastMessage *)otherMessage
{
NSComparisonResult result = NSOrderedAscending;
Expand Down Expand Up @@ -87,16 +104,39 @@ - (instancetype)initWithManagedObject:(MXRoomLastMessageMO *)model
_originServerTs = model.s_originServerTs;
_isEncrypted = model.s_isEncrypted;
_sender = model.s_sender;
_text = model.s_text;
if (model.s_attributedText)

NSData* archivedSensitiveData;
if (model.s_sensitiveData && model.s_isEncrypted)
{
_attributedText = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_attributedText];
archivedSensitiveData = [self decrypt:model.s_sensitiveData];
}
if (model.s_others)
else
{
_others = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_others];
archivedSensitiveData = model.s_sensitiveData;
}

if (archivedSensitiveData)
{
NSDictionary* sensitiveDataDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:archivedSensitiveData];

_text = sensitiveDataDictionary[kCodingKeyText];
_attributedText = sensitiveDataDictionary[kCodingKeyAttributedText];
_others = sensitiveDataDictionary[kCodingKeyOthers];
}
else // fallback logic for old database versions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can completely avoid having to deal with old versions by incrementing kMXFileVersion in MXFileStore. It'll wipe the data and force a fresh sync when the user upgrades. Probably better than running the old way in parallel and avoids having to think about migrations etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically I didn't change the MXFileStore so I'm not sure this is clean.
Btw does it mean people will need to do a full sync potentially taking a lot of time (for account big as Matthew's one)?

{
_text = model.s_text;

if (model.s_attributedText) {
_attributedText = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_attributedText];
}

if (model.s_others) {
_others = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_others];
}
}
}

return self;
}

Expand Down Expand Up @@ -142,20 +182,7 @@ - (void)encodeWithCoder:(NSCoder *)coder
[coder encodeObject:_sender forKey:kCodingKeySender];

// Build last message sensitive data
NSMutableDictionary *lastMessageDictionary = [NSMutableDictionary dictionary];
if (_text)
{
lastMessageDictionary[kCodingKeyText] = _text;
}
if (_attributedText)
{
lastMessageDictionary[kCodingKeyAttributedText] = _attributedText;
}
if (_others)
{
lastMessageDictionary[kCodingKeyOthers] = _others;
}

NSDictionary *lastMessageDictionary = [self sensitiveDataDictionary];
// And encrypt it if necessary
if (_isEncrypted)
{
Expand All @@ -173,6 +200,26 @@ - (void)encodeWithCoder:(NSCoder *)coder
}
}

- (NSDictionary*)sensitiveDataDictionary
{
NSMutableDictionary *lastMessageDictionary = [NSMutableDictionary dictionary];

if (_text)
{
lastMessageDictionary[kCodingKeyText] = _text;
}
if (_attributedText)
{
lastMessageDictionary[kCodingKeyAttributedText] = _attributedText;
}
if (_others)
{
lastMessageDictionary[kCodingKeyOthers] = _others;
}

return lastMessageDictionary;
}

#pragma mark - Data encryption

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21E258" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22C65" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="MXRoomLastMessage" representedClassName="MXRoomLastMessageMO" syncable="YES">
<attribute name="s_attributedText" optional="YES" attributeType="Binary" valueTransformerName=""/>
<attribute name="s_eventId" attributeType="String"/>
<attribute name="s_isEncrypted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="s_originServerTs" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="s_others" optional="YES" attributeType="Binary"/>
<attribute name="s_sender" attributeType="String"/>
<attribute name="s_sensitiveData" optional="YES" attributeType="Binary"/>
<attribute name="s_text" optional="YES" attributeType="String"/>
<relationship name="s_ofRoom" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MXRoomSummary" inverseName="s_lastMessage" inverseEntity="MXRoomSummary"/>
</entity>
Expand Down Expand Up @@ -62,10 +63,4 @@
<attribute name="s_usersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="s_ofRoom" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MXRoomSummary" inverseName="s_trust" inverseEntity="MXRoomSummary"/>
</entity>
<elements>
<element name="MXRoomLastMessage" positionX="117" positionY="90" width="128" height="149"/>
<element name="MXRoomMembersCount" positionX="297.1484375" positionY="-66.14453125" width="128" height="89"/>
<element name="MXRoomSummary" positionX="-63" positionY="-18" width="128" height="509"/>
<element name="MXUsersTrustLevelSummary" positionX="236.4921875" positionY="519.4296875" width="128" height="104"/>
</elements>
pixlwave marked this conversation as resolved.
Show resolved Hide resolved
</model>
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,17 @@ public class MXRoomLastMessageMO: NSManagedObject {
@NSManaged public var s_originServerTs: UInt64
@NSManaged public var s_isEncrypted: Bool
@NSManaged public var s_sender: String
@NSManaged public var s_sensitiveData: Data?

@available(*, deprecated, message: "Store sensitive information on s_sensitiveData instead")
@NSManaged public var s_text: String?
@NSManaged public var s_attributedText: Data?

@available(*, deprecated, message: "Store sensitive information on s_sensitiveData instead")
@NSManaged public var s_others: Data?

@available(*, deprecated, message: "Store sensitive information on s_sensitiveData instead")
@NSManaged public var s_attributedText: Data?

@discardableResult
internal static func insert(roomLastMessage lastMessage: MXRoomLastMessage,
into moc: NSManagedObjectContext) -> MXRoomLastMessageMO {
Expand All @@ -47,19 +54,11 @@ public class MXRoomLastMessageMO: NSManagedObject {
s_originServerTs = lastMessage.originServerTs
s_isEncrypted = lastMessage.isEncrypted
s_sender = lastMessage.sender
s_text = lastMessage.text

if let attributedText = lastMessage.attributedText {
s_attributedText = NSKeyedArchiver.archivedData(withRootObject: attributedText)
} else {
s_attributedText = nil
}
s_sensitiveData = lastMessage.sensitiveData()

if let others = lastMessage.others {
s_others = NSKeyedArchiver.archivedData(withRootObject: others)
} else {
s_others = nil
}
// Cleaning up unencrypted data in the old database versions. In the future these properties should be deleted from Core Data.
s_text = nil;
s_others = nil;
s_attributedText = nil;
}

}
1 change: 1 addition & 0 deletions changelog.d/pr-1718.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Encryption: add encryption to rooms' last messages.