Browse Source

Refactor cipher workers

Marcin Krzyzanowski 7 years ago
parent
commit
1b0ad8bd1c

+ 185 - 152
CryptoSwift.playground/Contents.swift

@@ -5,167 +5,200 @@
  */
 import CryptoSwift
 import Foundation
-/*:
- # Data types conversinn
- */
-let data = Data(bytes: [0x01, 0x02, 0x03])
-let bytes = data.bytes
-let bytesHex = Array<UInt8>(hex: "0x010203")
-let hexString = bytesHex.toHexString()
-
-/*:
- # Digest
- */
-data.md5()
-data.sha1()
-data.sha224()
-data.sha256()
-data.sha384()
-data.sha512()
-
-bytes.sha1()
-"123".sha1()
-Digest.sha1(bytes)
-
-//: Digest calculated incrementally
-do {
-    var digest = MD5()
-    _ = try digest.update(withBytes: [0x31, 0x32])
-    _ = try digest.update(withBytes: [0x33])
-    let result = try digest.finish()
-    print(result)
-} catch {}
-
-/*:
- # CRC
- */
-bytes.crc16()
-bytes.crc32()
-bytes.crc32c()
-
-/*:
- # HMAC
- */
-
-do {
-    let key: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 23, 25, 26, 27, 28, 29, 30, 31, 32]
-    try Poly1305(key: key).authenticate(bytes)
-    try HMAC(key: key, variant: .sha256).authenticate(bytes)
-} catch {}
-
-/*:
- # PBKDF1, PBKDF2
- */
-
-do {
-    let password: Array<UInt8> = Array("s33krit".utf8)
-    let salt: Array<UInt8> = Array("nacllcan".utf8)
-
-    try PKCS5.PBKDF1(password: password, salt: salt, variant: .sha1, iterations: 4096).calculate()
-
-    let value = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 4096, variant: .sha256).calculate()
-    print(value)
-} catch {}
-
-/*:
- # Padding
- */
-Padding.pkcs7.add(to: bytes, blockSize: AES.blockSize)
 
-/*:
- # ChaCha20
- */
+import CryptoSwift
 
 do {
-    let key: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
-    let iv: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8]
-    let message = Array<UInt8>(repeating: 7, count: 10)
+    let sharedKey = Data(hex: "ff72161a98db39eb81b92079e937120b67fefe3f07d53ebf995d5422eb4a7ee4")
+    let iv = Data(hex: "54b37c9f12934210bc44f01b2752dbce")
+    let ctr = CTR(iv: iv.bytes)
+    let aes = try AES(key: sharedKey.bytes, blockMode: ctr, padding: .noPadding)
 
-    let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message)
-    let decrypted = try ChaCha20(key: key, iv: iv).decrypt(encrypted)
-    print(decrypted)
-} catch {
-    print(error)
-}
+    var fencryptor = try aes.makeEncryptor()
 
-/*:
- # AES
- ### One-time shot.
- Encrypt all data at once.
- */
-do {
-    let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap") // aes128
-    let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
-    print(ciphertext.toHexString())
-} catch {
-    print(error)
-}
+    let devicePublicKey = Data(hex: "5e10a67b08a33dcd4d8fb3edda5c4cd7f11b14a5da0ca96972f21cdba2bf0444")
+    let deviceVerifyData = Data(hex: "b9b808c634aab0387f6cc02ee30e1e606c6d6d4dcb9a18fa1d9fc94a1cb267bd")
 
