Browse Source

Merge branch 'develop'

Marcin Krzyzanowski 7 năm trước cách đây
mục cha
commit
3f6869c469

+ 7 - 1
CHANGELOG

@@ -1,3 +1,9 @@
+0.11.0
+- API: Cryptor.seek() is throwable
+- Adds proper stream support for CTR encryption with Updaptable interface.
+- Refactor internals for the stream cipher modes.
+- Set minimum deployment target to 8.0 (again).
+
 0.10.0
 0.10.0
 - API: BlockMode is no longer an enum. Please migrate to eg. CBC() etc...
 - API: BlockMode is no longer an enum. Please migrate to eg. CBC() etc...
 - Adds AES-GCM support. #97 - Feature sponsored by GesundheitsCloud (http://www.gesundheitscloud.de/)
 - Adds AES-GCM support. #97 - Feature sponsored by GesundheitsCloud (http://www.gesundheitscloud.de/)
@@ -93,7 +99,7 @@
 - Set deployment targets for all platform. Fixes Carthage builds.
 - Set deployment targets for all platform. Fixes Carthage builds.
 - New: SHA-3 implementation (request #291)
 - New: SHA-3 implementation (request #291)
 - SHA-1 conforms to Updatable protocol and may be calculated incrementally.
 - SHA-1 conforms to Updatable protocol and may be calculated incrementally.
-- SHA-2 conforms to Updatable protocol and may be calculated incrementally. 
+- SHA-2 conforms to Updatable protocol and may be calculated incrementally.
 
 
 0.6.0
 0.6.0
 - Remove bridge() workaround for Linux (not needed)
 - Remove bridge() workaround for Linux (not needed)

+ 1 - 1
CryptoSwift.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
 Pod::Spec.new do |s|
   s.name         = "CryptoSwift"
   s.name         = "CryptoSwift"
-  s.version      = "0.10.0"
+  s.version      = "0.11.0"
   s.source       = { :git => "https://github.com/krzyzanowskim/CryptoSwift.git", :tag => "#{s.version}" }
   s.source       = { :git => "https://github.com/krzyzanowskim/CryptoSwift.git", :tag => "#{s.version}" }
   s.summary      = "Cryptography in Swift. SHA, MD5, CRC, PBKDF, Poly1305, HMAC, CMAC, HDKF, ChaCha20, Rabbit, Blowfish, AES."
   s.summary      = "Cryptography in Swift. SHA, MD5, CRC, PBKDF, Poly1305, HMAC, CMAC, HDKF, ChaCha20, Rabbit, Blowfish, AES."
   s.description  = "Cryptography functions and helpers for Swift implemented in Swift. SHA-1, SHA-2, SHA-3, MD5, PBKDF1, PBKDF2, CRC, Poly1305, HMAC, ChaCha20, Rabbit, Blowfish, AES"
   s.description  = "Cryptography functions and helpers for Swift implemented in Swift. SHA-1, SHA-2, SHA-3, MD5, PBKDF1, PBKDF2, CRC, Poly1305, HMAC, ChaCha20, Rabbit, Blowfish, AES"

+ 20 - 12
CryptoSwift.xcodeproj/project.pbxproj

@@ -37,6 +37,7 @@
 		7564F05A2072EAEB00CA5A96 /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		7564F05A2072EAEB00CA5A96 /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		7564F0642072ED7000CA5A96 /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		7564F0642072ED7000CA5A96 /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		7564F0652072ED7000CA5A96 /* CryptoSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		7564F0652072ED7000CA5A96 /* CryptoSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		756A64C62111083B00BE8805 /* StreamEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756A64C52111083B00BE8805 /* StreamEncryptor.swift */; };
 		7576F64D20725BD6006688F8 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7576F64C20725BD5006688F8 /* Default-568h@2x.png */; };
 		7576F64D20725BD6006688F8 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7576F64C20725BD5006688F8 /* Default-568h@2x.png */; };
 		757DA2531A4ED0A4002BA3EF /* PaddingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2521A4ED0A4002BA3EF /* PaddingTests.swift */; };
 		757DA2531A4ED0A4002BA3EF /* PaddingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2521A4ED0A4002BA3EF /* PaddingTests.swift */; };
 		757DA2551A4ED408002BA3EF /* AESTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2541A4ED408002BA3EF /* AESTests.swift */; };
 		757DA2551A4ED408002BA3EF /* AESTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2541A4ED408002BA3EF /* AESTests.swift */; };
