mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-27 13:24:13 +08:00
156 lines
5.4 KiB
C#
156 lines
5.4 KiB
C#
|
//! \file RC6.cs
|
||
|
//! \date Mon Oct 03 14:47:13 2016
|
||
|
//! \brief RC6 encryption implementation.
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Linq;
|
||
|
using System.Security.Cryptography;
|
||
|
using GameRes.Utility;
|
||
|
|
||
|
namespace GameRes.Cryptography
|
||
|
{
|
||
|
public sealed class RC6 : ICryptoTransform
|
||
|
{
|
||
|
internal const int BlockSize = 16;
|
||
|
internal const int DefaultRounds = 20;
|
||
|
|
||
|
public bool CanTransformMultipleBlocks { get { return true; } }
|
||
|
public bool CanReuseTransform { get { return false; } }
|
||
|
public int InputBlockSize { get { return BlockSize; } }
|
||
|
public int OutputBlockSize { get { return BlockSize; } }
|
||
|
|
||
|
uint[] m_state;
|
||
|
byte[] m_iv;
|
||
|
const uint P = 0xB7E15163;
|
||
|
const uint Q = 0x9E3779B9;
|
||
|
|
||
|
public RC6 (byte[] key, byte[] iv)
|
||
|
{
|
||
|
m_state = new uint[2 * (DefaultRounds + 2)];
|
||
|
int key_length = Math.Max ((key.Length + 3) / 4, 1);
|
||
|
var key_copy = new uint[key_length];
|
||
|
Buffer.BlockCopy (key, 0, key_copy, 0, key.Length);
|
||
|
|
||
|
m_state[0] = P;
|
||
|
for (int i = 1; i < m_state.Length; ++i)
|
||
|
m_state[i] = m_state[i-1] + Q;
|
||
|
|
||
|
uint a = 0, b = 0;
|
||
|
int n = 3 * Math.Max (m_state.Length, key_length);
|
||
|
for (int h = 0; h < n; ++h)
|
||
|
{
|
||
|
a = m_state[h % m_state.Length] = Binary.RotL (m_state[h % m_state.Length] + a + b, 3);
|
||
|
b = key_copy[h % key_length] = Binary.RotL ((key_copy[h % key_length] + a + b), (int)(a + b));
|
||
|
}
|
||
|
|
||
|
m_iv = new byte[BlockSize];
|
||
|
if (iv != null)
|
||
|
Buffer.BlockCopy (iv, 0, m_iv, 0, Math.Min (iv.Length, BlockSize));
|
||
|
}
|
||
|
|
||
|
public int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset)
|
||
|
{
|
||
|
int out_count = count / BlockSize;
|
||
|
for (int i = 0; i < out_count; ++i)
|
||
|
{
|
||
|
// CFB mode
|
||
|
Encrypt (m_iv, 0, outBuffer, outOffset);
|
||
|
for (int j = 0; j < BlockSize; ++j)
|
||
|
{
|
||
|
byte b = inBuffer[offset++];
|
||
|
outBuffer[outOffset++] ^= b;
|
||
|
m_iv[j] = b;
|
||
|
}
|
||
|
}
|
||
|
return out_count * BlockSize;
|
||
|
}
|
||
|
|
||
|
public byte[] TransformFinalBlock (byte[] inBuffer, int offset, int count)
|
||
|
{
|
||
|
if (count < BlockSize)
|
||
|
return new ArraySegment<byte> (inBuffer, offset, count).ToArray();
|
||
|
var output = new byte[count];
|
||
|
int tail = count / BlockSize * BlockSize;
|
||
|
count -= TransformBlock (inBuffer, offset, count, output, 0);
|
||
|
if (count > 0)
|
||
|
Buffer.BlockCopy (inBuffer, offset+tail, output, tail, count);
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
private void Encrypt (byte[] inBuffer, int offset, byte[] outBuffer, int outOffset)
|
||
|
{
|
||
|
uint a = LittleEndian.ToUInt32 (inBuffer, offset);
|
||
|
uint b = LittleEndian.ToUInt32 (inBuffer, offset+4);
|
||
|
uint c = LittleEndian.ToUInt32 (inBuffer, offset+8);
|
||
|
uint d = LittleEndian.ToUInt32 (inBuffer, offset+12);
|
||
|
|
||
|
b += m_state[0];
|
||
|
d += m_state[1];
|
||
|
int sptr = 2;
|
||
|
|
||
|
for (int i = 0; i < DefaultRounds; ++i)
|
||
|
{
|
||
|
uint t, u;
|
||
|
t = Binary.RotL (b * (2 * b + 1), 5);
|
||
|
u = Binary.RotL (d * (2 * d + 1), 5);
|
||
|
a = Binary.RotL (a ^ t, (int)u) + m_state[sptr++];
|
||
|
c = Binary.RotL (c ^ u, (int)t) + m_state[sptr++];
|
||
|
t = a;
|
||
|
a = b;
|
||
|
b = c;
|
||
|
c = d;
|
||
|
d = t;
|
||
|
}
|
||
|
a += m_state[sptr];
|
||
|
c += m_state[sptr+1];
|
||
|
|
||
|
LittleEndian.Pack (a, outBuffer, outOffset);
|
||
|
LittleEndian.Pack (b, outBuffer, outOffset+4);
|
||
|
LittleEndian.Pack (c, outBuffer, outOffset+8);
|
||
|
LittleEndian.Pack (d, outBuffer, outOffset+12);
|
||
|
}
|
||
|
|
||
|
private void Decrypt (byte[] inBuffer, int offset, byte[] outBuffer, int outOffset)
|
||
|
{
|
||
|
uint a = LittleEndian.ToUInt32 (inBuffer, offset);
|
||
|
uint b = LittleEndian.ToUInt32 (inBuffer, offset+4);
|
||
|
uint c = LittleEndian.ToUInt32 (inBuffer, offset+8);
|
||
|
uint d = LittleEndian.ToUInt32 (inBuffer, offset+12);
|
||
|
|
||
|
int sptr = m_state.Length - 2;
|
||
|
c -= m_state[sptr+1];
|
||
|
a -= m_state[sptr];
|
||
|
|
||
|
for (int i = 0; i < DefaultRounds; ++i)
|
||
|
{
|
||
|
uint t, u;
|
||
|
sptr -= 2;
|
||
|
t = a;
|
||
|
a = d;
|
||
|
d = c;
|
||
|
c = b;
|
||
|
b = t;
|
||
|
u = Binary.RotL (d*(2*d+1), 5);
|
||
|
t = Binary.RotL (b*(2*b+1), 5);
|
||
|
c = Binary.RotR (c-m_state[sptr+1], (int)t) ^ u;
|
||
|
a = Binary.RotR (a-m_state[sptr ], (int)u) ^ t;
|
||
|
}
|
||
|
d -= m_state[1];
|
||
|
b -= m_state[0];
|
||
|
|
||
|
LittleEndian.Pack (a, outBuffer, outOffset);
|
||
|
LittleEndian.Pack (b, outBuffer, outOffset+4);
|
||
|
LittleEndian.Pack (c, outBuffer, outOffset+8);
|
||
|
LittleEndian.Pack (d, outBuffer, outOffset+12);
|
||
|
}
|
||
|
|
||
|
#region IDisposable implementation
|
||
|
public void Dispose ()
|
||
|
{
|
||
|
GC.SuppressFinalize (this);
|
||
|
}
|
||
|
#endregion
|
||
|
}
|
||
|
}
|