Skip to content

Commit

Permalink
Merge pull request #1740 from matrix-org/andy/user_trust
Browse files Browse the repository at this point in the history
Simplify user verification state
  • Loading branch information
Anderas authored Mar 20, 2023
2 parents efdf2f4 + 1dafa92 commit 4fb70e6
Show file tree
Hide file tree
Showing 35 changed files with 456 additions and 462 deletions.
66 changes: 36 additions & 30 deletions MatrixSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions MatrixSDK/Crypto/CrossSigning/Data/MXCrossSigningInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#import <Foundation/Foundation.h>

#import "MXCrossSigningKey.h"
#import "MXUserTrustLevel.h"

@class MXCryptoUserIdentityWrapper;

Expand Down Expand Up @@ -56,7 +55,7 @@ extern NSString *const MXCrossSigningInfoTrustLevelDidChangeNotification;

#pragma mark - Additional information

@property (nonatomic, readonly) MXUserTrustLevel *trustLevel;
@property (nonatomic, readonly) BOOL isVerified;

@end

Expand Down
80 changes: 60 additions & 20 deletions MatrixSDK/Crypto/CrossSigning/Data/MXCrossSigningInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,38 @@

NSString *const MXCrossSigningInfoTrustLevelDidChangeNotification = @"MXCrossSigningInfoTrustLevelDidChangeNotification";

#pragma mark - Deprecated user trust

/**
Deprecated model of user trust that distinguished local vs cross-signing verification
This model is no longer used and is replaced by a combined `isVerified` property on `MXCrossSigningInfo`.
For backwards compatibility (reading archived values) the model needs to be kept around, albeit as private only.
*/
@interface MXDeprecatedUserTrustLevel : NSObject <NSCoding>
@property (nonatomic, readonly) BOOL isCrossSigningVerified;
@end

@implementation MXDeprecatedUserTrustLevel
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self)
{
// We ignore `isLocallyVerified` field and only consider `isCrossSigningVerified`
_isCrossSigningVerified = [aDecoder decodeBoolForKey:@"isCrossSigningVerified"];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
MXLogFailure(@"[MXDeprecatedUserTrustLevel] encode: This model should only be used for decoding existing data, not encoding new data");
}
@end

#pragma mark - CrossSigningInfo

@implementation MXCrossSigningInfo

- (instancetype)initWithUserIdentity:(MXCryptoUserIdentityWrapper *)userIdentity
Expand All @@ -43,7 +75,7 @@ - (instancetype)initWithUserIdentity:(MXCryptoUserIdentityWrapper *)userIdentity
keys[MXCrossSigningKeyType.userSigning] = userIdentity.userSignedKeys;
}
_keys = keys.copy;
_trustLevel = userIdentity.trustLevel;
_isVerified = userIdentity.isVerified;
}
return self;
}
Expand Down Expand Up @@ -92,7 +124,22 @@ - (id)initWithCoder:(NSCoder *)aDecoder
{
_userId = [aDecoder decodeObjectForKey:@"userId"];
_keys = [aDecoder decodeObjectForKey:@"keys"];
_trustLevel = [aDecoder decodeObjectForKey:@"trustLevel"];

// Initial version (i.e. version 0) of the model stored user trust via `MXUserTrustLevel` submodel.
// If we are reading this version out we need to decode verification state from this model before
// migrating it over to `isVerified`
NSInteger version = [aDecoder decodeIntegerForKey:@"version"];
if (version == 0)
{
[NSKeyedUnarchiver setClass:MXDeprecatedUserTrustLevel.class forClassName:@"MXUserTrustLevel"];
MXDeprecatedUserTrustLevel *trust = [aDecoder decodeObjectForKey:@"trustLevel"];
// Only convert cross-signed verification status, not local verification status
_isVerified = trust.isCrossSigningVerified;
}
else
{
_isVerified = [aDecoder decodeBoolForKey:@"isVerified"];
}
}
return self;
}
Expand All @@ -101,7 +148,8 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_userId forKey:@"userId"];
[aCoder encodeObject:_keys forKey:@"keys"];
[aCoder encodeObject:_trustLevel forKey:@"trustLevel"];
[aCoder encodeBool:_isVerified forKey:@"isVerified"];
[aCoder encodeInteger:1 forKey:@"version"];
}


