Forráskód Böngészése

Add Rabbit stream cipher

Dima Kalachov 9 éve
szülő
commit
64f7158568

+ 24 - 0
CryptoSwift.xcodeproj/project.pbxproj

@@ -42,6 +42,15 @@
 		5596BDE31BC8F384007E38D5 /* Generics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758C764019B61AE500653BC6 /* Generics.swift */; };
 		5596BDE41BC8F384007E38D5 /* IntegerConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75BE4EB01B1E4A9F007A2B57 /* IntegerConvertible.swift */; };
 		5596BDE51BC8F384007E38D5 /* BytesSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D63F741BB711270041579B /* BytesSequence.swift */; };
+		674A73671BF4E08C00866C5B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674A73661BF4E08C00866C5B /* Rabbit.swift */; };
+		674A73681BF4E08C00866C5B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674A73661BF4E08C00866C5B /* Rabbit.swift */; };
+		674A73691BF4E08C00866C5B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674A73661BF4E08C00866C5B /* Rabbit.swift */; };
+		674A736A1BF4E08C00866C5B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674A73661BF4E08C00866C5B /* Rabbit.swift */; };
+		674A736F1BF5D85B00866C5B /* RabbitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674A736E1BF5D85B00866C5B /* RabbitTests.swift */; };
+		6764D7D31BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */; };
+		6764D7D41BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */; };
+		6764D7D51BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */; };
+		6764D7D61BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */; };
 		75100F8F19B0BC890005C5F5 /* Poly1305Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75100F8E19B0BC890005C5F5 /* Poly1305Tests.swift */; };
 		75164E4919AD30AC00737F30 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75164E4819AD30AC00737F30 /* Utils.swift */; };
 		751C5C3D19B26B000094C75D /* Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 751C5C3C19B26B000094C75D /* Poly1305.swift */; };
@@ -245,6 +254,9 @@
 
 /* Begin PBXFileReference section */
 		5596BDBB1BC8F220007E38D5 /* CryptoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CryptoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		674A73661BF4E08C00866C5B /* Rabbit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rabbit.swift; sourceTree = "<group>"; };
+		674A736E1BF5D85B00866C5B /* RabbitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RabbitTests.swift; sourceTree = "<group>"; };
+		6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rabbit+Foundation.swift"; sourceTree = "<group>"; };
 		750A545F1992D2680017DA75 /* MD5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = "<group>"; };
 		75100F8E19B0BC890005C5F5 /* Poly1305Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poly1305Tests.swift; sourceTree = "<group>"; };
 		75153D4119AA3C7900750381 /* SHA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA2.swift; sourceTree = "<group>"; };
@@ -386,6 +398,7 @@
 				75153D4119AA3C7900750381 /* SHA2.swift */,
 				757EF7F419AAA82400586276 /* CRC.swift */,
 				75EB380019ABDD710002375A /* ChaCha20.swift */,
+				674A73661BF4E08C00866C5B /* Rabbit.swift */,
 				751C5C3C19B26B000094C75D /* Poly1305.swift */,
 				758A94251A65AEB100E46135 /* HMAC.swift */,
 				75A74B261A1FF6B2004419F1 /* AES.swift */,
@@ -425,6 +438,7 @@
 				758A94271A65C59200E46135 /* HMACTests.swift */,
 				757DA2541A4ED408002BA3EF /* AESTests.swift */,
 				757DA2581A4ED4D7002BA3EF /* ChaCha20Tests.swift */,
+				674A736E1BF5D85B00866C5B /* RabbitTests.swift */,
 				755FB1D9199E347D00475437 /* ExtensionsTest.swift */,
 				757DA2521A4ED0A4002BA3EF /* PaddingTests.swift */,
 				756BFDCA1A82B87300B9D9A4 /* Bridging.h */,
@@ -447,6 +461,7 @@
 				75FB9C951BB8A4BD009CAFC5 /* AES+Foundation.swift */,
 				75FB9C961BB8A4BD009CAFC5 /* Array<UInt8>+Foundation.swift */,
 				75FB9C971BB8A4BD009CAFC5 /* ChaCha20+Foundation.swift */,
