import Foundation import Postbox import TelegramApi import SyncCore private enum MessagePreParsingError: Error { case invalidChatState case malformedData case protocolViolation } func processSecretChatIncomingEncryptedOperations(transaction: Transaction, peerId: PeerId) -> Bool { if let state = transaction.getPeerChatState(peerId) as? SecretChatState { var updatedState = state var removeTagLocalIndices: [Int32] = [] var addedDecryptedOperations = false transaction.operationLogEnumerateEntries(peerId: peerId, tag: OperationLogTags.SecretIncomingEncrypted, { entry in if let operation = entry.contents as? SecretChatIncomingEncryptedOperation { if let key = updatedState.keychain.key(fingerprint: operation.keyFingerprint) { var decryptedContents = withDecryptedMessageContents(parameters: SecretChatEncryptionParameters(key: key, mode: .v2(role: updatedState.role)), data: operation.contents) if decryptedContents == nil { decryptedContents = withDecryptedMessageContents(parameters: SecretChatEncryptionParameters(key: key, mode: .v1), data: operation.contents) } if let decryptedContents = decryptedContents { withExtendedLifetime(decryptedContents, { let buffer = BufferReader(Buffer(bufferNoCopy: decryptedContents)) do { guard let topLevelSignature = buffer.readInt32() else { throw MessagePreParsingError.malformedData } let parsedLayer: Int32 let sequenceInfo: SecretChatOperationSequenceInfo? if topLevelSignature == 0x1be31789 { guard let _ = parseBytes(buffer) else { throw MessagePreParsingError.malformedData } guard let layerValue = buffer.readInt32() else { throw MessagePreParsingError.malformedData } guard let seqInValue = buffer.readInt32() else { throw MessagePreParsingError.malformedData } guard let seqOutValue = buffer.readInt32() else { throw MessagePreParsingError.malformedData } switch updatedState.role { case .creator: if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) == 0) || (seqOutValue & 1) != 0 { throw MessagePreParsingError.protocolViolation } case .participant: if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) != 0) || (seqOutValue & 1) == 0 { throw MessagePreParsingError.protocolViolation } } sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: seqInValue / 2, operationIndex: seqOutValue / 2) if layerValue == 17 { parsedLayer = 46 } else { parsedLayer = layerValue } } else { parsedLayer = 8 sequenceInfo = nil buffer.reset() } guard let messageContents = buffer.readBuffer(decryptedContents.length - Int(buffer.offset)) else { throw MessagePreParsingError.malformedData } let entryTagLocalIndex: StorePeerOperationLogEntryTagLocalIndex switch updatedState.embeddedState { case .terminated: throw MessagePreParsingError.invalidChatState case .handshake: throw MessagePreParsingError.invalidChatState case .basicLayer: if parsedLayer >= 46 { guard let sequenceInfo = sequenceInfo else { throw MessagePreParsingError.protocolViolation } let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: secretChatCommonSupportedLayer(remoteLayer: parsedLayer), locallyRequestedLayer: nil, remotelyRequestedLayer: nil), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) transaction.setPeerChatState(peerId, state: updatedState) entryTagLocalIndex = .manual(sequenceBasedLayerState.baseIncomingOperationIndex + sequenceInfo.operationIndex) } else { if parsedLayer != 8 && parsedLayer != 17 { throw MessagePreParsingError.protocolViolation } entryTagLocalIndex = .automatic } case let .sequenceBasedLayer(sequenceState): if parsedLayer < 46 { throw MessagePreParsingError.protocolViolation } entryTagLocalIndex = .manual(sequenceState.baseIncomingOperationIndex + sequenceInfo!.operationIndex) } transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretIncomingDecrypted, tagLocalIndex: entryTagLocalIndex, tagMergedIndex: .none, contents: SecretChatIncomingDecryptedOperation(timestamp: operation.timestamp, layer: parsedLayer, sequenceInfo: sequenceInfo, contents: MemoryBuffer(messageContents), file: operation.mediaFileReference)) addedDecryptedOperations = true } catch let error { if let error = error as? MessagePreParsingError { switch error { case .invalidChatState: break case .malformedData, .protocolViolation: break } } Logger.shared.log("SecretChat", "peerId \(peerId) malformed data after decryption") } removeTagLocalIndices.append(entry.tagLocalIndex) }) } else { Logger.shared.log("SecretChat", "peerId \(peerId) couldn't decrypt message content") removeTagLocalIndices.append(entry.tagLocalIndex) } } else { Logger.shared.log("SecretChat", "peerId \(peerId) key \(operation.keyFingerprint) doesn't exist") } } else { assertionFailure() } return true }) for index in removeTagLocalIndices { let removed = transaction.operationLogRemoveEntry(peerId: peerId, tag: OperationLogTags.SecretIncomingEncrypted, tagLocalIndex: index) assert(removed) } return addedDecryptedOperations } else { return false } }