diff dcrypt/crypto/ciphers/RC6.d @ 0:0e08791a1418

Initial import.
author Thomas Dixon <reikon@reikon.us>
date Sun, 10 Aug 2008 14:20:17 -0400
parents
children 483e4467b5f6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcrypt/crypto/ciphers/RC6.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,208 @@
+/**
+ * This file is part of the dcrypt project.
+ *
+ * It should be noted that this algorithm is very similar to RC5.
+ * Currently there are no plans to implement RC5, but should that change
+ * in the future, it may be wise to rewrite both RC5 and RC6 to use some
+ * kind of template or base class.
+ *
+ * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
+ * License:   MIT
+ * Authors:   Thomas Dixon
+ */
+
+module dcrypt.crypto.ciphers.RC6;
+
+import dcrypt.misc.Util;
+import dcrypt.crypto.BlockCipher;
+
+/**
+ * Implementation of the RC6-32/20/b cipher designed by 
+ * Ron Rivest et al. of RSA Security.
+ *
+ * Note: This algorithm is patented and trademarked.
+ */
+class RC6 : BlockCipher {
+    private const uint ROUNDS = 20,
+                       BLOCK_SIZE = 16,
+                       // Magic constants for a 32 bit word size
+                       P = 0xb7e15163,
+                       Q = 0x9e3779b9;
+    private uint[] S;
+    private ubyte[] workingKey;
+    private bool initialized,
+                 encrypt;
+    
+    char[] name() {
+        return "RC6";
+    }
+    
+    uint blockSize() {
+        return BLOCK_SIZE;
+    }
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        this.encrypt = encrypt;
+        
+        uint len = keyParams.key.length;
+        if (len != 16 && len != 24 && len != 32)
+            throw new InvalidKeyError(
+                    name()~": Invalid key length (requires 16/24/32 bytes)");
+        
+        S = new uint[2*ROUNDS+4];        
+                   
+        workingKey = keyParams.key;
+        setup(workingKey);
+        
+        initialized = true;
+    }
+    
+    uint processBlock(void[] input_, uint inOff, void[] output_, uint outOff) {
+        if (!initialized)
+            throw new NotInitializedError(name()~": Cipher not initialized");
+            
+        ubyte[] input = cast(ubyte[]) input_;
+        ubyte[] output = cast(ubyte[]) output_;
+                    
+        if ((inOff + BLOCK_SIZE) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + BLOCK_SIZE) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+        
+        uint A = Util.ubytesToUintLittle(input, inOff),
+             B = Util.ubytesToUintLittle(input, inOff+4),
+             C = Util.ubytesToUintLittle(input, inOff+8),
+             D = Util.ubytesToUintLittle(input, inOff+12),
+             t,
+             u;
+             
+        if (encrypt) {
+            B += S[0];
+            D += S[1];
+            for (int i = 1; i <= ROUNDS; i++) {
+                t = Util.rotateLeft(B*((B<<1)+1), 5);
+                u = Util.rotateLeft(D*((D<<1)+1), 5);
+                A = Util.rotateLeft(A^t, u) + S[i<<1];
+                C = Util.rotateLeft(C^u, t) + S[(i<<1)+1];
+                t = A;
+                A = B;
+                B = C;
+                C = D;
+                D = t;
+            }
+            A += S[2*ROUNDS+2];
+            C += S[2*ROUNDS+3];
+        } else {
+            C -= S[2*ROUNDS+3];
+            A -= S[2*ROUNDS+2];
+            for (int i = ROUNDS; i >= 1; i--) {
+                t = D;
+                D = C;
+                C = B;
+                B = A;
+                A = t;
+                u = Util.rotateLeft(D*((D<<1)+1), 5);
+                t = Util.rotateLeft(B*((B<<1)+1), 5);
+                C = Util.rotateRight(C-S[(i<<1)+1], t) ^ u;
+                A = Util.rotateRight(A-S[i<<1], u) ^ t;
+            }
+            D -= S[1];
+            B -= S[0];
+        }
+
+        Util.uintToUbytesLittle(A, output, outOff);
+        Util.uintToUbytesLittle(B, output, outOff+4);
+        Util.uintToUbytesLittle(C, output, outOff+8);
+        Util.uintToUbytesLittle(D, output, outOff+12);
+        
+        return BLOCK_SIZE;
+    }
+    
+    void reset() {
+        setup(workingKey);
+    }
+    
+    void setup(ubyte[] key) {
+        uint c = key.length/4;
+        uint[] L = new uint[c];
+        for (int i = 0, j = 0; i < c; i++, j+=4)
+            L[i] = Util.ubytesToUintLittle(key, j);
+        S[0] = P;
+        for (int i = 1; i <= 2*ROUNDS+3; i++)
+            S[i] = S[i-1] + Q;
+        uint A, B, i, j, v = 3*(2*ROUNDS+4); // Relying on ints initializing to 0
+        for (int s = 1; s <= v; s++) {
+            A = S[i] = Util.rotateLeft(S[i]+A+B, 3);
+            B = L[j] = Util.rotateLeft(L[j]+A+B, A+B);
+            i = (i + 1) % (2*ROUNDS+4);
+            j = (j + 1) % c;
+        }
+    }
+    
+    /** Some RC6 test vectors from the spec. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "00000000000000000000000000000000",
+                
+                "0123456789abcdef0112233445566778",
+                
+                "00000000000000000000000000000000"~
+                "0000000000000000",
+                
+                "0123456789abcdef0112233445566778"~
+                "899aabbccddeeff0",
+                
+                "00000000000000000000000000000000"~
+                "00000000000000000000000000000000",
+                
+                "0123456789abcdef0112233445566778"~
+                "899aabbccddeeff01032547698badcfe"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1",
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1",
+                "00000000000000000000000000000000",
+                "02132435465768798a9bacbdcedfe0f1"
+            ];
+                
+            static const char[][] test_ciphertexts = [
+                "8fc3a53656b1f778c129df4e9848a41e",
+                "524e192f4715c6231f51f6367ea43f18",
+                "6cd61bcb190b30384e8a3f168690ae82",
+                "688329d019e505041e52e92af95291d4",
+                "8f5fbd0510d15fa893fa3fda6e857ec2",
+                "c8241816f0d7e48920ad16a1674e5d48"
+            ];
+                
+            RC6 t = new RC6();
+            foreach (uint i, char[] test_key; test_keys) {
+                ubyte[] buffer = new ubyte[t.blockSize];
+                char[] result;
+                SymmetricKey key = new SymmetricKey(Util.hexToUbytes(test_key));
+                
+                // Encryption
+                t.init(true, key);
+                t.processBlock(Util.hexToUbytes(test_plaintexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            
+                // Decryption
+                t.init(false, key);
+                t.processBlock(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        t.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }
+        }
+    }
+}