Expand All @@ -113,31 +161,23 @@ - (instancetype)initWithUserId:(NSString *)userId
if (self)
{
_userId = userId;
_trustLevel = [MXUserTrustLevel new];
_isVerified = NO;
}
return self;
}

- (void)setTrustLevel:(MXUserTrustLevel*)trustLevel
{
_trustLevel = trustLevel;
}

- (BOOL)updateTrustLevel:(MXUserTrustLevel*)trustLevel
- (void)setIsVerified:(BOOL)isVerified
{
BOOL updated = NO;

if (![_trustLevel isEqual:trustLevel])
if (_isVerified == isVerified)
{
_trustLevel = trustLevel;
updated = YES;
[self didUpdateTrustLevel];
return;
}

return updated;

_isVerified = isVerified;
[self didUpdateVerificationState];
}

- (void)didUpdateTrustLevel
- (void)didUpdateVerificationState
{
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:MXCrossSigningInfoTrustLevelDidChangeNotification object:self userInfo:nil];
Expand All @@ -158,7 +198,7 @@ - (void)addCrossSigningKey:(MXCrossSigningKey*)crossSigningKey type:(NSString*)t

- (NSString *)description
{
return [NSString stringWithFormat:@"<MXCrossSigningInfo: %p> Trusted: %@\nMSK: %@\nSSK: %@\nUSK: %@", self, @(self.trustLevel.isCrossSigningVerified), self.masterKeys, self.selfSignedKeys, self.userSignedKeys];
return [NSString stringWithFormat:@"<MXCrossSigningInfo: %p> Verified: %@\nMSK: %@\nSSK: %@\nUSK: %@", self, @(self.isVerified), self.masterKeys, self.selfSignedKeys, self.userSignedKeys];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ NS_ASSUME_NONNULL_BEGIN

- (instancetype)initWithUserId:(NSString *)userId;

- (void)setTrustLevel:(MXUserTrustLevel*)trustLevel;
- (BOOL)updateTrustLevel:(MXUserTrustLevel*)trustLevel;
- (void)setIsVerified:(BOOL)isVerified;
- (void)addCrossSigningKey:(MXCrossSigningKey*)crossSigningKey type:(NSString*)type;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import MatrixSDKCrypto
public let masterKeys: MXCrossSigningKey?
public let selfSignedKeys: MXCrossSigningKey?
public let userSignedKeys: MXCrossSigningKey?
public let trustLevel: MXUserTrustLevel
public let isVerified: Bool

internal init(identity: UserIdentity, isVerified: Bool) {
switch identity {
Expand All @@ -43,13 +43,7 @@ import MatrixSDKCrypto
self.selfSignedKeys = .init(jsonString: selfSigningKey)
self.userSignedKeys = nil
}

// `MatrixSDKCrypto` does not distinguish local and cross-signed
// verification status for users
trustLevel = MXUserTrustLevel(
crossSigningVerified: isVerified,
locallyVerified: isVerified
)
self.isVerified = isVerified
}
}

