123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
/**
 * Copyright: Copyright (C) Thomas Dixon 2008. All rights reserved.
 * License:   BSD style: $(LICENSE)
 * Authors:   Thomas Dixon
 */

module tango.util.cipher.Cipher;

private import tango.core.Exception : IllegalArgumentException;

/** Base symmetric cipher class */
abstract class Cipher
{
    enum bool ENCRYPT = true,
              DECRYPT = false;
                      
    protected bool _initialized,
                   _encrypt;
    
    /**
     * Process a block of plaintext data from the input array
     * and place it in the output array.
     *
     * Params:
     *     input_  = Array containing input data.
     *     output_  = Array to hold the output data.
     *
     * Returns: The amount of encrypted data processed.
     */
    abstract uint update(const(void[]) input_, void[] output_);
    
    /** Returns: The name of the algorithm of this cipher. */
    @property abstract const(char)[] name();
    
    /** Reset cipher to its state immediately subsequent the last init. */
    abstract void reset();
   
    /**
     * throw an InvalidArgument exception
     * 
     * Params:
     *     msg = message to associate with the exception
     */
    static void invalid (const(char[]) msg)
    {
        throw new IllegalArgumentException (msg.idup);
    }
     
    /** Returns: Whether or not the cipher has been initialized. */
    final const bool initialized()
    {
        return _initialized;
    }
}



/** Interface for a standard block cipher. */
abstract class BlockCipher : Cipher
{
    /** Returns: The block size in bytes that this cipher will operate on. */
    @property abstract const uint blockSize();
}


/** Interface for a standard stream cipher. */
abstract class StreamCipher : Cipher
{   
    /**
     * Process one byte of input.
     *
     * Params:
     *     input = Byte to XOR with keystream.
     *
     * Returns: One byte of input XORed with the keystream.
     */
    abstract ubyte returnByte(ubyte input);
}

 
 /** Base padding class for implementing block padding schemes. */
 abstract class BlockCipherPadding
 {
    /** Returns: The name of the padding scheme implemented. */
    @property abstract const(char)[] name();

    /**
    * Generate padding to a specific length.
    *
    * Params:
    *     len = Length of padding to generate
    *
    * Returns: The padding bytes to be added.
    */ 
    abstract ubyte[] pad(uint len);

    /**
    * Return the number of pad bytes in the block.
    *
    * Params:
    *     input_ = Padded block of which to count the pad bytes.
    *
    * Returns: The number of pad bytes in the block.
    *
    * Throws: dcrypt.crypto.errors.InvalidPaddingError if 
    *         pad length cannot be discerned.
    */
    abstract uint unpad(const(void[]) input_);
 }

struct Bitwise
{
    static uint rotateLeft(uint x, uint y)
    {
        return (x << y) | (x >> (32u-y));
    }
    
    static uint rotateRight(uint x, uint y)
    {
        return (x >> y) | (x << (32u-y));    
    }
    
    static ulong rotateLeft(ulong x, uint y)
    {
        return (x << y) | (x >> (64u-y));
    }
    
    static ulong rotateRight(ulong x, uint y)
    {
        return (x >> y) | (x << (64u-y));    
    }
}


/** Converts between integral types and unsigned byte arrays */
struct ByteConverter
{
    private enum immutable(char)[] hexits = "0123456789abcdef";
    private enum immutable(char)[] base32digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    
    /** Conversions between little endian integrals and bytes */
    struct LittleEndian
    {
        /**
         * Converts the supplied array to integral type T
         * 
         * Params:
         *     x_ = The supplied array of bytes (ubytes, bytes, chars, whatever)
         * 
         * Returns:
         *     A integral of type T created with the supplied bytes placed
         *     in the specified byte order.
         */
        static T to(T)(const(void[]) x_)
        {
            const(ubyte[]) x = cast(const(ubyte[]))x_;
            
            T result = ((cast(T)x[0])       |
                       ((cast(T)x[1]) << 8));
                       
            static if (T.sizeof >= int.sizeof)
            {
                result |= ((cast(T)x[2]) << 16) |
                          ((cast(T)x[3]) << 24);
            }
            
            static if (T.sizeof >= long.sizeof)
            {
                result |= ((cast(T)x[4]) << 32) |
                          ((cast(T)x[5]) << 40) |
                          ((cast(T)x[6]) << 48) |
                          ((cast(T)x[7]) << 56);
            }
            
            return result;
        }
        
        /**
         * Converts the supplied integral to an array of unsigned bytes.
         * 
         * Params:
         *     input = Integral to convert to bytes
         * 
         * Returns:
         *     Integral input of type T split into its respective bytes
         *     with the bytes placed in the specified byte order.
         */
        static void from(T)(T input, ubyte[] output)
        {
            output[0] = cast(ubyte)(input);
            output[1] = cast(ubyte)(input >> 8);
            
            static if (T.sizeof >= int.sizeof)
            {
                output[2] = cast(ubyte)(input >> 16);
                output[3] = cast(ubyte)(input >> 24);
            }
            
            static if (T.sizeof >= long.sizeof)
            {
                output[4] = cast(ubyte)(input >> 32);
                output[5] = cast(ubyte)(input >> 40);
                output[6] = cast(ubyte)(input >> 48);
                output[7] = cast(ubyte)(input >> 56);
            }
        }
    }
    
    /** Conversions between big endian integrals and bytes */
    struct BigEndian
    {
        