+				6764D7D21BF62E16000FE52E /* Rabbit+Foundation.swift */,
 				75FB9C981BB8A4BD009CAFC5 /* NSData+Extension.swift */,
 				75FB9C991BB8A4BD009CAFC5 /* Utils+Foundation.swift */,
 				759B0A9B1BCDAB1200AF902E /* String+FoundationExtension.swift */,
@@ -674,6 +689,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				6764D7D41BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */,
 				5596BDD01BC8F384007E38D5 /* MD5.swift in Sources */,
 				5596BDD91BC8F384007E38D5 /* IntExtension.swift in Sources */,
 				5596BDC61BC8F384007E38D5 /* NSData+Extension.swift in Sources */,
@@ -705,6 +721,7 @@
 				5596BDD41BC8F384007E38D5 /* ChaCha20.swift in Sources */,
 				5596BDCF1BC8F384007E38D5 /* HashProtocol.swift in Sources */,
 				5596BDD71BC8F384007E38D5 /* AES.swift in Sources */,
+				674A73681BF4E08C00866C5B /* Rabbit.swift in Sources */,
 				5596BDDF1BC8F384007E38D5 /* CSArrayType+Extensions.swift in Sources */,
 				5596BDE31BC8F384007E38D5 /* Generics.swift in Sources */,
 				5596BDE01BC8F384007E38D5 /* String+Extension.swift in Sources */,
@@ -727,6 +744,7 @@
 				7539E32B1B3B4C6B0037F4E1 /* HMAC.swift in Sources */,
 				75EB380119ABDD710002375A /* ChaCha20.swift in Sources */,
 				75FB9C9A1BB8A4BD009CAFC5 /* AES+Foundation.swift in Sources */,
+				674A73671BF4E08C00866C5B /* Rabbit.swift in Sources */,
 				75A663A61AA0CAD00052110B /* Padding.swift in Sources */,
 				75164E4919AD30AC00737F30 /* Utils.swift in Sources */,
 				75D63F751BB711270041579B /* BytesSequence.swift in Sources */,
@@ -745,6 +763,7 @@
 				757EF7F519AAA82400586276 /* CRC.swift in Sources */,
 				75D94E2619B60C4F007CB2A4 /* UInt32Extension.swift in Sources */,
 				759B0A9C1BCDAB1200AF902E /* String+FoundationExtension.swift in Sources */,
+				6764D7D31BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */,
 				75A74B271A1FF6B2004419F1 /* AES.swift in Sources */,
 				75D63F771BB840050041579B /* ArraySlice<UInt8>+Bytes.swift in Sources */,
 				754C30B71AA13BC000E6FFA4 /* PKCS7.swift in Sources */,
@@ -765,6 +784,7 @@
 				754BE46819693E190098E6F3 /* HashTests.swift in Sources */,
 				757DA2591A4ED4D7002BA3EF /* ChaCha20Tests.swift in Sources */,
 				755FB1DA199E347D00475437 /* ExtensionsTest.swift in Sources */,
+				674A736F1BF5D85B00866C5B /* RabbitTests.swift in Sources */,
 				757DA2531A4ED0A4002BA3EF /* PaddingTests.swift in Sources */,
 				757DA2551A4ED408002BA3EF /* AESTests.swift in Sources */,
 			);
@@ -785,6 +805,7 @@
 				75D6149C1BD844F2001358B2 /* HMAC.swift in Sources */,
 				75D6149D1BD844F2001358B2 /* ChaCha20.swift in Sources */,
 				75D6149E1BD844F2001358B2 /* AES+Foundation.swift in Sources */,
+				674A73691BF4E08C00866C5B /* Rabbit.swift in Sources */,
 				75D6149F1BD844F2001358B2 /* Padding.swift in Sources */,
 				75D614A01BD844F2001358B2 /* Utils.swift in Sources */,
 				75D614A11BD844F2001358B2 /* BytesSequence.swift in Sources */,
@@ -803,6 +824,7 @@
 				75D614AE1BD844F2001358B2 /* CRC.swift in Sources */,
 				75D614AF1BD844F2001358B2 /* UInt32Extension.swift in Sources */,
 				75D614B01BD844F2001358B2 /* String+FoundationExtension.swift in Sources */,
