Переглянути джерело

Merge branch 'develop' into hex_string_to_bytes

Marcin Krzyzanowski 8 роки тому
батько
коміт
31695a3f69
28 змінених файлів з 283 додано та 177 видалено
  1. 4 4
      CryptoSwift.xcodeproj/project.pbxproj
  2. 22 0
      CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/2774350F-3E36-4FB9-835D-90E1E9EF7CE0.plist
  3. 22 0
      CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/7797B693-C86A-4026-B2CE-05813EFA26F4.plist
  4. 22 0
      CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/BD46E9D4-C65B-4C11-9BB5-B8B05CCE976F.plist
  5. 49 52
      Sources/CryptoSwift/AES.swift
  6. 52 0
      Sources/CryptoSwift/BatchedCollection.swift
  7. 1 1
      Sources/CryptoSwift/BlockCipher.swift
  8. 2 2
      Sources/CryptoSwift/BlockMode/BlockModeWorker.swift
  9. 6 6
      Sources/CryptoSwift/BlockMode/CBC.swift
  10. 5 5
      Sources/CryptoSwift/BlockMode/CFB.swift
  11. 3 3
      Sources/CryptoSwift/BlockMode/CTR.swift
  12. 4 4
      Sources/CryptoSwift/BlockMode/ECB.swift
  13. 4 4
      Sources/CryptoSwift/BlockMode/OFB.swift
  14. 5 5
      Sources/CryptoSwift/BlockMode/PCBC.swift
  15. 4 4
      Sources/CryptoSwift/Blowfish.swift
  16. 0 45
      Sources/CryptoSwift/BytesSequence.swift
  17. 2 2
      Sources/CryptoSwift/ChaCha20.swift
  18. 2 2
      Sources/CryptoSwift/Checksum.swift
  19. 2 2
      Sources/CryptoSwift/Cipher.swift
  20. 16 14
      Sources/CryptoSwift/Collection+Extension.swift
  21. 8 6
      Sources/CryptoSwift/MD5.swift
  22. 1 1
      Sources/CryptoSwift/SHA1.swift
  23. 1 1
      Sources/CryptoSwift/SHA2.swift
  24. 1 1
      Sources/CryptoSwift/SHA3.swift
  25. 6 2
      Sources/CryptoSwift/UInt64+Extension.swift
  26. 11 10
      Sources/CryptoSwift/Updatable.swift
  27. 14 1
      Sources/CryptoSwift/Utils.swift
  28. 14 0
      Tests/CryptoSwiftTests/AESTests.swift

+ 4 - 4
CryptoSwift.xcodeproj/project.pbxproj

@@ -35,7 +35,6 @@
 		757BC93C1C1CA5790093AAA9 /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91A1C1CA5790093AAA9 /* AES.swift */; };
 		757BC93C1C1CA5790093AAA9 /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91A1C1CA5790093AAA9 /* AES.swift */; };
 		757BC9401C1CA5790093AAA9 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91B1C1CA5790093AAA9 /* Array+Extension.swift */; };
 		757BC9401C1CA5790093AAA9 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91B1C1CA5790093AAA9 /* Array+Extension.swift */; };
 		757BC9481C1CA5790093AAA9 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91D1C1CA5790093AAA9 /* Authenticator.swift */; };
 		757BC9481C1CA5790093AAA9 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91D1C1CA5790093AAA9 /* Authenticator.swift */; };
-		757BC9501C1CA5790093AAA9 /* BytesSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC91F1C1CA5790093AAA9 /* BytesSequence.swift */; };
 		757BC9541C1CA5790093AAA9 /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9201C1CA5790093AAA9 /* ChaCha20.swift */; };
 		757BC9541C1CA5790093AAA9 /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9201C1CA5790093AAA9 /* ChaCha20.swift */; };
 		757BC9601C1CA5790093AAA9 /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9231C1CA5790093AAA9 /* Checksum.swift */; };
 		757BC9601C1CA5790093AAA9 /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9231C1CA5790093AAA9 /* Checksum.swift */; };
 		757BC9681C1CA5790093AAA9 /* CSArrayType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9251C1CA5790093AAA9 /* CSArrayType+Extensions.swift */; };
 		757BC9681C1CA5790093AAA9 /* CSArrayType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757BC9251C1CA5790093AAA9 /* CSArrayType+Extensions.swift */; };
@@ -68,6 +67,7 @@
 		758A94291A65C67400E46135 /* HMACTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
 		758A94291A65C67400E46135 /* HMACTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
 		758F58F11C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
 		758F58F11C8FB6E20054C377 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758F58F01C8FB6E20054C377 /* OFB.swift */; };
 		7595ADE81D86A29A0099EBEF /* HMAC+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7595ADE71D86A29A0099EBEF /* HMAC+Foundation.swift */; };
 		7595ADE81D86A29A0099EBEF /* HMAC+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7595ADE71D86A29A0099EBEF /* HMAC+Foundation.swift */; };
+		759FF2A81E226B7D00BC4EE1 /* BatchedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759FF2A71E226B7D00BC4EE1 /* BatchedCollection.swift */; };
 		75B601EB197D6A6C0009B53D /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		75B601EB197D6A6C0009B53D /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754BE45519693E190098E6F3 /* CryptoSwift.framework */; };
 		75C2E7681D55EFA1003D2BCA /* UInt16+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E7671D55EFA1003D2BCA /* UInt16+Extension.swift */; };
 		75C2E7681D55EFA1003D2BCA /* UInt16+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E7671D55EFA1003D2BCA /* UInt16+Extension.swift */; };
 		75C2E76D1D55F097003D2BCA /* Access.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E76C1D55F097003D2BCA /* Access.swift */; };
 		75C2E76D1D55F097003D2BCA /* Access.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75C2E76C1D55F097003D2BCA /* Access.swift */; };
@@ -186,7 +186,6 @@
 		757BC91A1C1CA5790093AAA9 /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AES.swift; path = Sources/CryptoSwift/AES.swift; sourceTree = SOURCE_ROOT; };
 		757BC91A1C1CA5790093AAA9 /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AES.swift; path = Sources/CryptoSwift/AES.swift; sourceTree = SOURCE_ROOT; };
 		757BC91B1C1CA5790093AAA9 /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Extension.swift"; path = "Sources/CryptoSwift/Array+Extension.swift"; sourceTree = SOURCE_ROOT; };
 		757BC91B1C1CA5790093AAA9 /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Extension.swift"; path = "Sources/CryptoSwift/Array+Extension.swift"; sourceTree = SOURCE_ROOT; };
 		757BC91D1C1CA5790093AAA9 /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Authenticator.swift; path = Sources/CryptoSwift/Authenticator.swift; sourceTree = SOURCE_ROOT; };
 		757BC91D1C1CA5790093AAA9 /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Authenticator.swift; path = Sources/CryptoSwift/Authenticator.swift; sourceTree = SOURCE_ROOT; };