-/*:
- ### Incremental encryption
+    let encryptedPK = try fencryptor.update(withBytes: devicePublicKey.bytes)
+    print("Encrypted pk : \(encryptedPK.toHexString())")
+    let decryptedVD = try fencryptor.update(withBytes: deviceVerifyData.bytes)
+    print("Decrypted device verify : \(decryptedVD.toHexString())")
 
- Instantiate Encryptor for AES encryption (or decryptor for decryption) and process input data partially.
- */
-do {
-    var encryptor = try AES(key: "passwordpassword", iv: "drowssapdrowssap").makeEncryptor()
+    let wifiConfigRequest = Data(hex: "5219080262150a0954656a6f6e6964686912085a6f657932363131")
+    let encryptedRequest = try fencryptor.update(withBytes: wifiConfigRequest.bytes)
+    print("Encrypted request : \(encryptedRequest.toHexString())")
 
-    var ciphertext = Array<UInt8>()
-    // aggregate partial results
-    ciphertext += try encryptor.update(withBytes: Array("Nullam quis risus ".utf8))
-    ciphertext += try encryptor.update(withBytes: Array("eget urna mollis ".utf8))
-    ciphertext += try encryptor.update(withBytes: Array("ornare vel eu leo.".utf8))
-    // finish at the end
-    ciphertext += try encryptor.finish()
+    let response = Data(hex: "083a536260fb")
+    let decryptedResponse = try fencryptor.finish(withBytes: response.bytes)
+    print("Decrypted response : \(decryptedResponse.toHexString())")
 
-    print(ciphertext.toHexString())
+//    let data = Data(hex: "0123456789abcdef")
+//    print("Encrypted sample data \(try fencryptor.update(withBytes: data.bytes).toHexString())")
+//    print("Decrypted sample data \(try fencryptor.update(withBytes: data.bytes).toHexString())")
 } catch {
-    print(error)
-}
-
-/*:
- ### Encrypt stream
- */
-do {
-    // write until all is written
-    func writeTo(stream: OutputStream, bytes: Array<UInt8>) {
-        var writtenCount = 0
-        while stream.hasSpaceAvailable && writtenCount < bytes.count {
-            writtenCount += stream.write(bytes, maxLength: bytes.count)
-        }
-    }
-
-    let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
-    var encryptor = try! aes.makeEncryptor()
 
-    // prepare streams
-    let data = Data(bytes: (0 ..< 100).map { $0 })
-    let inputStream = InputStream(data: data)
-    let outputStream = OutputStream(toMemory: ())
-    inputStream.open()
-    outputStream.open()
-
-    var buffer = Array<UInt8>(repeating: 0, count: 2)
-
-    // encrypt input stream data and write encrypted result to output stream
-    while inputStream.hasBytesAvailable {
-        let readCount = inputStream.read(&buffer, maxLength: buffer.count)
-        if readCount > 0 {
-            try encryptor.update(withBytes: buffer[0 ..< readCount]) { bytes in
-                writeTo(stream: outputStream, bytes: bytes)
-            }
-        }
-    }
-
-    // finalize encryption
-    try encryptor.finish { bytes in
-        writeTo(stream: outputStream, bytes: bytes)
-    }
-
-    // print result
-    if let ciphertext = outputStream.property(forKey: Stream.PropertyKey(rawValue: Stream.PropertyKey.dataWrittenToMemoryStreamKey.rawValue)) as? Data {
-        print("Encrypted stream data: \(ciphertext.toHexString())")
-    }
-
-} catch {
-    print(error)
 }
