Explorar o código

Add GCM Block Mode for use with AES in AES-GCM tandem. #97

- Introduce UInt128.
- BlockModeWorker perform additional call at the end to finalize work.
Marcin Krzyżanowski %!s(int64=7) %!d(string=hai) anos
pai
achega
f4cc13bd0d

+ 11 - 1
CryptoSwift.xcodeproj/project.pbxproj

@@ -33,11 +33,13 @@
 		75211FC220724A10004E41F8 /* Project-Shared.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 75211FB220724A10004E41F8 /* Project-Shared.xcconfig */; };
 		75211FC320724A10004E41F8 /* CryptoSwift-TestHostApp-Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 75211FB320724A10004E41F8 /* CryptoSwift-TestHostApp-Release.xcconfig */; };
 		75211FC420724A10004E41F8 /* CryptoSwift-Test.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 75211FB420724A10004E41F8 /* CryptoSwift-Test.xcconfig */; };
+		7523742D2083C61D0016D662 /* GCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7523742C2083C61C0016D662 /* GCM.swift */; };
 		7529366A20683DFC00195874 /* AEADChaCha20Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7529366920683DFC00195874 /* AEADChaCha20Poly1305.swift */; };
 		753B33011DAB84D600D06422 /* RandomBytesSequenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753B33001DAB84D600D06422 /* RandomBytesSequenceTests.swift */; };
 		754310442050111A003FB1DF /* CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754310432050111A003FB1DF /* CompactMap.swift */; };
 		75482EA41CB310B7001F66A5 /* PBKDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75482EA31CB310B7001F66A5 /* PBKDF.swift */; };
 		754BE46819693E190098E6F3 /* DigestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754BE46719693E190098E6F3 /* DigestTests.swift */; };
+		75563016208BCC6A000E0BE3 /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75563015208BCC6A000E0BE3 /* UInt128.swift */; };
 		755FB1DA199E347D00475437 /* ExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755FB1D9199E347D00475437 /* ExtensionsTest.swift */; };
 		7564F0522072EAEB00CA5A96 /* PBKDFPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F6207290F8006688F8 /* PBKDFPerf.swift */; };
 		7564F0532072EAEB00CA5A96 /* ChaCha20TestsPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7576F6F020728EAB006688F8 /* ChaCha20TestsPerf.swift */; };
@@ -65,6 +67,7 @@
 		7595C1602072E64900EA1A5F /* 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 */; };
+		75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D7AF37208BFB1600D22BEB /* UInt128.swift */; };
 		75EC527B1EE8B73A0048EB3B /* CryptoSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 75EC527A1EE8B6CA0048EB3B /* CryptoSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		75EC527C1EE8B8130048EB3B /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52381EE8B6CA0048EB3B /* AES.swift */; };
 		75EC527D1EE8B8130048EB3B /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52391EE8B6CA0048EB3B /* Array+Extension.swift */; };
@@ -263,6 +266,7 @@
 		75211FB220724A10004E41F8 /* Project-Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Project-Shared.xcconfig"; sourceTree = "<group>"; };
 		75211FB320724A10004E41F8 /* CryptoSwift-TestHostApp-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "CryptoSwift-TestHostApp-Release.xcconfig"; sourceTree = "<group>"; };
 		75211FB420724A10004E41F8 /* CryptoSwift-Test.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "CryptoSwift-Test.xcconfig"; sourceTree = "<group>"; };
+		7523742C2083C61C0016D662 /* GCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GCM.swift; sourceTree = "<group>"; };
 		7529366920683DFC00195874 /* AEADChaCha20Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEADChaCha20Poly1305.swift; sourceTree = "<group>"; };
 		7536A93E207254A000F39140 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
 		753B33001DAB84D600D06422 /* RandomBytesSequenceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomBytesSequenceTests.swift; sourceTree = "<group>"; };
@@ -274,7 +278,7 @@
 		754BE46719693E190098E6F3 /* DigestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestTests.swift; sourceTree = "<group>"; };
 		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>"; };
-		7564F0602072EAEB00CA5A96 /* TestsPerformanceDevice.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestsPerformanceDevice.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		7564F0602072EAEB00CA5A96 /* TestsPerformanceDevice.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = TestsPerformanceDevice.xctest; path = "TestsPerformance-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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>"; };
 		7576F6EB20726319006688F8 /* DigestTestsPerf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestTestsPerf.swift; sourceTree = "<group>"; };
@@ -292,6 +296,7 @@
 		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>"; };
 		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>"; };
 		75EC52381EE8B6CA0048EB3B /* AES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = "<group>"; };
 		75EC52391EE8B6CA0048EB3B /* Array+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = "<group>"; };
 		75EC523A1EE8B6CA0048EB3B /* Authenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Authenticator.swift; sourceTree = "<group>"; };
@@ -583,6 +588,7 @@
 				75EC52731EE8B6CA0048EB3B /* UInt16+Extension.swift */,
 				75EC52741EE8B6CA0048EB3B /* UInt32+Extension.swift */,
 				75EC52751EE8B6CA0048EB3B /* UInt64+Extension.swift */,