Expand Down
55 changes: 21 additions & 34 deletions MatrixSDK/Crypto/CrossSigning/MXCrossSigning.m
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ - (void)setupWithAuthParams:(NSDictionary*)authParams
[self.crypto.matrixRestClient uploadDeviceSigningKeys:signingKeys authParams:authParams success:^{

// Store our user's keys
[keys updateTrustLevel:[MXUserTrustLevel trustLevelWithCrossSigningVerified:YES locallyVerified:YES]];
[keys setIsVerified:YES];
[self.crypto.store storeCrossSigningKeys:keys];

// Cross-signing is bootstrapped
Expand Down Expand Up @@ -585,7 +585,7 @@ - (BOOL)isDeviceVerified:(MXDeviceInfo*)device
BOOL isDeviceVerified = NO;

MXCrossSigningInfo *userCrossSigning = [self.crypto.store crossSigningKeysForUser:device.userId];
MXUserTrustLevel *userTrustLevel = [self.crypto trustLevelForUser:device.userId];
BOOL isUserVerified = [self.crypto isUserVerified:device.userId];

MXCrossSigningKey *userSSK = userCrossSigning.selfSignedKeys;
if (!userSSK)
Expand All @@ -610,7 +610,7 @@ - (BOOL)isDeviceVerified:(MXDeviceInfo*)device
// ...then we trust this device as much as far as we trust the user
if (userSSKVerify && deviceVerify)
{
isDeviceVerified = userTrustLevel.isCrossSigningVerified;
isDeviceVerified = isUserVerified;
}

return isDeviceVerified;
Expand Down Expand Up @@ -685,17 +685,14 @@ - (void)resetTrust
for (MXCrossSigningInfo *crossSigningInfo in self.crypto.store.crossSigningKeys)
{
BOOL isCrossSigningVerified = [self isUserWithCrossSigningKeysVerified:crossSigningInfo];
if (crossSigningInfo.trustLevel.isCrossSigningVerified != isCrossSigningVerified)
if (crossSigningInfo.isVerified != isCrossSigningVerified)
{
MXLogDebug(@"[MXCrossSigning] resetTrust: Change trust for %@: %@ -> %@", crossSigningInfo.userId,
@(crossSigningInfo.trustLevel.isCrossSigningVerified),
@(crossSigningInfo.isVerified),
@(isCrossSigningVerified));

MXUserTrustLevel *newTrustLevel = [MXUserTrustLevel trustLevelWithCrossSigningVerified:isCrossSigningVerified locallyVerified:crossSigningInfo.trustLevel.isLocallyVerified];
if ([crossSigningInfo updateTrustLevel:newTrustLevel])
{
[self.crypto.store storeCrossSigningKeys:crossSigningInfo];
}
[crossSigningInfo setIsVerified:isCrossSigningVerified];
[self.crypto.store storeCrossSigningKeys:crossSigningInfo];

// Update trust on associated devices
[self checkTrustLevelForDevicesOfUser:crossSigningInfo.userId];
Expand Down Expand Up @@ -745,42 +742,32 @@ - (BOOL)isSelfTrusted

NSString *myUserId = _crypto.mxSession.myUserId;

// Is the master key trusted?
MXCrossSigningInfo *myCrossSigningInfo = [_crypto.store crossSigningKeysForUser:myUserId];
if (myCrossSigningInfo && myCrossSigningInfo.trustLevel.isLocallyVerified)
// Is it signed by a locally trusted device?
NSDictionary<NSString*, NSString*> *myUserSignatures = myMasterKey.signatures.map[myUserId];
for (NSString *publicKeyId in myUserSignatures)
{
isMasterKeyTrusted = YES;
}
else
{
// Is it signed by a locally trusted device?
NSDictionary<NSString*, NSString*> *myUserSignatures = myMasterKey.signatures.map[myUserId];
for (NSString *publicKeyId in myUserSignatures)
MXKey *key = [[MXKey alloc] initWithKeyFullId:publicKeyId value:myUserSignatures[publicKeyId]];
if ([key.type isEqualToString:kMXKeyEd25519Type])
{
MXKey *key = [[MXKey alloc] initWithKeyFullId:publicKeyId value:myUserSignatures[publicKeyId]];
if ([key.type isEqualToString:kMXKeyEd25519Type])
MXDeviceInfo *device = [self.crypto.store deviceWithDeviceId:key.keyId forUser:myUserId];
if (device && device.trustLevel.isLocallyVerified)
{
MXDeviceInfo *device = [self.crypto.store deviceWithDeviceId:key.keyId forUser:myUserId];
if (device && device.trustLevel.isLocallyVerified)
// Check signature validity
NSError *error;
isMasterKeyTrusted = [_crypto.olmDevice verifySignature:device.fingerprint JSON:myMasterKey.signalableJSONDictionary signature:key.value error:&error];

if (isMasterKeyTrusted)
{
// Check signature validity
NSError *error;
isMasterKeyTrusted = [_crypto.olmDevice verifySignature:device.fingerprint JSON:myMasterKey.signalableJSONDictionary signature:key.value error:&error];

if (isMasterKeyTrusted)
{
break;
}
break;
}
}
}
}


if (!isMasterKeyTrusted)
{
MXLogDebug(@"[MXCrossSigning] isSelfTrusted: NO (MSK not trusted). MSK: %@", myMasterKey);
MXLogDebug(@"[MXCrossSigning] isSelfTrusted: My cross-signing info: %@", myCrossSigningInfo);
MXLogDebug(@"[MXCrossSigning] isSelfTrusted: My cross-signing info: %@", [self.crypto.store crossSigningKeysForUser:myUserId]);
MXLogDebug(@"[MXCrossSigning] isSelfTrusted: My user devices: %@", [self.crypto.store devicesForUser:myUserId]);

return NO;
Expand Down
12 changes: 3 additions & 9 deletions MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning {
return .notBootstrapped
}

if info.trustLevel.isVerified {
if info.isVerified {
return hasAllPrivateKeys ? .canCrossSign : .trustCrossSigning
} else {
return .crossSigningExists
Expand Down Expand Up @@ -217,17 +217,11 @@ class MXCrossSigningV2: NSObject, MXCrossSigning {
}

extension MXCrossSigningV2: MXRecoveryServiceDelegate {
func setUserVerification(
_ verificationStatus: Bool,
forUser userId: String,
func setUserVerificationForUserId(
_ userId: String,
success: @escaping () -> Void,
failure: @escaping (Swift.Error?) -> Void
) {
guard verificationStatus else {
log.failure("Cannot unset user trust")
failure(Error.cannotUnsetTrust)
return
}
signUser(withUserId: userId, success: success, failure: failure)
}
}
15 changes: 1 addition & 14 deletions MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.m
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,11 @@ - (void)doKeyDownloadForUsers:(NSArray<NSString *> *)users token:(NSString *)tok
MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers: Detected cross-signing keys rotation");
myUserCrossSigningKeysChanged = YES;
}

// Use current trust level
MXUserTrustLevel *oldTrustLevel = storedCrossSigningKeys.trustLevel;
if (myUserCrossSigningKeysChanged)
{
// Except if we cannot trust it anymore
oldTrustLevel = [MXUserTrustLevel new];
}

[crossSigningKeys setTrustLevel:oldTrustLevel];

// Compute trust on this user
// Note this overwrites the previous value
BOOL isCrossSigningVerified = [self.crossSigning isUserWithCrossSigningKeysVerified:crossSigningKeys];
MXUserTrustLevel *newTrustLevel = [MXUserTrustLevel trustLevelWithCrossSigningVerified:isCrossSigningVerified
locallyVerified:oldTrustLevel.isLocallyVerified];

[crossSigningKeys updateTrustLevel:newTrustLevel];
[crossSigningKeys setIsVerified:isCrossSigningVerified];

// Note that keys which aren't in the response will be removed from the store
[self->crypto.store storeCrossSigningKeys:crossSigningKeys];
Expand Down
15 changes: 9 additions & 6 deletions MatrixSDK/Crypto/MXCrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,22 @@ extern NSString *const MXDeviceListDidUpdateUsersDevicesNotification;
failure:(nullable void (^)(NSError *error))failure;

/**
Update the verification state of the given user.
Verify the given user via cross-signing
@param verificationStatus the new verification status.
@param userId the user.
@param success A block object called when the operation succeeds.
@param failure A block object called when the operation fails.
*/
- (void)setUserVerification:(BOOL)verificationStatus forUser:(NSString*)userId
success:(nullable void (^)(void))success
failure:(nullable void (^)(NSError *error))failure;
- (void)setUserVerificationForUserId:(NSString*)userId
success:(nullable void (^)(void))success
failure:(nullable void (^)(NSError *error))failure;

/**
Is the user verified via cross-signing
*/
- (BOOL)isUserVerified:(NSString *)userId;

- (MXUserTrustLevel*)trustLevelForUser:(NSString*)userId;
- (nullable MXDeviceTrustLevel*)deviceTrustLevelForDevice:(NSString*)deviceId ofUser:(NSString*)userId;

/**
Expand Down
Loading

0 comments on commit 4fb70e6

Please sign in to comment.