Browse Source

AES uses universal Encryptor that uses Block or Stream mode depends on the Cipher mode used.

It happens in runtime so may affect the performance - something to address.
Disabled seek() functionality temporarily
Marcin Krzyzanowski 7 years ago
parent
commit
b75e3292ef

+ 8 - 0
CryptoSwift.xcodeproj/project.pbxproj

@@ -51,6 +51,8 @@
 		7595C15C2072E5B900EA1A5F /* ExtensionsTestPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F420729069006688F8 /* ExtensionsTestPerf.swift */; };
 		7595C15C2072E5B900EA1A5F /* ExtensionsTestPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F420729069006688F8 /* ExtensionsTestPerf.swift */; };
 		7595C15D2072E5B900EA1A5F /* PBKDFPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F6207290F8006688F8 /* PBKDFPerf.swift */; };
 		7595C15D2072E5B900EA1A5F /* PBKDFPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F6207290F8006688F8 /* PBKDFPerf.swift */; };
 		7595C1602072E64900EA1A5F /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		7595C1602072E64900EA1A5F /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
+		75B3ED77210F9DF7005D4ADA /* BlockDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B3ED76210F9DF7005D4ADA /* BlockDecryptor.swift */; };
+		75B3ED79210FA016005D4ADA /* Encryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B3ED78210FA016005D4ADA /* Encryptor.swift */; };
 		75B601EB197D6A6C0009B53D /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		75B601EB197D6A6C0009B53D /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		75C2E76D1D55F097003D2BCA /* Access.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E76C1D55F097003D2BCA /* Access.swift */; };
 		75C2E76D1D55F097003D2BCA /* Access.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E76C1D55F097003D2BCA /* Access.swift */; };
 		75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D7AF37208BFB1600D22BEB /* UInt128.swift */; };
 		75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D7AF37208BFB1600D22BEB /* UInt128.swift */; };
@@ -284,6 +286,8 @@
 		7595C14A2072E48C00EA1A5F /* TestsPerformance-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TestsPerformance-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		7595C14A2072E48C00EA1A5F /* TestsPerformance-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TestsPerformance-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		7595C14C2072E48C00EA1A5F /* TestsPerformance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestsPerformance.swift; sourceTree = "<group>"; };
 		7595C14C2072E48C00EA1A5F /* TestsPerformance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestsPerformance.swift; sourceTree = "<group>"; };
 		7595C14E2072E48C00EA1A5F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7595C14E2072E48C00EA1A5F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		75B3ED76210F9DF7005D4ADA /* BlockDecryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockDecryptor.swift; sourceTree = "<group>"; };
+		75B3ED78210FA016005D4ADA /* Encryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encryptor.swift; sourceTree = "<group>"; };
 		75C2E76C1D55F097003D2BCA /* Access.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Access.swift; sourceTree = "<group>"; };
 		75C2E76C1D55F097003D2BCA /* Access.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Access.swift; sourceTree = "<group>"; };
 		75D7AF37208BFB1600D22BEB /* UInt128.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UInt128.swift; sourceTree = "<group>"; };
 		75D7AF37208BFB1600D22BEB /* UInt128.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UInt128.swift; sourceTree = "<group>"; };
 		75EC52381EE8B6CA0048EB3B /* AES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = "<group>"; };
 		75EC52381EE8B6CA0048EB3B /* AES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = "<group>"; };
@@ -581,6 +585,8 @@
 				75EC52781EE8B6CA0048EB3B /* Utils.swift */,
 				75EC52781EE8B6CA0048EB3B /* Utils.swift */,
 				75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */,
 				75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */,
 				754310432050111A003FB1DF /* CompactMap.swift */,
 				754310432050111A003FB1DF /* CompactMap.swift */,
+				75B3ED76210F9DF7005D4ADA /* BlockDecryptor.swift */,
+				75B3ED78210FA016005D4ADA /* Encryptor.swift */,
 			);
 			);
 			path = CryptoSwift;
 			path = CryptoSwift;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -868,6 +874,7 @@
 				75EC52821EE8B8170048EB3B /* BlockMode.swift in Sources */,
 				75EC52821EE8B8170048EB3B /* BlockMode.swift in Sources */,
 				75EC52AE1EE8B83D0048EB3B /* SecureBytes.swift in Sources */,
 				75EC52AE1EE8B83D0048EB3B /* SecureBytes.swift in Sources */,
 				75EC528F1EE8B81A0048EB3B /* Cipher.swift in Sources */,
 				75EC528F1EE8B81A0048EB3B /* Cipher.swift in Sources */,
+				75B3ED79210FA016005D4ADA /* Encryptor.swift in Sources */,
 				75EC52A01EE8B8290048EB3B /* Int+Extension.swift in Sources */,
 				75EC52A01EE8B8290048EB3B /* Int+Extension.swift in Sources */,
 				75EC52B01EE8B83D0048EB3B /* SHA2.swift in Sources */,
 				75EC52B01EE8B83D0048EB3B /* SHA2.swift in Sources */,
 				752BED9D208C120D00FC4743 /* Blowfish+Foundation.swift in Sources */,
 				752BED9D208C120D00FC4743 /* Blowfish+Foundation.swift in Sources */,