+				75D7AF37208BFB1600D22BEB /* UInt128.swift */,
 				75EC52771EE8B6CA0048EB3B /* Updatable.swift */,
 				75EC52781EE8B6CA0048EB3B /* Utils.swift */,
 				75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */,
@@ -600,6 +606,7 @@
 				75EC52421EE8B6CA0048EB3B /* CBC.swift */,
 				75EC52431EE8B6CA0048EB3B /* CFB.swift */,
 				75EC52441EE8B6CA0048EB3B /* CTR.swift */,
+				7523742C2083C61C0016D662 /* GCM.swift */,
 				75EC52451EE8B6CA0048EB3B /* ECB.swift */,
 				75EC52461EE8B6CA0048EB3B /* OFB.swift */,
 				75EC52471EE8B6CA0048EB3B /* PCBC.swift */,
@@ -877,6 +884,7 @@
 				75EC52861EE8B8170048EB3B /* CFB.swift in Sources */,
 				75EC52901EE8B81A0048EB3B /* Collection+Extension.swift in Sources */,
 				0EE73E71204D598100110E11 /* CMAC.swift in Sources */,
+				7523742D2083C61D0016D662 /* GCM.swift in Sources */,
 				E6200E141FB9A7AE00258382 /* HKDF.swift in Sources */,
 				75EC529F1EE8B8230048EB3B /* HMAC.swift in Sources */,
 				75EC52B91EE8B83D0048EB3B /* ZeroPadding.swift in Sources */,
@@ -900,6 +908,7 @@
 				75EC52B41EE8B83D0048EB3B /* UInt32+Extension.swift in Sources */,
 				75EC52911EE8B81A0048EB3B /* Cryptors.swift in Sources */,
 				75EC52881EE8B8170048EB3B /* ECB.swift in Sources */,
+				75563016208BCC6A000E0BE3 /* UInt128.swift in Sources */,
 				75EC52841EE8B8170048EB3B /* BlockModeWorker.swift in Sources */,
 				75EC52A41EE8B8290048EB3B /* Operators.swift in Sources */,
 				75EC529A1EE8B8200048EB3B /* HMAC+Foundation.swift in Sources */,
@@ -923,6 +932,7 @@
 				751EE9781F93996100161FFC /* AES.Cryptors.swift in Sources */,
 				75EC528B1EE8B8170048EB3B /* RandomAccessBlockModeWorker.swift in Sources */,
 				75EC527D1EE8B8130048EB3B /* Array+Extension.swift in Sources */,
+				75D7AF38208BFB1600D22BEB /* UInt128.swift in Sources */,
 				75EC52B31EE8B83D0048EB3B /* UInt16+Extension.swift in Sources */,
 				75EC52A81EE8B8390048EB3B /* PKCS5.swift in Sources */,
 				1467460F2017BB3600DF04ED /* AEAD.swift in Sources */,

+ 12 - 7
Sources/CryptoSwift/AES.Cryptors.swift

@@ -31,14 +31,13 @@ extension AES {
     public struct Encryptor: 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
-        private let paddingRequired: Bool
 
         init(aes: AES) throws {
             padding = aes.padding
             worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.encrypt)
-            paddingRequired = aes.blockMode.options.contains(.paddingRequired)
         }
 
         public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