@@ -51,6 +52,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 /* BlockEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B3ED78210FA016005D4ADA /* BlockEncryptor.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 */; };
@@ -63,14 +66,13 @@
 		75EC52811EE8B8130048EB3B /* BlockCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523D1EE8B6CA0048EB3B /* BlockCipher.swift */; };
 		75EC52811EE8B8130048EB3B /* BlockCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523D1EE8B6CA0048EB3B /* BlockCipher.swift */; };
 		75EC52821EE8B8170048EB3B /* BlockMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */; };
 		75EC52821EE8B8170048EB3B /* BlockMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */; };
 		75EC52831EE8B8170048EB3B /* BlockModeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52401EE8B6CA0048EB3B /* BlockModeOptions.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 */; };
 		75EC52851EE8B8170048EB3B /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52421EE8B6CA0048EB3B /* CBC.swift */; };
 		75EC52861EE8B8170048EB3B /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52431EE8B6CA0048EB3B /* CFB.swift */; };
 		75EC52861EE8B8170048EB3B /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52431EE8B6CA0048EB3B /* CFB.swift */; };
 		75EC52871EE8B8170048EB3B /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52441EE8B6CA0048EB3B /* CTR.swift */; };
 		75EC52871EE8B8170048EB3B /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52441EE8B6CA0048EB3B /* CTR.swift */; };
 		75EC52881EE8B8170048EB3B /* ECB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52451EE8B6CA0048EB3B /* ECB.swift */; };
 		75EC52881EE8B8170048EB3B /* ECB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52451EE8B6CA0048EB3B /* ECB.swift */; };
 		75EC52891EE8B8170048EB3B /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52461EE8B6CA0048EB3B /* OFB.swift */; };
 		75EC52891EE8B8170048EB3B /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52461EE8B6CA0048EB3B /* OFB.swift */; };
 		75EC528A1EE8B8170048EB3B /* PCBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52471EE8B6CA0048EB3B /* PCBC.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 */; };
 		75EC528D1EE8B81A0048EB3B /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524A1EE8B6CA0048EB3B /* ChaCha20.swift */; };
 		75EC528E1EE8B81A0048EB3B /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524B1EE8B6CA0048EB3B /* Checksum.swift */; };
 		75EC528E1EE8B81A0048EB3B /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524B1EE8B6CA0048EB3B /* Checksum.swift */; };
 		75EC528F1EE8B81A0048EB3B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524C1EE8B6CA0048EB3B /* Cipher.swift */; };
 		75EC528F1EE8B81A0048EB3B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC524C1EE8B6CA0048EB3B /* Cipher.swift */; };
@@ -98,7 +100,7 @@
 		75EC52A91EE8B83D0048EB3B /* PKCS7Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52691EE8B6CA0048EB3B /* PKCS7Padding.swift */; };
 		75EC52A91EE8B83D0048EB3B /* PKCS7Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52691EE8B6CA0048EB3B /* PKCS7Padding.swift */; };
 		75EC52AA1EE8B83D0048EB3B /* Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */; };
 		75EC52AA1EE8B83D0048EB3B /* Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */; };
 		75EC52AB1EE8B83D0048EB3B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */; };
 		75EC52AB1EE8B83D0048EB3B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */; };
-		75EC52AC1EE8B83D0048EB3B /* RandomAccessCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526C1EE8B6CA0048EB3B /* RandomAccessCryptor.swift */; };
+		75EC52AC1EE8B83D0048EB3B /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526C1EE8B6CA0048EB3B /* Cryptor.swift */; };
 		75EC52AD1EE8B83D0048EB3B /* RandomBytesSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */; };
 		75EC52AD1EE8B83D0048EB3B /* RandomBytesSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */; };
 		75EC52AE1EE8B83D0048EB3B /* SecureBytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */; };
 		75EC52AE1EE8B83D0048EB3B /* SecureBytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */; };
 		75EC52AF1EE8B83D0048EB3B /* SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526F1EE8B6CA0048EB3B /* SHA1.swift */; };
 		75EC52AF1EE8B83D0048EB3B /* SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC526F1EE8B6CA0048EB3B /* SHA1.swift */; };
