123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
|
/**
* Copyright: Copyright (C) Thomas Dixon 2008. All rights reserved.
* License: BSD style: $(LICENSE)
* Authors: Thomas Dixon
*/
module tango.util.cipher.RC6;
private import tango.util.cipher.Cipher;
/**
* 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
{
enum uint ROUNDS = 20,
BLOCK_SIZE = 16,
// Magic constants for a 32 bit word size
P = 0xb7e15163,
Q = 0x9e3779b9;
uint[] S;
const(ubyte)[] workingKey;
}
this() {}
this(bool encrypt, ubyte[] key) {
this();
init(encrypt, key);
}
@property final override const(char)[] name()
{
return "RC6";
}
@property final override const uint blockSize()
{
return BLOCK_SIZE;
}
final void init(bool encrypt, ubyte[] key)
{
_encrypt = encrypt;
auto len = key.length;
if (len != 16 && len != 24 && len != 32)
invalid(name()~": Invalid key length (requires 16/24/32 bytes)");
S = new uint[2*ROUNDS+4];
workingKey = key;
setup(workingKey);
_initialized = true;
}
final override uint update(const(void[]) input_, void[] output_) {
if (!_initialized)
invalid(name()~": Cipher not initialized");
const(ubyte[]) input = cast(const(ubyte[])) input_;
ubyte[] output = cast(ubyte[]) output_;
if (input.length < BLOCK_SIZE)
invalid(name()~": Input buffer too short");
if (output.length < BLOCK_SIZE)
invalid(name()~": Output buffer too short");
uint A = ByteConverter.LittleEndian.to!(uint)(input[0..4]),
B = ByteConverter.LittleEndian.to!(uint)(input[4..8]),
C = ByteConverter.LittleEndian.to!(uint)(input[8..12]),
D = ByteConverter.LittleEndian.to!(uint)(input[12..16]),
t,
u;
if (_encrypt)
{
B += S[0];
D += S[1];
for (int i = 1; i <= ROUNDS; i++)
{
t = Bitwise.rotateLeft(B*((B<<1)+1), 5u);
u = Bitwise.rotateLeft(D*((D<<1)+1), 5u);
A = Bitwise.rotateLeft(A^t, u) + S[i<<1];
C = Bitwise.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 = Bitwise.rotateLeft(D*((D<<1)+1), 5u);
t = Bitwise.rotateLeft(B*((B<<1)+1), 5u);
C = Bitwise.rotateRight(C-S[(i<<1)+1], t) ^ u;
A = Bitwise.rotateRight(A-S[i<<1], u) ^ t;
}
D -= S[1];
B -= S[0];
}
ByteConverter.LittleEndian.from!(uint)(A, output[0..4]);
ByteConverter.LittleEndian.from!(uint)(B, output[4..8]);
ByteConverter.LittleEndian.from!(uint)(C, output[8..12]);
ByteConverter.LittleEndian.from!(uint)(D, output[12..16]);
return BLOCK_SIZE;
}
final override void reset()
{
setup(workingKey);
}
private void setup(const(ubyte)[] key)
{
size_t c = key.length/4;
uint[] L = new uint[c];
for (int i = 0, j = 0; i < c; i++, j+=4)
L[i] = ByteConverter.LittleEndian.to!(uint)(key[j..j+int.sizeof]);
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] = Bitwise.rotateLeft(S[i]+A+B, 3u);
B = L[j] = Bitwise.rotateLeft(L[j]+A+B, A+B);
i = (i + 1) % (2*ROUNDS+4);
j = (j + 1) % c;
}
}
/** Some RC6 test vectors from the spec. */
debug (UnitTest)
{
unittest
{
enum immutable(char)[][] test_keys = [
"00000000000000000000000000000000",
"0123456789abcdef0112233445566778",
"00000000000000000000000000000000"~
"0000000000000000",
"0123456789abcdef0112233445566778"~
"899aabbccddeeff0",
"00000000000000000000000000000000"~
"00000000000000000000000000000000",
"0123456789abcdef0112233445566778"~
"899aabbccddeeff01032547698badcfe"
];
enum immutable(char)[][] test_plaintexts = [
"00000000000000000000000000000000",
"02132435465768798a9bacbdcedfe0f1",
"00000000000000000000000000000000",
"02132435465768798a9bacbdcedfe0f1",
"00000000000000000000000000000000",
"02132435465768798a9bacbdcedfe0f1"
];
enum immutable(char)[][] test_ciphertexts = [
"8fc3a53656b1f778c129df4e9848a41e",
"524e192f4715c6231f51f6367ea43f18",
"6cd61bcb190b30384e8a3f168690ae82",
"688329d019e505041e52e92af95291d4",
"8f5fbd0510d15fa893fa3fda6e857ec2",
"c8241816f0d7e48920ad16a1674e5d48"
];
RC6 t = new RC6();
foreach (uint i, immutable(char)[] test_key; test_keys)
{
ubyte[] buffer = new ubyte[t.blockSize];
char[] result;
auto key = ByteConverter.hexDecode(test_key);
// Encryption
t.init(true, key);
t.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
result = ByteConverter.hexEncode(buffer);
assert(result == test_ciphertexts[i],
t.name~": ("~result~") != ("~test_ciphertexts[i]~")");
// Decryption
t.init(false, key);
t.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
result = ByteConverter.hexEncode(buffer);
assert(result == test_plaintexts[i],
t.name~": ("~result~") != ("~test_plaintexts[i]~")");
}
}
}
}
|