-		757BC91F1C1CA5790093AAA9 /* BytesSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BytesSequence.swift; path = Sources/CryptoSwift/BytesSequence.swift; sourceTree = SOURCE_ROOT; };
 		757BC9201C1CA5790093AAA9 /* ChaCha20.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChaCha20.swift; path = Sources/CryptoSwift/ChaCha20.swift; sourceTree = SOURCE_ROOT; };
 		757BC9201C1CA5790093AAA9 /* ChaCha20.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChaCha20.swift; path = Sources/CryptoSwift/ChaCha20.swift; sourceTree = SOURCE_ROOT; };
 		757BC9231C1CA5790093AAA9 /* Checksum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Checksum.swift; path = Sources/CryptoSwift/Checksum.swift; sourceTree = SOURCE_ROOT; };
 		757BC9231C1CA5790093AAA9 /* Checksum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Checksum.swift; path = Sources/CryptoSwift/Checksum.swift; sourceTree = SOURCE_ROOT; };
 		757BC9251C1CA5790093AAA9 /* CSArrayType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CSArrayType+Extensions.swift"; path = "Sources/CryptoSwift/CSArrayType+Extensions.swift"; sourceTree = SOURCE_ROOT; };
 		757BC9251C1CA5790093AAA9 /* CSArrayType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CSArrayType+Extensions.swift"; path = "Sources/CryptoSwift/CSArrayType+Extensions.swift"; sourceTree = SOURCE_ROOT; };
@@ -219,6 +218,7 @@
 		758A94271A65C59200E46135 /* HMACTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACTests.swift; sourceTree = "<group>"; };
 		758A94271A65C59200E46135 /* HMACTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACTests.swift; sourceTree = "<group>"; };
 		758F58F01C8FB6E20054C377 /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OFB.swift; path = Sources/CryptoSwift/BlockMode/OFB.swift; sourceTree = SOURCE_ROOT; };
 		758F58F01C8FB6E20054C377 /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OFB.swift; path = Sources/CryptoSwift/BlockMode/OFB.swift; sourceTree = SOURCE_ROOT; };
 		7595ADE71D86A29A0099EBEF /* HMAC+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HMAC+Foundation.swift"; sourceTree = "<group>"; };
 		7595ADE71D86A29A0099EBEF /* HMAC+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HMAC+Foundation.swift"; sourceTree = "<group>"; };
+		759FF2A71E226B7D00BC4EE1 /* BatchedCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BatchedCollection.swift; path = Sources/CryptoSwift/BatchedCollection.swift; sourceTree = SOURCE_ROOT; };
 		75C2E7671D55EFA1003D2BCA /* UInt16+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UInt16+Extension.swift"; path = "Sources/CryptoSwift/UInt16+Extension.swift"; sourceTree = SOURCE_ROOT; };
 		75C2E7671D55EFA1003D2BCA /* UInt16+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UInt16+Extension.swift"; path = "Sources/CryptoSwift/UInt16+Extension.swift"; sourceTree = SOURCE_ROOT; };
 		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>"; };
 		75CB93241C8F5EC10087740D /* CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CBC.swift; path = Sources/CryptoSwift/BlockMode/CBC.swift; sourceTree = SOURCE_ROOT; };
 		75CB93241C8F5EC10087740D /* CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CBC.swift; path = Sources/CryptoSwift/BlockMode/CBC.swift; sourceTree = SOURCE_ROOT; };
@@ -301,7 +301,7 @@
 				757BC91D1C1CA5790093AAA9 /* Authenticator.swift */,
 				757BC91D1C1CA5790093AAA9 /* Authenticator.swift */,
 				80545D121CA9FECD00474A99 /* Bit.swift */,
 				80545D121CA9FECD00474A99 /* Bit.swift */,
 				757F440D1CC1822A002B1F85 /* SecureBytes.swift */,
 				757F440D1CC1822A002B1F85 /* SecureBytes.swift */,
-				757BC91F1C1CA5790093AAA9 /* BytesSequence.swift */,
+				759FF2A71E226B7D00BC4EE1 /* BatchedCollection.swift */,
 				753B32FE1DAB711200D06422 /* RandomBytesSequence.swift */,
 				753B32FE1DAB711200D06422 /* RandomBytesSequence.swift */,
 				755655C61D080E3F00F004E7 /* Cryptors.swift */,
 				755655C61D080E3F00F004E7 /* Cryptors.swift */,
 				75DB81A71CDC06B100ED181A /* Updatable.swift */,
 				75DB81A71CDC06B100ED181A /* Updatable.swift */,
@@ -582,6 +582,7 @@
 				757BC9701C1CA5790093AAA9 /* Digest.swift in Sources */,
 				757BC9701C1CA5790093AAA9 /* Digest.swift in Sources */,
 				75CB93351C8F5FCE0087740D /* PCBC.swift in Sources */,
 				75CB93351C8F5FCE0087740D /* PCBC.swift in Sources */,
 				75CB93301C8F5F580087740D /* BlockMode.swift in Sources */,
 				75CB93301C8F5F580087740D /* BlockMode.swift in Sources */,
+				759FF2A81E226B7D00BC4EE1 /* BatchedCollection.swift in Sources */,
 				757BC9121C1CA56A0093AAA9 /* String+FoundationExtension.swift in Sources */,
 				757BC9121C1CA56A0093AAA9 /* String+FoundationExtension.swift in Sources */,
 				757BC9B41C1CA5790093AAA9 /* UInt8+Extension.swift in Sources */,
 				757BC9B41C1CA5790093AAA9 /* UInt8+Extension.swift in Sources */,
 				757BC9401C1CA5790093AAA9 /* Array+Extension.swift in Sources */,
 				757BC9401C1CA5790093AAA9 /* Array+Extension.swift in Sources */,
@@ -598,7 +599,6 @@
 				758F58F11C8FB6E20054C377 /* OFB.swift in Sources */,
 				758F58F11C8FB6E20054C377 /* OFB.swift in Sources */,
 				75CB934E1C8F609D0087740D /* BlockModeOptions.swift in Sources */,
 				75CB934E1C8F609D0087740D /* BlockModeOptions.swift in Sources */,
 				757BC9801C1CA5790093AAA9 /* IntegerConvertible.swift in Sources */,
 				757BC9801C1CA5790093AAA9 /* IntegerConvertible.swift in Sources */,
-				757BC9501C1CA5790093AAA9 /* BytesSequence.swift in Sources */,
 				757BC9881C1CA5790093AAA9 /* MD5.swift in Sources */,
 				757BC9881C1CA5790093AAA9 /* MD5.swift in Sources */,
 				757BC9A81C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				757BC9A81C1CA5790093AAA9 /* SHA1.swift in Sources */,
 				75DB81A81CDC06B100ED181A /* Updatable.swift in Sources */,
 				75DB81A81CDC06B100ED181A /* Updatable.swift in Sources */,

+ 22 - 0
CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/2774350F-3E36-4FB9-835D-90E1E9EF7CE0.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>classNames</key>
+	<dict>
+		<key>AESTests</key>
+		<dict>
+			<key>testAESEncryptPerformance()</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>2.5786</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Local Baseline</string>
+				</dict>
+			</dict>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 22 - 0
CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/7797B693-C86A-4026-B2CE-05813EFA26F4.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>classNames</key>
+	<dict>
+		<key>AESTests</key>
+		<dict>
+			<key>testAESEncryptPerformance()</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>3.093</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Local Baseline</string>
+				</dict>
+			</dict>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 22 - 0
CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/BD46E9D4-C65B-4C11-9BB5-B8B05CCE976F.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>classNames</key>
+	<dict>
+		<key>AESTests</key>
+		<dict>
+			<key>testAESEncryptPerformance()</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.23441</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Local Baseline</string>
+				</dict>
+			</dict>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 49 - 52
Sources/CryptoSwift/AES.swift

