Скачиваний:
23
Добавлен:
01.05.2014
Размер:
12 Кб
Скачать
// $Id: $
//
// $Log: $
// Revision 1.1.1  1998/04/10  raif
// + added code to generate Intermediate Values KAT.
// + cosmetics.
//
// Revision 1.1  1998/04/07  Serpent authors
// + revised slightly (endianness, and key schedule for variable lengths)
//
// Revision 1.0  1998/04/06  raif
// + original version.
//
// $Endlog$
/*
 * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
 * the Cryptix Development Team. All rights reserved.
 */
package Serpent;

import java.io.PrintWriter;
import java.security.InvalidKeyException;

//...........................................................................
/**
 * Serpent is a 128-bit 32-round block cipher with variable key lengths,
 * including 128-, 192- and 256-bit
 * keys conjectured to be at least as secure as three-key triple-DES.<p>
 *
 * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
 * candidate algorithm for the NIST AES Quest.<p>
 *
 * References:<ol>
 *  <li>Serpent: A New Block Cipher Proposal. This paper was published in the
 *  proceedings of the "Fast Software Encryption Workshop No. 5" held in
 *  Paris in March 1998. LNCS, Springer Verlag.<p>
 *  <li>Reference implementation of the standard Serpent cipher written in C
 *  by <a href="http://www.cl.cam.ac.uk/~fms/">Frank Stajano</a>.</ol><p>
 *
 * <b>Copyright</b> &copy; 1997, 1998
 * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
 * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
 * <br>All rights reserved.<p>
 *
 * <b>$Revision: $</b>
 * @author  Raif S. Naffah
 * @author  Serpent authors (Ross Anderson, Eli Biham and Lars Knudsen)
 */
public final class Serpent_Algorithm // implicit no-argument constructor
{
// Debugging methods and variables
//...........................................................................

    static final String NAME = "Serpent_Algorithm";
    static final boolean IN = true, OUT = false;

    static final boolean DEBUG = Serpent_Properties.GLOBAL_DEBUG;
    static final int debuglevel = DEBUG ? Serpent_Properties.getLevel(NAME) : 0;
    static final PrintWriter err = DEBUG ? Serpent_Properties.getOutput() : null;

    static final boolean TRACE = Serpent_Properties.isTraceable(NAME);

    static void debug (String s) { err.println(">>> "+NAME+": "+s); }
    static void trace (boolean in, String s) {
        if (TRACE) err.println((in?"==> ":"<== ")+NAME+"."+s);
    }
    static void trace (String s) { if (TRACE) err.println("<=> "+NAME+"."+s); }


// Constants and variables
//...........................................................................

    /**
     * This variable acts as a selector to allow the user to choose between
     * a bit-slice implementation (Serpent_BitSlice) and a standard one
     * (Serpent_Standard). When set to true the bit-slice implementation
     * code is used, otherwise it's the standard implementation code.<p>
     *
     * IMPORTANT: When you change the value of this variable, make sure
     * you re-compile all the classes in the Serpent package.
     */
    static final boolean USE_BIT_SLICE_IMPLEMENTATION = true;

    static final int BLOCK_SIZE = 16; // bytes in a Serpent data-block

    private static final char[] HEX_DIGITS = {
        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
    };


// Basic API methods
//...........................................................................

    /**
     * Expand a user-supplied key material into a session key.
     *
     * @param key  The user-key bytes (multiples of 4) to use.
     * @exception  InvalidKeyException  If the key is invalid.
     */
    public static synchronized Object makeKey (byte[] key)
    throws InvalidKeyException {
        if (key == null)
            throw new InvalidKeyException("Empty key");
/*
        if (!(key.length == 16 || key.length == 24 || key.length == 32))
             throw new InvalidKeyException("Incorrect key length");

	// Although it checks for size of 16, 24 or 32, it really
	// supports all multiples of 4 up to 32 in the code of makeKey.
*/
        if ((key.length % 4) != 0 || (key.length / 4) > 8)
             throw new InvalidKeyException("Incorrect key length");

        return USE_BIT_SLICE_IMPLEMENTATION ?
            Serpent_BitSlice.makeKey(key) :
            Serpent_Standard.makeKey(key);
    }

    /**
     * Encrypt exactly one block of plaintext.
     *
     * @param  in         The plaintext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for encryption.
     * @return The ciphertext generated from a plaintext using the session key.
     */
    public static byte[]
    blockEncrypt (byte[] in, int inOffset, Object sessionKey) {
        return USE_BIT_SLICE_IMPLEMENTATION ?
            Serpent_BitSlice.blockEncrypt(in, inOffset, sessionKey) :
            Serpent_Standard.blockEncrypt(in, inOffset, sessionKey);
    }

    /**
     * Decrypt exactly one block of ciphertext.
     *
     * @param  in         The ciphertext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for decryption.
     * @return The plaintext generated from a ciphertext using the session key.
     */
    public static byte[]
    blockDecrypt (byte[] in, int inOffset, Object sessionKey) {
        return USE_BIT_SLICE_IMPLEMENTATION ?
            Serpent_BitSlice.blockDecrypt(in, inOffset, sessionKey) :
            Serpent_Standard.blockDecrypt(in, inOffset, sessionKey);
    }

