diff dcrypt/crypto/ciphers/RC4.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/RC4.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,203 @@
+/**
+ * This file is part of the dcrypt project.
+ *
+ * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
+ * License:   MIT
+ * Authors:   Thomas Dixon
+ */
+
+module dcrypt.crypto.ciphers.RC4;
+
+import dcrypt.crypto.StreamCipher;
+
+version (UnitTest) {
+    import dcrypt.misc.Util;
+}
+
+/** Implementation of RC4 designed by Ron Rivest of RSA Security. */
+class RC4 : StreamCipher {
+    private ubyte[] state,
+                    workingKey;
+    private ubyte x, y;
+    private bool initialized;
+    
+    void init(bool encrypt, CipherParameters params) {
+        SymmetricKey keyParams = cast(SymmetricKey)params;
+        if (!keyParams)
+            throw new InvalidParameterError(
+                    name()~": Invalid parameter passed to init");
+        if (keyParams.key.length < 0 || keyParams.key.length > 256)
+            throw new InvalidKeyError(
+                name()~": Invalid key length (requires 1-256 bytes)");
+        workingKey = keyParams.key;
+        state = new ubyte[256];
+        setup(workingKey);
+        initialized = true;
+    }
+    
+    char[] name() {
+        return "RC4";
+    }
+    
+    ubyte returnByte(ubyte input) {
+        y += state[++x];
+        ubyte t = state[x];
+        state[x] = state[y];
+        state[y] = t;
+        return (input^state[cast(ubyte)(state[x]+state[y])]);
+    }
+    
+    void processBytes(void[] input_, uint inOff, uint len, 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 + len) > input.length)
+            throw new ShortBufferError(name()~": Input buffer too short");
+            
+        if ((outOff + len) > output.length)
+            throw new ShortBufferError(name()~": Output buffer too short");
+            
+        for (int i = 0; i < len; i++) {
+            y += state[++x];
+            ubyte t = state[x];
+            state[x] = state[y];
+            state[y] = t;
+            output[outOff++] = input[inOff++] ^ state[cast(ubyte)(state[x]+state[y])];
+        }
+    }
+    
+    void reset() { 
+        setup(workingKey);
+    }
+    
+    // Do RC4's key setup in a separate method to ease resetting
+    private void setup(ubyte[] key) {
+        for (int i = 0; i < 256; i++)
+            state[i] = cast(ubyte)i;
+            
+        x = 0;
+        for (int i = 0; i < 256; i++) {
+            x += key[i % key.length] + state[i];
+            ubyte t = state[i];
+            state[i] = state[x];
+            state[x] = t;
+        }
+        x = y = 0;
+    }
+    
+    /** Some RC4 test vectors. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "0123456789abcdef",
+                "0123456789abcdef",
+                "0000000000000000",
+                "ef012345",
+                "0123456789abcdef"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "0123456789abcdef",
+                "0000000000000000",
+                "0000000000000000",
+                "00000000000000000000",
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"~
+                "01010101010101010101010101010101"
+            ];
+                 
+            static const char[][] test_ciphertexts = [
+                "75b7878099e0c596",
+                "7494c2e7104b0879",
+                "de188941a3375d3a",
+                "d6a141a7ec3c38dfbd61",
+                "7595c3e6114a09780c4ad452338e1ffd"~
+                "9a1be9498f813d76533449b6778dcad8"~
+                "c78a8d2ba9ac66085d0e53d59c26c2d1"~
+                "c490c1ebbe0ce66d1b6b1b13b6b919b8"~
+                "47c25a91447a95e75e4ef16779cde8bf"~
+                "0a95850e32af9689444fd377108f98fd"~
+                "cbd4e726567500990bcc7e0ca3c4aaa3"~
+                "04a387d20f3b8fbbcd42a1bd311d7a43"~
+                "03dda5ab078896ae80c18b0af66dff31"~
+                "9616eb784e495ad2ce90d7f772a81747"~
+                "b65f62093b1e0db9e5ba532fafec4750"~
+                "8323e671327df9444432cb7367cec82f"~
+                "5d44c0d00b67d650a075cd4b70dedd77"~
+                "eb9b10231b6b5b741347396d62897421"~
+                "d43df9b42e446e358e9c11a9b2184ecb"~
+                "ef0cd8e7a877ef968f1390ec9b3d35a5"~
+                "585cb009290e2fcde7b5ec66d9084be4"~
+                "4055a619d9dd7fc3166f9487f7cb2729"~ 
+                "12426445998514c15d53a18c864ce3a2"~ 
+                "b7555793988126520eacf2e3066e230c"~  
+                "91bee4dd5304f5fd0405b35bd99c7313"~
+                "5d3d9bc335ee049ef69b3867bf2d7bd1"~
+                "eaa595d8bfc0066ff8d31509eb0c6caa"~
+                "006c807a623ef84c3d33c195d23ee320"~
+                "c40de0558157c822d4b8c569d849aed5"~
+                "9d4e0fd7f379586b4b7ff684ed6a189f"~
+                "7486d49b9c4bad9ba24b96abf924372c"~
+                "8a8fffb10d55354900a77a3db5f205e1"~
+                "b99fcd8660863a159ad4abe40fa48934"~
+                "163ddde542a6585540fd683cbfd8c00f"~
+                "12129a284deacc4cdefe58be7137541c"~
+                "047126c8d49e2755ab181ab7e940b0c0"
+            ];
+    
+            RC4 r = new RC4();
+            foreach (uint i, char[] test_key; test_keys) {
+                ubyte[] buffer = new ubyte[test_plaintexts[i].length>>1];
+                char[] result;
+                
+                r.init(true, new SymmetricKey(Util.hexToUbytes(test_key)));
+                
+                // Encryption
+                r.processBytes(Util.hexToUbytes(test_plaintexts[i]), 0, buffer.length, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_ciphertexts[i],
+                        r.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+                        
+                r.reset();
+                
+                // Decryption
+                r.processBytes(Util.hexToUbytes(test_ciphertexts[i]), 0, buffer.length, buffer, 0);
+                result = Util.ubytesToHex(buffer);
+                assert(result == test_plaintexts[i],
+                        r.name()~": ("~result~") != ("~test_ciphertexts[i]~")");
+            }
+        }
+    }
+}