+				6764D7D51BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */,
 				75D614B11BD844F2001358B2 /* AES.swift in Sources */,
 				75D614B21BD844F2001358B2 /* ArraySlice<UInt8>+Bytes.swift in Sources */,
 				75D614B31BD844F2001358B2 /* PKCS7.swift in Sources */,
@@ -817,6 +839,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				6764D7D61BF62E16000FE52E /* Rabbit+Foundation.swift in Sources */,
 				75DF77481BC8EB59006E9520 /* UInt64Extension.swift in Sources */,
 				75DF77491BC8EB59006E9520 /* Poly1305.swift in Sources */,
 				75DF774A1BC8EB59006E9520 /* Hash.swift in Sources */,
@@ -848,6 +871,7 @@
 				75DF77641BC8EB59006E9520 /* AES.swift in Sources */,
 				75DF77651BC8EB59006E9520 /* ArraySlice<UInt8>+Bytes.swift in Sources */,
 				75DF77661BC8EB59006E9520 /* PKCS7.swift in Sources */,
+				674A736A1BF4E08C00866C5B /* Rabbit.swift in Sources */,
 				75DF77671BC8EB59006E9520 /* Authenticator.swift in Sources */,
 				75DF77681BC8EB59006E9520 /* HashProtocol.swift in Sources */,
 				75DF77691BC8EB59006E9520 /* Generics.swift in Sources */,

+ 28 - 0
CryptoSwift/Foundation/Rabbit+Foundation.swift

@@ -0,0 +1,28 @@
+//
+//  Rabbit+Foundation.swift
+//  CryptoSwift
+//
+//  Created by Dima Kalachov on 13/11/15.
+//  Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
+//
+
+import Foundation
+
+extension Rabbit {
+    convenience public init?(key: String) {
+        guard let kkey = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)?.arrayOfBytes() else {
+            return nil
+        }
+        self.init(key: kkey)
+        
+    }
+    
+    convenience public init?(key: String, iv: String) {
+        guard let kkey = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)?.arrayOfBytes(),
+            let iiv = iv.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)?.arrayOfBytes()
+            else {
+                return nil
+        }
+        self.init(key: kkey, iv: iiv)
+    }
+}

+ 206 - 0
CryptoSwift/Rabbit.swift

