// // Copyright 2026 Element Creations Ltd. // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. // Please see LICENSE files in the repository root for full details. // import CommonCrypto import Foundation enum ClassicAppAES { enum Error: Swift.Error { case cannotInitializeCryptor case decryptionFailed(CCCryptorStatus) } /// Decrypt data using AES-256 in CTR mode. /// - Parameters: /// - data: The data to decrypt /// - aesKey: The AES decryption key (256-bit) /// - iv: The initialization vector /// - Returns: The decrypted data, or nil if decryption fails /// - Throws: An error if decryption fails static func decrypt(_ data: Data, aesKey: Data, iv: Data) throws -> Data { var cryptor: CCCryptorRef? var status: CCCryptorStatus // Create the cryptor status = iv.withUnsafeBytes { ivBytes in aesKey.withUnsafeBytes { keyBytes in CCCryptorCreateWithMode(CCOperation(kCCDecrypt), CCMode(kCCModeCTR), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ivBytes.baseAddress, keyBytes.baseAddress, kCCKeySizeAES256, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor) } } guard status == kCCSuccess, let cryptor else { MXLog.error("Failed to create cryptor: \(status)") throw Error.cannotInitializeCryptor } // Get the output buffer size let bufferLength = CCCryptorGetOutputLength(cryptor, data.count, false) var buffer = Data(count: bufferLength) var outLength = 0 status = data.withUnsafeBytes { dataBytes in let count = buffer.count return buffer.withUnsafeMutableBytes { bufferBytes in CCCryptorUpdate(cryptor, dataBytes.baseAddress, data.count, bufferBytes.baseAddress, count, &outLength) } } let releaseStatus = CCCryptorRelease(cryptor) status = (status == kCCSuccess && releaseStatus == kCCSuccess) ? CCCryptorStatus(kCCSuccess) : status guard status == kCCSuccess else { MXLog.error("Decryption failed: \(status)") throw Error.decryptionFailed(status) } return buffer } }