@@ -58,6 +57,11 @@ extension AES {
             }
             accumulated.removeFirst(processedBytes)
             processedBytesTotalCount += processedBytes
+
+            if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
+                encrypted = finalizingWorker.finalize(encrypt: encrypted.slice)
+            }
+
             return encrypted
         }
     }
@@ -71,7 +75,6 @@ extension AES {
         private let padding: Padding
         private var accumulated = Array<UInt8>()
         private var processedBytesTotalCount: Int = 0
-        private let paddingRequired: Bool
 
         private var offset: Int = 0
         private var offsetToRemove: Int = 0
@@ -80,14 +83,12 @@ extension AES {
             padding = aes.padding
 
             switch aes.blockMode {
-            case .CFB, .OFB, .CTR:
+            case .CFB, .OFB, .CTR, .GCM:
                 // CFB, OFB, CTR uses encryptBlock to decrypt
                 worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.encrypt)
-            default:
+            case .CBC, .ECB, .PCBC:
                 worker = try aes.blockMode.worker(blockSize: AES.blockSize, cipherOperation: aes.decrypt)
             }
-
-            paddingRequired = aes.blockMode.options.contains(.paddingRequired)
         }
 
         public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
@@ -122,6 +123,10 @@ extension AES {
                 plaintext = padding.remove(from: plaintext, blockSize: AES.blockSize)
             }
 
+            if var finalizingWorker = worker as? BlockModeWorkerFinalizing, isLast == true {
+                plaintext = finalizingWorker.finalize(decrypt: plaintext.slice)
+            }
+
             return plaintext
         }
 

+ 1 - 2
Sources/CryptoSwift/BatchedCollection.swift

@@ -36,8 +36,7 @@ struct BatchedCollection<Base: Collection>: Collection {
     let size: Int
     typealias Index = BatchedCollectionIndex<Base>
     private func nextBreak(after idx: Base.Index) -> Base.Index {
-        return base.index(idx, offsetBy: size, limitedBy: base.endIndex)
-            ?? base.endIndex
+        return base.index(idx, offsetBy: size, limitedBy: base.endIndex) ?? base.endIndex
     }
 
     var startIndex: Index {

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

@@ -16,7 +16,14 @@
 typealias CipherOperationOnBlock = (_ block: ArraySlice<UInt8>) -> Array<UInt8>?
 
 public enum BlockMode {
-    case ECB, CBC(iv: Array<UInt8>), PCBC(iv: Array<UInt8>), CFB(iv: Array<UInt8>), OFB(iv: Array<UInt8>), CTR(iv: Array<UInt8>)
+    case ECB
+    case CBC(iv: Array<UInt8>)
+    case PCBC(iv: Array<UInt8>)
+    case CFB(iv: Array<UInt8>)
+    case OFB(iv: Array<UInt8>)
+    case CTR(iv: Array<UInt8>)
+    /// Additional authenticated data (AAD) is optional.
+    case GCM(iv: Array<UInt8>, aad: Array<UInt8>?)
 
     public enum Error: Swift.Error {
         /// Invalid key or IV
@@ -54,6 +61,11 @@ public enum BlockMode {
                 throw Error.invalidInitializationVector
             }
             return CTRModeWorker(iv: iv.slice, cipherOperation: cipherOperation)
+        case let .GCM(iv, aad):
+            if iv.isEmpty {
+                throw Error.invalidInitializationVector
+            }
+            return GCMModeWorker(iv: iv.slice, aad: aad?.slice, cipherOperation: cipherOperation)
         }
     }
 
@@ -71,6 +83,8 @@ public enum BlockMode {
             return .initializationVectorRequired
         case .PCBC:
             return [.initializationVectorRequired, .paddingRequired]
+        case .GCM:
+            return .initializationVectorRequired
         }
     }
 }

+ 8 - 0
Sources/CryptoSwift/BlockMode/BlockModeWorker.swift

@@ -15,6 +15,14 @@
 
 protocol BlockModeWorker {
     var cipherOperation: CipherOperationOnBlock { get }
+
     mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8>
     mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
 }
