view dcrypt/crypto/ciphers/XTEA.d @ 6:5cb17e09d685

Minor edits to the unittests of hash functions and ciphers. Added AES and test vectors.
author Thomas Dixon <reikon@reikon.us>
date Sat, 16 Aug 2008 22:43:22 -0400
parents 71aae178f89a
children 23c62e28b3a4
line wrap: on
line source

/**
 * 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;
        uint[] subkeys,
               sum0,
               sum1;
        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_plaintexts[i]~")");
            }
        }
    }
}