+///*:
+// # Data types conversinn
+// */
+//let data = Data(bytes: [0x01, 0x02, 0x03])
+//let bytes = data.bytes
+//let bytesHex = Array<UInt8>(hex: "0x010203")
+//let hexString = bytesHex.toHexString()
+//
+///*:
+// # Digest
+// */
+//data.md5()
+//data.sha1()
+//data.sha224()
+//data.sha256()
+//data.sha384()
+//data.sha512()
+//
+//bytes.sha1()
+//"123".sha1()
+//Digest.sha1(bytes)
+//
+////: Digest calculated incrementally
+//do {
+//    var digest = MD5()
+//    _ = try digest.update(withBytes: [0x31, 0x32])
+//    _ = try digest.update(withBytes: [0x33])
+//    let result = try digest.finish()
+//    print(result)
+//} catch {}
+//
+///*:
+// # CRC
+// */
+//bytes.crc16()
+//bytes.crc32()
+//bytes.crc32c()
+//
+///*:
+// # HMAC
+// */
+//
+//do {
+//    let key: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 23, 25, 26, 27, 28, 29, 30, 31, 32]
+//    try Poly1305(key: key).authenticate(bytes)
+//    try HMAC(key: key, variant: .sha256).authenticate(bytes)
+//} catch {}
+//
+///*:
+// # PBKDF1, PBKDF2
+// */
+//
+//do {
+//    let password: Array<UInt8> = Array("s33krit".utf8)
+//    let salt: Array<UInt8> = Array("nacllcan".utf8)
+//
+//    try PKCS5.PBKDF1(password: password, salt: salt, variant: .sha1, iterations: 4096).calculate()
+//
+//    let value = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 4096, variant: .sha256).calculate()
+//    print(value)
+//} catch {}
+//
+///*:
+// # Padding
+// */
+//Padding.pkcs7.add(to: bytes, blockSize: AES.blockSize)
+//
+///*:
+// # ChaCha20
+// */
+//
+//do {
+//    let key: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
+//    let iv: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8]
+//    let message = Array<UInt8>(repeating: 7, count: 10)
+//
+//    let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message)
+//    let decrypted = try ChaCha20(key: key, iv: iv).decrypt(encrypted)
+//    print(decrypted)
+//} catch {
+//    print(error)
+//}
+//
+///*:
+// # AES
+// ### One-time shot.
+// Encrypt all data at once.
+// */
+//do {
+//    let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap") // aes128
+//    let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
+//    print(ciphertext.toHexString())
+//} catch {
+//    print(error)
+//}
+//
+///*:
+// ### Incremental encryption
+//
+// Instantiate Encryptor for AES encryption (or decryptor for decryption) and process input data partially.
+// */
+//do {
+//    var encryptor = try AES(key: "passwordpassword", iv: "drowssapdrowssap").makeEncryptor()
+//
+//    var ciphertext = Array<UInt8>()
+//    // aggregate partial results
+//    ciphertext += try encryptor.update(withBytes: Array("Nullam quis risus ".utf8))
+//    ciphertext += try encryptor.update(withBytes: Array("eget urna mollis ".utf8))
+//    ciphertext += try encryptor.update(withBytes: Array("ornare vel eu leo.".utf8))
+//    // finish at the end
+//    ciphertext += try encryptor.finish()
+//
+//    print(ciphertext.toHexString())
+//} catch {
+//    print(error)
+//}
+//
+///*:
+// ### Encrypt stream
+// */
+//do {
+//    // write until all is written
+//    func writeTo(stream: OutputStream, bytes: Array<UInt8>) {
+//        var writtenCount = 0
+//        while stream.hasSpaceAvailable && writtenCount < bytes.count {
+//            writtenCount += stream.write(bytes, maxLength: bytes.count)
+//        }
+//    }
+//
+//    let aes = try AES(key: "passwordpassword", iv: "drowssapdrowssap")
+//    var encryptor = try! aes.makeEncryptor()
+//
+//    // prepare streams
+//    let data = Data(bytes: (0 ..< 100).map { $0 })
+//    let inputStream = InputStream(data: data)
+//    let outputStream = OutputStream(toMemory: ())
+//    inputStream.open()
+//    outputStream.open()
+//
+//    var buffer = Array<UInt8>(repeating: 0, count: 2)
+//
+//    // encrypt input stream data and write encrypted result to output stream
+//    while inputStream.hasBytesAvailable {
+//        let readCount = inputStream.read(&buffer, maxLength: buffer.count)
+//        if readCount > 0 {
+//            try encryptor.update(withBytes: buffer[0 ..< readCount]) { bytes in
+//                writeTo(stream: outputStream, bytes: bytes)
+//            }
+//        }
+//    }
+//
+//    // finalize encryption
+//    try encryptor.finish { bytes in
+//        writeTo(stream: outputStream, bytes: bytes)
+//    }
+//
+//    // print result
+//    if let ciphertext = outputStream.property(forKey: Stream.PropertyKey(rawValue: Stream.PropertyKey.dataWrittenToMemoryStreamKey.rawValue)) as? Data {
+//        print("Encrypted stream data: \(ciphertext.toHexString())")
+//    }
+//
+//} catch {
+//    print(error)
+//}