@@ -133,70 +133,70 @@ fileprivate extension AES {
     func encrypt(block: Array<UInt8>) -> Array<UInt8>? {
     func encrypt(block: Array<UInt8>) -> Array<UInt8>? {
 
 
         if blockMode.options.contains(.PaddingRequired) && block.count != AES.blockSize {
         if blockMode.options.contains(.PaddingRequired) && block.count != AES.blockSize {
-            return block
+            return Array(block)
         }
         }
 
 
         let rounds = self.variant.Nr
         let rounds = self.variant.Nr
         let rk = self.expandedKey
         let rk = self.expandedKey
-        var b = block[block.indices].toUInt32Array()
+
+        var b0 = UInt32(block[block.startIndex + 0 + (0 << 2)]) << 0 | UInt32(block[block.startIndex + 1 + (0 << 2)]) << 8 | UInt32(block[block.startIndex + 2 + (0 << 2)]) << 16 | UInt32(block[block.startIndex + 3 + (0 << 2)]) << 24
+        var b1 = UInt32(block[block.startIndex + 0 + (1 << 2)]) << 0 | UInt32(block[block.startIndex + 1 + (1 << 2)]) << 8 | UInt32(block[block.startIndex + 2 + (1 << 2)]) << 16 | UInt32(block[block.startIndex + 3 + (1 << 2)]) << 24
+        var b2 = UInt32(block[block.startIndex + 0 + (2 << 2)]) << 0 | UInt32(block[block.startIndex + 1 + (2 << 2)]) << 8 | UInt32(block[block.startIndex + 2 + (2 << 2)]) << 16 | UInt32(block[block.startIndex + 3 + (2 << 2)]) << 24
+        var b3 = UInt32(block[block.startIndex + 0 + (3 << 2)]) << 0 | UInt32(block[block.startIndex + 1 + (3 << 2)]) << 8 | UInt32(block[block.startIndex + 2 + (3 << 2)]) << 16 | UInt32(block[block.startIndex + 3 + (3 << 2)]) << 24
 
 
         var t = Array<UInt32>(repeating: 0, count: 4)
         var t = Array<UInt32>(repeating: 0, count: 4)
 
 
         for r in 0 ..< rounds - 1 {
         for r in 0 ..< rounds - 1 {
-            t[0] = b[0] ^ rk[r][0]
-            t[1] = b[1] ^ rk[r][1]
-            t[2] = b[2] ^ rk[r][2]
-            t[3] = b[3] ^ rk[r][3]
+            t[0] = b0 ^ rk[r][0]
+            t[1] = b1 ^ rk[r][1]
+            t[2] = b2 ^ rk[r][2]
+            t[3] = b3 ^ rk[r][3]
 
 
             let lb00 = T0[Int(t[0] & 0xFF)]
             let lb00 = T0[Int(t[0] & 0xFF)]
             let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
             let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
             let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
             let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
             let lb03 = T3[Int(t[3] >> 24)]
             let lb03 = T3[Int(t[3] >> 24)]
-            b[0] = lb00 ^ lb01 ^ lb02 ^ lb03
+            b0 = lb00 ^ lb01 ^ lb02 ^ lb03
 
 
             let lb10 = T0[Int(t[1] & 0xFF)]
             let lb10 = T0[Int(t[1] & 0xFF)]
             let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
             let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
             let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
             let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
             let lb13 = T3[Int(t[0] >> 24)]
             let lb13 = T3[Int(t[0] >> 24)]
-            b[1] = lb10 ^ lb11 ^ lb12 ^ lb13
+            b1 = lb10 ^ lb11 ^ lb12 ^ lb13
 
 
             let lb20 = T0[Int(t[2] & 0xFF)]
             let lb20 = T0[Int(t[2] & 0xFF)]
             let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
             let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
             let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
             let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
             let lb23 = T3[Int(t[1] >> 24)]
             let lb23 = T3[Int(t[1] >> 24)]
-            b[2] = lb20 ^ lb21 ^ lb22 ^ lb23
+            b2 = lb20 ^ lb21 ^ lb22 ^ lb23
 
 
             let lb30 = T0[Int(t[3] & 0xFF)]
             let lb30 = T0[Int(t[3] & 0xFF)]
             let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
             let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
             let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
             let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
             let lb33 = T3[Int(t[2] >> 24)]
             let lb33 = T3[Int(t[2] >> 24)]
-            b[3] = lb30 ^ lb31 ^ lb32 ^ lb33
+            b3 = lb30 ^ lb31 ^ lb32 ^ lb33
         }
         }
 
 
         // last round
         // last round
         let r = rounds - 1
         let r = rounds - 1
 
 
-        t[0] = b[0] ^ rk[r][0]
-        t[1] = b[1] ^ rk[r][1]
-        t[2] = b[2] ^ rk[r][2]
-        t[3] = b[3] ^ rk[r][3]
+        t[0] = b0 ^ rk[r][0]
+        t[1] = b1 ^ rk[r][1]
+        t[2] = b2 ^ rk[r][2]
+        t[3] = b3 ^ rk[r][3]
 
 
         // rounds
         // rounds
-        b[0] = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
-        b[1] = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
-        b[2] = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
-        b[3] = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
-
-        var out = Array<UInt8>()
-        out.reserveCapacity(b.count * 4)
-        for num in b {
-            out.append(UInt8(num & 0xFF))
-            out.append(UInt8((num >> 8) & 0xFF))
-            out.append(UInt8((num >> 16) & 0xFF))
-            out.append(UInt8((num >> 24) & 0xFF))
-        }
-
-        return out
+        b0 = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
+        b1 = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
+        b2 = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
+        b3 = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
+
+        return [
+            UInt8(b0 & 0xFF),UInt8((b0 >> 8) & 0xFF),UInt8((b0 >> 16) & 0xFF),UInt8((b0 >> 24) & 0xFF),
+            UInt8(b1 & 0xFF),UInt8((b1 >> 8) & 0xFF),UInt8((b1 >> 16) & 0xFF),UInt8((b1 >> 24) & 0xFF),
+            UInt8(b2 & 0xFF),UInt8((b2 >> 8) & 0xFF),UInt8((b2 >> 16) & 0xFF),UInt8((b2 >> 24) & 0xFF),
+            UInt8(b3 & 0xFF),UInt8((b3 >> 8) & 0xFF),UInt8((b3 >> 16) & 0xFF),UInt8((b3 >> 24) & 0xFF)
+        ] as Array<UInt8>
     }
     }
 
 
     func decrypt(block: Array<UInt8>) -> Array<UInt8>? {
     func decrypt(block: Array<UInt8>) -> Array<UInt8>? {
@@ -312,18 +312,7 @@ fileprivate extension AES {
     func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
     func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
 
 
         func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
         func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
-            var arr = Array<UInt32>()
-            for idx in stride(from: expanded.startIndex, to: expanded.endIndex, by: 4) {
-                let four = Array(expanded[idx ..< idx.advanced(by: 4)].reversed())
-                let num = UInt32(bytes: four)
-                arr.append(num)
-            }
-
-            var allarr = Array<Array<UInt32>>()
-            for idx in stride(from: arr.startIndex, to: arr.endIndex, by: 4) {
-                allarr.append(Array(arr[idx ..< idx.advanced(by: 4)]))
-            }
-            return allarr
+            return expanded.batched(by: 4).map({ UInt32(bytes: $0.reversed()) }).batched(by: 4).map({ Array($0) })
         }
         }
 
 
         /*
         /*
@@ -437,15 +426,15 @@ extension AES {
         mutating public func update<T: Collection>(withBytes bytes: T, isLast: Bool = false) throws -> Array<UInt8> where T.Iterator.Element == UInt8 {
         mutating public func update<T: Collection>(withBytes bytes: T, isLast: Bool = false) throws -> Array<UInt8> where T.Iterator.Element == UInt8 {
             self.accumulated += bytes
             self.accumulated += bytes
 
 
-            if isLast {
+            if isLast && self.paddingRequired {
                 self.accumulated = padding.add(to: self.accumulated, blockSize: AES.blockSize)
                 self.accumulated = padding.add(to: self.accumulated, blockSize: AES.blockSize)
             }
             }
 
 
             var processedBytes = 0
             var processedBytes = 0
             var encrypted = Array<UInt8>()
             var encrypted = Array<UInt8>()
             encrypted.reserveCapacity(self.accumulated.count)
             encrypted.reserveCapacity(self.accumulated.count)
-            for chunk in self.accumulated.chunks(size: AES.blockSize) {
-                if (isLast || (self.accumulated.count - processedBytes) >= AES.blockSize) {
+            for chunk in self.accumulated.batched(by: AES.blockSize) {
+                if isLast || (self.accumulated.count - processedBytes) >= AES.blockSize {
                     encrypted += worker.encrypt(chunk)
                     encrypted += worker.encrypt(chunk)
                     processedBytes += chunk.count
                     processedBytes += chunk.count
                 }
                 }
@@ -497,7 +486,7 @@ extension AES {
             var processedBytes = 0
             var processedBytes = 0
             var plaintext = Array<UInt8>()
             var plaintext = Array<UInt8>()
             plaintext.reserveCapacity(self.accumulated.count)
             plaintext.reserveCapacity(self.accumulated.count)
-            for chunk in self.accumulated.chunks(size: AES.blockSize) {
+            for chunk in self.accumulated.batched(by: AES.blockSize) {
                 if (isLast || (self.accumulated.count - processedBytes) >= AES.blockSize) {
                 if (isLast || (self.accumulated.count - processedBytes) >= AES.blockSize) {
                     plaintext += self.worker.decrypt(chunk)
                     plaintext += self.worker.decrypt(chunk)
 
 
@@ -552,15 +541,17 @@ extension AES: Cryptors {
 // MARK: Cipher
 // MARK: Cipher
 extension AES: Cipher {
 extension AES: Cipher {
 
 
-    public func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int {
-        let chunks = Array(bytes).chunks(size: AES.blockSize)
+    public func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int, C.SubSequence: Collection, C.SubSequence.Iterator.Element == C.Iterator.Element {
+        let chunks = bytes.batched(by: AES.blockSize)
 
 
         var oneTimeCryptor = self.makeEncryptor()
         var oneTimeCryptor = self.makeEncryptor()
         var out = Array<UInt8>()
         var out = Array<UInt8>()
         out.reserveCapacity(bytes.count)
         out.reserveCapacity(bytes.count)
-        for idx in chunks.indices {
-            out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == chunks.endIndex.advanced(by: -1))
+        for chunk in chunks {
+            out += try oneTimeCryptor.update(withBytes: chunk, isLast: false)
         }
         }
+        // Padding may be added at the very end
+        out += try oneTimeCryptor.finish()
 
 
         if blockMode.options.contains(.PaddingRequired) && (out.count % AES.blockSize != 0) {
         if blockMode.options.contains(.PaddingRequired) && (out.count % AES.blockSize != 0) {
             throw Error.dataPaddingRequired
             throw Error.dataPaddingRequired
@@ -569,17 +560,23 @@ extension AES: Cipher {
         return out
         return out
     }
     }
 
 
-    public func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int {
+    public func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int, C.SubSequence: Collection, C.SubSequence.Iterator.Element == C.Iterator.Element {
         if blockMode.options.contains(.PaddingRequired) && (bytes.count % AES.blockSize != 0) {
         if blockMode.options.contains(.PaddingRequired) && (bytes.count % AES.blockSize != 0) {
             throw Error.dataPaddingRequired
             throw Error.dataPaddingRequired
         }
         }
 
 
         var oneTimeCryptor = self.makeDecryptor()
         var oneTimeCryptor = self.makeDecryptor()
-        let chunks = Array(bytes).chunks(size: AES.blockSize)
+        let chunks = bytes.batched(by: AES.blockSize)
         var out = Array<UInt8>()
         var out = Array<UInt8>()
         out.reserveCapacity(bytes.count)
         out.reserveCapacity(bytes.count)
+
+        var lastIdx = chunks.startIndex
+        chunks.indices.formIndex(&lastIdx, offsetBy: chunks.count - 1)
+
+        // To properly remove padding, `isLast` has to be known when called with the last chunk of ciphertext
+        // Last chunk of ciphertext may contains padded data so next call to update(..) won't be able to remove it
         for idx in chunks.indices {
         for idx in chunks.indices {
-            out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == chunks.endIndex.advanced(by: -1))
+            out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == lastIdx)
         }
         }
         return out
         return out
     }
     }

+ 52 - 0
Sources/CryptoSwift/BatchedCollection.swift

@@ -0,0 +1,52 @@
+//
+//  BatchedCollection.swift
+//  CryptoSwift
+//
+//  Created by Marcin Krzyzanowski on 08/01/2017.
+//  Copyright © 2017 Marcin Krzyzanowski. All rights reserved.
+//
+
+struct BatchedCollectionIndex<Base: Collection> {
+    let range: Range<Base.Index>
+}
+
+extension BatchedCollectionIndex: Comparable {
+    static func ==<Base: Collection>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
+        return lhs.range.lowerBound == rhs.range.lowerBound
+    }
+    static func < <Base: Collection>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
+        return lhs.range.lowerBound < rhs.range.lowerBound
+    }
+}
+
+protocol BatchedCollectionType: Collection {
+    associatedtype Base: Collection
+}
+
+struct BatchedCollection<Base: Collection>: Collection {
+    let base: Base
+    let size: Base.IndexDistance
+    typealias Index = BatchedCollectionIndex<Base>
+    private func nextBreak(after idx: Base.Index) -> Base.Index {
+        return base.index(idx, offsetBy: size, limitedBy: base.endIndex)
+            ?? base.endIndex
+    }
+    var startIndex: Index {
+        return Index(range: base.startIndex..<nextBreak(after: base.startIndex))
+    }
+    var endIndex: Index {
+        return Index(range: base.endIndex..<base.endIndex)
+    }
+    func index(after idx: Index) -> Index {
+        return Index(range: idx.range.upperBound..<nextBreak(after: idx.range.upperBound))
+    }
+    subscript(idx: Index) -> Base.SubSequence {
+        return base[idx.range]
+    }
+}
+
+extension Collection {
+    func batched(by size: IndexDistance) -> BatchedCollection<Self> {
+        return BatchedCollection(base: self, size: size)
+    }
+}

+ 1 - 1
Sources/CryptoSwift/BlockCipher.swift

@@ -6,6 +6,6 @@
 //  Copyright © 2016 Marcin Krzyzanowski. All rights reserved.
 //  Copyright © 2016 Marcin Krzyzanowski. All rights reserved.
 //
 //
 
 
-protocol BlockCipher: class {
+protocol BlockCipher: Cipher {
     static var blockSize: Int { get }
     static var blockSize: Int { get }
 }
 }

+ 2 - 2
Sources/CryptoSwift/BlockMode/BlockModeWorker.swift

@@ -8,6 +8,6 @@
 
 
 protocol BlockModeWorker {
 protocol BlockModeWorker {
     var cipherOperation: CipherOperationOnBlock { get }
     var cipherOperation: CipherOperationOnBlock { get }
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8>
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8>
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8>
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
 }
 }

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

@@ -20,20 +20,20 @@ struct CBCModeWorker: BlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
         guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
-            return plaintext
+            return Array(plaintext)
         }
         }
         prev = ciphertext
         prev = ciphertext
         return ciphertext
         return ciphertext
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
-        guard let plaintext = cipherOperation(ciphertext) else {
-            return ciphertext
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+        guard let plaintext = cipherOperation(Array(ciphertext)) else {
+            return Array(ciphertext)
         }
         }
         let result = xor(prev ?? iv, plaintext)
         let result = xor(prev ?? iv, plaintext)
-        self.prev = ciphertext
+        self.prev = Array(ciphertext)
         return result
         return result
     }
     }
 }
 }

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

@@ -20,20 +20,20 @@ struct CFBModeWorker: BlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let ciphertext = cipherOperation(prev ?? iv) else {
         guard let ciphertext = cipherOperation(prev ?? iv) else {
-            return plaintext
+            return Array(plaintext)
         }
         }
         prev = xor(plaintext, ciphertext)
         prev = xor(plaintext, ciphertext)
         return prev ?? []
         return prev ?? []
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let plaintext = cipherOperation(prev ?? iv) else {
         guard let plaintext = cipherOperation(prev ?? iv) else {
-            return ciphertext
+            return Array(ciphertext)
         }
         }
         let result = xor(plaintext, ciphertext)
         let result = xor(plaintext, ciphertext)
-        self.prev = ciphertext
+        self.prev = Array(ciphertext)
         return result
         return result
     }
     }
 }
 }

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

@@ -20,18 +20,18 @@ struct CTRModeWorker: RandomAccessBlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         let nonce = buildNonce(iv, counter: UInt64(counter))
         let nonce = buildNonce(iv, counter: UInt64(counter))
         counter = counter + 1
         counter = counter + 1
 
 
         guard let ciphertext = cipherOperation(nonce) else {
         guard let ciphertext = cipherOperation(nonce) else {
-            return plaintext
+            return Array(plaintext)
         }
         }
 
 
         return xor(plaintext, ciphertext)
         return xor(plaintext, ciphertext)
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
         return encrypt(ciphertext)
         return encrypt(ciphertext)
     }
     }
 }
 }

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

@@ -16,14 +16,14 @@ struct ECBModeWorker: BlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
-        guard let ciphertext = cipherOperation(plaintext) else {
-            return plaintext
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+        guard let ciphertext = cipherOperation(Array(plaintext)) else {
+            return Array(plaintext)
         }
         }
         return ciphertext
         return ciphertext
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
         return encrypt(ciphertext)
         return encrypt(ciphertext)
     }
     }
 }
 }

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

@@ -20,17 +20,17 @@ struct OFBModeWorker: BlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let ciphertext = cipherOperation(prev ?? iv) else {
         guard let ciphertext = cipherOperation(prev ?? iv) else {
-            return plaintext
+            return Array(plaintext)
         }
         }
         prev = ciphertext
         prev = ciphertext
         return xor(plaintext, ciphertext)
         return xor(plaintext, ciphertext)
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let decrypted = cipherOperation(prev ?? iv) else {
         guard let decrypted = cipherOperation(prev ?? iv) else {
-            return ciphertext
+            return Array(ciphertext)
         }
         }
         let plaintext = xor(decrypted, ciphertext)
         let plaintext = xor(decrypted, ciphertext)
         self.prev = decrypted
         self.prev = decrypted

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

@@ -20,17 +20,17 @@ struct PCBCModeWorker: BlockModeWorker {
         self.cipherOperation = cipherOperation
         self.cipherOperation = cipherOperation
     }
     }
 
 
-    mutating func encrypt(_ plaintext: Array<UInt8>) -> Array<UInt8> {
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
         guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
         guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
-            return plaintext
+            return Array(plaintext)
         }
         }
         prev = xor(plaintext, ciphertext)
         prev = xor(plaintext, ciphertext)
         return ciphertext
         return ciphertext
     }
     }
 
 
-    mutating func decrypt(_ ciphertext: Array<UInt8>) -> Array<UInt8> {
-        guard let plaintext = cipherOperation(ciphertext) else {
-            return ciphertext
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+        guard let plaintext = cipherOperation(Array(ciphertext)) else {
+            return Array(ciphertext)
         }
         }
         let result = xor(prev ?? iv, plaintext)
         let result = xor(prev ?? iv, plaintext)
         self.prev = xor(plaintext, ciphertext)
         self.prev = xor(plaintext, ciphertext)

+ 4 - 4
Sources/CryptoSwift/Blowfish.swift

@@ -467,8 +467,8 @@ extension Blowfish: Cipher {
         var out = Array<UInt8>()
         var out = Array<UInt8>()
         out.reserveCapacity(bytes.count)
         out.reserveCapacity(bytes.count)
 
 
-        for chunk in BytesSequence(chunkSize: Blowfish.blockSize, data: bytes) {
-            out += self.encryptWorker.encrypt(Array(chunk)) //FIXME: copying here is innefective
+        for chunk in bytes.batched(by: Blowfish.blockSize) {
+            out += self.encryptWorker.encrypt(chunk)
         }
         }
 
 
         if blockMode.options.contains(.PaddingRequired) && (out.count % Blowfish.blockSize != 0) {
         if blockMode.options.contains(.PaddingRequired) && (out.count % Blowfish.blockSize != 0) {
@@ -492,8 +492,8 @@ extension Blowfish: Cipher {
         var out = Array<UInt8>()
         var out = Array<UInt8>()
         out.reserveCapacity(bytes.count)
         out.reserveCapacity(bytes.count)
 
 
-        for chunk in BytesSequence(chunkSize: Blowfish.blockSize, data: Array(bytes)) {
-            out += self.decryptWorker.decrypt(Array(chunk)) //FIXME: copying here is innefective
+        for chunk in Array(bytes).batched(by: Blowfish.blockSize) {
+            out += self.decryptWorker.decrypt(chunk) //FIXME: copying here is innefective
         }
         }
 
 
         out = padding.remove(from: out, blockSize: Blowfish.blockSize)
         out = padding.remove(from: out, blockSize: Blowfish.blockSize)

+ 0 - 45
Sources/CryptoSwift/BytesSequence.swift

@@ -1,45 +0,0 @@
-//
-//  BytesSequence.swift
-//  CryptoSwift
-//
-//  Created by Marcin Krzyzanowski on 26/09/15.
-//  Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
-//
-
-/// Generic version of BytesSequence is slower, therefore specialized version is in use
-///
-// struct BytesSequence<D: RandomAccessCollection>: Sequence where D.Iterator.Element == UInt8, D.IndexDistance == Int, D.SubSequence.IndexDistance == Int, D.Index == Int {
-//    let chunkSize: D.IndexDistance
-//    let data: D
-//
-//    func makeIterator() -> AnyIterator<D.SubSequence> {
-//        var offset = data.startIndex
-//        return AnyIterator {
-//            let end = Swift.min(self.chunkSize, self.data.count - offset)
-//            let result = self.data[offset..<offset + end]
-//            offset = offset.advanced(by: result.count)
-//            if !result.isEmpty {
-//                return result
-//            }
-//            return nil
-//        }
-//    }
-// }
-
-struct BytesSequence: Sequence {
-    let chunkSize: Array<UInt8>.IndexDistance
-    let data: Array<UInt8>
-
-    func makeIterator() -> AnyIterator<ArraySlice<UInt8>> {
-        var offset = data.startIndex
-        return AnyIterator {
-            let end = Swift.min(self.chunkSize, self.data.count &- offset)
-            let result = self.data[offset ..< offset &+ end]
-            offset = offset.advanced(by: result.count)
-            if !result.isEmpty {
-                return result
-            }
-            return nil
-        }
-    }
-}

+ 2 - 2
Sources/CryptoSwift/ChaCha20.swift

@@ -260,7 +260,7 @@ extension ChaCha20 {
 
 
             var encrypted = Array<UInt8>()
             var encrypted = Array<UInt8>()
             encrypted.reserveCapacity(self.accumulated.count)
             encrypted.reserveCapacity(self.accumulated.count)
-            for chunk in BytesSequence(chunkSize: ChaCha20.blockSize, data: self.accumulated) {
+            for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
                 if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
                 if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
                     encrypted += try chacha.encrypt(chunk)
                     encrypted += try chacha.encrypt(chunk)
                     self.accumulated.removeFirst(chunk.count) //TODO: improve performance
                     self.accumulated.removeFirst(chunk.count) //TODO: improve performance
@@ -297,7 +297,7 @@ extension ChaCha20 {
 
 
             var plaintext = Array<UInt8>()
             var plaintext = Array<UInt8>()
             plaintext.reserveCapacity(self.accumulated.count)
             plaintext.reserveCapacity(self.accumulated.count)
-            for chunk in BytesSequence(chunkSize: ChaCha20.blockSize, data: self.accumulated) {
+            for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
                 if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
                 if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
                     plaintext += try chacha.decrypt(chunk)
                     plaintext += try chacha.decrypt(chunk)
 
 

+ 2 - 2
Sources/CryptoSwift/Checksum.swift

@@ -76,7 +76,7 @@ public final class Checksum {
 
 
     func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
     func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
         var crc: UInt32 = seed != nil ? seed! : 0xffffffff
         var crc: UInt32 = seed != nil ? seed! : 0xffffffff
-        for chunk in BytesSequence(chunkSize: 256, data: message) {
+        for chunk in message.batched(by: 256) {
             for b in chunk {
             for b in chunk {
                 let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xff)
                 let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xff)
                 crc = (crc >> 8) ^ Checksum.table32[idx]
                 crc = (crc >> 8) ^ Checksum.table32[idx]
@@ -87,7 +87,7 @@ public final class Checksum {
 
 
     func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
     func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
         var crc: UInt16 = seed != nil ? seed! : 0x0000
         var crc: UInt16 = seed != nil ? seed! : 0x0000
-        for chunk in BytesSequence(chunkSize: 256, data: message) {
+        for chunk in message.batched(by: 256) {
             for b in chunk {
             for b in chunk {
                 crc = (crc >> 8) ^ Checksum.table16[Int((crc ^ UInt16(b)) & 0xFF)]
                 crc = (crc >> 8) ^ Checksum.table16[Int((crc ^ UInt16(b)) & 0xFF)]
             }
             }

+ 2 - 2
Sources/CryptoSwift/Cipher.swift

@@ -16,11 +16,11 @@ public protocol Cipher: class {
     ///
     ///
     /// - parameter bytes: Plaintext data
     /// - parameter bytes: Plaintext data
     /// - returns: Encrypted data
     /// - returns: Encrypted data
-    func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int
+    func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int, C.SubSequence: Collection, C.SubSequence.Iterator.Element == C.Iterator.Element, C.SubSequence.Index == C.Index, C.SubSequence.IndexDistance == C.IndexDistance
 
 
     /// Decrypt given bytes at once
     /// Decrypt given bytes at once
     ///
     ///
     /// - parameter bytes: Ciphertext data
     /// - parameter bytes: Ciphertext data
     /// - returns: Plaintext data
     /// - returns: Plaintext data
-    func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int
+    func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Iterator.Element == UInt8, C.IndexDistance == Int, C.Index == Int, C.SubSequence: Collection, C.SubSequence.Iterator.Element == C.Iterator.Element, C.SubSequence.Index == C.Index, C.SubSequence.IndexDistance == C.IndexDistance
 }
 }

+ 16 - 14
Sources/CryptoSwift/Collection+Extension.swift

@@ -9,14 +9,15 @@
 extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
 extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
 
 
     func toUInt32Array() -> Array<UInt32> {
     func toUInt32Array() -> Array<UInt32> {
+        let count = self.count
         var result = Array<UInt32>()
         var result = Array<UInt32>()
         result.reserveCapacity(16)
         result.reserveCapacity(16)
-        for idx in stride(from: self.startIndex, to: self.endIndex, by: MemoryLayout<UInt32>.size) {
+        for idx in stride(from: self.startIndex, to: self.endIndex, by: 4) {
             var val: UInt32 = 0
             var val: UInt32 = 0
-            val |= self.count > 3 ? UInt32(self[idx.advanced(by: 3)]) << 24 : 0
-            val |= self.count > 2 ? UInt32(self[idx.advanced(by: 2)]) << 16 : 0
-            val |= self.count > 1 ? UInt32(self[idx.advanced(by: 1)]) << 8 : 0
-            val |= self.count > 0 ? UInt32(self[idx]) : 0
+            val |= count > 3 ? UInt32(self[idx.advanced(by: 3)]) << 24 : 0
+            val |= count > 2 ? UInt32(self[idx.advanced(by: 2)]) << 16 : 0
+            val |= count > 1 ? UInt32(self[idx.advanced(by: 1)]) << 8 : 0
+            val |= count > 0 ? UInt32(self[idx]) : 0
             result.append(val)
             result.append(val)
         }
         }
 
 
@@ -24,18 +25,19 @@ extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
     }
     }
 
 
     func toUInt64Array() -> Array<UInt64> {
     func toUInt64Array() -> Array<UInt64> {
+        let count = self.count
         var result = Array<UInt64>()
         var result = Array<UInt64>()
         result.reserveCapacity(32)
         result.reserveCapacity(32)
-        for idx in stride(from: self.startIndex, to: self.endIndex, by: MemoryLayout<UInt64>.size) {
+        for idx in stride(from: self.startIndex, to: self.endIndex, by: 8) {
             var val: UInt64 = 0
             var val: UInt64 = 0
-            val |= self.count > 7 ? UInt64(self[idx.advanced(by: 7)]) << 56 : 0
-            val |= self.count > 6 ? UInt64(self[idx.advanced(by: 6)]) << 48 : 0
-            val |= self.count > 5 ? UInt64(self[idx.advanced(by: 5)]) << 40 : 0
-            val |= self.count > 4 ? UInt64(self[idx.advanced(by: 4)]) << 32 : 0
-            val |= self.count > 3 ? UInt64(self[idx.advanced(by: 3)]) << 24 : 0
-            val |= self.count > 2 ? UInt64(self[idx.advanced(by: 2)]) << 16 : 0
-            val |= self.count > 1 ? UInt64(self[idx.advanced(by: 1)]) << 8 : 0
-            val |= self.count > 0 ? UInt64(self[idx.advanced(by: 0)]) << 0 : 0
+            val |= count > 7 ? UInt64(self[idx.advanced(by: 7)]) << 56 : 0
+            val |= count > 6 ? UInt64(self[idx.advanced(by: 6)]) << 48 : 0
+            val |= count > 5 ? UInt64(self[idx.advanced(by: 5)]) << 40 : 0
+            val |= count > 4 ? UInt64(self[idx.advanced(by: 4)]) << 32 : 0
+            val |= count > 3 ? UInt64(self[idx.advanced(by: 3)]) << 24 : 0
+            val |= count > 2 ? UInt64(self[idx.advanced(by: 2)]) << 16 : 0
+            val |= count > 1 ? UInt64(self[idx.advanced(by: 1)]) << 8 : 0
+            val |= count > 0 ? UInt64(self[idx.advanced(by: 0)]) << 0 : 0
             result.append(val)
             result.append(val)
         }
         }
 
 

+ 8 - 6
Sources/CryptoSwift/MD5.swift

@@ -52,10 +52,7 @@ public final class MD5: DigestType {
 
 
     // mutating currentHash in place is way faster than returning new result
     // mutating currentHash in place is way faster than returning new result
     fileprivate func process(block chunk: ArraySlice<UInt8>, currentHash: inout Array<UInt32>) {
     fileprivate func process(block chunk: ArraySlice<UInt8>, currentHash: inout Array<UInt32>) {
-
-        // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
-        var M = chunk.toUInt32Array()
-        assert(M.count == 16, "Invalid array")
+        assert(chunk.count == 16 * 4)
 
 
         // Initialize hash value for this chunk:
         // Initialize hash value for this chunk:
         var A: UInt32 = currentHash[0]
         var A: UInt32 = currentHash[0]
@@ -93,7 +90,12 @@ public final class MD5: DigestType {
             dTemp = D
             dTemp = D
             D = C
             D = C
             C = B
             C = B
-            B = B &+ rotateLeft(A &+ F &+ k[j] &+ M[g], by: s[j])
+
+            // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 and get M[g] value
+            let gAdvanced = g << 2
+            let Mg = UInt32(chunk[chunk.startIndex + gAdvanced]) | UInt32(chunk[chunk.startIndex + gAdvanced &+ 1]) << 8 | UInt32(chunk[chunk.startIndex + gAdvanced &+ 2]) << 16 | UInt32(chunk[chunk.startIndex + gAdvanced &+ 3]) << 24
+
+            B = B &+ rotateLeft(A &+ F &+ k[j] &+ Mg, by: s[j])
             A = dTemp
             A = dTemp
         }
         }
 
 
@@ -121,7 +123,7 @@ extension MD5: Updatable {
         }
         }
 
 
         var processedBytes = 0
         var processedBytes = 0
-        for chunk in BytesSequence(chunkSize: MD5.blockSize, data: self.accumulated) {
+        for chunk in self.accumulated.batched(by: MD5.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= MD5.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= MD5.blockSize) {
                 self.process(block: chunk, currentHash: &self.accumulatedHash)
                 self.process(block: chunk, currentHash: &self.accumulatedHash)
                 processedBytes += chunk.count
                 processedBytes += chunk.count

+ 1 - 1
Sources/CryptoSwift/SHA1.swift

@@ -107,7 +107,7 @@ extension SHA1: Updatable {
         }
         }
 
 
         var processedBytes = 0
         var processedBytes = 0
-        for chunk in BytesSequence(chunkSize: SHA1.blockSize, data: self.accumulated) {
+        for chunk in self.accumulated.batched(by: SHA1.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize) {
                 self.process(block: chunk, currentHash: &self.accumulatedHash)
                 self.process(block: chunk, currentHash: &self.accumulatedHash)
                 processedBytes += chunk.count
                 processedBytes += chunk.count

+ 1 - 1
Sources/CryptoSwift/SHA2.swift

@@ -273,7 +273,7 @@ extension SHA2: Updatable {
         }
         }
 
 
         var processedBytes = 0
         var processedBytes = 0
-        for chunk in BytesSequence(chunkSize: self.blockSize, data: self.accumulated) {
+        for chunk in self.accumulated.batched(by: self.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= self.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= self.blockSize) {
                 switch self.variant {
                 switch self.variant {
                 case .sha224, .sha256:
                 case .sha224, .sha256:

+ 1 - 1
Sources/CryptoSwift/SHA3.swift

@@ -257,7 +257,7 @@ extension SHA3: Updatable {
         }
         }
 
 
         var processedBytes = 0
         var processedBytes = 0
-        for chunk in BytesSequence(chunkSize: self.blockSize, data: self.accumulated) {
+        for chunk in self.accumulated.batched(by: self.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= self.blockSize) {
             if (isLast || (self.accumulated.count - processedBytes) >= self.blockSize) {
                 self.process(block: chunk.toUInt64Array(), currentHash: &self.accumulatedHash)
                 self.process(block: chunk.toUInt64Array(), currentHash: &self.accumulatedHash)
                 processedBytes += chunk.count
                 processedBytes += chunk.count

+ 6 - 2
Sources/CryptoSwift/UInt64+Extension.swift

@@ -9,13 +9,17 @@
 /** array of bytes */
 /** array of bytes */
 extension UInt64 {
 extension UInt64 {
 
 
+//    init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
+//        self = 0
+//    }
+
     @_specialize(ArraySlice<UInt8>)
     @_specialize(ArraySlice<UInt8>)
-    init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
+    init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.IndexDistance == Int, T.Index == Int {
         self = UInt64(bytes: bytes, fromIndex: bytes.startIndex)
         self = UInt64(bytes: bytes, fromIndex: bytes.startIndex)
     }
     }
 
 
     @_specialize(ArraySlice<UInt8>)
     @_specialize(ArraySlice<UInt8>)
-    init<T: Collection>(bytes: T, fromIndex index: T.Index) where T.Iterator.Element == UInt8, T.Index == Int {
+    init<T: Collection>(bytes: T, fromIndex index: T.Index) where T.Iterator.Element == UInt8, T.IndexDistance == Int, T.Index == Int {
         let val0 = UInt64(bytes[index.advanced(by: 0)]) << 56
         let val0 = UInt64(bytes[index.advanced(by: 0)]) << 56
         let val1 = UInt64(bytes[index.advanced(by: 1)]) << 48
         let val1 = UInt64(bytes[index.advanced(by: 1)]) << 48
         let val2 = UInt64(bytes[index.advanced(by: 2)]) << 40
         let val2 = UInt64(bytes[index.advanced(by: 2)]) << 40

+ 11 - 10
Sources/CryptoSwift/Updatable.swift

@@ -11,18 +11,19 @@
 public protocol Updatable {
 public protocol Updatable {
     /// Update given bytes in chunks.
     /// Update given bytes in chunks.
     ///
     ///
-    /// - parameter bytes: Bytes to process
-    /// - parameter isLast: (Optional) Given chunk is the last one. No more updates after this call.
+    /// - parameter bytes: Bytes to process.
+    /// - parameter isLast: Indicate if given chunk is the last one. No more updates after this call.
     /// - returns: Processed data or empty array.
     /// - returns: Processed data or empty array.
     mutating func update<T: Collection>(withBytes bytes: T, isLast: Bool) throws -> Array<UInt8> where T.Iterator.Element == UInt8
     mutating func update<T: Collection>(withBytes bytes: T, isLast: Bool) throws -> Array<UInt8> where T.Iterator.Element == UInt8
 
 
     /// Update given bytes in chunks.
     /// Update given bytes in chunks.
     ///
     ///
-    /// - parameter bytes: Bytes to process
-    /// - parameter isLast: (Optional) Given chunk is the last one. No more updates after this call.
-    /// - parameter output: Resulting data
-    /// - returns: Processed data or empty array.
-    mutating func update<T: Collection>(withBytes bytes: T, isLast: Bool, output: (Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8
+    /// - Parameters:
+    ///   - bytes: Bytes to process.
+    ///   - isLast: Indicate if given chunk is the last one. No more updates after this call.
+    ///   - output: Resulting bytes callback.
+    /// - Returns: Processed data or empty array.
+    mutating func update<T: Collection>(withBytes bytes: T, isLast: Bool, output: (_ bytes: Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8
 
 
     /// Finish updates. This may apply padding.
     /// Finish updates. This may apply padding.
     /// - parameter bytes: Bytes to process
     /// - parameter bytes: Bytes to process
@@ -33,12 +34,12 @@ public protocol Updatable {
     /// - parameter bytes: Bytes to process
     /// - parameter bytes: Bytes to process
     /// - parameter output: Resulting data
     /// - parameter output: Resulting data
     /// - returns: Processed data.
     /// - returns: Processed data.
-    mutating func finish<T: Collection>(withBytes bytes: T, output: (Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8
+    mutating func finish<T: Collection>(withBytes bytes: T, output: (_ bytes: Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8
 }
 }
 
 
 extension Updatable {
 extension Updatable {
 
 
-    mutating public func update<T: Collection>(withBytes bytes: T, isLast: Bool = false, output: (Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8 {
+    mutating public func update<T: Collection>(withBytes bytes: T, isLast: Bool = false, output: (_ bytes: Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8 {
         let processed = try self.update(withBytes: bytes, isLast: isLast)
         let processed = try self.update(withBytes: bytes, isLast: isLast)
         if (!processed.isEmpty) {
         if (!processed.isEmpty) {
             output(processed)
             output(processed)
@@ -53,7 +54,7 @@ extension Updatable {
         return try self.update(withBytes: [], isLast: true)
         return try self.update(withBytes: [], isLast: true)
     }
     }
 
 
-    mutating public func finish<T: Collection>(withBytes bytes: T, output: (Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8 {
+    mutating public func finish<T: Collection>(withBytes bytes: T, output: (_ bytes: Array<UInt8>) -> Void) throws where T.Iterator.Element == UInt8 {
         let processed = try self.update(withBytes: bytes, isLast: true)
         let processed = try self.update(withBytes: bytes, isLast: true)
         if (!processed.isEmpty) {
         if (!processed.isEmpty) {
             output(processed)
             output(processed)

+ 14 - 1
Sources/CryptoSwift/Utils.swift

@@ -53,13 +53,26 @@ func reversed(_ uint32: UInt32) -> UInt32 {
 }
 }
 
 
 func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
 func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
+    return xor(a.suffix(from: a.startIndex), b.suffix(from: b.startIndex))
+}
+
+func xor(_ a: Array<UInt8>, _ b: ArraySlice<UInt8>) -> Array<UInt8> {
+    return xor(a.suffix(from: a.startIndex), b.suffix(from: b.startIndex))
+}
+
+func xor(_ a: ArraySlice<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
+    return xor(a.suffix(from: a.startIndex), b.suffix(from: b.startIndex))
+}
+
+func xor(_ a: ArraySlice<UInt8>, _ b: ArraySlice<UInt8>) -> Array<UInt8> {
     var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
     var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
     for i in 0 ..< xored.count {
     for i in 0 ..< xored.count {
-        xored[i] = a[i] ^ b[i]
+        xored[xored.startIndex.advanced(by: i)] = a[a.startIndex.advanced(by: i)] ^ b[b.startIndex.advanced(by: i)]
     }
     }
     return xored
     return xored
 }
 }
 
 
+
 /**
 /**
  ISO/IEC 9797-1 Padding method 2.
  ISO/IEC 9797-1 Padding method 2.
  Add a single bit with value 1 to the end of the data.
  Add a single bit with value 1 to the end of the data.

+ 14 - 0
Tests/CryptoSwiftTests/AESTests.swift

@@ -306,6 +306,20 @@ final class AESTests: XCTestCase {
         let decrypted = try? aes2.decrypt(encrypted)
         let decrypted = try? aes2.decrypt(encrypted)
         XCTAssertTrue(decrypted! != plaintext, "failed")
         XCTAssertTrue(decrypted! != plaintext, "failed")
     }
     }
+
+    // https://github.com/krzyzanowskim/CryptoSwift/issues/394
+    func testOpenSSL_394() {
+        let plaintext = Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8)
+        let key = Array("passwordpassword".utf8).md5() // -md md5
+        let iv  = Array("drowssapdrowssap".utf8) // -iv 64726f777373617064726f7773736170
+        let aes = try! AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()) // -aes-128-cbc
+        let ciphertext = try! aes.encrypt(plaintext) // enc
+
+        // $ echo -n "Nullam quis risus eget urna mollis ornare vel eu leo." | openssl enc -aes-128-cbc -md md5 -nosalt -iv 64726f777373617064726f7773736170 -pass pass:passwordpassword -base64
+        // cij+965z2Xqoj9tIHgtA72ZPfv5sxnt76vwkIt1CodYY313oa7mr0pSc5o++g0CX
+        // YczxK2fGIa84xtwDtRMwBQ==
+        XCTAssertEqual(ciphertext.toBase64(), "cij+965z2Xqoj9tIHgtA72ZPfv5sxnt76vwkIt1CodYY313oa7mr0pSc5o++g0CXYczxK2fGIa84xtwDtRMwBQ==")
+    }
 }
 }
 
 
 #if !CI
 #if !CI