view dcrypt/crypto/ciphers/TEA.d @ 1:483e4467b5f6

Added Blowfish with test vectors. Minor cleanup of other cipher classes (should probably clean more). Continued work on high-level cipher API (didn't get very far).
author Thomas Dixon <reikon@reikon.us>
date Tue, 12 Aug 2008 05:48:06 -0400
parents 0e08791a1418
children 71aae178f89a
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.Util;
import dcrypt.crypto.BlockCipher;

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

        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);
        
        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;
            }
        }
        
        Util.uintToUbytesBig(v0, output, outOff);
        Util.uintToUbytesBig(v1, output, outOff+4);
        
        return BLOCK_SIZE;
    }
    
    /** Some TEA test vectors. */
    unittest {
        static const char[][] test_keys = [
            "00000000000000000000000000000000",
            "00000000000000000000000000000000",
            "0123456712345678234567893456789a",
            "0123456712345678234567893456789a"
        ];
             
        static const char[][] test_plaintexts = [
            "0000000000000000",
            "0102030405060708",
            "0000000000000000",
            "0102030405060708"
        ];
            
        static const char[][] test_ciphertexts = [
            "41ea3a0a94baa940",
            "6a2f9cf3fccf3c55",
            "34e943b0900f5dcb",
            "773dc179878a81c0"
        ];
            
        
        TEA t = new TEA();
        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]~")");
        }
    }
}