Browse Source

AES decryption

Marcin Krzyżanowski 10 years ago
parent
commit
4b49441c88

+ 52 - 8
CryptoSwift/AES.swift

@@ -127,18 +127,17 @@ public class AES {
         }
         }
         
         
         let blocks = message.bytes().chunks(AES.blockSizeBytes())
         let blocks = message.bytes().chunks(AES.blockSizeBytes())
-        let out = blockMode.processBlocks(blocks, iv: self.iv?.bytes(), cipher: encryptBlock)
+        let out = blockMode.encryptBlocks(blocks, iv: self.iv?.bytes(), cipher: encryptBlock)
         return out == nil ? nil : NSData.withBytes(out!)
         return out == nil ? nil : NSData.withBytes(out!)
     }
     }
     
     
     private func encryptBlock(block:[Byte]) -> [Byte] {
     private func encryptBlock(block:[Byte]) -> [Byte] {
         let expandedKey = expandKey()
         let expandedKey = expandKey()
         
         
-        var state:[[Byte]] = [[Byte]](count: 4, repeatedValue: [Byte](count: variant.Nb, repeatedValue: 0))
-        var idx = 0
+        var state:[[Byte]] = [[Byte]](count: variant.Nb, repeatedValue: [Byte](count: variant.Nb, repeatedValue: 0))
         for (i, row) in enumerate(state) {
         for (i, row) in enumerate(state) {
             for (j, val) in enumerate(row) {
             for (j, val) in enumerate(row) {
-                state[j][i] = block[idx++]
+                state[j][i] = block[i * row.count + j]
             }
             }
         }
         }
         
         
@@ -165,10 +164,55 @@ public class AES {
         return out
         return out
     }
     }
     
     
-    func decrypt(message:NSData) -> NSData? {
-        //TODO: to do
-        assertionFailure("Not implemented")
-        return nil
+    public func decrypt(message:NSData) -> NSData? {
+        if (message.length % AES.blockSizeBytes() != 0) {
+            // 128 bit block exceeded
+            assertionFailure("AES 128-bit block exceeded!")
+            return nil
+        }
+        
+        let blocks = message.bytes().chunks(AES.blockSizeBytes())
+        var out:[Byte]?
+        if (blockMode == .CFB) {
+            // CFB uses encryptBlock to decrypt
+            out = blockMode.decryptBlocks(blocks, iv: self.iv?.bytes(), cipher: encryptBlock)
+        } else {
+            out = blockMode.decryptBlocks(blocks, iv: self.iv?.bytes(), cipher: decryptBlock)
+        }
+        return out == nil ? nil : NSData.withBytes(out!)
+    }
+    
+    private func decryptBlock(block:[Byte]) -> [Byte] {
+        let expandedKey = expandKey()
+        
+        var state:[[Byte]] = [[Byte]](count: variant.Nb, repeatedValue: [Byte](count: variant.Nb, repeatedValue: 0))
+        for (i, row) in enumerate(state) {
+            for (j, val) in enumerate(row) {
+                state[j][i] = block[i * row.count + j]
+            }
+        }
+        
+        state = addRoundKey(state,expandedKey, variant.Nr)
+        
+        for roundCount in reverse(1..<variant.Nr) {
+            state = invShiftRows(state)
+            state = invSubBytes(state)
+            state = addRoundKey(state, expandedKey, roundCount)
+            state = invMixColumns(state)
+        }
+        
+        state = invShiftRows(state)
+        state = invSubBytes(state)
+        state = addRoundKey(state, expandedKey, 0)
+        
+        var out:[Byte] = [Byte]()
+        for i in 0..<state.count {
+            for j in 0..<state[0].count {
+                out.append(state[j][i])
+            }
+        }
+        
+        return out
     }
     }
     
     
     public func expandKey() -> [Byte] {
     public func expandKey() -> [Byte] {

+ 83 - 7
CryptoSwift/CipherBlockMode.swift

@@ -20,7 +20,7 @@ public enum CipherBlockMode {
     
     
     :returns: encrypted bytes
     :returns: encrypted bytes
     */
     */
-    func processBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+    func encryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
         
         
         // if IV is not available, fallback to plain
         // if IV is not available, fallback to plain
         var finalBlockMode:CipherBlockMode = self
         var finalBlockMode:CipherBlockMode = self
@@ -30,11 +30,28 @@ public enum CipherBlockMode {
         
         
         switch (finalBlockMode) {
         switch (finalBlockMode) {
         case CBC:
         case CBC:
-            return CBCMode.processBlocks(blocks, iv: iv, cipher: cipher)
+            return CBCMode.encryptBlocks(blocks, iv: iv, cipher: cipher)
         case CFB:
         case CFB:
-            return CFBMode.processBlocks(blocks, iv: iv, cipher: cipher)
+            return CFBMode.encryptBlocks(blocks, iv: iv, cipher: cipher)
         case Plain:
         case Plain:
-            return PlainMode.processBlocks(blocks, cipher: cipher)
+            return PlainMode.encryptBlocks(blocks, cipher: cipher)
+        }
+    }
+    
+    func decryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+        // if IV is not available, fallback to plain
+        var finalBlockMode:CipherBlockMode = self
+        if (iv == nil) {
+            finalBlockMode = .Plain
+        }
+        
+        switch (finalBlockMode) {
+        case CBC:
+            return CBCMode.decryptBlocks(blocks, iv: iv, cipher: cipher)
+        case CFB:
+            return CFBMode.decryptBlocks(blocks, iv: iv, cipher: cipher)
+        case Plain:
+            return PlainMode.decryptBlocks(blocks, cipher: cipher)
         }
         }
     }
     }
 }
 }
