浏览代码

Transition to generators for all block modes

Marcin Krzyżanowski 9 年之前
父节点
当前提交
fcd2032350

+ 10 - 10
CryptoSwift.xcodeproj/project.pbxproj

@@ -181,6 +181,10 @@
 		7588034F1C8F8C43008C1576 /* BlockModeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7588034B1C8F8C33008C1576 /* BlockModeGenerator.swift */; };
 		758A94281A65C59200E46135 /* HMACTests.swift in Resources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
 		758A94291A65C67400E46135 /* HMACTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
+		758F58F11C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
+		758F58F21C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
+		758F58F31C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
+		758F58F41C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
 		75B601EB197D6A6C0009B53D /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		75CB93251C8F5EC10087740D /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93241C8F5EC10087740D /* CBC.swift */; };
 		75CB93261C8F5EC10087740D /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93241C8F5EC10087740D /* CBC.swift */; };
@@ -198,10 +202,6 @@
 		75CB933B1C8F5FFD0087740D /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93391C8F5FFD0087740D /* CFB.swift */; };
 		75CB933C1C8F5FFD0087740D /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93391C8F5FFD0087740D /* CFB.swift */; };
 		75CB933D1C8F5FFD0087740D /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93391C8F5FFD0087740D /* CFB.swift */; };
-		75CB933F1C8F60070087740D /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB933E1C8F60070087740D /* OFB.swift */; };
-		75CB93401C8F60070087740D /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB933E1C8F60070087740D /* OFB.swift */; };
-		75CB93411C8F60070087740D /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB933E1C8F60070087740D /* OFB.swift */; };
-		75CB93421C8F60070087740D /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB933E1C8F60070087740D /* OFB.swift */; };
 		75CB93441C8F603C0087740D /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93431C8F603C0087740D /* CTR.swift */; };
 		75CB93451C8F603C0087740D /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93431C8F603C0087740D /* CTR.swift */; };
 		75CB93461C8F603C0087740D /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CB93431C8F603C0087740D /* CTR.swift */; };
@@ -355,11 +355,11 @@
 		757DA2581A4ED4D7002BA3EF /* ChaCha20Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha20Tests.swift; sourceTree = "<group>"; };
 		7588034B1C8F8C33008C1576 /* BlockModeGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlockModeGenerator.swift; path = Sources/CryptoSwift/BlockMode/BlockModeGenerator.swift; sourceTree = SOURCE_ROOT; };
 		758A94271A65C59200E46135 /* HMACTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACTests.swift; sourceTree = "<group>"; };
+		758F58F01C8FB6E20054C377 /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OFB.swift; path = Sources/CryptoSwift/BlockMode/OFB.swift; sourceTree = SOURCE_ROOT; };
 		75CB93241C8F5EC10087740D /* CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CBC.swift; path = Sources/CryptoSwift/BlockMode/CBC.swift; sourceTree = SOURCE_ROOT; };
 		75CB932F1C8F5F580087740D /* CipherBlockMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CipherBlockMode.swift; path = Sources/CryptoSwift/BlockMode/CipherBlockMode.swift; sourceTree = SOURCE_ROOT; };
 		75CB93341C8F5FCE0087740D /* PCBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PCBC.swift; path = Sources/CryptoSwift/BlockMode/PCBC.swift; sourceTree = SOURCE_ROOT; };
 		75CB93391C8F5FFD0087740D /* CFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CFB.swift; path = Sources/CryptoSwift/BlockMode/CFB.swift; sourceTree = SOURCE_ROOT; };
-		75CB933E1C8F60070087740D /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OFB.swift; path = Sources/CryptoSwift/OFB.swift; sourceTree = SOURCE_ROOT; };
 		75CB93431C8F603C0087740D /* CTR.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CTR.swift; path = Sources/CryptoSwift/BlockMode/CTR.swift; sourceTree = SOURCE_ROOT; };
 		75CB93481C8F60700087740D /* ECB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ECB.swift; path = Sources/CryptoSwift/BlockMode/ECB.swift; sourceTree = SOURCE_ROOT; };
 		75CB934D1C8F609D0087740D /* BlockModeOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlockModeOptions.swift; path = Sources/CryptoSwift/BlockMode/BlockModeOptions.swift; sourceTree = SOURCE_ROOT; };
@@ -537,7 +537,7 @@
 				75CB93241C8F5EC10087740D /* CBC.swift */,
 				75CB93341C8F5FCE0087740D /* PCBC.swift */,
 				75CB93391C8F5FFD0087740D /* CFB.swift */,
