Mercurial > projects > dcrypt
diff dcrypt/crypto/ciphers/RC6.d @ 0:0e08791a1418
Initial import.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Sun, 10 Aug 2008 14:20:17 -0400 |
parents | |
children | 483e4467b5f6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/ciphers/RC6.d Sun Aug 10 14:20:17 2008 -0400 @@ -0,0 +1,208 @@ +/** + * This file is part of the dcrypt project. + * + * 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. + * + * 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. + * + * Note: This algorithm is patented and trademarked. + */ +class RC6 : BlockCipher { + private const uint ROUNDS = 20, + BLOCK_SIZE = 16, + // Magic constants for a 32 bit word size + P = 0xb7e15163, + Q = 0x9e3779b9; + private uint[] S; + private ubyte[] workingKey; + private bool initialized, + encrypt; + + 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"); + this.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 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 A = Util.ubytesToUintLittle(input, inOff), + B = Util.ubytesToUintLittle(input, inOff+4), + C = Util.ubytesToUintLittle(input, inOff+8), + D = Util.ubytesToUintLittle(input, inOff+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, outOff); + Util.uintToUbytesLittle(B, output, outOff+4); + Util.uintToUbytesLittle(C, output, outOff+8); + Util.uintToUbytesLittle(D, output, outOff+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.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]~")"); + } + } + } +}