diff dcrypt/crypto/ciphers/XTEA.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/XTEA.d	Sun Aug 10 14:20:17 2008 -0400
@@ -0,0 +1,163 @@
+/**
+ * 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.XTEA;
+
+import dcrypt.misc.Util;
+import dcrypt.crypto.BlockCipher;
+
+/** Implementation of the XTEA cipher designed by
+    David Wheeler and Roger Needham. */
+class XTEA : BlockCipher {
+    private const uint ROUNDS = 32,
+                       KEY_SIZE = 16,
+                       BLOCK_SIZE = 8,
+                       DELTA = 0x9e3779b9;
+    private uint[] subkeys,
+                   sum0,
+                   sum1;
+    private bool initialized,
+                 encrypt;
+    
+    void reset(){}
+    
+    char[] name() {
+        return "XTEA";
+    }
+    
+    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;
+                    
+        if (keyParams.key.length != KEY_SIZE)
+            throw new InvalidKeyError(
+                    name()~": Invalid key length (requires 16 bytes)");
+        
+        subkeys = new uint[4];
+        sum0 = new uint[32];
+        sum1 = new uint[32];
+        
+        int i, j;
+        for (i = j = 0; i < 4; i++, j+=4)
+            subkeys[i] = Util.ubytesToUintBig(keyParams.key, j);
+            
+        // Precompute the values of sum + k[] to speed up encryption
+        for (i = j = 0; i < ROUNDS; i++) {
+            sum0[i] = (j + subkeys[j & 3]);
+            j += DELTA;
+            sum1[i] = (j + subkeys[j >> 11 & 3]);
+        }
+        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 v0 = Util.ubytesToUintBig(input, inOff),
+             v1 = Util.ubytesToUintBig(input, inOff+4);
+             
+        if (encrypt) {
+            for (int i = 0; i < ROUNDS; i++) {
+                v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ sum0[i];
+                v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ sum1[i];
+            }
+        } else {
+            for (int i = ROUNDS-1; i >= 0; i--) {
+                v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ sum1[i];
+                v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ sum0[i];
+            }
+        }
+        
+        Util.uintToUbytesBig(v0, output, outOff);
+        Util.uintToUbytesBig(v1, output, outOff+4);
+        
+        return BLOCK_SIZE;
+    }
+    
+    /** Some XTEA test vectors. */
+    version (UnitTest) {
+        unittest {
+            static const char[][] test_keys = [
+                "00000000000000000000000000000000",
+                "00000000000000000000000000000000",
+                "0123456712345678234567893456789a",
+                "0123456712345678234567893456789a",
+                "00000000000000000000000000000001",
+                "01010101010101010101010101010101",
+                "0123456789abcdef0123456789abcdef",
+                "0123456789abcdef0123456789abcdef",
+                "00000000000000000000000000000000",
+                "00000000000000000000000000000000"
+            ];
+                 
+            static const char[][] test_plaintexts = [
+                "0000000000000000",
+                "0102030405060708",
+                "0000000000000000",
+                "0102030405060708",
+                "0000000000000001",
+                "0101010101010101",
+                "0123456789abcdef",
+                "0000000000000000",
+                "0123456789abcdef",
+                "4141414141414141"
+            ];
+                
+            static const char[][] test_ciphertexts = [
+                "dee9d4d8f7131ed9",
+                "065c1b8975c6a816",
+                "1ff9a0261ac64264",
+                "8c67155b2ef91ead",
+                "9f25fa5b0f86b758",
+                "c2eca7cec9b7f992",
+                "27e795e076b2b537",
+                "5c8eddc60a95b3e1",
+                "7e66c71c88897221",
+                "ed23375a821a8c2d"
+            ];
+                
+            XTEA t = new XTEA();
+            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]~")");
+            }
+        }
+    }
+}