@@ -269,6 +271,7 @@
 		755D27BC1D06DE6400C41692 /* CryptoSwift.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = CryptoSwift.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		755D27BC1D06DE6400C41692 /* CryptoSwift.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = CryptoSwift.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		755FB1D9199E347D00475437 /* ExtensionsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionsTest.swift; sourceTree = "<group>"; };
 		755FB1D9199E347D00475437 /* ExtensionsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionsTest.swift; sourceTree = "<group>"; };
 		7564F0602072EAEB00CA5A96 /* TestsPerformance-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TestsPerformance-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		7564F0602072EAEB00CA5A96 /* TestsPerformance-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TestsPerformance-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		756A64C52111083B00BE8805 /* StreamEncryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamEncryptor.swift; sourceTree = "<group>"; };
 		756BFDCA1A82B87300B9D9A4 /* Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bridging.h; sourceTree = "<group>"; };
 		756BFDCA1A82B87300B9D9A4 /* Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bridging.h; sourceTree = "<group>"; };
 		7576F64C20725BD5006688F8 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
 		7576F64C20725BD5006688F8 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
 		7576F6EB20726319006688F8 /* DigestTestsPerf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestTestsPerf.swift; sourceTree = "<group>"; };
 		7576F6EB20726319006688F8 /* DigestTestsPerf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestTestsPerf.swift; sourceTree = "<group>"; };
@@ -285,6 +288,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 /* BlockEncryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockEncryptor.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>"; };
@@ -295,14 +300,13 @@
 		75EC523D1EE8B6CA0048EB3B /* BlockCipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockCipher.swift; sourceTree = "<group>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		75EC524B1EE8B6CA0048EB3B /* Checksum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checksum.swift; sourceTree = "<group>"; };
@@ -333,7 +337,7 @@
 		75EC52691EE8B6CA0048EB3B /* PKCS7Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKCS7Padding.swift; sourceTree = "<group>"; };
 		75EC52691EE8B6CA0048EB3B /* PKCS7Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKCS7Padding.swift; sourceTree = "<group>"; };
 		75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poly1305.swift; sourceTree = "<group>"; };
 		75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poly1305.swift; sourceTree = "<group>"; };
 		75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rabbit.swift; sourceTree = "<group>"; };
 		75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rabbit.swift; sourceTree = "<group>"; };
-		75EC526C1EE8B6CA0048EB3B /* RandomAccessCryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomAccessCryptor.swift; sourceTree = "<group>"; };
+		75EC526C1EE8B6CA0048EB3B /* Cryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = "<group>"; };
 		75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomBytesSequence.swift; sourceTree = "<group>"; };
 		75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomBytesSequence.swift; sourceTree = "<group>"; };
 		75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBytes.swift; sourceTree = "<group>"; };
 		75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBytes.swift; sourceTree = "<group>"; };
 		75EC526F1EE8B6CA0048EB3B /* SHA1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHA1.swift; sourceTree = "<group>"; };
 		75EC526F1EE8B6CA0048EB3B /* SHA1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHA1.swift; sourceTree = "<group>"; };
@@ -566,7 +570,7 @@
 				75EC52651EE8B6CA0048EB3B /* PKCS */,
 				75EC52651EE8B6CA0048EB3B /* PKCS */,
 				75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */,
 				75EC526A1EE8B6CA0048EB3B /* Poly1305.swift */,
 				75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */,
 				75EC526B1EE8B6CA0048EB3B /* Rabbit.swift */,
-				75EC526C1EE8B6CA0048EB3B /* RandomAccessCryptor.swift */,
+				75EC526C1EE8B6CA0048EB3B /* Cryptor.swift */,
 				75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */,
 				75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */,
 				75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */,
 				75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */,
 				75EC526F1EE8B6CA0048EB3B /* SHA1.swift */,
 				75EC526F1EE8B6CA0048EB3B /* SHA1.swift */,
@@ -583,6 +587,9 @@
 				75EC52781EE8B6CA0048EB3B /* Utils.swift */,
 				75EC52781EE8B6CA0048EB3B /* Utils.swift */,
 				75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */,
 				75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */,
 				754310432050111A003FB1DF /* CompactMap.swift */,
 				754310432050111A003FB1DF /* CompactMap.swift */,