-				75CB933E1C8F60070087740D /* OFB.swift */,
+				758F58F01C8FB6E20054C377 /* OFB.swift */,
 				75CB93431C8F603C0087740D /* CTR.swift */,
 				75CB93481C8F60700087740D /* ECB.swift */,
 			);
@@ -795,6 +795,7 @@
 				757BC9A11C1CA5790093AAA9 /* Poly1305.swift in Sources */,
 				75CB93451C8F603C0087740D /* CTR.swift in Sources */,
 				757BC9451C1CA5790093AAA9 /* ArraySliceUInt8+Bytes.swift in Sources */,
+				758F58F21C8FB6E20054C377 /* OFB.swift in Sources */,
 				757BC9BD1C1CA5790093AAA9 /* UInt64Extension.swift in Sources */,
 				757BC9991C1CA5790093AAA9 /* PKCS5.swift in Sources */,
 				75CB934F1C8F609D0087740D /* BlockModeOptions.swift in Sources */,
@@ -804,7 +805,6 @@
 				757BC9A91C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				757BC9911C1CA5790093AAA9 /* Operators.swift in Sources */,
 				75CB933B1C8F5FFD0087740D /* CFB.swift in Sources */,
-				75CB93401C8F60070087740D /* OFB.swift in Sources */,
 				757BC9B91C1CA5790093AAA9 /* UInt32Extension.swift in Sources */,
 				757BC9131C1CA56A0093AAA9 /* String+FoundationExtension.swift in Sources */,
 				757BC98D1C1CA5790093AAA9 /* Multiplatform.swift in Sources */,
@@ -850,6 +850,7 @@
 				75CB93441C8F603C0087740D /* CTR.swift in Sources */,
 				757BC9441C1CA5790093AAA9 /* ArraySliceUInt8+Bytes.swift in Sources */,
 				757BC9BC1C1CA5790093AAA9 /* UInt64Extension.swift in Sources */,
+				758F58F11C8FB6E20054C377 /* OFB.swift in Sources */,
 				757BC9981C1CA5790093AAA9 /* PKCS5.swift in Sources */,
 				75CB934E1C8F609D0087740D /* BlockModeOptions.swift in Sources */,
 				757BC9801C1CA5790093AAA9 /* IntegerConvertible.swift in Sources */,
@@ -858,7 +859,6 @@
 				757BC9A81C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				757BC9901C1CA5790093AAA9 /* Operators.swift in Sources */,
 				75CB933A1C8F5FFD0087740D /* CFB.swift in Sources */,
-				75CB933F1C8F60070087740D /* OFB.swift in Sources */,
 				7588034C1C8F8C33008C1576 /* BlockModeGenerator.swift in Sources */,
 				757BC9B81C1CA5790093AAA9 /* UInt32Extension.swift in Sources */,
 				757BC8FE1C1CA56A0093AAA9 /* AES+Foundation.swift in Sources */,
@@ -921,6 +921,7 @@
 				757BC9A21C1CA5790093AAA9 /* Poly1305.swift in Sources */,
 				75CB93461C8F603C0087740D /* CTR.swift in Sources */,
 				757BC9461C1CA5790093AAA9 /* ArraySliceUInt8+Bytes.swift in Sources */,
+				758F58F31C8FB6E20054C377 /* OFB.swift in Sources */,
 				757BC9BE1C1CA5790093AAA9 /* UInt64Extension.swift in Sources */,
 				757BC99A1C1CA5790093AAA9 /* PKCS5.swift in Sources */,
 				75CB93501C8F609D0087740D /* BlockModeOptions.swift in Sources */,
@@ -930,7 +931,6 @@
 				757BC9AA1C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				757BC9921C1CA5790093AAA9 /* Operators.swift in Sources */,
 				75CB933C1C8F5FFD0087740D /* CFB.swift in Sources */,
-				75CB93411C8F60070087740D /* OFB.swift in Sources */,
 				757BC9BA1C1CA5790093AAA9 /* UInt32Extension.swift in Sources */,
 				757BC9001C1CA56A0093AAA9 /* AES+Foundation.swift in Sources */,
 				757BC98E1C1CA5790093AAA9 /* Multiplatform.swift in Sources */,