+ 4 - 8
CryptoSwift.xcodeproj/project.pbxproj

@@ -63,14 +63,13 @@
 		75EC52811EE8B8130048EB3B /* BlockCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523D1EE8B6CA0048EB3B /* BlockCipher.swift */; };
 		75EC52821EE8B8170048EB3B /* BlockMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */; };
 		75EC52831EE8B8170048EB3B /* BlockModeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52401EE8B6CA0048EB3B /* BlockModeOptions.swift */; };
-		75EC52841EE8B8170048EB3B /* BlockModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52411EE8B6CA0048EB3B /* BlockModeWorker.swift */; };
+		75EC52841EE8B8170048EB3B /* CipherModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52411EE8B6CA0048EB3B /* CipherModeWorker.swift */; };
 		75EC52851EE8B8170048EB3B /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52421EE8B6CA0048EB3B /* CBC.swift */; };
 		75EC52861EE8B8170048EB3B /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52431EE8B6CA0048EB3B /* CFB.swift */; };
 		75EC52871EE8B8170048EB3B /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52441EE8B6CA0048EB3B /* CTR.swift */; };
 		75EC52881EE8B8170048EB3B /* ECB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52451EE8B6CA0048EB3B /* ECB.swift */; };
 		75EC52891EE8B8170048EB3B /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52461EE8B6CA0048EB3B /* OFB.swift */; };
 		75EC528A1EE8B8170048EB3B /* PCBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52471EE8B6CA0048EB3B /* PCBC.swift */; };
-		75EC528B1EE8B8170048EB3B /* RandomAccessBlockModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52481EE8B6CA0048EB3B /* RandomAccessBlockModeWorker.swift */; };
 		75EC528D1EE8B81A0048EB3B /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524A1EE8B6CA0048EB3B /* ChaCha20.swift */; };
 		75EC528E1EE8B81A0048EB3B /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524B1EE8B6CA0048EB3B /* Checksum.swift */; };
 		75EC528F1EE8B81A0048EB3B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524C1EE8B6CA0048EB3B /* Cipher.swift */; };
@@ -295,14 +294,13 @@
 		75EC523D1EE8B6CA0048EB3B /* BlockCipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockCipher.swift; sourceTree = "<group>"; };
 		75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockMode.swift; sourceTree = "<group>"; };
 		75EC52401EE8B6CA0048EB3B /* BlockModeOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockModeOptions.swift; sourceTree = "<group>"; };
-		75EC52411EE8B6CA0048EB3B /* BlockModeWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockModeWorker.swift; sourceTree = "<group>"; };
+		75EC52411EE8B6CA0048EB3B /* CipherModeWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CipherModeWorker.swift; sourceTree = "<group>"; };
 		75EC52421EE8B6CA0048EB3B /* CBC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBC.swift; sourceTree = "<group>"; };
 		75EC52431EE8B6CA0048EB3B /* CFB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CFB.swift; sourceTree = "<group>"; };
 		75EC52441EE8B6CA0048EB3B /* CTR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CTR.swift; sourceTree = "<group>"; };
 		75EC52451EE8B6CA0048EB3B /* ECB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ECB.swift; sourceTree = "<group>"; };
 		75EC52461EE8B6CA0048EB3B /* OFB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OFB.swift; sourceTree = "<group>"; };
 		75EC52471EE8B6CA0048EB3B /* PCBC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PCBC.swift; sourceTree = "<group>"; };
-		75EC52481EE8B6CA0048EB3B /* RandomAccessBlockModeWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomAccessBlockModeWorker.swift; sourceTree = "<group>"; };
 		75EC52491EE8B6CA0048EB3B /* Blowfish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blowfish.swift; sourceTree = "<group>"; };
 		75EC524A1EE8B6CA0048EB3B /* ChaCha20.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChaCha20.swift; sourceTree = "<group>"; };
 		75EC524B1EE8B6CA0048EB3B /* Checksum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checksum.swift; sourceTree = "<group>"; };
@@ -592,7 +590,7 @@
 			children = (
 				75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */,
 				75EC52401EE8B6CA0048EB3B /* BlockModeOptions.swift */,