+				75B3ED76210F9DF7005D4ADA /* BlockDecryptor.swift */,
+				75B3ED78210FA016005D4ADA /* BlockEncryptor.swift */,
+				756A64C52111083B00BE8805 /* StreamEncryptor.swift */,
 			);
 			);
 			path = CryptoSwift;
 			path = CryptoSwift;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -592,7 +599,7 @@
 			children = (
 			children = (
 				75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */,
 				75EC523F1EE8B6CA0048EB3B /* BlockMode.swift */,
 				75EC52401EE8B6CA0048EB3B /* BlockModeOptions.swift */,
 				75EC52401EE8B6CA0048EB3B /* BlockModeOptions.swift */,
-				75EC52411EE8B6CA0048EB3B /* BlockModeWorker.swift */,
+				75EC52411EE8B6CA0048EB3B /* CipherModeWorker.swift */,
 				75EC52421EE8B6CA0048EB3B /* CBC.swift */,
 				75EC52421EE8B6CA0048EB3B /* CBC.swift */,
 				75EC52431EE8B6CA0048EB3B /* CFB.swift */,
 				75EC52431EE8B6CA0048EB3B /* CFB.swift */,
 				75EC52441EE8B6CA0048EB3B /* CTR.swift */,
 				75EC52441EE8B6CA0048EB3B /* CTR.swift */,
@@ -600,7 +607,6 @@
 				75EC52451EE8B6CA0048EB3B /* ECB.swift */,
 				75EC52451EE8B6CA0048EB3B /* ECB.swift */,
 				75EC52461EE8B6CA0048EB3B /* OFB.swift */,
 				75EC52461EE8B6CA0048EB3B /* OFB.swift */,
 				75EC52471EE8B6CA0048EB3B /* PCBC.swift */,
 				75EC52471EE8B6CA0048EB3B /* PCBC.swift */,
-				75EC52481EE8B6CA0048EB3B /* RandomAccessBlockModeWorker.swift */,
 			);
 			);
 			path = BlockMode;
 			path = BlockMode;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -867,10 +873,11 @@
 				7529366A20683DFC00195874 /* AEADChaCha20Poly1305.swift in Sources */,
 				7529366A20683DFC00195874 /* AEADChaCha20Poly1305.swift in Sources */,
 				75EC529E1EE8B8230048EB3B /* Generics.swift in Sources */,
 				75EC529E1EE8B8230048EB3B /* Generics.swift in Sources */,
 				75EC52AA1EE8B83D0048EB3B /* Poly1305.swift in Sources */,
 				75EC52AA1EE8B83D0048EB3B /* Poly1305.swift in Sources */,
-				75EC52AC1EE8B83D0048EB3B /* RandomAccessCryptor.swift in Sources */,
+				75EC52AC1EE8B83D0048EB3B /* Cryptor.swift in Sources */,
 				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 /* BlockEncryptor.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 */,
@@ -880,11 +887,12 @@
 				75EC52811EE8B8130048EB3B /* BlockCipher.swift in Sources */,
 				75EC52811EE8B8130048EB3B /* BlockCipher.swift in Sources */,
 				75EC52941EE8B81A0048EB3B /* DigestType.swift in Sources */,
 				75EC52941EE8B81A0048EB3B /* DigestType.swift in Sources */,
 				75EC529B1EE8B8200048EB3B /* Rabbit+Foundation.swift in Sources */,
 				75EC529B1EE8B8200048EB3B /* Rabbit+Foundation.swift in Sources */,
+				756A64C62111083B00BE8805 /* StreamEncryptor.swift in Sources */,
 				75EC52A61EE8B8390048EB3B /* PBKDF1.swift in Sources */,
 				75EC52A61EE8B8390048EB3B /* PBKDF1.swift in Sources */,
 				75EC52B41EE8B83D0048EB3B /* UInt32+Extension.swift in Sources */,
 				75EC52B41EE8B83D0048EB3B /* UInt32+Extension.swift in Sources */,
 				75EC52911EE8B81A0048EB3B /* Cryptors.swift in Sources */,
 				75EC52911EE8B81A0048EB3B /* Cryptors.swift in Sources */,
 				75EC52881EE8B8170048EB3B /* ECB.swift in Sources */,
 				75EC52881EE8B8170048EB3B /* ECB.swift in Sources */,
-				75EC52841EE8B8170048EB3B /* BlockModeWorker.swift in Sources */,
+				75EC52841EE8B8170048EB3B /* CipherModeWorker.swift in Sources */,
 				75EC52A41EE8B8290048EB3B /* Operators.swift in Sources */,
 				75EC52A41EE8B8290048EB3B /* Operators.swift in Sources */,
 				75EC529A1EE8B8200048EB3B /* HMAC+Foundation.swift in Sources */,
 				75EC529A1EE8B8200048EB3B /* HMAC+Foundation.swift in Sources */,
 				75EC52B21EE8B83D0048EB3B /* String+Extension.swift in Sources */,
 				75EC52B21EE8B83D0048EB3B /* String+Extension.swift in Sources */,
