// // Copyright 2017 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // #import "OWSRecipientIdentity.h" #import #import NS_ASSUME_NONNULL_BEGIN NSString *OWSVerificationStateToString(OWSVerificationState verificationState) { switch (verificationState) { case OWSVerificationStateDefault: return @"OWSVerificationStateDefault"; case OWSVerificationStateVerified: return @"OWSVerificationStateVerified"; case OWSVerificationStateNoLongerVerified: return @"OWSVerificationStateNoLongerVerified"; case OWSVerificationStateDefaultAcknowledged: return @"OWSVerificationStateDefaultAcknowledged"; } } static SSKProtoVerifiedState OWSVerificationStateToProtoState(OWSVerificationState verificationState) { switch (verificationState) { case OWSVerificationStateDefault: case OWSVerificationStateDefaultAcknowledged: return SSKProtoVerifiedStateDefault; case OWSVerificationStateVerified: return SSKProtoVerifiedStateVerified; case OWSVerificationStateNoLongerVerified: return SSKProtoVerifiedStateUnverified; } } SSKProtoVerified *_Nullable BuildVerifiedProtoWithAddress(SignalServiceAddress *address, NSData *identityKey, OWSVerificationState verificationState, NSUInteger paddingBytesLength) { OWSCAssertDebug(identityKey.length == OWSIdentityManagerObjCBridge.identityKeyLength); OWSCAssertDebug(address.isValid); // we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device // will figure that out on it's own. OWSCAssertDebug(verificationState != OWSVerificationStateNoLongerVerified); SSKProtoVerifiedBuilder *verifiedBuilder = [SSKProtoVerified builder]; verifiedBuilder.destinationAci = address.aciString; verifiedBuilder.identityKey = identityKey; verifiedBuilder.state = OWSVerificationStateToProtoState(verificationState); if (paddingBytesLength > 0) { // We add the same amount of padding in the VerificationStateSync message and it's corresponding NullMessage so // that the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. // We pad the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be // *additionally* padded by the superclass while being sent. The end result is we send a NullMessage of a // non-distinct size, and a verification sync which is ~1-512 bytes larger then that. verifiedBuilder.nullMessage = [Cryptography generateRandomBytes:paddingBytesLength]; } return [verifiedBuilder buildInfallibly]; } NSUInteger const RecipientIdentitySchemaVersion = 1; @interface OWSRecipientIdentity () @property (atomic) OWSVerificationState verificationState; @property (nonatomic, readonly) NSUInteger recipientIdentitySchemaVersion; @end /** * Record for a recipients identity key and some meta data around it used to make trust decisions. * * NOTE: Instances of this class MUST only be retrieved/persisted via it's internal `dbConnection`, * which makes some special accommodations to enforce consistency. */ @implementation OWSRecipientIdentity #pragma mark - Table Contents - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { if (![coder decodeObjectForKey:@"verificationState"]) { _verificationState = OWSVerificationStateDefault; } if (_recipientIdentitySchemaVersion < 1) { _accountId = [coder decodeObjectForKey:@"recipientId"]; OWSAssertDebug(_accountId); } _recipientIdentitySchemaVersion = RecipientIdentitySchemaVersion; } return self; } - (instancetype)initWithAccountId:(NSString *)accountId identityKey:(NSData *)identityKey isFirstKnownKey:(BOOL)isFirstKnownKey createdAt:(NSDate *)createdAt verificationState:(OWSVerificationState)verificationState { self = [super initWithUniqueId:accountId]; if (!self) { return self; } _accountId = accountId; _identityKey = identityKey; _isFirstKnownKey = isFirstKnownKey; _createdAt = createdAt; _verificationState = verificationState; _recipientIdentitySchemaVersion = RecipientIdentitySchemaVersion; return self; } // --- CODE GENERATION MARKER // This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run // `sds_codegen.sh`. // clang-format off - (instancetype)initWithGrdbId:(int64_t)grdbId uniqueId:(NSString *)uniqueId accountId:(NSString *)accountId createdAt:(NSDate *)createdAt identityKey:(NSData *)identityKey isFirstKnownKey:(BOOL)isFirstKnownKey verificationState:(OWSVerificationState)verificationState { self = [super initWithGrdbId:grdbId uniqueId:uniqueId]; if (!self) { return self; } _accountId = accountId; _createdAt = createdAt; _identityKey = identityKey; _isFirstKnownKey = isFirstKnownKey; _verificationState = verificationState; return self; } // clang-format on // --- CODE GENERATION MARKER - (void)updateWithVerificationState:(OWSVerificationState)verificationState transaction:(SDSAnyWriteTransaction *)transaction { OWSAssertDebug(transaction); // Ensure changes are persisted without clobbering any work done on another thread or instance. [self anyUpdateWithTransaction:transaction block:^(OWSRecipientIdentity *_Nonnull obj) { obj.verificationState = verificationState; }]; } - (BOOL)wasIdentityVerified { switch (self.verificationState) { case OWSVerificationStateVerified: case OWSVerificationStateNoLongerVerified: return YES; case OWSVerificationStateDefault: case OWSVerificationStateDefaultAcknowledged: return NO; } } #pragma mark - debug + (void)printAllIdentities { OWSLogInfo(@"### All Recipient Identities ###"); __block int count = 0; [self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) { [OWSRecipientIdentity anyEnumerateWithTransaction:transaction batched:YES block:^(OWSRecipientIdentity *recipientIdentity, BOOL *stop) { OWSLogInfo(@"Identity %d: %@", count, recipientIdentity.debugDescription); }]; }]; } @end NS_ASSUME_NONNULL_END