@@ -976,6 +976,7 @@
 				757BC9A31C1CA5790093AAA9 /* Poly1305.swift in Sources */,
 				75CB93471C8F603C0087740D /* CTR.swift in Sources */,
 				757BC9471C1CA5790093AAA9 /* ArraySliceUInt8+Bytes.swift in Sources */,
+				758F58F41C8FB6E20054C377 /* OFB.swift in Sources */,
 				757BC9BF1C1CA5790093AAA9 /* UInt64Extension.swift in Sources */,
 				757BC99B1C1CA5790093AAA9 /* PKCS5.swift in Sources */,
 				75CB93511C8F609D0087740D /* BlockModeOptions.swift in Sources */,
@@ -985,7 +986,6 @@
 				757BC9AB1C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				757BC9931C1CA5790093AAA9 /* Operators.swift in Sources */,
 				75CB933D1C8F5FFD0087740D /* CFB.swift in Sources */,
-				75CB93421C8F60070087740D /* OFB.swift in Sources */,
 				757BC9BB1C1CA5790093AAA9 /* UInt32Extension.swift in Sources */,
 				757BC9151C1CA56A0093AAA9 /* String+FoundationExtension.swift in Sources */,
 				757BC98F1C1CA5790093AAA9 /* Multiplatform.swift in Sources */,

+ 18 - 18
CryptoSwiftTests/AESTests.swift

@@ -181,25 +181,25 @@ final class AESTests: XCTestCase {
         XCTAssertEqual(decrypted, plaintext, "decryption failed")
     }
 
-//    func testAES_encrypt_performance() {
-//        let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
-//        let iv:[UInt8] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
-//        let message = [UInt8](count: 1024 * 1024, repeatedValue: 7)
-//        let aes = try! AES(key: key, iv: iv, blockMode: .CBC)
-//        measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, forBlock: { () -> Void in
-//            try! aes.encrypt(message, padding: PKCS7())
-//        })
-//    }
+    func testAES_encrypt_performance() {
+        let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
+        let iv:[UInt8] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
+        let message = [UInt8](count: 1024 * 1024, repeatedValue: 7)
+        let aes = try! AES(key: key, iv: iv, blockMode: .CBC)
+        measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, forBlock: { () -> Void in
+            try! aes.encrypt(message, padding: PKCS7())
+        })
+    }
 
-//    func testAES_decrypt_performance() {
-//        let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
-//        let iv:[UInt8] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
-//        let message = [UInt8](count: 1024 * 1024, repeatedValue: 7)
-//        let aes = try! AES(key: key, iv: iv, blockMode: .CBC)
-//        measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, forBlock: { () -> Void in
-//            try! aes.decrypt(message, padding: PKCS7())
-//        })
-//    }
+    func testAES_decrypt_performance() {
+        let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
+        let iv:[UInt8] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
+        let message = [UInt8](count: 1024 * 1024, repeatedValue: 7)
+        let aes = try! AES(key: key, iv: iv, blockMode: .CBC)
+        measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, forBlock: { () -> Void in
+            try! aes.decrypt(message, padding: PKCS7())
+        })
+    }
 
     func testAESPerformanceCommonCrypto() {
         let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];

+ 3 - 1
Sources/CryptoSwift/AES.swift