@@ -905,7 +913,6 @@
 				75EC52891EE8B8170048EB3B /* OFB.swift in Sources */,
 				75EC52891EE8B8170048EB3B /* OFB.swift in Sources */,
 				75EC52831EE8B8170048EB3B /* BlockModeOptions.swift in Sources */,
 				75EC52831EE8B8170048EB3B /* BlockModeOptions.swift in Sources */,
 				751EE9781F93996100161FFC /* AES.Cryptors.swift in Sources */,
 				751EE9781F93996100161FFC /* AES.Cryptors.swift in Sources */,
-				75EC528B1EE8B8170048EB3B /* RandomAccessBlockModeWorker.swift in Sources */,
 				75EC527D1EE8B8130048EB3B /* Array+Extension.swift in Sources */,
 				75EC527D1EE8B8130048EB3B /* Array+Extension.swift in Sources */,
 				75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */,
 				75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */,
 				75EC52B31EE8B83D0048EB3B /* UInt16+Extension.swift in Sources */,
 				75EC52B31EE8B83D0048EB3B /* UInt16+Extension.swift in Sources */,
@@ -918,6 +925,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 */,

+ 9 - 149
Sources/CryptoSwift/AES.Cryptors.swift

@@ -16,157 +16,17 @@
 // MARK: Cryptors
 // MARK: Cryptors
 
 
 extension AES: Cryptors {
 extension AES: Cryptors {
-    public func makeEncryptor() throws -> AES.Encryptor {
-        return try AES.Encryptor(aes: self)
-    }
-
-    public func makeDecryptor() throws -> AES.Decryptor {
-        return try AES.Decryptor(aes: self)
-    }
-}
-
-// MARK: Encryptor
-
-extension AES {
-    public struct Encryptor: Cryptor, Updatable {
-        private var worker: BlockModeWorker
-        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
+    public func makeEncryptor() throws -> Cryptor & Updatable {
+        let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: encrypt)
+        if worker is StreamModeWorker {
+            return try StreamEncryptor(blockSize: AES.blockSize, padding: padding, worker)
         }
         }
+        return try BlockEncryptor(blockSize: AES.blockSize, padding: padding, worker)
     }
     }
-}
-
-// MARK: Decryptor
-
-extension AES {
-    public struct Decryptor: RandomAccessCryptor, Updatable {
-        private var worker: BlockModeWorker
-        private let padding: Padding
-        private let additionalBufferSize: Int
-        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
-        }
-
-        public mutating 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 < worker.blockSize + additionalBufferSize {
-                return []
-            }
-
-            let accumulatedWithoutSuffix: Array<UInt8>
-            if additionalBufferSize > 0 {
-                // FIXME: how slow is that?
-                accumulatedWithoutSuffix = Array(accumulated.prefix(accumulated.count - 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: worker.blockSize) {
-                if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= worker.blockSize {
-
-                    if isLast, var finalizingWorker = worker as? BlockModeWorkerFinalizing {
-                        chunk = try finalizingWorker.willDecryptLast(block: chunk + accumulated.suffix(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: worker.blockSize)
-            }
-
-            return plaintext
-        }
-
-        @discardableResult public mutating 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
-            self.worker = worker
-
-            offset = position % worker.blockSize
-
-            accumulated = []
-
-            return true
-        }
+    public func makeDecryptor() throws -> Cryptor & Updatable {
+        let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
+        let worker = try blockMode.worker(blockSize: AES.blockSize, cipherOperation: cipherOperation)
+        return try BlockDecryptor(blockSize: AES.blockSize, padding: padding, worker)
     }
     }
 }
 }

+ 84 - 0
Sources/CryptoSwift/BlockDecryptor.swift

@@ -0,0 +1,84 @@
+//  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: Cryptor, Updatable {
+    private let blockSize: Int
+    private let padding: Padding
+    private var worker: CipherModeWorker
+    private var accumulated = Array<UInt8>()
+
+    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> {
+        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)
+                }
+
+                if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
+                    plaintext = try finalizingWorker.didDecryptLast(block: plaintext.slice)
+                }
+
+                processedBytesCount += chunk.count
+            }
+        }
+        accumulated.removeFirst(processedBytesCount) // super-slow
+
+        if isLast {
+            plaintext = padding.remove(from: plaintext, blockSize: blockSize)
+        }
+
+        return plaintext
+    }
+
+    public func seek(to position: Int) throws {
+        guard var worker = self.worker as? StreamModeWorker else {
+            fatalError("Not supported")
+        }
+
+        try worker.seek(to: position)
+        self.worker = worker
+
+        accumulated = []
+    }
+}

