Эх сурвалжийг харах

Counter (CTR) cipher block mode

Marcin Krzyżanowski 10 жил өмнө
parent
commit
f4088b7759

+ 4 - 3
CryptoSwift/AES.swift

@@ -197,10 +197,11 @@ final public class AES {
         
         let blocks = bytes.chunks(AES.blockSize)
         let out:[UInt8]?
-        if (blockMode == .CFB) {
-            // CFB uses encryptBlock to decrypt
+        switch (blockMode) {
+        case .CFB, .CTR:
+            // CFB, CTR uses encryptBlock to decrypt
             out = blockMode.decryptBlocks(blocks, iv: self.iv, cipherOperation: encryptBlock)
-        } else {
+        default:
             out = blockMode.decryptBlocks(blocks, iv: self.iv, cipherOperation: decryptBlock)
         }
         

+ 56 - 10
CryptoSwift/CipherBlockMode.swift

@@ -18,7 +18,7 @@ private protocol BlockMode {
 }
 
 public enum CipherBlockMode {
-    case ECB, CBC, CFB
+    case ECB, CBC, CFB, CTR
     
     private var mode:BlockMode {
         switch (self) {
@@ -28,6 +28,8 @@ public enum CipherBlockMode {
             return CFBMode()
         case ECB:
             return ECBMode()
+        case CTR:
+            return CTRMode()
         }
     }
     
@@ -55,7 +57,7 @@ public enum CipherBlockMode {
         return finalBlockMode.mode.encryptBlocks(blocks, iv: iv, cipherOperation: cipherOperation)
     }
     
-    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:(block:[UInt8]) -> [UInt8]?) -> [UInt8]? {
+    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
         // if IV is not available, fallback to plain
         var finalBlockMode:CipherBlockMode = self
         if (iv == nil) {
@@ -67,19 +69,18 @@ public enum CipherBlockMode {
 }
 
 /**
-*  Cipher-block chaining (CBC)
+Cipher-block chaining (CBC)
 */
 private struct CBCMode: BlockMode {
-    var needIV:Bool = true
+    let needIV = true
     
     func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
         precondition(blocks.count > 0)
         assert(iv != nil, "CFB require IV")
         if (iv == nil) {
-            return nil;
+            return nil
         }
         
-        
         var out:[UInt8] = [UInt8]()
         out.reserveCapacity(blocks.count * blocks[0].count)
         var prevCiphertext = iv! // for the first time prevCiphertext = iv
@@ -114,10 +115,10 @@ private struct CBCMode: BlockMode {
 }
 
 /**
-*  Cipher feedback (CFB)
+Cipher feedback (CFB)
 */
 private struct CFBMode: BlockMode {
-    var needIV:Bool = true
+    let needIV = true
     
     func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
         assert(iv != nil, "CFB require IV")
@@ -145,10 +146,10 @@ private struct CFBMode: BlockMode {
 
 
 /**
-*  Electronic codebook (ECB)
+Electronic codebook (ECB)
 */
 private struct ECBMode: BlockMode {
-    var needIV:Bool = false
+    let needIV = false
     func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
         var out:[UInt8] = [UInt8]()
         out.reserveCapacity(blocks.count * blocks[0].count)
@@ -164,3 +165,48 @@ private struct ECBMode: BlockMode {
         return encryptBlocks(blocks, iv: iv, cipherOperation: cipherOperation)
     }
 }
+
+/**
+Counter (CTR)
+*/
+private struct CTRMode: BlockMode {
+    let needIV = true
+    
+    private func buildNonce(iv: [UInt8], counter: UInt) -> [UInt8] {
+        let noncePartLen = AES.blockSize / 2
+        let noncePrefix = Array(iv[0..<noncePartLen])
+        let nonceSuffix = arrayOfBytes(counter)
+        
+        var nonce = noncePrefix
+        nonce += nonceSuffix
+        return nonce
+    }
+    
+    func encryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
+        //var counter:UInt = 17940646550795321087
+        var counter:UInt = 0
+        var out:[UInt8] = [UInt8]()
+        out.reserveCapacity(blocks.count * blocks[0].count)
+        for plaintext in blocks {
+            let nonce = buildNonce(iv!, counter: counter++)
+            if let encrypted = cipherOperation(block: nonce) {
+                out.extend(xor(plaintext, b: encrypted))
+            }
+        }
+        return out
+    }
+    
+    func decryptBlocks(blocks:[[UInt8]], iv:[UInt8]?, cipherOperation:CipherOperationOnBlock) -> [UInt8]? {
+        var counter:UInt = 0
+        var out:[UInt8] = [UInt8]()
+        out.reserveCapacity(blocks.count * blocks[0].count)
+        for plaintext in blocks {
+            let nonce = buildNonce(iv!, counter: counter++)
+            if let encrypted = cipherOperation(block: nonce) {
+                out.extend(xor(encrypted, b: plaintext))
+            }
+        }
+        return out
+    }
+
+}

+ 17 - 0
CryptoSwiftTests/AESTests.swift

@@ -86,6 +86,23 @@ final class AESTests: XCTestCase {
         }
     }
     
+    func testAES_encrypt_ctr() {
+        let key:[UInt8] = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
+        let iv:[UInt8] = [0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff]
+        let plaintext:[UInt8] = [0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a]
+        let expected:[UInt8] = [103, 238, 5, 84, 116, 153, 248, 188, 240, 195, 131, 36, 232, 96, 92, 40]
+        
+        if let aes = AES(key: key, iv:iv, blockMode: .CTR) {
+            XCTAssertTrue(aes.blockMode == .CTR, "Invalid block mode")
+            let encrypted = aes.encrypt(plaintext, padding: nil)
+            XCTAssertEqual(encrypted!, expected, "encryption failed")
+            let decrypted = aes.decrypt(encrypted!, padding: nil)
+            XCTAssertEqual(decrypted!, plaintext, "decryption failed")
+        } else {
+            XCTAssert(false, "failed")
+        }
+    }
+    
     func testAES_SubBytes() {
         let input:[[UInt8]] = [[0x00, 0x10, 0x20, 0x30],
             [0x40, 0x50, 0x60, 0x70],

+ 1 - 0
README.md

@@ -32,6 +32,7 @@ Good mood
 - Electronic codebook ([ECB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29))
 - Cipher-block chaining ([CBC](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29))
 - Cipher feedback ([CFB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29))
+- Counter ([CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29))
 
 #####Data padding
 - [PKCS#7](http://tools.ietf.org/html/rfc5652#section-6.3)