@@ -0,0 +1,206 @@
+//
+//  Rabbit.swift
+//  CryptoSwift
+//
+//  Created by Dima Kalachov on 12/11/15.
+//  Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
+//
+
+final public class Rabbit {
+    
+    /// Size of IV in bytes
+    public static let ivSize = 64 / 8
+    
+    /// Size of key in bytes
+    public static let keySize = 128 / 8
+    
+    /// Size of block in bytes
+    public static let blockSize = 128 / 8
+    
+    /// Key
+    private let key: [UInt8]
+    
+    /// IV (optional)
+    private let iv: [UInt8]?
+    
+    /// State variables
+    private var x = [UInt32](count: 8, repeatedValue: 0)
+    
+    /// Counter variables
+    private var c = [UInt32](count: 8, repeatedValue: 0)
+    
+    /// Counter carry
+    private var p7: UInt32 = 0
+    
+    /// 'a' constants
+    private var a: [UInt32] = [
+        0x4D34D34D,
+        0xD34D34D3,
+        0x34D34D34,
+        0x4D34D34D,
+        0xD34D34D3,
+        0x34D34D34,
+        0x4D34D34D,
+        0xD34D34D3,
+    ]
+    
+    // MARK: - Initializers
+    convenience public init?(key:[UInt8]) {
+        self.init(key: key, iv: nil)
+    }
+    
+    public init?(key:[UInt8], iv:[UInt8]?) {
+        self.key = key
+        self.iv = iv
+        
+        guard key.count == Rabbit.keySize && (iv == nil || iv!.count == Rabbit.ivSize) else {
+            return nil
+        }
+    }
+    
+    // MARK: -
+    private func setup() {
+        p7 = 0
+        
+        // Key divided into 8 subkeys
+        var k = [UInt32](count: 8, repeatedValue: 0)
+        for var j = 0; j < 8; j++ {
+            k[j] = UInt32(key[Rabbit.blockSize - (2*j + 1)]) | (UInt32(key[Rabbit.blockSize - (2*j + 2)]) << 8)
+        }
+        
+        // Initialize state and counter variables from subkeys
+        for var j = 0; j < 8; j++ {
+            if j % 2 == 0 {
+                x[j] = (k[(j+1) % 8] << 16) | k[j]
+                c[j] = (k[(j+4) % 8] << 16) | k[(j+5) % 8]
+            } else {
+                x[j] = (k[(j+5) % 8] << 16) | k[(j+4) % 8]
+                c[j] = (k[j] << 16)         | k[(j+1) % 8]
+            }
+        }
+        
+        // Iterate system four times
+        nextState()
+        nextState()
+        nextState()
+        nextState()
+        
+        // Reinitialize counter variables
+        for var j = 0; j < 8; j++ {
+            c[j] = c[j] ^ x[(j+4) % 8]
+        }
+        
+        if let iv = iv {
+            setupIV(iv)
+        }
+    }
+    
+    private func setupIV(iv: [UInt8]) {
+        // 63...56 55...48 47...40 39...32 31...24 23...16 15...8 7...0 IV bits
+        //    0       1       2       3       4       5       6     7   IV bytes in array
+        let iv0: UInt32 = integerWithBytes([iv[4], iv[5], iv[6], iv[7]])
+        let iv1: UInt32 = integerWithBytes([iv[0], iv[1], iv[4], iv[5]])
+        let iv2: UInt32 = integerWithBytes([iv[0], iv[1], iv[2], iv[3]])
+        let iv3: UInt32 = integerWithBytes([iv[2], iv[3], iv[6], iv[7]])
+        
+        // Modify the counter state as function of the IV
+        c[0] = c[0] ^ iv0
+        c[1] = c[1] ^ iv1
+        c[2] = c[2] ^ iv2
+        c[3] = c[3] ^ iv3
+        c[4] = c[4] ^ iv0
+        c[5] = c[5] ^ iv1
+        c[6] = c[6] ^ iv2
+        c[7] = c[7] ^ iv3
+        
+        // Iterate system four times
+        nextState()
+        nextState()
+        nextState()
+        nextState()
+    }
+    
+    private func nextState() {
+        // Before an iteration the counters are incremented
+        var carry = p7
+        for var j = 0; j < 8; j++ {
+            let prev = c[j]
+            c[j] = prev &+ a[j] &+ carry
+            carry = prev > c[j] ? 1 : 0 // detect overflow
+        }
+        p7 = carry // save last carry bit
+        
+        // Iteration of the system
+        var newX = [UInt32](count: 8, repeatedValue: 0)
+        newX[0] = g(0) &+ rotateLeft(g(7), 16) &+ rotateLeft(g(6), 16)
+        newX[1] = g(1) &+ rotateLeft(g(0), 8)  &+ g(7)
+        newX[2] = g(2) &+ rotateLeft(g(1), 16) &+ rotateLeft(g(0), 16)
+        newX[3] = g(3) &+ rotateLeft(g(2), 8)  &+ g(1)
+        newX[4] = g(4) &+ rotateLeft(g(3), 16) &+ rotateLeft(g(2), 16)
+        newX[5] = g(5) &+ rotateLeft(g(4), 8)  &+ g(3)
+        newX[6] = g(6) &+ rotateLeft(g(5), 16) &+ rotateLeft(g(4), 16)
+        newX[7] = g(7) &+ rotateLeft(g(6), 8)  &+ g(5)
+        x = newX
+    }
+    
+    private func g(j: Int) -> UInt32 {
+        let sum = x[j] &+ c[j]
+        let square = UInt64(sum) * UInt64(sum)
+        return UInt32(truncatingBitPattern: square ^ (square >> 32))
+    }
+    
+    private func nextOutput() -> [UInt8] {
+        nextState()
+        
+        var output16 = [UInt16](count: Rabbit.blockSize / 2, repeatedValue: 0)
+        output16[7] = UInt16(truncatingBitPattern: x[0]) ^ UInt16(truncatingBitPattern: x[5] >> 16)
+        output16[6] = UInt16(truncatingBitPattern: x[0] >> 16) ^ UInt16(truncatingBitPattern: x[3])
+        output16[5] = UInt16(truncatingBitPattern: x[2]) ^ UInt16(truncatingBitPattern: x[7] >> 16)
+        output16[4] = UInt16(truncatingBitPattern: x[2] >> 16) ^ UInt16(truncatingBitPattern: x[5])
+        output16[3] = UInt16(truncatingBitPattern: x[4]) ^ UInt16(truncatingBitPattern: x[1] >> 16)
+        output16[2] = UInt16(truncatingBitPattern: x[4] >> 16) ^ UInt16(truncatingBitPattern: x[7])
+        output16[1] = UInt16(truncatingBitPattern: x[6]) ^ UInt16(truncatingBitPattern: x[3] >> 16)
+        output16[0] = UInt16(truncatingBitPattern: x[6] >> 16) ^ UInt16(truncatingBitPattern: x[1])
+        
+        var output8 = [UInt8](count: Rabbit.blockSize, repeatedValue: 0)
+        for var j = 0; j < output16.count; j++ {
+            output8[j * 2] = UInt8(truncatingBitPattern: output16[j] >> 8)
+            output8[j * 2 + 1] = UInt8(truncatingBitPattern: output16[j])
+        }
+        return output8
+    }
+    
+    // MARK: - Public
+    public func encrypt(bytes: [UInt8]) -> [UInt8] {
+        setup()
+        
+        var result = [UInt8](count: bytes.count, repeatedValue: 0)
+        var output = nextOutput()
+        for var byteIdx = 0, outputIdx = 0; byteIdx < bytes.count; byteIdx++, outputIdx++ {
+            if (outputIdx == Rabbit.blockSize) {
+                output = nextOutput()
+                outputIdx = 0
+            }
+            
+            result[byteIdx] = bytes[byteIdx] ^ output[outputIdx]
+        }
+        return result
+    }
+    
+    public func decrypt(bytes: [UInt8]) -> [UInt8] {
+        return encrypt(bytes)
+    }
+}
+
+
+// MARK: - Cipher
+
+extension Rabbit: Cipher {
+    public func cipherEncrypt(bytes:[UInt8]) -> [UInt8] {
+        return self.encrypt(bytes)
+    }
+    
+    public func cipherDecrypt(bytes: [UInt8]) -> [UInt8] {
+        return self.decrypt(bytes)
+    }
+}