@@ -125,7 +125,8 @@ final public class AES {
         let blocks = finalBytes.chunks(AES.blockSize)
         let encryptGenerator = blockMode.encryptGenerator(iv, cipherOperation: encryptBlock, inputGenerator: AnyGenerator<Array<UInt8>>(blocks.generate()))
 
-        var out = [UInt8]() // can't preallocate because count of blocks is unknown
+        var out = [UInt8]()
+        out.reserveCapacity(bytes.count)
         for processedBlock in AnySequence<Array<UInt8>>({ encryptGenerator }) {
             out.appendContentsOf(processedBlock)
         }
@@ -203,6 +204,7 @@ final public class AES {
         
         let blocks = bytes.chunks(AES.blockSize)
         var out = [UInt8]()
+        out.reserveCapacity(bytes.count)
         switch (blockMode) {
         case .CFB, .OFB, .CTR:
             // CFB, OFB, CTR uses encryptBlock to decrypt

+ 1 - 1
Sources/CryptoSwift/BlockMode/BlockModeGenerator.swift

@@ -16,5 +16,5 @@ typealias CipherOperationOnBlock = (block: [UInt8]) -> [UInt8]?
 
 protocol BlockModeGenerator: GeneratorType {
     var options: BlockModeOptions { get }
-    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>)
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>)
 }

+ 1 - 1
Sources/CryptoSwift/BlockMode/CBC.swift

@@ -18,7 +18,7 @@ struct CBCModeEncryptGenerator: BlockModeGenerator {
     private let cipherOperation: CipherOperationOnBlock
     private var prevCiphertext: Element?
 
-    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>) {
         self.iv = iv
         self.cipherOperation = cipherOperation
         self.inputGenerator = inputGenerator

+ 45 - 29
Sources/CryptoSwift/BlockMode/CFB.swift

@@ -8,43 +8,59 @@
 //  Cipher feedback (CFB)
 //
 
-struct CFBMode {
-    static let options: BlockModeOptions = [.InitializationVectorRequired]
+struct CFBModeEncryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
 
-    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
-        }
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
 
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
 
-        var lastCiphertext = iv
-        for plaintext in blocks {
-            if let ciphertext = cipherOperation(block: lastCiphertext) {
-                lastCiphertext = xor(plaintext, ciphertext)
-                out.appendContentsOf(lastCiphertext)
-            }
-        }
-        return out
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
     }
 
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
+    mutating func next() -> Element? {
+        guard let plaintext = inputGenerator.next(),
+            let ciphertext = cipherOperation(block: prevCiphertext ?? iv)
+            else {
+                return nil
         }
 
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
+        self.prevCiphertext = xor(plaintext, ciphertext)
+        return self.prevCiphertext
+    }
+}
+
+struct CFBModeDecryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
+
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
 
-        var lastCiphertext = iv
-        for ciphertext in blocks {
-            if let decrypted = cipherOperation(block: lastCiphertext) {
-                out.appendContentsOf(xor(decrypted, ciphertext))
-            }
-            lastCiphertext = ciphertext
+    mutating func next() -> Element? {
+        guard let ciphertext = inputGenerator.next(),
+            let decrypted = cipherOperation(block: self.prevCiphertext ?? iv)
+            else {
+                return nil
         }
 
-        return out
+        let result = xor(decrypted, ciphertext)
+        self.prevCiphertext = ciphertext
+        return result
     }
-}
+}

+ 58 - 37
Sources/CryptoSwift/BlockMode/CTR.swift

@@ -8,52 +8,73 @@
 //  Counter (CTR)
 //
 