+ 57 - 0
Sources/CryptoSwift/BlockEncryptor.swift

@@ -0,0 +1,57 @@
+//  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 BlockEncryptor: 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 var lastBlockRemainder = 0
+
+    init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+        self.blockSize = blockSize
+        self.padding = padding
+        self.worker = worker
+    }
+
+    // MARK: Updatable
+    public func update(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
+    }
+
+    func seek(to: Int) throws {
+        fatalError("Not supported")
+    }
+}

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

@@ -18,5 +18,7 @@ public typealias CipherOperationOnBlock = (_ block: ArraySlice<UInt8>) -> Array<
 public protocol BlockMode {
 public protocol BlockMode {
     var options: BlockModeOption { get }
     var options: BlockModeOption { get }
     //TODO: doesn't have to be public
     //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
 }
 }
+
+typealias StreamMode = BlockMode

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

@@ -29,7 +29,7 @@ public struct CBC: BlockMode {
         self.iv = iv
         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 {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
             throw Error.invalidInitializationVector
         }
         }

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

@@ -29,7 +29,7 @@ public struct CFB: BlockMode {
         self.iv = iv
         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 {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
             throw Error.invalidInitializationVector
         }
         }

+ 76 - 18
Sources/CryptoSwift/BlockMode/CTR.swift

@@ -14,9 +14,8 @@
 //
 //
 
 
 //  Counter (CTR)
 //  Counter (CTR)