+ 130 - 0
CryptoSwiftTests/RabbitTests.swift

@@ -0,0 +1,130 @@
+//
+//  RabbitTests.swift
+//  CryptoSwift
+//
+//  Created by Dima Kalachov on 13/11/15.
+//  Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
+//
+
+import XCTest
+@testable import CryptoSwift
+
+class RabbitTests: XCTestCase {
+    
+    override func setUp() {
+        super.setUp()
+    }
+    
+    override func tearDown() {
+        super.tearDown()
+    }
+    
+    func testInitialization() {
+        var key = [UInt8](count: Rabbit.keySize - 1, repeatedValue: 0)
+        var iv: [UInt8]?
+        XCTAssertNil(Rabbit(key: key, iv: iv))
+        
+        key = [UInt8](count: Rabbit.keySize + 1, repeatedValue: 0)
+        XCTAssertNil(Rabbit(key: key, iv: iv))
+        
+        key = [UInt8](count: Rabbit.keySize, repeatedValue: 0)
+        XCTAssertNotNil(Rabbit(key: key, iv: iv))
+        
+        iv = [UInt8](count: Rabbit.ivSize - 1, repeatedValue: 0)
+        XCTAssertNil(Rabbit(key: key, iv: iv))
+        
+        iv = [UInt8](count: Rabbit.ivSize, repeatedValue: 0)
+        XCTAssertNotNil(Rabbit(key: key, iv: iv))
+    }
+    
+    func testRabbitWithoutIV() {
+        // Examples from Appendix A: Test Vectors in http://tools.ietf.org/rfc/rfc4503.txt
+        let cases: [([UInt8], [UInt8])] = [
+            // First case
+            (
+                [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+                [
+                    0xB1, 0x57, 0x54, 0xF0, 0x36, 0xA5, 0xD6, 0xEC, 0xF5, 0x6B, 0x45, 0x26, 0x1C, 0x4A, 0xF7, 0x02,
+                    0x88, 0xE8, 0xD8, 0x15, 0xC5, 0x9C, 0x0C, 0x39, 0x7B, 0x69, 0x6C, 0x47, 0x89, 0xC6, 0x8A, 0xA7,
+                    0xF4, 0x16, 0xA1, 0xC3, 0x70, 0x0C, 0xD4, 0x51, 0xDA, 0x68, 0xD1, 0x88, 0x16, 0x73, 0xD6, 0x96,
+                ]
+            ),
+            // Second case
+            (
+                [0x91, 0x28, 0x13, 0x29, 0x2E, 0x3D, 0x36, 0xFE, 0x3B, 0xFC, 0x62, 0xF1, 0xDC, 0x51, 0xC3, 0xAC],
+                [
+                    0x3D, 0x2D, 0xF3, 0xC8, 0x3E, 0xF6, 0x27, 0xA1, 0xE9, 0x7F, 0xC3, 0x84, 0x87, 0xE2, 0x51, 0x9C,
+                    0xF5, 0x76, 0xCD, 0x61, 0xF4, 0x40, 0x5B, 0x88, 0x96, 0xBF, 0x53, 0xAA, 0x85, 0x54, 0xFC, 0x19,
+                    0xE5, 0x54, 0x74, 0x73, 0xFB, 0xDB, 0x43, 0x50, 0x8A, 0xE5, 0x3B, 0x20, 0x20, 0x4D, 0x4C, 0x5E,
+                ]
+            ),
+            // Third case
+            (
+                [0x83, 0x95, 0x74, 0x15, 0x87, 0xE0, 0xC7, 0x33, 0xE9, 0xE9, 0xAB, 0x01, 0xC0, 0x9B, 0x00, 0x43,],
+                [
+                    0x0C, 0xB1, 0x0D, 0xCD, 0xA0, 0x41, 0xCD, 0xAC, 0x32, 0xEB, 0x5C, 0xFD, 0x02, 0xD0, 0x60, 0x9B,
+                    0x95, 0xFC, 0x9F, 0xCA, 0x0F, 0x17, 0x01, 0x5A, 0x7B, 0x70, 0x92, 0x11, 0x4C, 0xFF, 0x3E, 0xAD,
+                    0x96, 0x49, 0xE5, 0xDE, 0x8B, 0xFC, 0x7F, 0x3F, 0x92, 0x41, 0x47, 0xAD, 0x3A, 0x94, 0x74, 0x28,
+                ]
+            ),
+        ]
+        
+        let plainText = [UInt8](count: 48, repeatedValue: 0)
+        for (key, expectedCipher) in cases {
+            let rabbit = Rabbit(key: key)!
+            let cipherText = rabbit.encrypt(plainText)
+            XCTAssertEqual(cipherText, expectedCipher)
+            XCTAssertEqual(rabbit.decrypt(cipherText), plainText)
+        }
+    }
+    
+    func testRabbitWithIV() {
+        // Examples from Appendix A: Test Vectors in http://tools.ietf.org/rfc/rfc4503.txt
+        let key = [UInt8](count: Rabbit.keySize, repeatedValue: 0)
+        let cases: [([UInt8], [UInt8])] = [
+            (
+                [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+                [
+                    0xC6, 0xA7, 0x27, 0x5E, 0xF8, 0x54, 0x95, 0xD8, 0x7C, 0xCD, 0x5D, 0x37, 0x67, 0x05, 0xB7, 0xED,
+                    0x5F, 0x29, 0xA6, 0xAC, 0x04, 0xF5, 0xEF, 0xD4, 0x7B, 0x8F, 0x29, 0x32, 0x70, 0xDC, 0x4A, 0x8D,
+                    0x2A, 0xDE, 0x82, 0x2B, 0x29, 0xDE, 0x6C, 0x1E, 0xE5, 0x2B, 0xDB, 0x8A, 0x47, 0xBF, 0x8F, 0x66,
+                ]
+            ),
+            (
+                [0xC3, 0x73, 0xF5, 0x75, 0xC1, 0x26, 0x7E, 0x59],
+                [
+                    0x1F, 0xCD, 0x4E, 0xB9, 0x58, 0x00, 0x12, 0xE2, 0xE0, 0xDC, 0xCC, 0x92, 0x22, 0x01, 0x7D, 0x6D,
+                    0xA7, 0x5F, 0x4E, 0x10, 0xD1, 0x21, 0x25, 0x01, 0x7B, 0x24, 0x99, 0xFF, 0xED, 0x93, 0x6F, 0x2E,
+                    0xEB, 0xC1, 0x12, 0xC3, 0x93, 0xE7, 0x38, 0x39, 0x23, 0x56, 0xBD, 0xD0, 0x12, 0x02, 0x9B, 0xA7,
+                ]
+            ),
+            (
+                [0xA6, 0xEB, 0x56, 0x1A, 0xD2, 0xF4, 0x17, 0x27],
+                [
+                    0x44, 0x5A, 0xD8, 0xC8, 0x05, 0x85, 0x8D, 0xBF, 0x70, 0xB6, 0xAF, 0x23, 0xA1, 0x51, 0x10, 0x4D,
+                    0x96, 0xC8, 0xF2, 0x79, 0x47, 0xF4, 0x2C, 0x5B, 0xAE, 0xAE, 0x67, 0xC6, 0xAC, 0xC3, 0x5B, 0x03,
+                    0x9F, 0xCB, 0xFC, 0x89, 0x5F, 0xA7, 0x1C, 0x17, 0x31, 0x3D, 0xF0, 0x34, 0xF0, 0x15, 0x51, 0xCB,
+                ]
+            ),
+        ]
+        
+        let plainText = [UInt8](count: 48, repeatedValue: 0)
+        for (iv, expectedCipher) in cases {
+            let rabbit = Rabbit(key: key, iv: iv)!
+            let cipherText = rabbit.encrypt(plainText)
+            XCTAssertEqual(cipherText, expectedCipher)
+            XCTAssertEqual(rabbit.decrypt(cipherText), plainText)
+        }
+    }
+    
+    func testRabbitPerformance() {
+        let key: [UInt8] = [UInt8](count: Rabbit.keySize, repeatedValue: 0)
+        let iv: [UInt8] = [UInt8](count: Rabbit.ivSize, repeatedValue: 0)
+        let message = [UInt8](count: (1024 * 1024) * 1, repeatedValue: 7)
+        measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: true, forBlock: { () -> Void in
+            let encrypted = Rabbit(key: key, iv: iv)?.encrypt(message)
+            self.stopMeasuring()
+            XCTAssert(encrypted != nil, "not encrypted")
+        })
+    }
+}

+ 8 - 0
README.md

@@ -36,6 +36,7 @@ Good mood
 #####Cipher
 - [AES-128, AES-192, AES-256](http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf)
 - [ChaCha20](http://cr.yp.to/chacha/chacha-20080128.pdf)
+- [Rabbit](https://tools.ietf.org/html/rfc4503)
 
 #####Message authenticators
 - [Poly1305](http://cr.yp.to/mac/poly1305-20050329.pdf)
@@ -171,6 +172,13 @@ let encrypted: [UInt8] = ChaCha20(key: key, iv: iv).encrypt(message)
 let decrypted: [UInt8] = ChaCha20(key: key, iv: iv).decrypt(encrypted)
 ```
 
+Rabbit
+
+```swift
+let encrypted = Rabbit(key: key, iv: iv)?.encrypt(plaintext)
+let decrypted = Rabbit(key: key, iv: iv)?.decrypt(encrypted!)
+```
+
 AES
 
 Notice regarding padding: *Manual padding of data is optional and CryptoSwift by default always will add PKCS7 padding before encryption, and remove after decryption when __Cipher__ enum is used. If you need manually disable/enable padding, you can do this by setting parameter for encrypt()/decrypt() on class __AES__.*