-				75EC52411EE8B6CA0048EB3B /* BlockModeWorker.swift */,
+				75EC52411EE8B6CA0048EB3B /* CipherModeWorker.swift */,
 				75EC52421EE8B6CA0048EB3B /* CBC.swift */,
 				75EC52431EE8B6CA0048EB3B /* CFB.swift */,
 				75EC52441EE8B6CA0048EB3B /* CTR.swift */,
@@ -600,7 +598,6 @@
 				75EC52451EE8B6CA0048EB3B /* ECB.swift */,
 				75EC52461EE8B6CA0048EB3B /* OFB.swift */,
 				75EC52471EE8B6CA0048EB3B /* PCBC.swift */,
-				75EC52481EE8B6CA0048EB3B /* RandomAccessBlockModeWorker.swift */,
 			);
 			path = BlockMode;
 			sourceTree = "<group>";
@@ -884,7 +881,7 @@
 				75EC52B41EE8B83D0048EB3B /* UInt32+Extension.swift in Sources */,
 				75EC52911EE8B81A0048EB3B /* Cryptors.swift in Sources */,
 				75EC52881EE8B8170048EB3B /* ECB.swift in Sources */,
-				75EC52841EE8B8170048EB3B /* BlockModeWorker.swift in Sources */,
+				75EC52841EE8B8170048EB3B /* CipherModeWorker.swift in Sources */,
 				75EC52A41EE8B8290048EB3B /* Operators.swift in Sources */,
 				75EC529A1EE8B8200048EB3B /* HMAC+Foundation.swift in Sources */,
 				75EC52B21EE8B83D0048EB3B /* String+Extension.swift in Sources */,
@@ -905,7 +902,6 @@
 				75EC52891EE8B8170048EB3B /* OFB.swift in Sources */,
 				75EC52831EE8B8170048EB3B /* BlockModeOptions.swift in Sources */,
 				751EE9781F93996100161FFC /* AES.Cryptors.swift in Sources */,
-				75EC528B1EE8B8170048EB3B /* RandomAccessBlockModeWorker.swift in Sources */,
 				75EC527D1EE8B8130048EB3B /* Array+Extension.swift in Sources */,
 				75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */,
 				75EC52B31EE8B83D0048EB3B /* UInt16+Extension.swift in Sources */,

+ 67 - 30
Sources/CryptoSwift/AES.Cryptors.swift

@@ -17,11 +17,14 @@
 
 extension AES: Cryptors {
     public func makeEncryptor() throws -> AES.Encryptor {
-        return try AES.Encryptor(aes: self)
+        let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: encrypt)
+        return try AES.Encryptor(padding: padding, worker)
     }
 
     public func makeDecryptor() throws -> AES.Decryptor {
-        return try AES.Decryptor(aes: self)
+        let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
+        let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: cipherOperation)
+        return try AES.Decryptor(padding: padding, worker)
     }
 }
 