-//
 
 
-public struct CTR: BlockMode {
+public struct CTR: StreamMode {
     public enum Error: Swift.Error {
     public enum Error: Swift.Error {
         /// Invalid IV
         /// Invalid IV
         case invalidInitializationVector
         case invalidInitializationVector
@@ -24,42 +23,101 @@ public struct CTR: BlockMode {
 
 
     public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
     public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
     private let iv: Array<UInt8>
     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.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 {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
             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 {
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct CTRModeWorker: StreamModeWorker, CounterModeWorker {
+    typealias Counter = CTRCounter
+
+    final class CTRCounter {
+        private let constPrefix: Array<UInt8>
+        private var value: UInt64
+        //TODO: make it an updatable value, computing is too slow
+        var bytes: Array<UInt8> {
+            return constPrefix + value.bytes()
+        }
+
+        init(_ initialValue: Array<UInt8>) {
+            let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2)
+            constPrefix = Array(initialValue[initialValue.startIndex..<halfIndex])
+
+            let suffixBytes = Array(initialValue[halfIndex...])
+            value = UInt64(bytes: suffixBytes)
+        }
+
+        convenience init(nonce: Array<UInt8>, startAt index: Int) {
+            self.init(buildCounterValue(nonce, counter: UInt64(index)))
+        }
+
+        static func +=(lhs: CTRCounter, rhs: Int) {
+            lhs.value += UInt64(rhs)
+        }
+    }
+
     let cipherOperation: CipherOperationOnBlock
     let cipherOperation: CipherOperationOnBlock
-    let blockSize: Int
     let additionalBufferSize: Int = 0
     let additionalBufferSize: Int = 0
-    private let iv: ArraySlice<UInt8>
-    var counter: UInt = 0
+    let iv: Array<UInt8>
+    var counter: CTRCounter
 
 
-    init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
-        self.blockSize = blockSize
-        self.iv = iv
+    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) {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
+        self.blockSize = blockSize
+        self.iv = Array(iv)
+
+        // the first keystream is calculated from the nonce = initial value of counter
+        self.counter = CTRCounter(nonce: Array(iv), startAt: counter)
+        self.keystream = Array(cipherOperation(self.counter.bytes.slice)!)
     }
     }
 
 
+    mutating func seek(to position: Int) throws {
+        let offset = position % blockSize
+        counter = CTRCounter(nonce: iv, startAt: position / blockSize)
+        keystream = Array(cipherOperation(counter.bytes.slice)!)
+        keystreamPosIdx = offset
+    }
+
+    // plaintext is at most blockSize long
     mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
     mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
-        let nonce = buildNonce(iv, counter: UInt64(counter))
-        counter = counter + 1
+        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
+            }
 
 
-        guard let ciphertext = cipherOperation(nonce.slice) else {
-            return Array(plaintext)
+            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> {
@@ -67,7 +125,7 @@ struct CTRModeWorker: RandomAccessBlockModeWorker {
     }
     }
 }
 }
 
 
-private func buildNonce(_ iv: ArraySlice<UInt8>, counter: UInt64) -> Array<UInt8> {
+private func buildCounterValue(_ iv: Array<UInt8>, counter: UInt64) -> Array<UInt8> {
     let noncePartLen = iv.count / 2
     let noncePartLen = iv.count / 2
     let noncePrefix = iv[iv.startIndex..<iv.startIndex.advanced(by: noncePartLen)]
     let noncePrefix = iv[iv.startIndex..<iv.startIndex.advanced(by: noncePartLen)]
     let nonceSuffix = iv[iv.startIndex.advanced(by: noncePartLen)..<iv.startIndex.advanced(by: iv.count)]
     let nonceSuffix = iv[iv.startIndex.advanced(by: noncePartLen)..<iv.startIndex.advanced(by: iv.count)]

+ 15 - 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.
 //  - 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 cipherOperation: CipherOperationOnBlock { get }
-    var blockSize: Int { get }
+
     // Additional space needed when incrementally process data
     // Additional space needed when incrementally process data
     // eg. for GCM combined mode
     // eg. for GCM combined mode
     var additionalBufferSize: Int { get }
     var additionalBufferSize: Int { get }
@@ -24,6 +24,19 @@ public protocol BlockModeWorker {
     mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
     mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
 }
 }
 
 
+public protocol BlockModeWorker: CipherModeWorker {
+    var blockSize: Int { get }
+}
+
+public protocol CounterModeWorker: CipherModeWorker {
+    associatedtype Counter
+    var counter: Counter { get set }
+}
+
+public protocol StreamModeWorker: CipherModeWorker {
+    mutating func seek(to position: Int) throws
+}
+
 // 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

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

@@ -22,7 +22,7 @@ public struct ECB: BlockMode {
     public init() {
     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)
         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
         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 {
         if iv.isEmpty {
             throw Error.invalidInitializationVector
             throw Error.invalidInitializationVector
         }
         }

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

@@ -29,7 +29,7 @@ public struct OFB: BlockMode {
         self.iv = iv
         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 {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
             throw Error.invalidInitializationVector
         }
         }

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

@@ -29,7 +29,7 @@ public struct PCBC: BlockMode {
         self.iv = iv
         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 {
         if iv.count != blockSize {
             throw Error.invalidInitializationVector
             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 blockMode: BlockMode
     private let padding: Padding
     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 let N = 16 // rounds
     private var P: Array<UInt32>
     private var P: Array<UInt32>

+ 17 - 6
Sources/CryptoSwift/ChaCha20.swift

@@ -19,6 +19,7 @@
 public final class ChaCha20: BlockCipher {
 public final class ChaCha20: BlockCipher {
     public enum Error: Swift.Error {
     public enum Error: Swift.Error {
         case invalidKeyOrInitializationVector
         case invalidKeyOrInitializationVector
+        case notSupported
     }
     }
 
 
     public static let blockSize = 64 // 512 / 8
     public static let blockSize = 64 // 512 / 8
@@ -254,7 +255,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
 
 
@@ -275,13 +276,17 @@ extension ChaCha20 {
             }
             }
             return encrypted
             return encrypted
         }
         }
+
+        public func seek(to: Int) throws {
+            throw Error.notSupported
+        }
     }
     }
 }
 }
 
 
 // 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
@@ -320,17 +325,23 @@ extension ChaCha20 {
 
 
             return plaintext
             return plaintext
         }
         }
+
+        public func seek(to: Int) throws {
+            throw Error.notSupported
+        }
     }
     }
 }
 }
 
 
 // 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)
     }
     }
 }
 }

+ 3 - 7
Sources/CryptoSwift/RandomAccessCryptor.swift → Sources/CryptoSwift/Cryptor.swift

@@ -13,14 +13,10 @@
 //  - 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
-public protocol Cryptor { }
-
-public protocol RandomAccessCryptor: Cryptor {
+/// Cryptor (Encryptor or Decryptor)
+public protocol 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
-    @discardableResult mutating func seek(to: Int) -> Bool
+    mutating func seek(to: Int) throws
 }
 }

+ 2 - 4
Sources/CryptoSwift/Cryptors.swift

@@ -21,14 +21,12 @@ 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>

+ 1 - 1
Sources/CryptoSwift/Info.plist

@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
 	<key>CFBundleShortVersionString</key>
-	<string>0.10.0</string>
+	<string>0.11.0</string>
 	<key>CFBundleSignature</key>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<string>????</string>
 	<key>CFBundleVersion</key>
 	<key>CFBundleVersion</key>

