view dcrypt/crypto/ciphers/TEA.d @ 28:ad687db713a4

Further reworked the code for hash padding. Replaced all instances of 'char[]' with 'string' and removed a few 'const' modifiers as per Glenn Haecker's patch for D2 compatibility. Updated CONTRIBUTORS file.
author Thomas Dixon <reikon@reikon.us>
date Sun, 10 May 2009 22:38:48 -0400
parents 8b5eaf3c2979
children
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.TEA;

import dcrypt.misc.ByteConverter;
import dcrypt.crypto.BlockCipher;

/** Implementation of the TEA cipher designed by
    David Wheeler and Roger Needham. */
class TEA : BlockCipher
{
    private
    {
        static const uint ROUNDS = 32,
                          KEY_SIZE = 16,
                          BLOCK_SIZE = 8,
                          DELTA = 0x9e3779b9u,
                          DECRYPT_SUM = 0xc6ef3720u;
        uint sk0, sk1, sk2, sk3, sum;
    }
    
    void reset(){}
    
    string name()
    {
        return "TEA";
    }
    
    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");
                    
        _encrypt = encrypt;
                    
        if (keyParams.key.length != KEY_SIZE)
            throw new InvalidKeyError(
                    name()~": Invalid key length (requires 16 bytes)");
        
        sk0 = ByteConverter.BigEndian.to!(uint)(keyParams.key[0..4]);
        sk1 = ByteConverter.BigEndian.to!(uint)(keyParams.key[4..8]);
        sk2 = ByteConverter.BigEndian.to!(uint)(keyParams.key[8..12]);
        sk3 = ByteConverter.BigEndian.to!(uint)(keyParams.key[12..16]);

        _initialized = true;
    }
    
    uint update(void[] input_, void[] output_)
    {
        if (!_initialized)
            throw new NotInitializedError(name()~": Cipher not initialized");
            
        ubyte[] input = cast(ubyte[]) input_,
                output = cast(ubyte[]) output_;
                    
        if (input.length < BLOCK_SIZE)
            throw new ShortBufferError(name()~": Input buffer too short");
            
        if (output.length < BLOCK_SIZE)
            throw new ShortBufferError(name()~": Output buffer too short");
        
        uint v0 = ByteConverter.BigEndian.to!(uint)(input[0..4]),
             v1 = ByteConverter.BigEndian.to!(uint)(input[4..8]);
        
        sum = _encrypt ? 0 : DECRYPT_SUM;
        for (int i = 0; i < ROUNDS; i++)
        {
            if (_encrypt)
            {
                sum += DELTA;
                v0 += ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
                v1 += ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
            }
            else
            {
                v1 -= ((v0 << 4) + sk2) ^ (v0 + sum) ^ ((v0 >> 5) + sk3);
                v0 -= ((v1 << 4) + sk0) ^ (v1 + sum) ^ ((v1 >> 5) + sk1);
                sum -= DELTA;
            }
        }
        
        output[0..4] = ByteConverter.BigEndian.from!(uint)(v0);
        output[4..8] = ByteConverter.BigEndian.from!(uint)(v1);
        
        return BLOCK_SIZE;
    }
    
    /** Some TEA test vectors. */
    debug (UnitTest)
    {
        unittest
        {
            static string[] test_keys = [
                "00000000000000000000000000000000",
                "00000000000000000000000000000000",
                "0123456712345678234567893456789a",
                "0123456712345678234567893456789a"
            ];
                 
            static string[] test_plaintexts = [
                "0000000000000000",
                "0102030405060708",
                "0000000000000000",
                "0102030405060708"
            ];
                
            static string[] test_ciphertexts = [
                "41ea3a0a94baa940",
                "6a2f9cf3fccf3c55",
                "34e943b0900f5dcb",
                "773dc179878a81c0"
            ];
                
            
            TEA t = new TEA();
            foreach (uint i, string test_key; test_keys)
            {
                ubyte[] buffer = new ubyte[t.blockSize];
                string result;
                SymmetricKey key = new SymmetricKey(ByteConverter.hexDecode(test_key));
                
                // Encryption
                t.init(true, key);
                t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
                result = ByteConverter.hexEncode(buffer);
                assert(result == test_ciphertexts[i],
                        t.name~": ("~result~") != ("~test_ciphertexts[i]~")");

                // Decryption
                t.init(false, key);
                t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
                result = ByteConverter.hexEncode(buffer);
                assert(result == test_plaintexts[i],
                        t.name~": ("~result~") != ("~test_plaintexts[i]~")");
            }
        }
    }
}