@@ -914,6 +921,7 @@
 				75EC529D1EE8B8200048EB3B /* Utils+Foundation.swift in Sources */,
 				75EC529D1EE8B8200048EB3B /* Utils+Foundation.swift in Sources */,
 				75EC527E1EE8B8130048EB3B /* Authenticator.swift in Sources */,
 				75EC527E1EE8B8130048EB3B /* Authenticator.swift in Sources */,
 				75EC52AB1EE8B83D0048EB3B /* Rabbit.swift in Sources */,
 				75EC52AB1EE8B83D0048EB3B /* Rabbit.swift in Sources */,
+				75B3ED77210F9DF7005D4ADA /* BlockDecryptor.swift in Sources */,
 				75EC529C1EE8B8200048EB3B /* String+FoundationExtension.swift in Sources */,
 				75EC529C1EE8B8200048EB3B /* String+FoundationExtension.swift in Sources */,
 				75EC52B81EE8B83D0048EB3B /* Utils.swift in Sources */,
 				75EC52B81EE8B83D0048EB3B /* Utils.swift in Sources */,
 				75EC52981EE8B8200048EB3B /* Array+Foundation.swift in Sources */,
 				75EC52981EE8B8200048EB3B /* Array+Foundation.swift in Sources */,

+ 1 - 1
CryptoSwift.xcodeproj/xcshareddata/xcschemes/CryptoSwift.xcscheme

@@ -37,7 +37,7 @@
       </BuildActionEntries>
       </BuildActionEntries>
    </BuildAction>
    </BuildAction>
    <TestAction
    <TestAction
-      buildConfiguration = "Test"
+      buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       disableMainThreadChecker = "YES"
       disableMainThreadChecker = "YES"

+ 4 - 184
Sources/CryptoSwift/AES.Cryptors.swift

@@ -16,194 +16,14 @@
 // MARK: Cryptors
 // MARK: Cryptors
 
 
 extension AES: Cryptors {
 extension AES: Cryptors {
-    public func makeEncryptor() throws -> AES.Encryptor {
+    public func makeEncryptor() throws -> Cryptor & Updatable {
         let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: encrypt)
         let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: encrypt)
-        return try AES.Encryptor(padding: padding, worker)
+        return try Encryptor(blockSize: AES.blockSize, padding: padding, worker)
     }
     }
 
 
-    public func makeDecryptor() throws -> AES.Decryptor {
+    public func makeDecryptor() throws -> Cryptor & Updatable {
         let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
         let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
         let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: cipherOperation)
         let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: cipherOperation)
