// // CryptoSwift // // Copyright (C) 2014-2017 Marcin Krzyżanowski // This software is provided 'as-is', without any express or implied warranty. // // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: // // - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. // - This notice may not be removed or altered from any source or binary distribution. // // Counter (CTR) public struct CTR: StreamMode { public enum Error: Swift.Error { /// Invalid IV case invalidInitializationVector } public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] private let iv: Array private let counter: Int public init(iv: Array, counter: Int = 0) { self.iv = iv self.counter = counter } public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { if iv.count != blockSize { throw Error.invalidInitializationVector } return CTRModeWorker(blockSize: blockSize, iv: iv.slice, counter: counter, cipherOperation: cipherOperation) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct CTRModeWorker: StreamModeWorker, CounterModeWorker { typealias Counter = CTRCounter final class CTRCounter { private let constPrefix: Array private var value: UInt64 //TODO: make it an updatable value, computing is too slow var bytes: Array { return constPrefix + value.bytes() } init(_ initialValue: Array) { let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2) constPrefix = Array(initialValue[initialValue.startIndex.., startAt index: Int) { self.init(buildCounterValue(nonce, counter: UInt64(index))) } static func +=(lhs: CTRCounter, rhs: Int) { lhs.value += UInt64(rhs) } } let cipherOperation: CipherOperationOnBlock let additionalBufferSize: Int = 0 let iv: Array var counter: CTRCounter private let blockSize: Int // The same keystream is used for the block length plaintext // As new data is added, keystream suffix is used to xor operation. private var keystream: Array private var keystreamPosIdx = 0 init(blockSize: Int, iv: ArraySlice, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) { self.cipherOperation = cipherOperation self.blockSize = blockSize self.iv = Array(iv) // the first keystream is calculated from the nonce = initial value of counter self.counter = CTRCounter(nonce: Array(iv), startAt: counter) self.keystream = Array(cipherOperation(self.counter.bytes.slice)!) } mutating func seek(to position: Int) throws { let offset = position % blockSize counter = CTRCounter(nonce: iv, startAt: position / blockSize) keystream = Array(cipherOperation(counter.bytes.slice)!) keystreamPosIdx = offset } // plaintext is at most blockSize long mutating func encrypt(block plaintext: ArraySlice) -> Array { var result = Array(reserveCapacity: plaintext.count) var processed = 0 while processed < plaintext.count { // Update keystream if keystreamPosIdx == blockSize { counter += 1 keystream = Array(cipherOperation(counter.bytes.slice)!) keystreamPosIdx = 0 } let xored: Array = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) keystreamPosIdx += xored.count processed += xored.count result += xored } return result } mutating func decrypt(block ciphertext: ArraySlice) -> Array { return encrypt(block: ciphertext) } } private func buildCounterValue(_ iv: Array, counter: UInt64) -> Array { let noncePartLen = iv.count / 2 let noncePrefix = iv[iv.startIndex..