        static T to(T)(const(void[]) x_)
        {
            const(ubyte[]) x = cast(const(ubyte[]))x_;
            
            static if (is(T == ushort) || is(T == short))
            {
                return cast(T) (((x[0] & 0xff) << 8) |
                                 (x[1] & 0xff));
            }
            else static if (is(T == uint) || is(T == int))
            {
                return cast(T) (((x[0] & 0xff) << 24) |
                                ((x[1] & 0xff) << 16) |
                                ((x[2] & 0xff) << 8)  |
                                 (x[3] & 0xff));
            }
            else static if (is(T == ulong) || is(T == long))
            {
                return cast(T) ((cast(T)(x[0] & 0xff) << 56) |
                                (cast(T)(x[1] & 0xff) << 48) |
                                (cast(T)(x[2] & 0xff) << 40) |
                                (cast(T)(x[3] & 0xff) << 32) |
                                ((x[4] & 0xff) << 24) |
                                ((x[5] & 0xff) << 16) |
                                ((x[6] & 0xff) << 8)  |
                                 (x[7] & 0xff));
            }
        }
        
        static void from(T)(T input, ubyte[] output)
        {
            static if (T.sizeof == long.sizeof)
            {
                output[0] = cast(ubyte)(input >> 56);
                output[1] = cast(ubyte)(input >> 48);
                output[2] = cast(ubyte)(input >> 40);
                output[3] = cast(ubyte)(input >> 32);
                output[4] = cast(ubyte)(input >> 24);
                output[5] = cast(ubyte)(input >> 16);
                output[6] = cast(ubyte)(input >> 8);
                output[7] = cast(ubyte)(input);
            }
            else static if (T.sizeof == int.sizeof)
            {
                output[0] = cast(ubyte)(input >> 24);
                output[1] = cast(ubyte)(input >> 16);
                output[2] = cast(ubyte)(input >> 8);
                output[3] = cast(ubyte)(input);
            }
            else static if (T.sizeof == short.sizeof)
            {
                output[0] = cast(ubyte)(input >> 8);
                output[1] = cast(ubyte)(input);
            }
        }
    }

    static char[] hexEncode(const(void[]) input_)
    {
        const(ubyte[]) input = cast(const(ubyte[]))input_;
        char[] output = new char[input.length<<1];
        
        int i = 0;
        foreach (ubyte j; input)
        { 
            output[i++] = hexits[j>>4];
            output[i++] = hexits[j&0xf];
        }
        
        return output;    
    }
    
    static char[] base32Encode(const(void[]) input_, bool doPad=true)
    {
        if (!input_)
            return "".dup;
        const(ubyte[]) input = cast(const(ubyte[]))input_;
        char[] output;
        auto inputbits = input.length*8;
        auto inputquantas = inputbits / 40;
        if (inputbits % 40)
            output = new char[(inputquantas+1) * 8];
        else
            output = new char[inputquantas * 8];

        int i = 0;
        ushort remainder;
        ubyte remainlen;
        foreach (ubyte j; input)
        {
            remainder = cast(ushort)(remainder<<8) | j;
            remainlen += 8;
            while (remainlen > 5) {
                output[i++] = base32digits[(remainder>>(remainlen-5))&0b11111];
                remainlen -= 5;
            }
        }
        if (remainlen)
            output[i++] = base32digits[(remainder<<(5-remainlen))&0b11111];
        while (doPad && (i < output.length)) {
            output[i++] = '=';
        }

        return output[0..i];
    }

    static ubyte[] hexDecode(const(char[]) input)
    {
        char[] inputAsLower = stringToLower(input);
        ubyte[] output = new ubyte[input.length>>1];
        
        static __gshared ubyte[char] hexitIndex;
        for (int i = 0; i < hexits.length; i++)
            hexitIndex[hexits[i]] = cast(ubyte) i;
            
        for (int i = 0, j = 0; i < output.length; i++)
        {
            output[i] = cast(ubyte) (hexitIndex[inputAsLower[j++]] << 4);
            output[i] |= hexitIndex[inputAsLower[j++]]; 
        }
        
        return output;
    }
    
    static ubyte[] base32Decode(const(char[]) input)
    {
        static __gshared ubyte[char] b32Index;
        for (int i = 0; i < base32digits.length; i++)
            b32Index[base32digits[i]] = cast(ubyte) i;

        auto outlen = (input.length*5)/8;
        ubyte[] output = new ubyte[outlen];

        ushort remainder;
        ubyte remainlen;
        size_t oIndex;
        foreach (c; stringToUpper(input))
        {
            if (c == '=')
                continue;
            remainder = cast(ushort)(remainder<<5) | b32Index[c];
            remainlen += 5;
            while (remainlen >= 8) {
                output[oIndex++] = cast(ubyte) (remainder >> (remainlen-8));
                remainlen -= 8;
            }
        }

        return output[0..oIndex];
    }

    private static char[] stringToLower(const(char[]) input)
    {
        char[] output = new char[input.length];
        
        foreach (int i, char c; input) 
            output[i] = cast(char) ((c >= 'A' && c <= 'Z') ? c+32 : c);
            
        return cast(char[])output;
    }

    private static char[] stringToUpper(const(char[]) input)
    {
        char[] output = new char[input.length];

        foreach (int i, char c; input)
            output[i] = cast(char) ((c >= 'a' && c <= 'z') ? c-32 : c);

        return cast(char[])output;
    }
}