-        return try AES.Decryptor(padding: padding, worker)
-    }
-}
-
-// MARK: Encryptor
-
-extension AES {
-    public struct Encryptor: Cryptor, Updatable {
-        private var worker: CipherModeWorker
-        private let padding: Padding
-        // Accumulated bytes. Not all processed bytes.
-        private var accumulated = Array<UInt8>()
-        private var processedBytesTotalCount: Int = 0
-
-        init(padding: Padding, _ worker: CipherModeWorker) throws {
-            self.padding = padding
-            self.worker = worker
-        }
-
-        // MARK: Updatable
-        public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
-            accumulated += bytes
-
-            if isLast {
-                accumulated = padding.add(to: accumulated, blockSize: AES.blockSize)
-            }
-
-            var processedBytes = 0
-            var encrypted = Array<UInt8>(reserveCapacity: accumulated.count)
-            for chunk in accumulated.batched(by: AES.blockSize) {
-                if isLast || (accumulated.count - processedBytes) >= AES.blockSize {
-                    encrypted += worker.encrypt(block: chunk)
-                    processedBytes += chunk.count
-                }
-            }
-            accumulated.removeFirst(processedBytes)
-            processedBytesTotalCount += processedBytes
-
-            if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
-                encrypted = try finalizingWorker.finalize(encrypt: encrypted.slice)
-            }
-
-            return encrypted
-        }
-    }
-
-//    public struct StreamEncryptor: Cryptor, Updatable {
-//        private var worker: CipherModeWorker
-//        private let padding: Padding
-        // Accumulated bytes. Not all processed bytes.
-//        private var accumulated = Array<UInt8>()
-//        private var processedBytesTotalCount: Int = 0
-
-//        init(aes: AES) throws {
-//            padding = aes.padding
-//            worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.encrypt)
-//        }
-
-        // MARK: Updatable
-//        public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
-//            accumulated += bytes
-
-//            if isLast {
-//                accumulated = padding.add(to: accumulated, blockSize: AES.blockSize)
-//            }
-//
-//            var processedBytes = 0
-//            var encrypted = Array<UInt8>(reserveCapacity: accumulated.count)
-//            for chunk in accumulated.batched(by: AES.blockSize) {
-//                if isLast || (accumulated.count - processedBytes) >= AES.blockSize {
-//                    encrypted += worker.encrypt(block: chunk)
-//                    processedBytes += chunk.count
-//                }
-//            }
-//            accumulated.removeFirst(processedBytes)
-//            processedBytesTotalCount += processedBytes
-//
-//            if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
-//                encrypted = try finalizingWorker.finalize(encrypt: encrypted.slice)
-//            }
-//
-//            return encrypted
-//            return []
-//        }
-//    }
-}
-
-// MARK: Decryptor
-
-extension AES {
-
-    public class Decryptor: RandomAccessCryptor, Updatable {
-        private let blockSize = AES.blockSize
-        private let padding: Padding
-        private var worker: CipherModeWorker
-        private var accumulated = Array<UInt8>()
-        private var processedBytesTotalCount: Int = 0
-
-        private var offset: Int = 0
-        private var offsetToRemove: Int = 0
-
-        init(padding: Padding, _ worker: CipherModeWorker) throws {
-            self.padding = padding
-            self.worker = worker
-        }
-
-        public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
-            // prepend "offset" number of bytes at the beginning
-            if offset > 0 {
-                accumulated += Array<UInt8>(repeating: 0, count: offset) + bytes
-                offsetToRemove = offset
-                offset = 0
-            } else {
-                accumulated += bytes
-            }
-
-            // If a worker (eg GCM) can combine ciphertext + tag
-            // we need to remove tag from the ciphertext.
-            if !isLast && accumulated.count < blockSize + worker.additionalBufferSize {
-                return []
-            }
-
-            let accumulatedWithoutSuffix: Array<UInt8>
-            if worker.additionalBufferSize > 0 {
-                // FIXME: how slow is that?
-                accumulatedWithoutSuffix = Array(accumulated.prefix(accumulated.count - worker.additionalBufferSize))
-            } else {
-                accumulatedWithoutSuffix = accumulated
-            }
-
-            var processedBytesCount = 0
-            var plaintext = Array<UInt8>(reserveCapacity: accumulatedWithoutSuffix.count)
-            // Processing in a block-size manner. It's good for block modes, but bad for stream modes.
-            for var chunk in accumulatedWithoutSuffix.batched(by: blockSize) {
-                if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize {
-
-                    if isLast, var finalizingWorker = worker as? BlockModeWorkerFinalizing {
-                        chunk = try finalizingWorker.willDecryptLast(block: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
-                    }
-
-                    if !chunk.isEmpty {
-                        plaintext += worker.decrypt(block: chunk)
-                    }
-
-                    // remove "offset" from the beginning of first chunk
-                    if offsetToRemove > 0 {
-                        plaintext.removeFirst(offsetToRemove)
-                        offsetToRemove = 0
-                    }
-
-                    if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
-                        plaintext = try finalizingWorker.didDecryptLast(block: plaintext.slice)
-                    }
-
-                    processedBytesCount += chunk.count
-                }
-            }
-            accumulated.removeFirst(processedBytesCount) // super-slow
-            processedBytesTotalCount += processedBytesCount
-
-            if isLast {
-                plaintext = padding.remove(from: plaintext, blockSize: blockSize)
-            }
-
-            return plaintext
-        }
-
-        @discardableResult public func seek(to position: Int) -> Bool {
-            guard var worker = self.worker as? RandomAccessBlockModeWorker else {
-                return false
-            }
-
-            worker.counter = UInt(position / blockSize)
-            self.worker = worker
-
-            offset = position % blockSize
-
-            accumulated = []
-
-            return true
-        }
+        return try BlockDecryptor(blockSize: AES.blockSize, padding: padding, worker)
     }
     }
 }
 }

+ 1 - 1
Sources/CryptoSwift/AES.swift

@@ -518,7 +518,7 @@ extension AES: Cipher {
             throw Error.dataPaddingRequired
             throw Error.dataPaddingRequired
         }
         }
 
 
