diff --git a/FastRandom.cs b/FastRandom.cs
new file mode 100644
index 00000000..bb0c29a1
--- /dev/null
+++ b/FastRandom.cs
@@ -0,0 +1,287 @@
+using System;
+
+namespace StreamFormatDecryptor
+{
+ public class FastRandom
+ {
+ // The +1 ensures NextDouble doesn't generate 1.0
+ private const double REAL_UNIT_INT = 1.0 / (int.MaxValue + 1.0);
+ private const double REAL_UNIT_UINT = 1.0 / (uint.MaxValue + 1.0);
+ private const uint W = 273326509;
+ private const uint Y = 842502087, Z = 3579807591;
+ private uint w;
+
+ private uint x, y, z;
+
+ #region Constructors
+
+ ///
+ /// Initialises a new instance using time dependent seed.
+ ///
+ public FastRandom()
+ {
+ // Initialise using the system tick count.
+ Reinitialise(Environment.TickCount);
+ }
+
+ ///
+ /// Initialises a new instance using an int value as seed.
+ /// This constructor signature is provided to maintain compatibility with
+ /// System.Random
+ ///
+ public FastRandom(int seed)
+ {
+ Reinitialise(seed);
+ }
+
+ #endregion
+
+ #region Public Methods [Reinitialisation]
+
+ ///
+ /// Reinitialises using an int value as a seed.
+ ///
+ ///
+ public void Reinitialise(int seed)
+ {
+ // The only stipulation stated for the xorshift RNG is that at least one of
+ // the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
+ // resetting of the x seed
+ x = (uint)seed;
+ y = Y;
+ z = Z;
+ w = W;
+ bitBufferIdx = 32;
+ }
+
+ #endregion
+
+ #region Public Methods [Next* methods]
+
+ private uint bitBuffer;
+ private int bitBufferIdx = 32;
+
+ ///
+ /// Generates a uint. Values returned are over the full range of a uint,
+ /// uint.MinValue to uint.MaxValue, including the min and max values.
+ ///
+ ///
+ public uint NextUInt()
+ {
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+ return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
+ }
+
+ ///
+ /// Generates a random int. Values returned are over the range 0 to int.MaxValue-1.
+ /// MaxValue is not generated to remain functionally equivalent to System.Random.Next().
+ /// If you require an int from the full range, including negative values then call
+ /// NextUint() and cast the value to an int.
+ ///
+ ///
+ public int Next()
+ {
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+ return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
+ }
+
+ ///
+ /// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
+ ///
+ ///
+ ///
+ public int Next(int upperBound)
+ {
+ if (upperBound < 0)
+ throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
+
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+
+ // The explicit int cast before the first multiplication gives better performance.
+ // See comments in NextDouble.
+ return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
+ }
+
+ ///
+ /// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
+ /// upperBound must be >= lowerBound. lowerBound may be negative.
+ ///
+ ///
+ ///
+ ///
+ public int Next(int lowerBound, int upperBound)
+ {
+ if (lowerBound > upperBound)
+ throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
+
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+
+ // The explicit int cast before the first multiplication gives better performance.
+ // See comments in NextDouble.
+ int range = upperBound - lowerBound;
+ if (range < 0)
+ {
+ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
+ // We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
+ return lowerBound +
+ (int)((REAL_UNIT_UINT * (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (upperBound - (long)lowerBound));
+ }
+
+ // 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int anf gain
+ // a little more performance.
+ return lowerBound +
+ (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * range);
+ }
+
+ ///
+ /// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
+ ///
+ ///
+ public double NextDouble()
+ {
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+
+ // Here we can gain a 2x speed improvement by generating a value that can be cast to
+ // an int instead of the more easily available uint. If we then explicitly cast to an
+ // int the compiler will then cast the int to a double to perform the multiplication,
+ // this final cast is a lot faster than casting from a uint to a double. The extra cast
+ // to an int is very fast (the allocated bits remain the same) and so the overall effect
+ // of the extra cast is a significant performance improvement.
+ return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
+ }
+
+ ///
+ /// Fills the provided byte array with random bytes.
+ /// Increased performance is achieved by dividing and packaging bits directly from the
+ /// random number generator and storing them in 4 byte 'chunks'.
+ ///
+ ///
+ public void NextBytes(byte[] buffer)
+ {
+ // Fill up the bulk of the buffer in chunks of 4 bytes at a time.
+ uint x = this.x, y = this.y, z = this.z, w = this.w;
+ int i = 0;
+ uint t;
+ for (; i < buffer.Length - 3;)
+ {
+ // Generate 4 bytes.
+ t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+
+ buffer[i++] = (byte)(w & 0x000000FF);
+ buffer[i++] = (byte)((w & 0x0000FF00) >> 8);
+ buffer[i++] = (byte)((w & 0x00FF0000) >> 16);
+ buffer[i++] = (byte)((w & 0xFF000000) >> 24);
+ }
+
+ // Fill up any remaining bytes in the buffer.
+ if (i < buffer.Length)
+ {
+ // Generate 4 bytes.
+ t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+
+ buffer[i++] = (byte)(w & 0x000000FF);
+ if (i < buffer.Length)
+ {
+ buffer[i++] = (byte)((w & 0x0000FF00) >> 8);
+ if (i < buffer.Length)
+ {
+ buffer[i++] = (byte)((w & 0x00FF0000) >> 16);
+ if (i < buffer.Length)
+ {
+ buffer[i] = (byte)((w & 0xFF000000) >> 24);
+ }
+ }
+ }
+ }
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+
+// ///
+// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
+// /// thus providing a nice speedup. Note that this requires the unsafe compilation flag to be specified
+// /// and so is commented out by default.
+// ///
+// ///
+// public unsafe void NextBytesUnsafe(byte[] buffer)
+// {
+// if(buffer.Length % 4 != 0)
+// throw new ArgumentException("Buffer length must be divisible by 4", "buffer");
+//
+// uint x=this.x, y=this.y, z=this.z, w=this.w;
+// uint t;
+//
+// fixed(byte* pByte0 = buffer)
+// {
+// uint* pDWord = (uint*)pByte0;
+// for(int i = 0, len = buffer.Length>>2; i < len; i++)
+// {
+// t=(x^(x<<11));
+// x=y; y=z; z=w;
+// *pDWord++ = w = (w^(w>>19))^(t^(t>>8));
+// }
+// }
+//
+// this.x=x; this.y=y; this.z=z; this.w=w;
+// }
+
+ // Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
+ // with bitBufferIdx.
+
+ ///
+ /// Generates random bool.
+ /// Increased performance is achieved by buffering 32 random bits for
+ /// future calls. Thus the random number generator is only invoked once
+ /// in every 32 calls.
+ ///
+ ///
+ public bool NextBool()
+ {
+ if (bitBufferIdx == 32)
+ {
+ // Generate 32 more bits.
+ uint t = (x ^ (x << 11));
+ x = y;
+ y = z;
+ z = w;
+ bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+
+ // Reset the idx that tells us which bit to read next.
+ bitBufferIdx = 1;
+ return (bitBuffer & 0x1) == 1;
+ }
+
+ bitBufferIdx++;
+ return ((bitBuffer >>= 1) & 0x1) == 1;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index 9e49b3c8..2788f883 100644
--- a/Program.cs
+++ b/Program.cs
@@ -29,6 +29,8 @@ public class Program
private static Dictionary fFiles;
public static FileStream fileStream;
+
+ private static readonly byte[] knownPlain = new byte[64];
public static void ContinueOnPress()
{
@@ -183,6 +185,7 @@ public static void Main(string[] args)
Console.WriteLine($"Decryption key: {keyOut}");
string key = keyOut.ToLower().Replace("-", string.Empty);
+ new FastRandom(1990).NextBytes(knownPlain);
//DecryptFile(fileStream, fEnum.fDecryptMode.OSUM); // Put a reader on decrypted fileStream
@@ -200,22 +203,32 @@ public static void Main(string[] args)
// exists only to keep BinaryReader position moving
{
int countRedundant = br.ReadInt32();
+ Console.WriteLine($"Read {countRedundant} raw entries:");
for (int i = 0; i < countRedundant; i++)
{
- br.ReadInt16();
- br.ReadString();
+ Console.WriteLine($"{br.ReadInt16()}: {br.ReadString()}");
}
int mapCount = br.ReadInt32();
- for (int i = 0; i < mapCount; i++) {br.ReadString(); br.ReadInt32();}
+ for (int i = 0; i < mapCount; i++)
+ {
+ Console.WriteLine($"{br.ReadString()}: {br.ReadInt32()}");
+ }
+
- doPostProcessing(br);
+ doPostProcessing(br);
}
}
static void doPostProcessing(BinaryReader br)
{
+ //Known plain comparison
+ FastEncryptorStream comparer = new FastEncryptorStream(br.BaseStream, fEnum.EncryptionMethod.One, SafeEncryptionProvider.ConvertByteArrayToUIntArray(keyRaw));
+ byte[] plain = new byte[64];
+ comparer.Read(plain, 0, 64);
+ if (!plain.SequenceEqual(knownPlain)) throw new Exception("Nope, we didn't got the key");
+
// Set offset on FileInfo
fOffsetFileinfo = (int)br.BaseStream.Position;
diff --git a/SafeEncryptionProvider.cs b/SafeEncryptionProvider.cs
index 4a25ba60..091a5ea4 100644
--- a/SafeEncryptionProvider.cs
+++ b/SafeEncryptionProvider.cs
@@ -41,7 +41,7 @@ public void Init(uint[] pkey, fEnum.EncryptionMethod EM)
k = pkey;
m = EM;
- byte[] keyB = ConvertUIntArrayToByteArray(pkey);
+ kB = ConvertUIntArrayToByteArray(pkey);
}
private void CheckKey()
@@ -96,80 +96,87 @@ private static byte RotateRight(byte val, byte n)
#endregion
- #region Encryption ONE (Disabled)
-/**
- private void EncryptDecryptOneSafe(byte[] bufferArr, int bufferIdx, int bufferLen, bool isEncrypted)
+ #region Encryption ONE
+
+ private void EncryptDecryptOneSafe(byte[] bufferArr, byte[] resultArr, int bufferLen, bool isEncrypted,
+ int bufferPtr = 0, int resultPtr = 0) // Pointers always start at 0
{
- uint fullWordCount = unchecked((uint)bufferLen / 8);
- uint leftover = (uint)(bufferLen % 8); //remaining of fullWordCount
-
- uint[] intWordArrB = ConvertByteArrayToUIntArray(bufferArr);
+ uint fullWordCount = unchecked((uint)bufferLen / 8),
+ leftover = unchecked((uint)bufferLen) % 8;
- intWordArrB -= 2;
- intWordArrO -= 2;
+ uint[] intWordArrB = ConvertByteArrayToUIntArray(bufferArr),
+ intWordArrO = ConvertByteArrayToUIntArray(resultArr);
+ int intWordPtrB = 0;
+ int intWordPtrO = 0;
+
+ // Back up pointers by 2
+ intWordPtrB -= 2;
+ intWordPtrO -= 2;
if (isEncrypted)
+ {
for (int wordCount = 0; wordCount < fullWordCount; wordCount++)
- EncryptWordOne(intWordArrB += 2, intWordArrO += 2);
+ EncryptWordOneSafe(intWordArrB, intWordArrO, intWordPtrB += 2, intWordPtrO += 2);
+ }
else
+ {
for (int wordCount = 0; wordCount < fullWordCount; wordCount++)
- DecryptWordOne(intWordArrB += 2, intWordArrO += 2);
+ DecryptWordOneSafe(intWordArrB, intWordArrO, intWordPtrB += 2, intWordPtrO += 2);
+ }
- if (leftover == 0) return; // no leftover for me? get lost :c
+ if (leftover == 0) return; // Where's my leftover :c
- byte[] bufferEnd = bufferArr + bufferLen;
- byte[] byteWordArrB2 = bufferEnd - leftover;
- byte[] byteWordArrO2 = ConvertUIntArrayToByteArray(resultArr + bufferLen - leftover); ;
+ byte[] bufferEnd = BitConverter.GetBytes(bufferArr[bufferPtr] + bufferLen);
+ int bufferEndPtr = 0;
+ byte[] byteWordArrB2 = BitConverter.GetBytes(bufferEnd[bufferEndPtr] - leftover);
+ int byteWordPtrB2 = 0;
+ byte[] byteWordArrO2 = BitConverter.GetBytes(resultArr[resultPtr] + bufferLen - leftover);
+ int byteWordPtrO2 = 0;
- // copy leftoverBuffer[] -> result[]
- Array.Copy(byteWordArrB2, byteWordArrO2, leftover);
-
- // deal with leftover
+ //copy leftover buffer array to result array
+ Array.Copy(byteWordArrB2, byteWordPtrB2, byteWordArrO2, byteWordPtrO2, bufferEnd[bufferEndPtr]);
+
+ // Deal with the leftover
if (isEncrypted)
- SimpleEncryptBytesSafe(byteWordArrO2 - leftover, 0, unchecked((int)leftover));
+ SimpleEncryptBytesSafe(byteWordArrO2, (int)(byteWordPtrO2 - leftover), unchecked((int)leftover));
else
- SimpleDecryptBytesSafe(byteWordArrO2 - leftover, 0, unchecked((int)leftover));
+ SimpleDecryptBytesSafe(byteWordArrO2, (int)(byteWordPtrO2 - leftover), unchecked((int)leftover));
}
-**/
+
#region Sub-Functions (Encrypt/Decrypt)
-/**
- private void EncryptWordOne(uint[] v uint[] o)
+ private void EncryptWordOneSafe(uint[] v, uint[] o, int vPtr, int oPtr)
{
uint i;
- uint v0 = v[0];
- uint v1 = v[1];
+ uint v0 = v[vPtr];
+ uint v1 = v[vPtr + 1];
uint sum = 0;
for (i = 0; i < r; i++)
{
- //todo: cache sum + k for better speed
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
sum += d;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
}
-
- o[0] = v0;
- o[1] = v1;
+ o[oPtr] = v0;
+ o[oPtr + 1] = v1;
}
-
- private void DecryptWordOne(uint[] v , uint[] o )
+
+ private void DecryptWordOneSafe(uint[] v, uint[] o, int vPtr, int oPtr)
{
uint i;
- uint v0 = v[0];
- uint v1 = v[1];
+ uint v0 = v[vPtr];
+ uint v1 = v[vPtr + 1];
uint sum = unchecked(d * r);
for (i = 0; i < r; i++)
{
- //todo: cache sum + k for better speed
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
sum -= d;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
}
- o[0] = v0;
- o[1] = v1;
+ o[oPtr] = v0;
+ o[oPtr + 1] = v1;
}
-**/
#endregion
@@ -227,7 +234,6 @@ private void EncryptDecryptTwoSafe(byte[] bufferArr, bool isEncrypted, int count
byte[] leftoverBufferProcessed = ConvertUIntArrayToByteArray(leftoverBufferWords);
Buffer.BlockCopy(leftoverBufferProcessed, 0, bufferArr, (int)(offset + fullWordCount * NMaxBytes), (int)_n * 4);
-
if (isEncrypted)
SimpleEncryptBytesSafe(bufferArr, (int)(count - leftover) + offset, count);
else
@@ -299,38 +305,44 @@ private void EncryptDecryptHomebrew(byte[] bufferArr, int offset,int bufferLen,
#endregion
#region Encrypt/Decrypt Main
-
- private void EncryptDecryptSafe(byte[] bufferArr, int bufferLen, bool isEncrypted)
- {
- EncryptDecryptTwoSafe(bufferArr, isEncrypted, bufferLen, 0);
- }
- private void EncryptDecryptSafe(byte[] bufferArr, byte[] outputArr, int bufferLength, bool encrypt)
+ private void EncryptDecryptSafe(byte[] bufferArr, int bufferLen, bool isEncrypted,
+ int bufferPtr = 0)
{
switch (m)
{
case fEnum.EncryptionMethod.One:
- throw new NotImplementedException("Holy shit it's the chosen one encryption");
-
+ EncryptDecryptOneSafe(bufferArr, bufferArr, bufferLen, isEncrypted, bufferPtr, bufferPtr);
+ break;
case fEnum.EncryptionMethod.Two:
- throw new NotSupportedException(); //nuh uh
-
- case fEnum.EncryptionMethod.Three: return;
-
+ EncryptDecryptTwoSafe(bufferArr, isEncrypted, bufferLen, bufferPtr);
+ break;
+ case fEnum.EncryptionMethod.Three:
+ EncryptDecryptHomebrew(bufferArr, bufferPtr, bufferLen, isEncrypted);
+ break;
case fEnum.EncryptionMethod.Four:
CheckKey();
break;
}
}
- public void EncryptDecrypt(byte[] buffer, byte[] output, int bufStart, int outputStart, int count,
- bool encrypt)
+ private void EncryptDecryptSafe(byte[] bufferArr, byte[] outputArr, int bufferLen, bool isEncrypted,
+ int bufferPtr = 0, int resultPtr = 0)
{
- //only Two is ported to managed code, so the encryption method is ignored
- if (output != null)
- throw new NotSupportedException("Custom output is not supported when SAFE_ENCRYPTION is enabled.");
- EncryptDecryptTwoSafe(buffer, encrypt, count, bufStart);
+ switch (m)
+ {
+ case fEnum.EncryptionMethod.One:
+ EncryptDecryptOneSafe(bufferArr, outputArr, bufferLen, isEncrypted, bufferPtr, resultPtr);
+ break;
+ case fEnum.EncryptionMethod.Three:
+ case fEnum.EncryptionMethod.Two:
+ throw new NotSupportedException();
+ case fEnum.EncryptionMethod.Four:
+ CheckKey();
+ break;
+ }
}
+
#endregion
@@ -344,22 +356,22 @@ public void EncryptDecrypt(byte[] buffer, byte[] output, int bufStart, int outp
**/
public void Decrypt(byte[] buffer)
{
- EncryptDecrypt(buffer, null, 0, 0, buffer.Length, false);
+ EncryptDecryptSafe(buffer, buffer.Length, false, 0);
}
public void Decrypt(byte[] buffer, int start, int count)
{
- EncryptDecrypt(buffer, null, start, 0, count, false);
+ EncryptDecryptSafe(buffer, count, false, start);
}
public void Decrypt(byte[] buffer, byte[] output)
{
- EncryptDecrypt(buffer, output, 0, 0, buffer.Length, false);
+ EncryptDecryptSafe(buffer, output, buffer.Length, false, 0, 0);
}
public void Decrypt(byte[] buffer, byte[] output, int bufStart, int outStart, int count)
{
- EncryptDecrypt(buffer, output, bufStart, outStart, count, false);
+ EncryptDecryptSafe(buffer, output, count, false, bufStart, outStart);
}
#endregion
@@ -372,22 +384,22 @@ public void Decrypt(byte[] buffer, byte[] output, int bufStart, int outStart, in
**/
public void Encrypt(byte[] buffer)
{
- EncryptDecrypt(buffer, null, 0, 0, buffer.Length, true);
+ EncryptDecryptSafe(buffer, buffer.Length, true, 0);
}
public void Encrypt(byte[] buffer, int start, int count)
{
- EncryptDecrypt(buffer, null, start, 0, count, true);
+ EncryptDecryptSafe(buffer, count, true, start);
}
public void Encrypt(byte[] buffer, byte[] output)
{
- EncryptDecrypt(buffer, output, 0, 0, buffer.Length, true);
+ EncryptDecryptSafe(buffer, output, buffer.Length, true, 0, 0);
}
public void Encrypt(byte[] buffer, byte[] output, int bufStart, int outStart, int count)
{
- EncryptDecrypt(buffer, output, bufStart, outStart, count, true);
+ EncryptDecryptSafe(buffer, output, count, true, bufStart, outStart);
}
diff --git a/obj/Debug/net8.0/fStreamDecryptor.AssemblyInfo.cs b/obj/Debug/net8.0/fStreamDecryptor.AssemblyInfo.cs
index 5c164964..89ec43a6 100644
--- a/obj/Debug/net8.0/fStreamDecryptor.AssemblyInfo.cs
+++ b/obj/Debug/net8.0/fStreamDecryptor.AssemblyInfo.cs
@@ -13,7 +13,7 @@
[assembly: System.Reflection.AssemblyCompanyAttribute("fStreamDecryptor")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+6349b257b3c1251ab831d37be89a3bd44f17ab40")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+c89a2b1f21459d4620ed405672c6c50d83231410")]
[assembly: System.Reflection.AssemblyProductAttribute("fStreamDecryptor")]
[assembly: System.Reflection.AssemblyTitleAttribute("fStreamDecryptor")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]