+
+// TODO: remove and merge with BlockModeWorker
+protocol BlockModeWorkerFinalizing: BlockModeWorker {
+    // Any final calculations, eg. calculate tag
+    mutating func finalize(encrypt ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
+    mutating func finalize(decrypt plaintext: ArraySlice<UInt8>) -> Array<UInt8>
+}

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

@@ -43,9 +43,9 @@ struct CTRModeWorker: RandomAccessBlockModeWorker {
 }
 
 private func buildNonce(_ iv: ArraySlice<UInt8>, counter: UInt64) -> Array<UInt8> {
-    let noncePartLen = AES.blockSize / 2
-    let noncePrefix = Array(iv[iv.startIndex..<iv.startIndex.advanced(by: noncePartLen)])
-    let nonceSuffix = Array(iv[iv.startIndex.advanced(by: noncePartLen)..<iv.startIndex.advanced(by: iv.count)])
+    let noncePartLen = iv.count / 2
+    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 c = UInt64(bytes: nonceSuffix) + counter
     return noncePrefix + c.bytes()
 }

+ 257 - 0
Sources/CryptoSwift/BlockMode/GCM.swift

@@ -0,0 +1,257 @@
+//
+//  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.
+//
+
+//  Galois/Counter Mode (GCM)
+//  https://csrc.nist.gov/publications/detail/sp/800-38d/final
+//  ref: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.694.695&rep=rep1&type=pdf
+//
+
+struct GCMModeWorker: BlockModeWorkerFinalizing {
+    let cipherOperation: CipherOperationOnBlock
+    // GCM nonce is 96-bits by default. It's the most effective length for the IV
+    static let nonceSize = 12
+    // GCM is designed for 128-bit ciphers like AES (but not really for Blowfish). 64-bit mode is not implemented.
+    private let blockSize = 16 // 128 bit
+    private let iv: ArraySlice<UInt8>
+    // 128 bit tag. Other possible tags 4,8,12,13,14,15,16
+    private static let tagSize = 16
+    private var counter: UInt128
+    private let eky0: UInt128 // move to GF?
+    private let h: UInt128    // move to GF?
+    // Additional authenticated data
+    private let aad: ArraySlice<UInt8>?
+
+    // Note: need new worker to reset instance
+    private lazy var gf: GF = {
+        if let aad = aad {
+            return GF(aad: Array(aad), h: h, blockSize: blockSize)
+        }
+        // Empty auth if not specified. AAD is optional.
+        return GF(aad: [UInt8](), h: h, blockSize: blockSize)
+    }()
+
+    init(iv: ArraySlice<UInt8>, aad: ArraySlice<UInt8>?, cipherOperation: @escaping CipherOperationOnBlock) {
+        self.cipherOperation = cipherOperation
+        self.iv = iv
+        self.aad = aad
+        self.h = UInt128(cipherOperation(Array<UInt8>(repeating: 0, count: blockSize).slice)!) // empty block
+        
+        // Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH
+        // counter = GF.ghash(aad: [UInt8](), ciphertext: nonce)
+        if iv.count == GCMModeWorker.nonceSize {
+            counter = makeCounter(nonce: Array(self.iv))
+        } else {
+            counter = GF.ghash(h: h, aad: [UInt8](), ciphertext: Array(iv), blockSize: blockSize)
+        }
+
+        // Set constants
+        eky0 = UInt128(cipherOperation(counter.bytes.slice)!)
+    }
+
+    mutating func encrypt(_ plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+        counter = incrementCounter(counter)
+
+        guard let ekyN = cipherOperation(counter.bytes.slice) else {
+            return Array(plaintext)
+        }
+
+        // plaintext block ^ ek1
+        let ciphertext = xor(plaintext, ekyN) as Array<UInt8>
+
+        // update ghash incrementally
+        gf.ghashUpdate(block: ciphertext)
+
+        return Array(ciphertext)
+    }
+
+    mutating func decrypt(_ ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+        counter = incrementCounter(counter)
+
+        let paddedCiphertext = addPadding(Array(ciphertext), blockSize: blockSize)
+        let paddedAuthData = addPadding([UInt8](), blockSize: blockSize)
+        let ghash = GF.ghash(h: h, aad: paddedAuthData, ciphertext: paddedCiphertext, blockSize: blockSize)
+        let computedT = (ghash ^ eky0).bytes.prefix(GCMModeWorker.tagSize)
+
+        // TODO: validate Tag(T)
+        //        if !GCM.tsCompare(d1: computedT, d2: givenT) {
+        //            throw GCMError.authTagValidation
+        //        }
+
+        guard let ek1 = cipherOperation(counter.bytes.slice) else {
+            return Array(ciphertext)
+        }
+
+        // ciphertext block ^ ek1
+        let plaintext = xor(ciphertext, ek1) as [UInt8]
+        return plaintext
+    }
+
+    mutating func finalize(encrypt ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+        // Calculate MAC tag for a given ciphertext.
+        let ghash = gf.ghashFinish()
+        let tag = Array((ghash ^ eky0).bytes.prefix(GCMModeWorker.tagSize))
+        // Append Tag at the end (arguable, but popular)
+        return Array(ciphertext) + tag
+    }
+
+    func finalize(decrypt plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+        return Array(plaintext)
+    }
+}
+
+private func makeCounter(nonce: Array<UInt8>) -> UInt128 {
+    return UInt128(nonce + [0, 0, 0, 1])
+}
+
+// Successive counter values are generated using the function incr(), which treats the rightmost 32
+// bits of its argument as a nonnegative integer with the least significant bit on the right
+private func incrementCounter(_ counter: UInt128) -> UInt128 {
+    let b = counter.i.b + 1
+    let a = (b == 0 ? counter.i.a + 1 : counter.i.a)
+    return UInt128((a, b))
+}
+
+// If data is not a multiple of block size bytes long then the remainder is zero padded
+// Note: It's similar to ZeroPadding, but it's not the same.
+private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+    if bytes.isEmpty {
+        return Array<UInt8>(repeating: 0, count: blockSize)
+    }
+
+    let remainder = bytes.count % blockSize
+    if remainder == 0 {
+        return bytes
+    }
+
+    let paddingCount = blockSize - remainder
+    if paddingCount > 0 {
+        return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
+    }
+    return bytes
+}
+
+// MARK: - GF
+
+private final class GF {
+    static let r = UInt128(a: 0xE100000000000000, b: 0)
+
+    let blockSize: Int
+    let h: UInt128
+
+    // AAD won't change
+    let aadLength: Int
+
+    // Updated for every consumed block
+    var ciphertextLength: Int
+
+    // Start with 0
+    var x: UInt128
+
+    init(aad: [UInt8], h: UInt128, blockSize: Int) {
+        self.blockSize = blockSize
+        self.aadLength = aad.count
+        self.ciphertextLength = 0
+        self.h = h
+        self.x = 0
+
+        // Calculate for AAD at the begining
+        x = GF.calculateX(aad: aad, x: x, h: h, blockSize: blockSize)
+    }
+
+    @discardableResult
+    func ghashUpdate(block ciphertextBlock: Array<UInt8>) -> UInt128 {
+        ciphertextLength += ciphertextBlock.count
+        x = GF.calculateX(block: addPadding(ciphertextBlock, blockSize: blockSize), x: x, h: h, blockSize: blockSize)
+        return x
+    }
+
+    func ghashFinish() -> UInt128 {
+        // len(A) || len(C)
+        let len = UInt128(a: UInt64(aadLength * 8), b: UInt64(ciphertextLength * 8))
+        x = GF.multiply((x ^ len), h)
+        return x
+    }
+
+    // GHASH. One-time calculation
+    static func ghash(x startx: UInt128 = 0, h: UInt128, aad: Array<UInt8>, ciphertext: Array<UInt8>, blockSize: Int) -> UInt128 {
+        var x = calculateX(aad: aad, x: startx, h: h, blockSize: blockSize)
+            x = calculateX(ciphertext: ciphertext, x: x, h: h, blockSize: blockSize)
+
+        // len(aad) || len(ciphertext)
+        let len = UInt128(a: UInt64(aad.count * 8), b: UInt64(ciphertext.count * 8))
+        x = multiply((x ^ len), h)
+
+        return x
+    }
+
+    // Calculate Ciphertext part, for all blocks
+    // Not used with incremental calculation.
+    private static func calculateX(ciphertext: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+        let pciphertext = addPadding(ciphertext, blockSize: blockSize)
+        let blocksCount = pciphertext.count / blockSize
+
+        var x = startx
+        for i in 0..<blocksCount {
+            let cpos = i * blockSize
+            let block = pciphertext[pciphertext.startIndex.advanced(by: cpos)..<pciphertext.startIndex.advanced(by: cpos + blockSize)]
+            x = calculateX(block: Array(block), x: x, h: h, blockSize: blockSize)
+        }
+        return x
+    }
+
+    // block is expected to be padded with addPadding
+    private static func calculateX(block ciphertextBlock: Array<UInt8>, x: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+        let k = x ^ UInt128(ciphertextBlock)
+        return multiply(k, h)
+    }
+
+    // Calculate AAD part, for all blocks
+    private static func calculateX(aad: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+        let paad = addPadding(aad, blockSize: blockSize)
+        let blocksCount = paad.count / blockSize
+
+        var x = startx
+        for i in 0..<blocksCount {
+            let apos = i * blockSize
+            let k = x ^ UInt128(paad[paad.startIndex.advanced(by: apos)..<paad.startIndex.advanced(by: apos + blockSize)])
+            x = multiply(k, h)
+        }
+
+        return x
+    }
+
+    // Multiplication GF(2^128).
+    private static func multiply(_ x: UInt128, _ y: UInt128) -> UInt128 {
+        var z: UInt128 = 0
+        var v = x
+        var k = UInt128(a: 1 << 63, b: 0)
+
+        for _ in 0..<128 {
+            if y & k == k {
+                z = z ^ v
+            }
+
+            if v & 1 != 1 {
+                v = v >> 1
+            } else {
+                v = (v >> 1) ^ r
+            }
+
+            k = k >> 1
+        }
+
+        return z
+    }
+}

