소스 검색

Incremental encryption with AES (using Cryptor)

Marcin Krzyżanowski 9 년 전
부모
커밋
6cdb92b50b
3개의 변경된 파일29개의 추가작업 그리고 19개의 파일을 삭제
  1. 1 1
      CHANGELOG
  2. 4 3
      CryptoSwiftTests/AESTests.swift
  3. 24 15
      Sources/CryptoSwift/AES.swift

+ 1 - 1
CHANGELOG

@@ -1,6 +1,6 @@
 0.5
 - Added PBKDF2 https://tools.ietf.org/html/rfc2898#section-5.1
-- UpdatableCryptor protocol allows incremental encryption (per block)
+- UpdatableCryptor protocol allows incremental encryption stream of data
 - CryptoSwift.playground
 
 0.4.1

+ 4 - 3
CryptoSwiftTests/AESTests.swift

@@ -86,9 +86,10 @@ final class AESTests: XCTestCase {
 
         var partialEncrypted = [UInt8]()
         var encryptor = aes.makeEncryptor()
-        partialEncrypted.appendContentsOf(try! encryptor.update(withBytes: Array(plaintext[0..<16])))
-        partialEncrypted.appendContentsOf(try! encryptor.finish(withBytes: Array(plaintext[16..<32])))
-        //partialEncrypted.appendContentsOf(try! encryptor.finish())
+        partialEncrypted.appendContentsOf(try! encryptor.update(withBytes: Array(plaintext[0..<8])))
+        partialEncrypted.appendContentsOf(try! encryptor.update(withBytes: Array(plaintext[8..<16])))
+        partialEncrypted.appendContentsOf(try! encryptor.update(withBytes: Array(plaintext[16..<32])))
+        partialEncrypted.appendContentsOf(try! encryptor.finish())
         XCTAssertEqual(encrypted, partialEncrypted, "encryption failed")
     }
 

+ 24 - 15
Sources/CryptoSwift/AES.swift

@@ -393,26 +393,35 @@ extension AES {
 extension AES {
     public struct Encryptor: Cryptor {
         private var worker: BlockModeWorker
-        let padding: Padding
+        private let padding: Padding
+        private var accumulated = [UInt8]()
+        private let paddingRequired: Bool
 
         init(aes: AES) {
             self.padding = aes.padding;
             self.worker = aes.blockMode.worker(aes.iv, cipherOperation: aes.encryptBlock)
+            self.paddingRequired = aes.blockMode.options.contains(.PaddingRequired)
         }
 
         mutating public func update(withBytes bytes:[UInt8], isLast: Bool = false) throws -> [UInt8] {
-            if isLast {
-                let paddedBytes = padding.add(bytes, blockSize: AES.blockSize)
-                var result = [UInt8]()
-                for chunk in paddedBytes.chunks(AES.blockSize) ?? [] {
-                    result.appendContentsOf(worker.encrypt(chunk))
+            self.accumulated += bytes
+
+            if (isLast) {
+                self.accumulated = padding.add(self.accumulated, blockSize: AES.blockSize)
+            }
+
+            //CTR does not require full block therefore work with anything
+            if (!self.paddingRequired || self.accumulated.count >= AES.blockSize) {
+                var encrypted = Array<UInt8>()
+                encrypted.reserveCapacity(self.accumulated.count)
+                for chunk in self.accumulated.chunks(AES.blockSize) {
+                    encrypted += worker.encrypt(chunk)
+                    self.accumulated.removeFirst(chunk.count)
                 }
-                return result
-            } else if bytes.count == 0 {
-                return bytes;
+                return encrypted
             }
 
-            return worker.encrypt(bytes)
+            return []
         }
     }
 }
@@ -421,7 +430,7 @@ extension AES {
 extension AES {
     public struct Decryptor: Cryptor {
         private var worker: BlockModeWorker
-        let padding: Padding
+        private let padding: Padding
 
         init(aes: AES) {
             self.padding = aes.padding;
@@ -466,11 +475,11 @@ extension AES: Cipher {
     public func encrypt(bytes:[UInt8]) throws -> [UInt8] {
         let chunks = bytes.chunks(AES.blockSize)
 
-        var oneTimeCryptor = Encryptor(aes: self)
+        var oneTimeCryptor = self.makeEncryptor()
         var out = [UInt8]()
         out.reserveCapacity(bytes.count)
         for (idx, block) in chunks.enumerate() {
-            out.appendContentsOf(try oneTimeCryptor.update(withBytes: block, isLast: idx == max(0,chunks.count - 1)))
+            out += try oneTimeCryptor.update(withBytes: block, isLast: idx == max(0,chunks.count - 1))
         }
 
         if blockMode.options.contains(.PaddingRequired) && (out.count % AES.blockSize != 0) {
@@ -485,12 +494,12 @@ extension AES: Cipher {
             throw Error.DataPaddingRequired
         }
 
-        var oneTimeCryptor = Decryptor(aes: self)
+        var oneTimeCryptor = self.makeDecryptor()
         let chunks = bytes.chunks(AES.blockSize)
         var out = [UInt8]()
         out.reserveCapacity(bytes.count)
         for (idx,chunk) in chunks.enumerate() {
-            out.appendContentsOf(try oneTimeCryptor.update(withBytes: chunk, isLast: idx == max(0,chunks.count - 1)))
+            out += try oneTimeCryptor.update(withBytes: chunk, isLast: idx == max(0,chunks.count - 1))
         }
         return out
     }