view dcrypt/crypto/ciphers/RC6.d @ 21:ec23779ee794

Removed redundant test vector from Blowfish unittest.
author Thomas Dixon <reikon@reikon.us>
date Sat, 10 Jan 2009 13:15:14 -0500
parents 5ce3012f1def
children 4589f8c5eb3c
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.RC6;

import dcrypt.misc.Util;
import dcrypt.crypto.BlockCipher;

/**
 * Implementation of the RC6-32/20/b cipher designed by 
 * Ron Rivest et al. of RSA Security.
 * 
 * It should be noted that this algorithm is very similar to RC5.
 * Currently there are no plans to implement RC5, but should that change
 * in the future, it may be wise to rewrite both RC5 and RC6 to use some
 * kind of template or base class.
 *
 * This algorithm is patented and trademarked.
 * 
 * References: http://people.csail.mit.edu/rivest/Rc6.pdf
 */
class RC6 : BlockCipher {
    private {
        const uint ROUNDS = 20,
                   BLOCK_SIZE = 16,
                   // Magic constants for a 32 bit word size
                   P = 0xb7e15163,
                   Q = 0x9e3779b9;
        uint[] S;
        ubyte[] workingKey;
    }
    
    char[] name() {
        return "RC6";
    }
    
    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;
        
        uint len = keyParams.key.length;
        if (len != 16 && len != 24 && len != 32)
            throw new InvalidKeyError(
                    name()~": Invalid key length (requires 16/24/32 bytes)");
        
        S = new uint[2*ROUNDS+4];        
                   
        workingKey = keyParams.key;
        setup(workingKey);
        
        _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 A = Util.ubytesToUintLittle(input, 0),
             B = Util.ubytesToUintLittle(input, 4),
             C = Util.ubytesToUintLittle(input, 8),
             D = Util.ubytesToUintLittle(input, 12),
             t,
             u;
             
        if (_encrypt) {
            B += S[0];
            D += S[1];
            for (int i = 1; i <= ROUNDS; i++) {
                t = Util.rotateLeft(B*((B<<1)+1), 5);
                u = Util.rotateLeft(D*((D<<1)+1), 5);
                A = Util.rotateLeft(A^t, u) + S[i<<1];
                C = Util.rotateLeft(C^u, t) + S[(i<<1)+1];
                t = A;
                A = B;
                B = C;
                C = D;
                D = t;
            }
            A += S[2*ROUNDS+2];
            C += S[2*ROUNDS+3];
        } else {
            C -= S[2*ROUNDS+3];
            A -= S[2*ROUNDS+2];
            for (int i = ROUNDS; i >= 1; i--) {
                t = D;
                D = C;
                C = B;
                B = A;
                A = t;
                u = Util.rotateLeft(D*((D<<1)+1), 5);
                t = Util.rotateLeft(B*((B<<1)+1), 5);
                C = Util.rotateRight(C-S[(i<<1)+1], t) ^ u;
                A = Util.rotateRight(A-S[i<<1], u) ^ t;
            }
            D -= S[1];
            B -= S[0];
        }

        Util.uintToUbytesLittle(A, output, 0);
        Util.uintToUbytesLittle(B, output, 4);
        Util.uintToUbytesLittle(C, output, 8);
        Util.uintToUbytesLittle(D, output, 12);
        
        return BLOCK_SIZE;
    }
    
    void reset() {
        setup(workingKey);
    }
    
    void setup(ubyte[] key) {
        uint c = key.length/4;
        uint[] L = new uint[c];
        for (int i = 0, j = 0; i < c; i++, j+=4)
            L[i] = Util.ubytesToUintLittle(key, j);
            
        S[0] = P;
        for (int i = 1; i <= 2*ROUNDS+3; i++)
            S[i] = S[i-1] + Q;
            
        uint A, B, i, j, v = 3*(2*ROUNDS+4); // Relying on ints initializing to 0   
        for (int s = 1; s <= v; s++) {
            A = S[i] = Util.rotateLeft(S[i]+A+B, 3);
            B = L[j] = Util.rotateLeft(L[j]+A+B, A+B);
            i = (i + 1) % (2*ROUNDS+4);
            j = (j + 1) % c;
        }
    }
    
    /** Some RC6 test vectors from the spec. */
    version (UnitTest) {
        unittest {
            static const char[][] test_keys = [
                "00000000000000000000000000000000",
                "0123456789abcdef0112233445566778",
                "00000000000000000000000000000000"~
                "0000000000000000",
                "0123456789abcdef0112233445566778"~
                "899aabbccddeeff0",
                "00000000000000000000000000000000"~
                "00000000000000000000000000000000",
                "0123456789abcdef0112233445566778"~
                "899aabbccddeeff01032547698badcfe"
            ];
                 
            static const char[][] test_plaintexts = [
                "00000000000000000000000000000000",
                "02132435465768798a9bacbdcedfe0f1",
                "00000000000000000000000000000000",
                "02132435465768798a9bacbdcedfe0f1",
                "00000000000000000000000000000000",
                "02132435465768798a9bacbdcedfe0f1"
            ];
                
            static const char[][] test_ciphertexts = [
                "8fc3a53656b1f778c129df4e9848a41e",
                "524e192f4715c6231f51f6367ea43f18",
                "6cd61bcb190b30384e8a3f168690ae82",
                "688329d019e505041e52e92af95291d4",
                "8f5fbd0510d15fa893fa3fda6e857ec2",
                "c8241816f0d7e48920ad16a1674e5d48"
            ];
                
            RC6 t = new RC6();
            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.update(Util.hexToUbytes(test_plaintexts[i]), buffer);
                result = Util.ubytesToHex(buffer);
                assert(result == test_ciphertexts[i],
                        t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
            
                // Decryption
                t.init(false, key);
                t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer);
                result = Util.ubytesToHex(buffer);
                assert(result == test_plaintexts[i],
                        t.name~": ("~result~") != ("~test_plaintexts[i]~")");
            }
        }
    }
}