+ 5 - 1
Sources/CryptoSwift/Blowfish.swift

@@ -25,6 +25,8 @@ public final class Blowfish {
         case invalidKeyOrInitializationVector
         /// Invalid IV
         case invalidInitializationVector
+        /// Invalid block mode
+        case invalidBlockMode
     }
 
     public static let blockSize: Int = 8 // 64 bit
@@ -332,8 +334,10 @@ public final class Blowfish {
         switch blockMode {
         case .CFB, .OFB, .CTR:
             decryptWorker = try blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: encrypt)
-        default:
+        case .CBC, .ECB, .PCBC:
             decryptWorker = try blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: decrypt)
+        case .GCM:
+            throw Error.invalidBlockMode
         }
     }
 

+ 88 - 0
Sources/CryptoSwift/UInt128.swift

@@ -0,0 +1,88 @@
+//
+//  UInt128.swift
+//
+//  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.
+//
+
+struct UInt128: Equatable, ExpressibleByIntegerLiteral {
+    let i: (a: UInt64, b: UInt64)
+
+    typealias IntegerLiteralType = UInt64
+
+    init(integerLiteral value: IntegerLiteralType) {
+        self = UInt128(value)
+    }
+
+    init(_ raw: Array<UInt8>) {
+        self = raw.prefix(MemoryLayout<UInt128>.stride).withUnsafeBytes({ (rawBufferPointer) -> UInt128 in
+            let arr = rawBufferPointer.bindMemory(to: UInt64.self)
+            return UInt128((arr[0].bigEndian, arr[1].bigEndian))
+        })
+    }
+
+    init(_ raw: ArraySlice<UInt8>) {
+        self.init(Array(raw))
+    }
+
+    init(_ i: (a: UInt64, b: UInt64)) {
+        self.i = i
+    }
+
+    init(a: UInt64, b: UInt64) {
+        self.init((a, b))
+    }
+
+    init(_ b: UInt64) {
+        self.init((0, b))
+    }
+
+    // Bytes
+    var bytes: Array<UInt8> {
+        var at = i.a.bigEndian
+        var bt = i.b.bigEndian
+
+        let ar = Data(bytes: &at, count: MemoryLayout.size(ofValue: at))
+        let br = Data(bytes: &bt, count: MemoryLayout.size(ofValue: bt))
+
+        var result = Data()
+        result.append(ar)
+        result.append(br)
+        return result.bytes
+    }
+
+    static func ^(n1: UInt128, n2: UInt128) -> UInt128 {
+        return UInt128((n1.i.a ^ n2.i.a, n1.i.b ^ n2.i.b))
+    }
+
+    static func &(n1: UInt128, n2: UInt128) -> UInt128 {
+        return UInt128((n1.i.a & n2.i.a, n1.i.b & n2.i.b))
+    }
+
+    static func >>(value: UInt128, by: Int) -> UInt128 {
+        var result = value
+        for _ in 0..<by {
+            let a = result.i.a >> 1
+            let b = result.i.b >> 1 + ((result.i.a & 1) << 63)
+            result = UInt128((a, b))
+        }
+        return result
+    }
+
+    // Equatable.
+    static func ==(lhs: UInt128, rhs: UInt128) -> Bool {
+        return lhs.i == rhs.i
+    }
+
+    static func !=(lhs: UInt128, rhs: UInt128) -> Bool {
+        return !(lhs == rhs)
+    }
+}