    /** A basic symmetric encryption/decryption test. */ 
    public static boolean self_test() {
if (DEBUG) trace(IN, "self_test()");
        boolean ok = false;
        try {
            byte[] kb = new byte[BLOCK_SIZE*2]; // all zeroes
// kb=fromString("0000000000000000000000000000000000000000000000000000000000000000");
            byte[] pt = fromEvenLengthString("00000003000000020000000100000000");
            int i;

            Object key = makeKey(kb);

            byte[] ct =  blockEncrypt(pt, 0, key);
            byte[] cpt = blockDecrypt(ct, 0, key);

            ok = areEqual(pt, cpt);
            if (!ok) {
if (DEBUG && debuglevel > 7) {
    debug("  plain: "+toReversedString(pt));
    debug(" cipher: "+toReversedString(ct));
    debug(" plain2: "+toReversedString(cpt));
}
                throw new RuntimeException("Symmetric operation failed");
            }
            ok = self_test(BLOCK_SIZE);
        } catch (Exception x) {
if (DEBUG && debuglevel > 0) {
    debug("Exception encountered during self-test: " + x.getMessage());
    x.printStackTrace();
}
        }
if (DEBUG && debuglevel > 0) debug("Self-test OK? " + ok);
if (DEBUG) trace(OUT, "self_test()");
        return ok;
    }


// Serpent own methods
//...........................................................................
    
    /** @return The length in bytes of the Algorithm input block. */
    public static int blockSize() { return BLOCK_SIZE; }

    /** A basic symmetric encryption/decryption test for a given key size. */
    private static boolean self_test (int keysize) {
if (DEBUG) trace(IN, "self_test("+keysize+")");
        boolean ok = false;
        try {
            byte[] kb = new byte[keysize];
            byte[] pt = new byte[BLOCK_SIZE];
            int i;

            for (i = 0; i < keysize; i++)
                kb[i] = (byte) i;
            for (i = 0; i < BLOCK_SIZE; i++)
                pt[i] = (byte) i;

if (DEBUG && debuglevel > 6) {
System.out.println("==========");
System.out.println();
System.out.println("KEYSIZE="+(8*keysize));
System.out.println("KEY="+toString(kb));
System.out.println();
}
            Object key = makeKey(kb);

if (DEBUG && debuglevel > 6) {
System.out.println("Intermediate Ciphertext Values (Encryption)");
System.out.println();
System.out.println("PT="+toString(pt));
}
            byte[] ct =  blockEncrypt(pt, 0, key);

if (DEBUG && debuglevel > 6) {
System.out.println("Intermediate Plaintext Values (Decryption)");
System.out.println();
System.out.println("CT="+toString(ct));
}
            byte[] cpt = blockDecrypt(ct, 0, key);

            ok = areEqual(pt, cpt);
            if (!ok)
                throw new RuntimeException("Symmetric operation failed");
        } catch (Exception x) {
if (DEBUG && debuglevel > 0) {
    debug("Exception encountered during self-test: " + x.getMessage());
    x.printStackTrace();
}
        }
if (DEBUG && debuglevel > 0) debug("Self-test OK? " + ok);
if (DEBUG) trace(OUT, "self_test()");
        return ok;
    }


// utility static methods (from cryptix.util.core ArrayUtil and Hex classes)
//...........................................................................
    
    /**
     * Compares two byte arrays for equality.
     *
     * @return true if the arrays have identical contents
     */
    private static boolean areEqual (byte[] a, byte[] b) {
        int aLength = a.length;
        if (aLength != b.length)
            return false;
        for (int i = 0; i < aLength; i++)
            if (a[i] != b[i])
                return false;
        return true;
    }

    /**
     * Returns a number from 0 to 15 corresponding to the hex
     * digit <i>ch</i>.
     */
    public static int fromDigit (char ch) {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        if (ch >= 'A' && ch <= 'F')
            return ch - 'A' + 10;
        if (ch >= 'a' && ch <= 'f')
            return ch - 'a' + 10;
        throw new IllegalArgumentException("Invalid hex digit '"+ch+"'");
    }

    /**
     * Returns a string of hexadecimal digits from a byte array. Each
     * byte is converted to 2 hex symbols.
     */
    private static String toString (byte[] ba) {
        int length = ba.length;
        char[] buf = new char[length * 2];
        for (int i = 0, j = 0, k; i < length; ) {
            k = ba[i++];
            buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
            buf[j++] = HEX_DIGITS[ k        & 0x0F];
        }
        return new String(buf);
    }


// other utility static methods
//...........................................................................

    /**
     * Returns an hexadecimal number (respresented as a string of hexadecimal 
     * digits from a byte array). Each byte is converted to 2 hex symbols.
     * The order is however, as of printing a number from a little-endian
     * internal representation (i.e., reverse order).
     */
    public static String toReversedString (byte[] ba) {
        int length = ba.length;
        char[] buf = new char[length * 2];
        for (int i = length-1, j = 0, k; i >=0; ) {
            k = ba[i--];
            buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
            buf[j++] = HEX_DIGITS[ k        & 0x0F];
        }
        return new String(buf);
    }
       
    /**
     * Returns a byte array from an hexadecimal number.
     */
    public static byte[] fromEvenLengthString (String hex) {
        int len = hex.length();
        byte[] buf = new byte[((len + 1) / 2)];

        int j = 0;
        if ((len % 2) == 1) throw new IllegalArgumentException(
            "string must have an even number of digits");

        while (len > 0) {
            buf[j++] = (byte) (fromDigit(hex.charAt(--len)) |
                              (fromDigit(hex.charAt(--len)) << 4));
        }
        return buf;
    }


// main(): use to generate the Intermediate Values KAT
//...........................................................................

    public static void main (String[] args) {
if (DEBUG && debuglevel > 6) {
System.out.println("Algorithm Name: "+Serpent_Properties.FULL_NAME);
System.out.println("Electronic Codebook (ECB) Mode");
System.out.println();
}
        self_test(16);
        self_test(24);
        self_test(32);
    }
}
Соседние файлы в папке Serpent