-        let oneTimeCryptor = try makeDecryptor()
+        var oneTimeCryptor = try makeDecryptor()
         let chunks = bytes.batched(by: AES.blockSize)
         let chunks = bytes.batched(by: AES.blockSize)
         if chunks.isEmpty {
         if chunks.isEmpty {
             throw Error.invalidData
             throw Error.invalidData

+ 106 - 0
Sources/CryptoSwift/BlockDecryptor.swift

@@ -0,0 +1,106 @@
+//  CryptoSwift
+//
+//  Copyright (C) 2014-__YEAR__ Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  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.
+//
+
+public class BlockDecryptor: RandomAccessCryptor, Updatable {
+    private let blockSize: Int
+    private let padding: Padding
+    private var worker: CipherModeWorker
+    private var accumulated = Array<UInt8>()
+    private var processedBytesTotalCount: Int = 0
+
+    private var offset: Int = 0
+    private var offsetToRemove: Int = 0
+
+    init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+        self.blockSize = blockSize
+        self.padding = padding
+        self.worker = worker
+    }
+
+    public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+        // prepend "offset" number of bytes at the beginning
+        if offset > 0 {
+            accumulated += Array<UInt8>(repeating: 0, count: offset) + bytes
+            offsetToRemove = offset
+            offset = 0
+        } else {
+            accumulated += bytes
+        }
+
+        // If a worker (eg GCM) can combine ciphertext + tag
+        // we need to remove tag from the ciphertext.
+        if !isLast && accumulated.count < blockSize + worker.additionalBufferSize {
+            return []
+        }
+
+        let accumulatedWithoutSuffix: Array<UInt8>
+        if worker.additionalBufferSize > 0 {
+            // FIXME: how slow is that?
+            accumulatedWithoutSuffix = Array(accumulated.prefix(accumulated.count - worker.additionalBufferSize))
+        } else {
+            accumulatedWithoutSuffix = accumulated
+        }
+
+        var processedBytesCount = 0
+        var plaintext = Array<UInt8>(reserveCapacity: accumulatedWithoutSuffix.count)
+        // Processing in a block-size manner. It's good for block modes, but bad for stream modes.
+        for var chunk in accumulatedWithoutSuffix.batched(by: blockSize) {
+            if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize {
+
+                if isLast, var finalizingWorker = worker as? BlockModeWorkerFinalizing {
+                    chunk = try finalizingWorker.willDecryptLast(block: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
+                }
+
+                if !chunk.isEmpty {
+                    plaintext += worker.decrypt(block: chunk)
+                }
+
+                // remove "offset" from the beginning of first chunk
+                if offsetToRemove > 0 {
+                    plaintext.removeFirst(offsetToRemove)
+                    offsetToRemove = 0
+                }
+
+                if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
+                    plaintext = try finalizingWorker.didDecryptLast(block: plaintext.slice)
+                }
+
+                processedBytesCount += chunk.count
+            }
+        }
+        accumulated.removeFirst(processedBytesCount) // super-slow
+        processedBytesTotalCount += processedBytesCount
+
+        if isLast {
+            plaintext = padding.remove(from: plaintext, blockSize: blockSize)
+        }
+
+        return plaintext
+    }
+
+    //        @discardableResult public func seek(to position: Int) -> Bool {
+    //            guard var worker = self.worker as? RandomAccessCipherModeWorker else {
+    //                return false
+    //            }
+    //
+    //            worker.counter = UInt(position / blockSize)
+    //            self.worker = worker
+    //
+    //            offset = position % blockSize
+    //
+    //            accumulated = []
+    //
+    //            return true
+    //        }
+}

+ 35 - 14
Sources/CryptoSwift/BlockMode/CTR.swift

@@ -14,7 +14,6 @@
 //
 //
 
 
 //  Counter (CTR)
 //  Counter (CTR)
-//
 
 
 public struct CTR: StreamMode {
 public struct CTR: StreamMode {
     public enum Error: Swift.Error {
     public enum Error: Swift.Error {
@@ -40,10 +39,12 @@ public struct CTR: StreamMode {
     }
     }
 }
 }
 
 
-struct CTRModeWorker: RandomAccessCipherModeWorker {
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct CTRModeWorker: StreamModeWorker, CounterModeWorker {
     typealias Counter = CTRCounter
     typealias Counter = CTRCounter
 
 
-    class CTRCounter {
+    final class CTRCounter {
         private let constPrefix: Array<UInt8>
         private let constPrefix: Array<UInt8>
         private var value: UInt64
         private var value: UInt64
         //TODO: make it an updatable value, computing is too slow
         //TODO: make it an updatable value, computing is too slow
@@ -55,7 +56,7 @@ struct CTRModeWorker: RandomAccessCipherModeWorker {
             let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2)
             let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2)
             constPrefix = Array(initialValue[initialValue.startIndex..<halfIndex])
             constPrefix = Array(initialValue[initialValue.startIndex..<halfIndex])
 
 
-            let suffixBytes = Array(initialValue[halfIndex..<initialValue.endIndex])
+            let suffixBytes = Array(initialValue[halfIndex...])
             value = UInt64(bytes: suffixBytes)
             value = UInt64(bytes: suffixBytes)
         }
         }
 
 
@@ -68,26 +69,46 @@ struct CTRModeWorker: RandomAccessCipherModeWorker {
         }
         }
     }
     }
 
 
-
     let cipherOperation: CipherOperationOnBlock
     let cipherOperation: CipherOperationOnBlock
     let additionalBufferSize: Int = 0
     let additionalBufferSize: Int = 0
-    var counter: Counter
+    let counter: Counter
+
+    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<UInt8>
+    private var keystreamPosIdx = 0
 
 
     init(blockSize: Int, iv: ArraySlice<UInt8>, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) {
     init(blockSize: Int, iv: ArraySlice<UInt8>, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) {
-        self.counter = Counter(nonce: Array(iv), startAt: counter)
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
+        self.blockSize = blockSize
+
+        // the first keystream is calculated from the nonce = initial value of counter
+        self.counter = Counter(nonce: Array(iv), startAt: counter)
+        self.keystream = Array(cipherOperation(self.counter.bytes.slice)!)
     }
     }
 
 
+    // plaintext is at most blockSize long
     mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
     mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