+ 101 - 0
Tests/Tests/AESTests.swift

@@ -363,6 +363,100 @@ final class AESTests: XCTestCase {
     }
 }
 
+// GCM Test Vectors
+extension AESTests {
+    func testAESGCMTestCase1() {
+        // Test Case 1
+        let key = Array<UInt8>(hex: "0x00000000000000000000000000000000")
+        let iv = Array<UInt8>(hex: "0x000000000000000000000000")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: nil), padding: .noPadding)
+        let encrypted = try! aes.encrypt([UInt8]())
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "58e2fccefa7e3061367f1d57a4e7455a")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase2() {
+        // Test Case 2
+        let key = Array<UInt8>(hex: "0x00000000000000000000000000000000")
+        let plaintext = Array<UInt8>(hex: "0x00000000000000000000000000000000")
+        let iv = Array<UInt8>(hex: "0x000000000000000000000000")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: nil), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "0388dace60b6a392f328c2b971b2fe78")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "ab6e47d42cec13bdf53a67b21257bddf")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase3() {
+        // Test Case 3
+        let key = Array<UInt8>(hex: "0xfeffe9928665731c6d6a8f9467308308")
+        let plaintext = Array<UInt8>(hex: "0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255")
+        let iv = Array<UInt8>(hex: "0xcafebabefacedbaddecaf888")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: nil), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "0x42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "0x4d5c2af327cd64a62cf35abd2ba6fab4")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase4() {
+        // Test Case 4
+        let key = Array<UInt8>(hex: "0xfeffe9928665731c6d6a8f9467308308")
+        let plaintext = Array<UInt8>(hex: "0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
+        let iv = Array<UInt8>(hex: "0xcafebabefacedbaddecaf888")
+        let auth = Array<UInt8>(hex: "0xfeedfacedeadbeeffeedfacedeadbeefabaddad2")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: auth), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "0x42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "0x5bc94fbc3221a5db94fae95ae7121a47")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase5() {
+        // Test Case 5
+        let key = Array<UInt8>(hex: "0xfeffe9928665731c6d6a8f9467308308")
+        let plaintext = Array<UInt8>(hex: "0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
+        let iv = Array<UInt8>(hex: "0xcafebabefacedbad")
+        let auth = Array<UInt8>(hex: "0xfeedfacedeadbeeffeedfacedeadbeefabaddad2")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: auth), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "0x61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "0x3612d2e79e3b0785561be14aaca2fccb")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase6() {
+        // Test Case 6
+        let key = Array<UInt8>(hex: "0xfeffe9928665731c6d6a8f9467308308")
+        let plaintext = Array<UInt8>(hex: "0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
+        let iv = Array<UInt8>(hex: "0x9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b")
+        let auth = Array<UInt8>(hex: "0xfeedfacedeadbeeffeedfacedeadbeefabaddad2")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: auth), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "0x8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "0x619cc5aefffe0bfa462af43c1699d050")) // T (128-bit)
+    }
+
+    func testAESGCMTestCase7() {
+        // Test Case 7
+        let key = Array<UInt8>(hex: "0x000000000000000000000000000000000000000000000000")
+        let plaintext = Array<UInt8>(hex: "")
+        let iv = Array<UInt8>(hex: "0x000000000000000000000000")
+
+        let aes = try! AES(key: key, blockMode: .GCM(iv: iv, aad: nil), padding: .noPadding)
+        let encrypted = try! aes.encrypt(plaintext)
+
+        XCTAssertEqual(Array(encrypted.prefix(encrypted.endIndex - 16)), [UInt8](hex: "")) // C
+        XCTAssertEqual(Array(encrypted.suffix(16)), [UInt8](hex: "0xcd33b28ac773f74ba00ed1f312572435")) // T (128-bit)
+    }
+}
+
 extension AESTests {
     static func allTests() -> [(String, (AESTests) -> () -> Void)] {
         let tests = [
@@ -388,6 +482,13 @@ extension AESTests {
             ("testIssue394", testIssue394),
             ("testIssue411", testIssue411),
             ("testAESWithWrongKey", testAESWithWrongKey),
+            ("testAESGCMTestCase1", testAESGCMTestCase1),
+            ("testAESGCMTestCase2", testAESGCMTestCase2),
+            ("testAESGCMTestCase3", testAESGCMTestCase3),
+            ("testAESGCMTestCase4", testAESGCMTestCase4),
+            ("testAESGCMTestCase5", testAESGCMTestCase5),
+            ("testAESGCMTestCase6", testAESGCMTestCase6),
+            ("testAESGCMTestCase7", testAESGCMTestCase7),
         ]
         return tests
     }

+ 11 - 2
Tests/Tests/PaddingTests.swift

@@ -44,7 +44,7 @@ final class PaddingTests: XCTestCase {
         XCTAssertEqual(clean, input, "PKCS7 failed")
     }
 
-    func testZeroPadding() {
+    func testZeroPadding1() {
         let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9]
         let expected: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0]
         let padding = ZeroPadding()
@@ -52,10 +52,19 @@ final class PaddingTests: XCTestCase {
         XCTAssertEqual(padding.remove(from: padding.add(to: input, blockSize: 16), blockSize: 16), input, "ZeroPadding failed")
     }
 
+    func testZeroPadding2() {
+        let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
+        let expected: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+        let padding = ZeroPadding()
+        XCTAssertEqual(padding.add(to: input, blockSize: 16), expected, "ZeroPadding failed")
+        XCTAssertEqual(padding.remove(from: padding.add(to: input, blockSize: 16), blockSize: 16), input, "ZeroPadding failed")
+    }
+
     static let allTests = [
         ("testPKCS7_0", testPKCS7_0),
         ("testPKCS7_1", testPKCS7_1),
         ("testPKCS7_2", testPKCS7_2),
-        ("testZeroPadding", testZeroPadding),
+        ("testZeroPadding1", testZeroPadding1),
+        ("testZeroPadding2", testZeroPadding2),
     ]
 }

+ 9 - 0
config/CryptoSwift-Debug.xcconfig

@@ -18,6 +18,15 @@ OTHER_SWIFT_FLAGS = -Xfrontend -debug-time-function-bodies
 
 SWIFT_COMPILATION_MODE = wholemodule
 
+// Swift Optimization Level
+//
+// * *None:* Compile without any optimization. [-Onone]
+// * *Optimize for Speed:* [-O]
+// * *Optimize for Size:* [-Osize]
+// * *Whole Module Optimization:* [-O -whole-module-optimization]
+
+SWIFT_OPTIMIZATION_LEVEL = -Onone
+
 
 
 // Exclusive Access to Memory

+ 10 - 1
config/Tests-Debug.xcconfig

@@ -25,4 +25,13 @@ LLVM_LTO = NO
 
 
 
-METAL_ENABLE_DEBUG_INFO = YES
+METAL_ENABLE_DEBUG_INFO = YES
+
+// Swift Optimization Level
+//
+// * *None:* Compile without any optimization. [-Onone]
+// * *Optimize for Speed:* [-O]
+// * *Optimize for Size:* [-Osize]
+// * *Whole Module Optimization:* [-O -whole-module-optimization]
+
+SWIFT_OPTIMIZATION_LEVEL = -Onone