Browse Source

ChaCha20 implements Cryptors protocol.

Marcin Krzyżanowski 9 years ago
parent
commit
502180df10

+ 1 - 0
CryptoSwiftTests/AESTests.swift

@@ -101,6 +101,7 @@ final class AESTests: XCTestCase {
 
             ciphertext += try encryptor.update(withBytes: plaintext.utf8.map({$0}))
             ciphertext += try encryptor.finish()
+            XCTAssertEqual(try aes.encrypt(plaintext.utf8.map({$0})), ciphertext, "encryption failed")
         } catch {
             XCTAssert(false, "\(error)")
         }

+ 2 - 2
CryptoSwiftTests/ChaCha20Tests.swift

@@ -95,7 +95,7 @@ final class ChaCha20Tests: XCTestCase {
             ciphertext += try encryptor.update(withBytes: Array(plaintext[8..<16]))
             ciphertext += try encryptor.update(withBytes: Array(plaintext[16..<32]))
             ciphertext += try encryptor.finish()
-            XCTAssertEqual(try cipher.encrypt(plaintext), ciphertext, "encryption failed")
+            XCTAssertEqual(try ChaCha20(key: key, iv:iv).encrypt(plaintext), ciphertext, "encryption failed")
         } catch {
             XCTFail()
         }
@@ -107,7 +107,7 @@ final class ChaCha20Tests: XCTestCase {
         let message = Array<UInt8>(repeating: 7, count: (1024 * 1024) * 1)
         measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, for: { () -> Void in
             do {
-                let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message)
+                let _ = try ChaCha20(key: key, iv: iv).encrypt(message)
             } catch {
                 XCTFail()
             }

+ 99 - 17
Sources/CryptoSwift/ChaCha20.swift

@@ -13,12 +13,12 @@ final public class ChaCha20: BlockCipher {
         case invalidKeyOrInitializationVector
     }
     
-    static let blockSize = 64 // 512 / 8
+    public static let blockSize = 64 // 512 / 8
     private let stateSize = 16
     private var context:Context?
     
     final private class Context {
-        var input:Array<UInt32> = Array<UInt32>(repeating: 0, count: 16)
+        var input = Array<UInt32>(repeating: 0, count: 16)
         
         deinit {
             for i in 0..<input.count {
@@ -76,20 +76,19 @@ final public class ChaCha20: BlockCipher {
         
         var addPos = 0;
         switch (kbits) {
-        case 256:
-            addPos += 16
-            // sigma
-            ctx.input[0] = 0x61707865 //apxe
-            ctx.input[1] = 0x3320646e //3 dn
-            ctx.input[2] = 0x79622d32 //yb-2
-            ctx.input[3] = 0x6b206574 //k et
-        default:
-            // tau
-            ctx.input[0] = 0x61707865 //apxe
-            ctx.input[1] = 0x3620646e //6 dn
-            ctx.input[2] = 0x79622d31 //yb-1
-            ctx.input[3] = 0x6b206574 //k et
-        break;
+            case 256:
+                addPos += 16
+                // sigma
+                ctx.input[0] = 0x61707865 //apxe
+                ctx.input[1] = 0x3320646e //3 dn
+                ctx.input[2] = 0x79622d32 //yb-2
+                ctx.input[3] = 0x6b206574 //k et
+            default:
+                // tau
+                ctx.input[0] = 0x61707865 //apxe
+                ctx.input[1] = 0x3620646e //6 dn
+                ctx.input[2] = 0x79622d31 //yb-1
+                ctx.input[3] = 0x6b206574 //k et
         }
         
         // 8 - 11
@@ -115,7 +114,7 @@ final public class ChaCha20: BlockCipher {
             throw Error.missingContext
         }
         
-        var c:Array<UInt8> = Array<UInt8>(repeating: 0, count: message.count)
+        var c = Array<UInt8>(repeating: 0, count: message.count)
         
         var cPos:Int = 0
         var mPos:Int = 0
@@ -159,6 +158,89 @@ final public class ChaCha20: BlockCipher {
     }
 }
 
+// MARK: Encryptor
+extension ChaCha20 {
+    public struct Encryptor: UpdatableCryptor {
+        private var accumulated = Array<UInt8>()
+        private let chacha: ChaCha20
+
+        init(chacha: ChaCha20) {
+            self.chacha = chacha
+        }
+
+        mutating public func update(withBytes bytes:Array<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+            self.accumulated += bytes
+
+            var encrypted = Array<UInt8>()
+            encrypted.reserveCapacity(self.accumulated.count)
+            for chunk in self.accumulated.chunks(size: ChaCha20.blockSize) {
+                if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
+                    encrypted += try chacha.encrypt(chunk)
+                    self.accumulated.removeFirst(chunk.count)
+                }
+            }
+            return encrypted
+        }
+    }
+}
+
+// MARK: Decryptor
+extension ChaCha20 {
+    public struct Decryptor: UpdatableCryptor {
+        private var accumulated = Array<UInt8>()
+
+        private var offset: Int = 0
+        private var offsetToRemove: Int = 0
+        private let chacha: ChaCha20
+
+        init(chacha: ChaCha20) {
+            self.chacha = chacha
+        }
+
+        mutating public func update(withBytes bytes:Array<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+            // prepend "offset" number of bytes at the begining
+            if self.offset > 0 {
+                self.accumulated += Array<UInt8>(repeating: 0, count: offset) + bytes
+                self.offsetToRemove = offset
+                self.offset = 0
+            } else {
+                self.accumulated += bytes
+            }
+
+            var plaintext = Array<UInt8>()
+            plaintext.reserveCapacity(self.accumulated.count)
+            for chunk in self.accumulated.chunks(size: ChaCha20.blockSize) {
+                if (isLast || self.accumulated.count >= ChaCha20.blockSize) {
+                    plaintext += try chacha.decrypt(chunk)
+
+                    // remove "offset" from the beginning of first chunk
+                    if self.offsetToRemove > 0 {
+                        plaintext.removeFirst(self.offsetToRemove);
+                        self.offsetToRemove = 0
+                    }
+
+                    self.accumulated.removeFirst(chunk.count)
+                }
+            }
+
+            return plaintext
+        }
+    }
+}
+
+
+// MARK: Cryptors
+extension ChaCha20: Cryptors {
+
+    public func makeEncryptor() -> ChaCha20.Encryptor {
+        return Encryptor(chacha: self)
+    }
+
+    public func makeDecryptor() -> ChaCha20.Decryptor {
+        return Decryptor(chacha: self)
+    }
+}
+
 // MARK: Cipher
 extension ChaCha20: Cipher {
     public func encrypt(_ bytes:Array<UInt8>) throws -> Array<UInt8> {