-        defer {
-            counter += 1
-        }
-
-        guard let ciphertext = cipherOperation(counter.bytes.slice) else {
-            return Array(plaintext)
+        var result = Array<UInt8>(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<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
+            keystreamPosIdx += xored.count
+            processed += xored.count
+            result += xored
         }
         }
 
 
-        return xor(plaintext, ciphertext)
+        return result
     }
     }
 
 
     mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
     mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {

+ 5 - 3
Sources/CryptoSwift/BlockMode/CipherModeWorker.swift

@@ -28,11 +28,13 @@ public protocol BlockModeWorker: CipherModeWorker {
     var blockSize: Int { get }
     var blockSize: Int { get }
 }
 }
 
 
-protocol RandomAccessCipherModeWorker: CipherModeWorker {
-     associatedtype Counter
-     var counter: Counter { set get }
+public protocol CounterModeWorker: CipherModeWorker {
+    associatedtype Counter
+    var counter: Counter { get }
 }
 }
 
 
+public protocol StreamModeWorker: CipherModeWorker { }
+
 // TODO: remove and merge with BlockModeWorker
 // TODO: remove and merge with BlockModeWorker
 public protocol BlockModeWorkerFinalizing: BlockModeWorker {
 public protocol BlockModeWorkerFinalizing: BlockModeWorker {
     // Any final calculations, eg. calculate tag
     // Any final calculations, eg. calculate tag

+ 8 - 6
Sources/CryptoSwift/ChaCha20.swift

@@ -254,7 +254,7 @@ extension ChaCha20: Cipher {
 // MARK: Encryptor
 // MARK: Encryptor
 
 
 extension ChaCha20 {
 extension ChaCha20 {
-    public struct Encryptor: Cryptor, Updatable {
+    public struct ChaChaEncryptor: Cryptor, Updatable {
         private var accumulated = Array<UInt8>()
         private var accumulated = Array<UInt8>()
         private let chacha: ChaCha20
         private let chacha: ChaCha20
 
 
@@ -281,7 +281,7 @@ extension ChaCha20 {
 // MARK: Decryptor
 // MARK: Decryptor
 
 
 extension ChaCha20 {
 extension ChaCha20 {
-    public struct Decryptor: Cryptor, Updatable {
+    public struct ChaChaDecryptor: Cryptor, Updatable {
         private var accumulated = Array<UInt8>()
         private var accumulated = Array<UInt8>()
 
 
         private var offset: Int = 0
         private var offset: Int = 0
@@ -326,11 +326,13 @@ extension ChaCha20 {
 // MARK: Cryptors
 // MARK: Cryptors
 
 
 extension ChaCha20: Cryptors {
 extension ChaCha20: Cryptors {
-    public func makeEncryptor() -> ChaCha20.Encryptor {
-        return Encryptor(chacha: self)
+    //TODO: Use BlockEncryptor/BlockDecryptor
+    
+    public func makeEncryptor() -> Cryptor & Updatable {
+        return ChaCha20.ChaChaEncryptor(chacha: self)
     }
     }
 
 
-    public func makeDecryptor() -> ChaCha20.Decryptor {
-        return Decryptor(chacha: self)
+    public func makeDecryptor() -> Cryptor & Updatable {
+        return ChaCha20.ChaChaDecryptor(chacha: self)
     }
     }
 }
 }

+ 2 - 5
Sources/CryptoSwift/Cryptors.swift

@@ -21,14 +21,11 @@ import Glibc
 
 
 /// Worker cryptor/decryptor of `Updatable` types
 /// Worker cryptor/decryptor of `Updatable` types
 public protocol Cryptors: class {
 public protocol Cryptors: class {
-    associatedtype Encryptor: Cryptor
-    associatedtype Decryptor: Cryptor
-
     /// Cryptor suitable for encryption
     /// Cryptor suitable for encryption
-    func makeEncryptor() throws -> Encryptor
+    func makeEncryptor() throws -> Cryptor & Updatable
 
 
     /// Cryptor suitable for decryption
     /// Cryptor suitable for decryption
-    func makeDecryptor() throws -> Decryptor
+    func makeDecryptor() throws -> Cryptor & Updatable
 
 
     /// Generate array of random bytes. Helper function.
     /// Generate array of random bytes. Helper function.
     static func randomIV(_ blockSize: Int) -> Array<UInt8>
     static func randomIV(_ blockSize: Int) -> Array<UInt8>

+ 83 - 0
Sources/CryptoSwift/Encryptor.swift

@@ -0,0 +1,83 @@
+//  CryptoSwift
+//
+//  Copyright (C) 2014-__YEAR__ Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  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.
+//
+final class Encryptor: Cryptor, Updatable {
+    private let blockSize: Int
+    private var worker: CipherModeWorker
+    private let padding: Padding
+    // Accumulated bytes. Not all processed bytes.
+    private var accumulated = Array<UInt8>(reserveCapacity: 16)
+
+    private let isStream: Bool
+    private var lastBlockRemainder = 0
+
+    init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+        self.blockSize = blockSize
+        self.padding = padding
+        self.worker = worker
+        self.isStream = worker is StreamModeWorker
+    }
+
+    // MARK: Updatable
+    public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+        if isStream {
+            return try self.updateStream(withBytes: bytes, isLast: isLast)
+        } else {
+            return try self.updateBlocks(withBytes: bytes, isLast: isLast)
+        }
+    }
+
+    private func updateStream(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+        accumulated = Array(bytes)
+        if isLast {
+            // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't.
+            accumulated = padding.add(to: accumulated, blockSize: blockSize - lastBlockRemainder)
+        }
+
+        var encrypted = Array<UInt8>(reserveCapacity: bytes.count)
+        for chunk in accumulated.batched(by: blockSize) {
+            encrypted += worker.encrypt(block: chunk)
+        }
+
+        // omit unecessary calculation if not needed
+        if padding != .noPadding {
+            lastBlockRemainder = encrypted.count.quotientAndRemainder(dividingBy: blockSize).remainder
+        }
+
+        return encrypted
+    }
+
+    private func updateBlocks(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+        accumulated += bytes
+
+        if isLast {
+            accumulated = padding.add(to: accumulated, blockSize: blockSize)
+        }
+
+        var encrypted = Array<UInt8>(reserveCapacity: accumulated.count)
+        for chunk in accumulated.batched(by: blockSize) {
+            if isLast || chunk.count == blockSize {
+                encrypted += worker.encrypt(block: chunk)
+            }
+        }
+
+        // Stream encrypts all, so it removes all elements
+        accumulated.removeFirst(encrypted.count)
+
+        if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
+            encrypted = try finalizingWorker.finalize(encrypt: encrypted.slice)
+        }
+
+        return encrypted
+    }
+}

+ 2 - 2
Sources/CryptoSwift/Padding.swift

@@ -24,7 +24,7 @@ public enum Padding: PaddingProtocol {
     public func add(to: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
     public func add(to: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
         switch self {
         switch self {
         case .noPadding:
         case .noPadding:
-            return NoPadding().add(to: to, blockSize: blockSize)
+            return to // NoPadding().add(to: to, blockSize: blockSize)
         case .zeroPadding:
         case .zeroPadding:
             return ZeroPadding().add(to: to, blockSize: blockSize)
             return ZeroPadding().add(to: to, blockSize: blockSize)
         case .pkcs7:
         case .pkcs7:
@@ -37,7 +37,7 @@ public enum Padding: PaddingProtocol {
     public func remove(from: Array<UInt8>, blockSize: Int?) -> Array<UInt8> {
     public func remove(from: Array<UInt8>, blockSize: Int?) -> Array<UInt8> {
         switch self {
         switch self {
         case .noPadding:
         case .noPadding:
-            return NoPadding().remove(from: from, blockSize: blockSize)
+            return from //NoPadding().remove(from: from, blockSize: blockSize)
         case .zeroPadding:
         case .zeroPadding:
             return ZeroPadding().remove(from: from, blockSize: blockSize)
             return ZeroPadding().remove(from: from, blockSize: blockSize)
         case .pkcs7:
         case .pkcs7:

+ 3 - 2
Sources/CryptoSwift/RandomAccessCryptor.swift

@@ -13,14 +13,15 @@
 //  - This notice may not be removed or altered from any source or binary distribution.
 //  - This notice may not be removed or altered from any source or binary distribution.
 //
 //
 
 
-/// Random access cryptor
+/// Cryptor (Encryptor or Decryptor)
 public protocol Cryptor { }
 public protocol Cryptor { }
 
 
+/// Random access cryptor
 public protocol RandomAccessCryptor: Cryptor {
 public protocol RandomAccessCryptor: Cryptor {
     /// Seek to position in file, if block mode allows random access.
     /// Seek to position in file, if block mode allows random access.
     ///
     ///
     /// - parameter to: new value of counter
     /// - parameter to: new value of counter
     ///
     ///
     /// - returns: true if seek succeed
     /// - returns: true if seek succeed
-    @discardableResult mutating func seek(to: Int) -> Bool
+    /// @discardableResult mutating func seek(to: Int) -> Bool
 }
 }

+ 66 - 43
Tests/Tests/AESTests.swift

@@ -221,13 +221,15 @@ final class AESTests: XCTestCase {
     func testAESEncryptCTRZeroPadding() {
     func testAESEncryptCTRZeroPadding() {
         let key: Array<UInt8> = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]
         let key: Array<UInt8> = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]
         let iv: Array<UInt8> = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]
         let iv: Array<UInt8> = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]
-        let plaintext: Array<UInt8> = [0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xfd]
+        let plaintext: Array<UInt8> = [0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0x01]
+        let expected: Array<UInt8>  = [0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x37, 0x2b, 0x7c, 0x3c, 0x67, 0x73, 0x51, 0x63, 0x18, 0xa0, 0x77, 0xd7, 0xfc, 0x50, 0x73, 0xae]
 
 
         let aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .zeroPadding)
         let aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .zeroPadding)
         let encrypted = try! aes.encrypt(plaintext)
         let encrypted = try! aes.encrypt(plaintext)
 
 
         XCTAssertEqual(plaintext.count, 17)
         XCTAssertEqual(plaintext.count, 17)
         XCTAssertEqual(encrypted.count, 32, "padding failed")
         XCTAssertEqual(encrypted.count, 32, "padding failed")
+        XCTAssertEqual(encrypted, expected, "encryption failed")
     }
     }
 
 
     func testAESEncryptCTRIrregularLength() {
     func testAESEncryptCTRIrregularLength() {
@@ -244,45 +246,45 @@ final class AESTests: XCTestCase {
     }
     }
 
 
     // https://github.com/krzyzanowskim/CryptoSwift/pull/290
     // https://github.com/krzyzanowskim/CryptoSwift/pull/290
-    func testAESDecryptCTRSeek() {
-        let key: Array<UInt8> = [0x52, 0x72, 0xb5, 0x9c, 0xab, 0x07, 0xc5, 0x01, 0x11, 0x7a, 0x39, 0xb6, 0x10, 0x35, 0x87, 0x02]
-        let iv: Array<UInt8> = [0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
-        var plaintext: Array<UInt8> = Array<UInt8>(repeating: 0, count: 6000)
-
-        for i in 0..<plaintext.count / 6 {
-            let s = String(format: "%05d", i).bytes
-            plaintext[i * 6 + 0] = s[0]
-            plaintext[i * 6 + 1] = s[1]
-            plaintext[i * 6 + 2] = s[2]
-            plaintext[i * 6 + 3] = s[3]
-            plaintext[i * 6 + 4] = s[4]
-            plaintext[i * 6 + 5] = "|".utf8.first!
-        }
-
-        let aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
-        let encrypted = try! aes.encrypt(plaintext)
-
-        var decryptor = try! aes.makeDecryptor()
-        decryptor.seek(to: 2)
-        var part1 = try! decryptor.update(withBytes: Array(encrypted[2..<5]))
-        part1 += try! decryptor.finish()
-        XCTAssertEqual(part1, Array(plaintext[2..<5]), "seek decryption failed")
-
-        decryptor.seek(to: 1000)
-        var part2 = try! decryptor.update(withBytes: Array(encrypted[1000..<1200]))
-        part2 += try! decryptor.finish()
-        XCTAssertEqual(part2, Array(plaintext[1000..<1200]), "seek decryption failed")
-
-        decryptor.seek(to: 5500)
-        var part3 = try! decryptor.update(withBytes: Array(encrypted[5500..<6000]))
-        part3 += try! decryptor.finish()
-        XCTAssertEqual(part3, Array(plaintext[5500..<6000]), "seek decryption failed")
-
-        decryptor.seek(to: 0)
-        var part4 = try! decryptor.update(withBytes: Array(encrypted[0..<80]))
-        part4 += try! decryptor.finish()
-        XCTAssertEqual(part4, Array(plaintext[0..<80]), "seek decryption failed")
-    }
+//    func testAESDecryptCTRSeek() {
+//        let key: Array<UInt8> = [0x52, 0x72, 0xb5, 0x9c, 0xab, 0x07, 0xc5, 0x01, 0x11, 0x7a, 0x39, 0xb6, 0x10, 0x35, 0x87, 0x02]
+//        let iv: Array<UInt8> = [0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
+//        var plaintext: Array<UInt8> = Array<UInt8>(repeating: 0, count: 6000)
+//
+//        for i in 0..<plaintext.count / 6 {
+//            let s = String(format: "%05d", i).bytes
+//            plaintext[i * 6 + 0] = s[0]
+//            plaintext[i * 6 + 1] = s[1]
+//            plaintext[i * 6 + 2] = s[2]
+//            plaintext[i * 6 + 3] = s[3]
+//            plaintext[i * 6 + 4] = s[4]
+//            plaintext[i * 6 + 5] = "|".utf8.first!
+//        }
+//
+//        let aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
+//        let encrypted = try! aes.encrypt(plaintext)
+//
+//        var decryptor = try! aes.makeDecryptor()
+//        decryptor.seek(to: 2)
+//        var part1 = try! decryptor.update(withBytes: Array(encrypted[2..<5]))
+//        part1 += try! decryptor.finish()
+//        XCTAssertEqual(part1, Array(plaintext[2..<5]), "seek decryption failed")
+//
+//        decryptor.seek(to: 1000)
+//        var part2 = try! decryptor.update(withBytes: Array(encrypted[1000..<1200]))
+//        part2 += try! decryptor.finish()
+//        XCTAssertEqual(part2, Array(plaintext[1000..<1200]), "seek decryption failed")
+//
+//        decryptor.seek(to: 5500)
+//        var part3 = try! decryptor.update(withBytes: Array(encrypted[5500..<6000]))
+//        part3 += try! decryptor.finish()
+//        XCTAssertEqual(part3, Array(plaintext[5500..<6000]), "seek decryption failed")
+//
+//        decryptor.seek(to: 0)
+//        var part4 = try! decryptor.update(withBytes: Array(encrypted[0..<80]))
+//        part4 += try! decryptor.finish()
+//        XCTAssertEqual(part4, Array(plaintext[0..<80]), "seek decryption failed")
+//    }
 
 
     // https://github.com/krzyzanowskim/CryptoSwift/pull/289
     // https://github.com/krzyzanowskim/CryptoSwift/pull/289
     func testAESEncryptCTRIrregularLengthIncrementalUpdate() {
     func testAESEncryptCTRIrregularLengthIncrementalUpdate() {
@@ -296,7 +298,7 @@ final class AESTests: XCTestCase {
         var encrypted = Array<UInt8>()
         var encrypted = Array<UInt8>()
         encrypted += try! encryptor.update(withBytes: plaintext[0..<5])
         encrypted += try! encryptor.update(withBytes: plaintext[0..<5])
         encrypted += try! encryptor.update(withBytes: plaintext[5..<15])
         encrypted += try! encryptor.update(withBytes: plaintext[5..<15])
-        encrypted += try! encryptor.update(withBytes: plaintext[15..<plaintext.count])
+        encrypted += try! encryptor.update(withBytes: plaintext[15...])
         encrypted += try! encryptor.finish()
         encrypted += try! encryptor.finish()
         XCTAssertEqual(encrypted, expected, "encryption failed")
         XCTAssertEqual(encrypted, expected, "encryption failed")
 
 
@@ -304,11 +306,31 @@ final class AESTests: XCTestCase {
         var decrypted = Array<UInt8>()
         var decrypted = Array<UInt8>()
         decrypted += try! decryptor.update(withBytes: expected[0..<5])
         decrypted += try! decryptor.update(withBytes: expected[0..<5])
         decrypted += try! decryptor.update(withBytes: expected[5..<15])
         decrypted += try! decryptor.update(withBytes: expected[5..<15])
-        decrypted += try! decryptor.update(withBytes: expected[15..<plaintext.count])
+        decrypted += try! decryptor.update(withBytes: expected[15...])
         decrypted += try! decryptor.finish()
         decrypted += try! decryptor.finish()
         XCTAssertEqual(decrypted, plaintext, "decryption failed")
         XCTAssertEqual(decrypted, plaintext, "decryption failed")
     }
     }
 
 
+
+    func testAESEncryptCTRStream() {
+        let key = Array<UInt8>(hex: "0xbe3e9020816eb838782e2d9f4a2f40d4")
+        let iv  =  Array<UInt8>(hex: "0x0000000000000000a9bbd681ded0c0c8")
+
+        do {
+            let aes = try AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
+            var encryptor = try aes.makeEncryptor()
+            
+            let encrypted1 = try encryptor.update(withBytes: [0x00, 0x01, 0x02, 0x03] as [UInt8])
+            XCTAssertEqual(encrypted1, Array<UInt8>(hex: "d79d0344"))
+            let encrypted2 = try encryptor.update(withBytes: [0x04, 0x05, 0x06, 0x07] as [UInt8])
+            XCTAssertEqual(encrypted2, Array<UInt8>(hex: "b2a08879"))
+            let encrypted3 = try encryptor.update(withBytes: [0x08] as [UInt8])
+            XCTAssertEqual(encrypted3, Array<UInt8>(hex: "db"))
+        } catch {
+            XCTFail(error.localizedDescription)
+        }
+    }
+
     func testAESWithWrongKey() {
     func testAESWithWrongKey() {
         let key: Array<UInt8> = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]
         let key: Array<UInt8> = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]
         let key2: Array<UInt8> = [0x22, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x33]
         let key2: Array<UInt8> = [0x22, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x33]
@@ -571,8 +593,9 @@ extension AESTests {
             ("testAESEncryptCTR", testAESEncryptCTR),
             ("testAESEncryptCTR", testAESEncryptCTR),
             ("testAESEncryptCTRZeroPadding", testAESEncryptCTRZeroPadding),
             ("testAESEncryptCTRZeroPadding", testAESEncryptCTRZeroPadding),
             ("testAESEncryptCTRIrregularLength", testAESEncryptCTRIrregularLength),
             ("testAESEncryptCTRIrregularLength", testAESEncryptCTRIrregularLength),
-            ("testAESDecryptCTRSeek", testAESDecryptCTRSeek),
+//            ("testAESDecryptCTRSeek", testAESDecryptCTRSeek),
             ("testAESEncryptCTRIrregularLengthIncrementalUpdate", testAESEncryptCTRIrregularLengthIncrementalUpdate),
             ("testAESEncryptCTRIrregularLengthIncrementalUpdate", testAESEncryptCTRIrregularLengthIncrementalUpdate),
+            ("testAESEncryptCTRStream", testAESEncryptCTRStream),
             ("testIssue298", testIssue298),
             ("testIssue298", testIssue298),
             ("testIssue394", testIssue394),
             ("testIssue394", testIssue394),
             ("testIssue411", testIssue411),
             ("testIssue411", testIssue411),