-struct CTRMode {
-    static let options = BlockModeOptions.InitializationVectorRequired
-
-    private func buildNonce(iv: [UInt8], counter: UInt64) -> [UInt8] {
-        let noncePartLen = AES.blockSize / 2
-        let noncePrefix = Array(iv[0..<noncePartLen])
-        let nonceSuffix = Array(iv[noncePartLen..<iv.count])
-        let c = UInt64.withBytes(nonceSuffix) + counter
-        return noncePrefix + arrayOfBytes(c)
-    }
+struct CTRModeEncryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
 
-    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        //var counter:UInt = 17940646550795321087
+    private let cipherOperation: CipherOperationOnBlock
+    private var counter: UInt = 0
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
 
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
+    mutating func next() -> Element? {
+        guard let plaintext = inputGenerator.next() else {
+            return nil
         }
 
-        var counter:UInt = 0
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-        for plaintext in blocks {
-            let nonce = buildNonce(iv, counter: UInt64(counter))
-            counter += 1
-            if let encrypted = cipherOperation(block: nonce) {
-                out.appendContentsOf(xor(plaintext, encrypted))
-            }
+        let nonce = buildNonce(iv, counter: UInt64(counter))
+        counter = counter + 1
+        if let encrypted = cipherOperation(block: nonce) {
+            return xor(plaintext, encrypted)
         }
-        return out
+
+        return nil
     }
+}
+
+struct CTRModeDecryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
 
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
+    private let cipherOperation: CipherOperationOnBlock
+    private var counter: UInt = 0
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
+
+    mutating func next() -> Element? {
+        guard let ciphertext = inputGenerator.next() else {
+            return nil
         }
 
-        var counter:UInt = 0
-        var out = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-        for ciphertext in blocks {
-            let nonce = buildNonce(iv, counter: UInt64(counter))
-            counter += 1
-            if let decrypted = cipherOperation(block: nonce) {
-                out.appendContentsOf(xor(decrypted, ciphertext))
-            }
+        let nonce = buildNonce(iv, counter: UInt64(counter))
+        counter = counter + 1
+
+        if let decrypted = cipherOperation(block: nonce) {
+            return xor(decrypted, ciphertext)
         }
-        return out
+
+        return nil
     }
 }
+
+private func buildNonce(iv: [UInt8], counter: UInt64) -> [UInt8] {
+    let noncePartLen = AES.blockSize / 2
+    let noncePrefix = Array(iv[0..<noncePartLen])
+    let nonceSuffix = Array(iv[noncePartLen..<iv.count])
+    let c = UInt64.withBytes(nonceSuffix) + counter
+    return noncePrefix + arrayOfBytes(c)
+}

+ 20 - 4
Sources/CryptoSwift/BlockMode/CipherBlockMode.swift

@@ -15,8 +15,16 @@ public enum CipherBlockMode {
         switch (self) {
         case CBC:
             return AnyGenerator<Array<UInt8>>(CBCModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
-        default:
-            fatalError("Unimplemented")
+        case CFB:
+            return AnyGenerator<Array<UInt8>>(CFBModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case OFB:
+            return AnyGenerator<Array<UInt8>>(OFBModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case CTR:
+            return AnyGenerator<Array<UInt8>>(CTRModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case PCBC:
+            return AnyGenerator<Array<UInt8>>(PCBCModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case ECB:
+            return AnyGenerator<Array<UInt8>>(ECBModeEncryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
         }
     }
 
@@ -24,8 +32,16 @@ public enum CipherBlockMode {
         switch (self) {
         case CBC:
             return AnyGenerator<Array<UInt8>>(CBCModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
-        default:
-            fatalError("Unimplemented")
+        case CFB:
+            return AnyGenerator<Array<UInt8>>(CFBModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case OFB:
+            return AnyGenerator<Array<UInt8>>(OFBModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case CTR:
+            return AnyGenerator<Array<UInt8>>(CTRModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case PCBC:
+            return AnyGenerator<Array<UInt8>>(PCBCModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
+        case ECB:
+            return AnyGenerator<Array<UInt8>>(ECBModeDecryptGenerator(iv: iv ?? [], cipherOperation: cipherOperation, inputGenerator: inputGenerator))
         }
     }
 

+ 23 - 14
Sources/CryptoSwift/BlockMode/ECB.swift

@@ -8,21 +8,30 @@
 //  Electronic codebook (ECB)
 //
 
-struct ECBMode {
-    static let options: BlockModeOptions = [.PaddingRequired]
+struct ECBModeEncryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
 
-    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8] {
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-        for plaintext in blocks {
-            if let encrypted = cipherOperation(block: plaintext) {
-                out.appendContentsOf(encrypted)
-            }
-        }
-        return out
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
+
+    private let cipherOperation: CipherOperationOnBlock
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
     }
-    
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8] {
-        return encryptBlocks(blocks, iv: iv, cipherOperation: cipherOperation)
+
+    mutating func next() -> Element? {
+        guard let plaintext = inputGenerator.next(),
+              let encrypted = cipherOperation(block: plaintext)
+        else {
+            return nil
+        }
+
+        return encrypted
     }
 }
+
+typealias ECBModeDecryptGenerator = ECBModeEncryptGenerator

+ 66 - 0
Sources/CryptoSwift/BlockMode/OFB.swift

@@ -0,0 +1,66 @@
+//
+//  OFB.swift
+//  CryptoSwift
+//
+//  Created by Marcin Krzyzanowski on 08/03/16.
+//  Copyright © 2016 Marcin Krzyzanowski. All rights reserved.
+//
+// Output Feedback (OFB)
+//
+
+struct OFBModeEncryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
+
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
+
+    mutating func next() -> Element? {
+        guard let plaintext = inputGenerator.next(),
+            let ciphertext = cipherOperation(block: prevCiphertext ?? iv)
+            else {
+                return nil
+        }
+
+        self.prevCiphertext = ciphertext
+        return xor(plaintext, ciphertext)
+    }
+}
+
+struct OFBModeDecryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
+
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
+
+    mutating func next() -> Element? {
+        guard let ciphertext = inputGenerator.next(),
+            let decrypted = cipherOperation(block: self.prevCiphertext ?? iv)
+            else {
+                return nil
+        }
+
+        let plaintext = xor(decrypted, ciphertext)
+        self.prevCiphertext = decrypted
+        return plaintext
+    }
+}

+ 45 - 38
Sources/CryptoSwift/BlockMode/PCBC.swift

@@ -8,52 +8,59 @@
 //  Propagating Cipher Block Chaining (PCBC)
 //
 
-struct PCBCMode {
-    static let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+struct PCBCModeEncryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
 
-    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        precondition(blocks.count > 0)
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
-        }
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
 
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-        var prevCiphertext = iv // for the first time prevCiphertext = iv
-        for plaintext in blocks {
-            guard let encrypted = cipherOperation(block: xor(prevCiphertext, plaintext)) else {
-                out.appendContentsOf(plaintext)
-                continue
-            }
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
 
-            let ciphertext = encrypted
-            out.appendContentsOf(ciphertext)
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Array<UInt8>>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
 
-            prevCiphertext = xor(plaintext, ciphertext)
+    mutating func next() -> Element? {
+        guard let plaintext = inputGenerator.next(),
+              let encrypted = cipherOperation(block: xor(prevCiphertext ?? iv, plaintext))
+        else {
+            return nil
         }
-        return out
+
+        self.prevCiphertext = xor(plaintext, encrypted)
+        return encrypted
     }
+}
 
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        precondition(blocks.count > 0)
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
-        }
+struct PCBCModeDecryptGenerator: BlockModeGenerator {
+    typealias Element = Array<UInt8>
+    let options: BlockModeOptions = [.InitializationVectorRequired, .PaddingRequired]
+
+    private let iv: Element
+    private let inputGenerator: AnyGenerator<Element>
 
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-        var prevCiphertext = iv // for the first time prevCiphertext = iv
-        for ciphertext in blocks {
-            guard let decrypted = cipherOperation(block: ciphertext) else {
-                out.appendContentsOf(ciphertext)
-                continue
-            }
-
-            let plaintext = xor(prevCiphertext, decrypted)
-            out.appendContentsOf(plaintext)
-            prevCiphertext = xor(plaintext, ciphertext)
+    private let cipherOperation: CipherOperationOnBlock
+    private var prevCiphertext: Element?
+
+    init(iv: Array<UInt8>, cipherOperation: CipherOperationOnBlock, inputGenerator: AnyGenerator<Element>) {
+        self.iv = iv
+        self.cipherOperation = cipherOperation
+        self.inputGenerator = inputGenerator
+    }
+
+    mutating func next() -> Element? {
+        guard let ciphertext = inputGenerator.next(),
+              let decrypted = cipherOperation(block: ciphertext)
+        else {
+            return nil
         }
-        
-        return out
+
+        let plaintext = xor(prevCiphertext ?? iv, decrypted)
+        self.prevCiphertext = xor(plaintext, ciphertext)
+        return plaintext
     }
 }

+ 0 - 58
Sources/CryptoSwift/OFB.swift

@@ -1,58 +0,0 @@
-//
-//  OFB.swift
-//  CryptoSwift
-//
-//  Created by Marcin Krzyzanowski on 08/03/16.
-//  Copyright © 2016 Marcin Krzyzanowski. All rights reserved.
-//
-// Output Feedback (OFB)
-//
-
-struct OFBMode {
-    static let options: BlockModeOptions = [.InitializationVectorRequired]
-
-    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
-        }
-
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-
-        var lastEncryptedBlock = iv
-        for plaintext in blocks {
-            guard let ciphertext = cipherOperation(block: lastEncryptedBlock) else {
-                out.appendContentsOf(plaintext)
-                continue
-            }
-
-            lastEncryptedBlock = ciphertext
-            out.appendContentsOf(xor(plaintext, ciphertext))
-        }
-        return out
-    }
-
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) throws -> [UInt8] {
-        guard let iv = iv else {
-            throw BlockError.MissingInitializationVector
-        }
-
-        var out:[UInt8] = [UInt8]()
-        out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
-
-        var lastDecryptedBlock = iv
-        for ciphertext in blocks {
-            guard let decrypted = cipherOperation(block: lastDecryptedBlock) else {
-                out.appendContentsOf(ciphertext)
-                continue
-            }
-
-            lastDecryptedBlock = decrypted
-
-            let plaintext = xor(decrypted, ciphertext)
-            out.appendContentsOf(plaintext)
-        }
-        
-        return out
-    }
-}