@@ -29,15 +32,15 @@ extension AES: Cryptors {
 
 extension AES {
     public struct Encryptor: Cryptor, Updatable {
-        private var worker: BlockModeWorker
+        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)
+        init(padding: Padding, _ worker: CipherModeWorker) throws {
+            self.padding = padding
+            self.worker = worker
         }
 
         // MARK: Updatable
@@ -66,34 +69,68 @@ extension AES {
             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 struct Decryptor: RandomAccessCryptor, Updatable {
-        private var worker: BlockModeWorker
+
+    public class Decryptor: RandomAccessCryptor, Updatable {
+        private let blockSize = AES.blockSize
         private let padding: Padding
-        private let additionalBufferSize: Int
+        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(aes: AES) throws {
-            padding = aes.padding
-
-            if aes.blockMode.options.contains(.useEncryptToDecrypt) {
-                worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.encrypt)
-            } else {
-                worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.decrypt)
-            }
-
-            additionalBufferSize = worker.additionalBufferSize
+        init(padding: Padding, _ worker: CipherModeWorker) throws {
+            self.padding = padding
+            self.worker = worker
         }
 
-        public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+        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
@@ -105,14 +142,14 @@ extension AES {
 
             // If a worker (eg GCM) can combine ciphertext + tag
             // we need to remove tag from the ciphertext.
-            if !isLast && accumulated.count < worker.blockSize + additionalBufferSize {
+            if !isLast && accumulated.count < blockSize + worker.additionalBufferSize {
                 return []
             }
 
             let accumulatedWithoutSuffix: Array<UInt8>
-            if additionalBufferSize > 0 {
+            if worker.additionalBufferSize > 0 {
                 // FIXME: how slow is that?
-                accumulatedWithoutSuffix = Array(accumulated.prefix(accumulated.count - additionalBufferSize))
+                accumulatedWithoutSuffix = Array(accumulated.prefix(accumulated.count - worker.additionalBufferSize))
             } else {
                 accumulatedWithoutSuffix = accumulated
             }
@@ -120,11 +157,11 @@ extension AES {
             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: worker.blockSize) {
-                if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= worker.blockSize {
+            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(additionalBufferSize)) // tag size
+                        chunk = try finalizingWorker.willDecryptLast(block: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
                     }
 
                     if !chunk.isEmpty {
@@ -148,21 +185,21 @@ extension AES {
             processedBytesTotalCount += processedBytesCount
 
             if isLast {
-                plaintext = padding.remove(from: plaintext, blockSize: worker.blockSize)
+                plaintext = padding.remove(from: plaintext, blockSize: blockSize)
             }
 
             return plaintext
         }
 
-        @discardableResult public mutating func seek(to position: Int) -> Bool {
+        @discardableResult public func seek(to position: Int) -> Bool {
             guard var worker = self.worker as? RandomAccessBlockModeWorker else {
                 return false
             }
 
-            worker.counter = UInt(position / AES.blockSize) // TODO: worker.blockSize
+            worker.counter = UInt(position / blockSize)
             self.worker = worker
 
-            offset = position % worker.blockSize
+            offset = position % blockSize
 
             accumulated = []
 

+ 1 - 1
Sources/CryptoSwift/AES.swift

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

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

@@ -18,5 +18,5 @@ public typealias CipherOperationOnBlock = (_ block: ArraySlice<UInt8>) -> Array<
 public protocol BlockMode {
     var options: BlockModeOption { get }
     //TODO: doesn't have to be public
-    func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker
+    func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker
 }

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

@@ -29,7 +29,7 @@ public struct CBC: BlockMode {
         self.iv = iv
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
         }

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

@@ -29,7 +29,7 @@ public struct CFB: BlockMode {
         self.iv = iv
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
         }

+ 8 - 7
Sources/CryptoSwift/BlockMode/CTR.swift

@@ -24,36 +24,37 @@ public struct CTR: BlockMode {
 
     public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
     private let iv: Array<UInt8>
+    private let counter: Int
 
-    public init(iv: Array<UInt8>) {
+    public init(iv: Array<UInt8>, counter: Int = 0) {
         self.iv = iv
+        self.counter = counter
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
         }
 
-        return CTRModeWorker(blockSize: blockSize, iv: iv.slice, cipherOperation: cipherOperation)
+        return CTRModeWorker(blockSize: blockSize, iv: iv.slice, counter: counter, cipherOperation: cipherOperation)
     }
 }
 
 struct CTRModeWorker: RandomAccessBlockModeWorker {
     let cipherOperation: CipherOperationOnBlock
-    let blockSize: Int
     let additionalBufferSize: Int = 0
     private let iv: ArraySlice<UInt8>
     var counter: UInt = 0
 
-    init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
-        self.blockSize = blockSize
+    init(blockSize: Int, iv: ArraySlice<UInt8>, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) {
         self.iv = iv
+        self.counter = UInt(counter)
         self.cipherOperation = cipherOperation
     }
 
     mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         let nonce = buildNonce(iv, counter: UInt64(counter))
-        counter = counter + 1
+        defer { counter += 1 }
 
         guard let ciphertext = cipherOperation(nonce.slice) else {
             return Array(plaintext)

+ 10 - 2
Sources/CryptoSwift/BlockMode/BlockModeWorker.swift → Sources/CryptoSwift/BlockMode/CipherModeWorker.swift

@@ -13,9 +13,9 @@
 //  - This notice may not be removed or altered from any source or binary distribution.
 //
 
-public protocol BlockModeWorker {
+public protocol CipherModeWorker {
     var cipherOperation: CipherOperationOnBlock { get }
-    var blockSize: Int { get }
+
     // Additional space needed when incrementally process data
     // eg. for GCM combined mode
     var additionalBufferSize: Int { get }
@@ -24,6 +24,14 @@ public protocol BlockModeWorker {
     mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
 }
 
+public protocol BlockModeWorker: CipherModeWorker {
+    var blockSize: Int { get }
+}
+
+protocol RandomAccessBlockModeWorker: CipherModeWorker {
+    var counter: UInt { set get }
+}
+
 // TODO: remove and merge with BlockModeWorker
 public protocol BlockModeWorkerFinalizing: BlockModeWorker {
     // Any final calculations, eg. calculate tag

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

@@ -22,7 +22,7 @@ public struct ECB: BlockMode {
     public init() {
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         return ECBModeWorker(blockSize: blockSize, cipherOperation: cipherOperation)
     }
 }

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

@@ -66,7 +66,7 @@ public final class GCM: BlockMode {
         self.authenticationTag = authenticationTag
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.isEmpty {
             throw Error.invalidInitializationVector
         }

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

@@ -29,7 +29,7 @@ public struct OFB: BlockMode {
         self.iv = iv
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
         }

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

@@ -29,7 +29,7 @@ public struct PCBC: BlockMode {
         self.iv = iv
     }
 
-    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> BlockModeWorker {
+    public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
         }

+ 0 - 18
Sources/CryptoSwift/BlockMode/RandomAccessBlockModeWorker.swift

@@ -1,18 +0,0 @@
-//
-//  CryptoSwift
-//
-//  Copyright (C) 2014-2017 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.
-//
-
-protocol RandomAccessBlockModeWorker: BlockModeWorker {
-    var counter: UInt { set get }
-}

+ 2 - 2
Sources/CryptoSwift/Blowfish.swift

@@ -34,8 +34,8 @@ public final class Blowfish {
 
     private let blockMode: BlockMode
     private let padding: Padding
-    private var decryptWorker: BlockModeWorker!
-    private var encryptWorker: BlockModeWorker!
+    private var decryptWorker: CipherModeWorker!
+    private var encryptWorker: CipherModeWorker!
 
     private let N = 16 // rounds
     private var P: Array<UInt32>