+ 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:

+ 54 - 0
Sources/CryptoSwift/StreamEncryptor.swift

@@ -0,0 +1,54 @@
+//  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 StreamEncryptor: 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 var lastBlockRemainder = 0
+
+    init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+        self.blockSize = blockSize
+        self.padding = padding
+        self.worker = worker
+    }
+
+    // MARK: Updatable
+    public func update(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
+    }
+
+    func seek(to: Int) throws {
+        fatalError("Not supported")
+    }
+}

+ 4 - 0
Sources/CryptoSwift/Updatable.swift

@@ -41,6 +41,10 @@ extension Updatable {
         }
         }
     }
     }
 
 
+    public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+        return try update(withBytes: bytes, isLast: isLast)
+    }
+
     public mutating func update(withBytes bytes: Array<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
     public mutating func update(withBytes bytes: Array<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
         return try update(withBytes: bytes.slice, isLast: isLast)
         return try update(withBytes: bytes.slice, isLast: isLast)
     }
     }

+ 35 - 11
Tests/Tests/AESTests.swift

@@ -120,7 +120,7 @@ final class AESTests: XCTestCase {
             ciphertext += try encryptor.finish()
             ciphertext += try encryptor.finish()
             XCTAssertEqual(try aes.encrypt(plaintext.bytes), ciphertext, "encryption failed")
             XCTAssertEqual(try aes.encrypt(plaintext.bytes), ciphertext, "encryption failed")
         } catch {
         } catch {
-            XCTAssert(false, "\(error)")
+            XCTFail("\(error)")
         }
         }
     }
     }
 
 
@@ -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() {
@@ -259,26 +261,27 @@ final class AESTests: XCTestCase {
             plaintext[i * 6 + 5] = "|".utf8.first!
             plaintext[i * 6 + 5] = "|".utf8.first!
         }
         }
 
 
-        let aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
+        var aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
         let encrypted = try! aes.encrypt(plaintext)
         let encrypted = try! aes.encrypt(plaintext)
 
 
+        aes = try! AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
         var decryptor = try! aes.makeDecryptor()
         var decryptor = try! aes.makeDecryptor()
-        decryptor.seek(to: 2)
+        try! decryptor.seek(to: 2)
         var part1 = try! decryptor.update(withBytes: Array(encrypted[2..<5]))
         var part1 = try! decryptor.update(withBytes: Array(encrypted[2..<5]))
         part1 += try! decryptor.finish()
         part1 += try! decryptor.finish()
         XCTAssertEqual(part1, Array(plaintext[2..<5]), "seek decryption failed")
         XCTAssertEqual(part1, Array(plaintext[2..<5]), "seek decryption failed")
 
 
-        decryptor.seek(to: 1000)
-        var part2 = try! decryptor.update(withBytes: Array(encrypted[1000..<1200]))
+        try! decryptor.seek(to: 1000)
+        var part2 = try! decryptor.update(withBytes: Array(encrypted[1000..<1010]))
         part2 += try! decryptor.finish()
         part2 += try! decryptor.finish()
-        XCTAssertEqual(part2, Array(plaintext[1000..<1200]), "seek decryption failed")
+        XCTAssertEqual(part2, Array(plaintext[1000..<1010]), "seek decryption failed")
 
 
-        decryptor.seek(to: 5500)
+        try! decryptor.seek(to: 5500)
         var part3 = try! decryptor.update(withBytes: Array(encrypted[5500..<6000]))
         var part3 = try! decryptor.update(withBytes: Array(encrypted[5500..<6000]))
         part3 += try! decryptor.finish()
         part3 += try! decryptor.finish()
         XCTAssertEqual(part3, Array(plaintext[5500..<6000]), "seek decryption failed")
         XCTAssertEqual(part3, Array(plaintext[5500..<6000]), "seek decryption failed")
 
 
-        decryptor.seek(to: 0)
+        try! decryptor.seek(to: 0)
         var part4 = try! decryptor.update(withBytes: Array(encrypted[0..<80]))
         var part4 = try! decryptor.update(withBytes: Array(encrypted[0..<80]))
         part4 += try! decryptor.finish()
         part4 += try! decryptor.finish()
         XCTAssertEqual(part4, Array(plaintext[0..<80]), "seek decryption failed")
         XCTAssertEqual(part4, Array(plaintext[0..<80]), "seek decryption failed")
@@ -296,7 +299,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 +307,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]
@@ -573,6 +596,7 @@ extension AESTests {
             ("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),