@@ -43,7 +60,7 @@ public enum CipherBlockMode {
 *  Cipher-block chaining (CBC)
 *  Cipher-block chaining (CBC)
 */
 */
 private struct CBCMode {
 private struct CBCMode {
-    static func processBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+    static func encryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
         
         
         if (iv == nil) {
         if (iv == nil) {
             assertionFailure("CBC require IV")
             assertionFailure("CBC require IV")
@@ -73,13 +90,40 @@ private struct CBCMode {
         }
         }
         return out;
         return out;
     }
     }
+    
+    static func decryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+        if (iv == nil) {
+            assertionFailure("CBC require IV")
+            return nil
+        }
+
+        var out:[Byte]?
+        var lastCiphertext:[Byte] = iv!
+        for (idx,ciphertext) in enumerate(blocks) {
+            if let decrypted = cipher(block: ciphertext) { // decrypt
+                
+                var xored:[Byte] = [Byte](count: lastCiphertext.count, repeatedValue: 0)
+                for i in 0..<ciphertext.count {
+                    xored[i] = lastCiphertext[i] ^ decrypted[i]
+                }
+
+                if (out == nil) {
+                    out = [Byte]()
+                }
+                out = out! + xored
+            }
+            lastCiphertext = ciphertext
+        }
+        
+        return out
+    }
 }
 }
 
 
 /**
 /**
 *  Cipher feedback (CFB)
 *  Cipher feedback (CFB)
 */
 */
 private struct CFBMode {
 private struct CFBMode {
-    static func processBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+    static func encryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
         
         
         if (iv == nil) {
         if (iv == nil) {
             assertionFailure("CFB require IV")
             assertionFailure("CFB require IV")
@@ -106,6 +150,34 @@ private struct CFBMode {
         }
         }
         return out;
         return out;
     }
     }
+    
+    static func decryptBlocks(blocks:[[Byte]], iv:[Byte]?, cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+        if (iv == nil) {
+            assertionFailure("CFB require IV")
+            return nil
+        }
+        
+        var out:[Byte]?
+        var lastCiphertext:[Byte] = iv!
+        for (idx,ciphertext) in enumerate(blocks) {
+            if let decrypted = cipher(block: lastCiphertext) {
+                var xored:[Byte] = [Byte](count: ciphertext.count, repeatedValue: 0)
+                for i in 0..<ciphertext.count {
+                    xored[i] = ciphertext[i] ^ decrypted[i]
+                }
+                lastCiphertext = xored
+                
+                
+                if (out == nil) {
+                    out = [Byte]()
+                }
+                
+                out = out! + xored
+            }
+        }
+        return out;
+    }
+
 }
 }
 
 
 
 
@@ -113,7 +185,7 @@ private struct CFBMode {
 *  Plain mode, don't use it. For debuging purposes only
 *  Plain mode, don't use it. For debuging purposes only
 */
 */
 private struct PlainMode {
 private struct PlainMode {
-    static func processBlocks(blocks:[[Byte]], cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+    static func encryptBlocks(blocks:[[Byte]], cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
         var out:[Byte]?
         var out:[Byte]?
         for (idx,plaintext) in enumerate(blocks) {
         for (idx,plaintext) in enumerate(blocks) {
             if let encrypted = cipher(block: plaintext) {
             if let encrypted = cipher(block: plaintext) {
@@ -127,4 +199,8 @@ private struct PlainMode {
         }
         }
         return out
         return out
     }
     }
+    
+    static func decryptBlocks(blocks:[[Byte]], cipher:(block:[Byte]) -> [Byte]?) -> [Byte]? {
+        return encryptBlocks(blocks, cipher: cipher)
+    }
 }
 }

+ 1 - 16
CryptoSwift/Playground/MyPlayground.playground/section-1.swift

@@ -2,19 +2,4 @@
 
 
 import Foundation
 import Foundation
 
 
-class Foo {
-    class func A() -> Int {
-        return 1
-    }
-
-    // YES
-    //func A() -> Int {
-    //    return 2
-    //}
-    
-    // NO
-    let A:Int = Foo.A()
-}
-
-//Foo.A()
-//Foo().A
+reverse(1..<4)

+ 10 - 4
CryptoSwiftTests/AESTests.swift

@@ -14,7 +14,7 @@ class AESTests: XCTestCase {
     // 128 bit key
     // 128 bit key
     let aesKey:[Byte] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]
     let aesKey:[Byte] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]
 
 
-    func testAES_encode() {
+    func testAES_encrypt() {
         let input:[Byte] = [0x00, 0x11, 0x22, 0x33,
         let input:[Byte] = [0x00, 0x11, 0x22, 0x33,
             0x44, 0x55, 0x66, 0x77,
             0x44, 0x55, 0x66, 0x77,
             0x88, 0x99, 0xaa, 0xbb,
             0x88, 0x99, 0xaa, 0xbb,
@@ -28,12 +28,14 @@ class AESTests: XCTestCase {
         if let aes = AES(key: NSData.withBytes(aesKey), iv: nil, blockMode: .Plain) {
         if let aes = AES(key: NSData.withBytes(aesKey), iv: nil, blockMode: .Plain) {
             let encrypted = aes.encrypt(NSData.withBytes(input))
             let encrypted = aes.encrypt(NSData.withBytes(input))
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
+            let decrypted = aes.decrypt(encrypted!)
+            XCTAssertEqual(decrypted!, NSData.withBytes(input), "decryption failed")
         } else {
         } else {
             XCTAssert(false, "failed")
             XCTAssert(false, "failed")
         }
         }
     }
     }
-    
-    func testAES_encode_cbc() {
+
+    func testAES_encrypt_cbc() {
         let key:[Byte] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
         let key:[Byte] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
         let iv:[Byte] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
         let iv:[Byte] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
         let plaintext:[Byte] = [0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a]
         let plaintext:[Byte] = [0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a]
@@ -43,12 +45,14 @@ class AESTests: XCTestCase {
             XCTAssertTrue(aes.blockMode == .CBC, "Invalid block mode")
             XCTAssertTrue(aes.blockMode == .CBC, "Invalid block mode")
             let encrypted = aes.encrypt(NSData.withBytes(plaintext))
             let encrypted = aes.encrypt(NSData.withBytes(plaintext))
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
+            let decrypted = aes.decrypt(encrypted!)
+            XCTAssertEqual(decrypted!, NSData.withBytes(plaintext), "decryption failed")
         } else {
         } else {
             XCTAssert(false, "failed")
             XCTAssert(false, "failed")
         }
         }
     }
     }
     
     
-    func testAES_encode_cfb() {
+    func testAES_encrypt_cfb() {
         let key:[Byte] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
         let key:[Byte] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
         let iv:[Byte] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
         let iv:[Byte] = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
         let plaintext:[Byte] = [0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a]
         let plaintext:[Byte] = [0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a]
@@ -58,6 +62,8 @@ class AESTests: XCTestCase {
             XCTAssertTrue(aes.blockMode == .CFB, "Invalid block mode")
             XCTAssertTrue(aes.blockMode == .CFB, "Invalid block mode")
             let encrypted = aes.encrypt(NSData.withBytes(plaintext))
             let encrypted = aes.encrypt(NSData.withBytes(plaintext))
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
             XCTAssertEqual(encrypted!, NSData.withBytes(expected), "encryption failed")
+            let decrypted = aes.decrypt(encrypted!)
+            XCTAssertEqual(decrypted!, NSData.withBytes(plaintext), "decryption failed")
         } else {
         } else {
             XCTAssert(false, "failed")
             XCTAssert(false, "failed")
         }
         }

+ 3 - 2
README.md

@@ -21,7 +21,7 @@ Good mood
 - [CRC32](http://en.wikipedia.org/wiki/Cyclic_redundancy_check) (well, kind of hash)
 - [CRC32](http://en.wikipedia.org/wiki/Cyclic_redundancy_check) (well, kind of hash)
 
 
 #####Cipher
 #####Cipher
-- [AES-128, AES-192, AES-256](http://csrc.nist.gov/publications/fips/fips197/fips-197.pd8) (encrypt only)
+- [AES-128, AES-192, AES-256](http://csrc.nist.gov/publications/fips/fips197/fips-197.pd8)
 - [ChaCha20](http://cr.yp.to/chacha/chacha-20080128.pdf)
 - [ChaCha20](http://cr.yp.to/chacha/chacha-20080128.pdf)
 
 
 #####Message authenticators
 #####Message authenticators
@@ -46,7 +46,7 @@ To install CryptoSwift, add it as a submodule to your project (on the top level
 Then, drag the CryptoSwift.xcodeproj file into your Xcode project, and add CryptoSwift.framework as a dependency for your target.
 Then, drag the CryptoSwift.xcodeproj file into your Xcode project, and add CryptoSwift.framework as a dependency for your target.
 
 
 #####iOS and OSX
 #####iOS and OSX
-Bu default project is setup for iOS. You need to switch to OSX SDK manually [see #8](https://github.com/krzyzanowskim/CryptoSwift/issues/8)
+By default project is setup for iOS. You need to switch to OSX SDK manually [see #8](https://github.com/krzyzanowskim/CryptoSwift/issues/8)
 
 
 ##Usage
 ##Usage
 
 
@@ -98,6 +98,7 @@ Working with Ciphers
 	// DECRYPT
 	// DECRYPT
 	let decryptedChaCha20 = Cipher.ChaCha20(setup).decrypt(encryptedData)
 	let decryptedChaCha20 = Cipher.ChaCha20(setup).decrypt(encryptedData)
 	let decryptedAES = Cipher.AES(setup).decrypt(encryptedData)
 	let decryptedAES = Cipher.AES(setup).decrypt(encryptedData)
+	// remember to remove padding if applied
 	
 	
 
 
 using extensions
 using extensions