326 lines
13 KiB
C#
326 lines
13 KiB
C#
using Sarand.Net.Standard.Base.App;
|
||
using Sarand.Net.Standard.Base.Core;
|
||
using Sarand.Net.Standard.Base.Utils;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
|
||
namespace Sarand.Net.Standard.Base.Data
|
||
{
|
||
//=======================================================================
|
||
/// <summary>
|
||
/// Legacy basic data encryption.
|
||
/// <para>Ref. ItemOptions, Session registration</para>
|
||
/// </summary>
|
||
public static class SacsDataFn
|
||
{
|
||
//-------------------------------------------------------------------
|
||
// Data members.
|
||
private static String acknowledge = "Sarand Software Solutions: www.sarand.com";
|
||
|
||
//-------------------------------------------------------------------
|
||
// S t r i n g E n c r y p t i o n
|
||
//-------------------------------------------------------------------
|
||
/// <summary>Encrypt string data.</summary>
|
||
public static String _saecs (String data, SacsData keyData = null)
|
||
{
|
||
// Raw data must be a multiple of 3 + 2 in length to allow
|
||
// for 4 char header and blocks of 3 encoded chars:
|
||
data = StringUtil.PadRight(data, ((data.Length / 3) * 3) + 2, ' ');
|
||
// Encrypt the data.
|
||
SacsData encryptedData = _saecp (new SacsData(data), keyData);
|
||
// Encode the encrypted data.
|
||
SacsData encodedData = _saecd (encryptedData.DataBuffer);
|
||
// Convert the result to a string.
|
||
String result = (encodedData != null ? (String)encodedData : "");
|
||
// Return the results.
|
||
return result;
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
/// <summary>Decrypt string data.</summary>
|
||
public static String _sadcs (String data, SacsData keyData = null)
|
||
{
|
||
// Decode the data.
|
||
SacsData decodedData = _sadcd (new SacsData(data).DataBuffer);
|
||
// Decrypt the data.
|
||
SacsData decryptedData = _sadcp (decodedData.DataBuffer, keyData);
|
||
// Convert to string.
|
||
String rawData = (decryptedData != null ? (String)decryptedData : "");
|
||
// Return the results.
|
||
return rawData.TrimEnd(new char[] { ' ' });
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
// E n c r y p t i o n
|
||
//-------------------------------------------------------------------
|
||
// Encrypted data includes a header with the following structure:
|
||
// Bytes 1 & 2 0x5aec "SArand EnCrypted"
|
||
// Byte 3 bits 0-2 Encryption method (currently only '0' supported).
|
||
// Byte 4 Reserved.
|
||
//-------------------------------------------------------------------
|
||
public static SacsData _saecp (SacsData rawData, SacsData keyData)
|
||
{
|
||
// Select encryption parameters.
|
||
// Dictates which sequence of encryption techniques is used (0 - 7).
|
||
int encryptMethod = 0;
|
||
#if DEBUG
|
||
if (encryptMethod != 0)
|
||
SaLogger.Warn("_saecp: Unknown enryption method - " + encryptMethod);
|
||
#endif
|
||
byte[] encryptedData = null;
|
||
int encryptedDataLength = 0;
|
||
|
||
//-------------------------------------------------------------------
|
||
// Version 1.0.0 - just use the RC4 method with default key.
|
||
if (encryptMethod == 0)
|
||
{
|
||
SacsData dataRC4 = new SacsData(RC4 (rawData, keyData));
|
||
encryptedData = dataRC4.DataBuffer;
|
||
encryptedDataLength = dataRC4.DataLength;
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
// Header length - May differ by encryption method.
|
||
ushort resultHeaderLength = 4;
|
||
// Create a buffer for the result.
|
||
int resultLength = (rawData.DataLength + resultHeaderLength);
|
||
byte[] resultBuffer = new byte [resultLength];
|
||
if (encryptMethod == 0)
|
||
{
|
||
// First two bytes always the same.
|
||
// 'SA' = "Sarand".
|
||
resultBuffer[0] = 0x5a;
|
||
// 'EC' = "Encrypted".
|
||
resultBuffer[1] = 0xec;
|
||
// Next byte holds the encrypt method in the 3 high bits:
|
||
resultBuffer[2] = (byte)(encryptMethod << 5);
|
||
resultBuffer[3] = 0;
|
||
}
|
||
// Copy the encrypted data to the remaining portion of the buffer.
|
||
Array.Copy(encryptedData, 0, resultBuffer, resultHeaderLength, encryptedData.Length);
|
||
// Return data.
|
||
return new SacsData(resultBuffer);
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
// D e c r y p t i o n
|
||
//-------------------------------------------------------------------
|
||
/// <summary>Decryot data.</summary>
|
||
public static SacsData _sadcp (byte[] rawData, SacsData keyData)
|
||
{
|
||
// Decode the header.
|
||
if (rawData == null || rawData.Length == 0)
|
||
{
|
||
// No data.
|
||
return new SacsData();
|
||
}
|
||
if (rawData.Length < 4)
|
||
{
|
||
SaLogger.Warn("_sadcp Invalid data length: " + rawData.Length + ". Expected multiple of 4.");
|
||
return new SacsData();
|
||
}
|
||
byte headerChar1 = rawData[0];
|
||
byte headerChar2 = rawData[1];
|
||
// High 3 bits of header byte #3.
|
||
int encryptMethod = rawData[2]>>5;
|
||
// May differ by encryption method.
|
||
ushort resultHeaderLength = 4;
|
||
// Verify the encrypted header:
|
||
if (headerChar1 != 0x5a || headerChar2 != 0xec)
|
||
{
|
||
SaLogger.Warn("_sadcp Invalid header.");
|
||
return new SacsData();
|
||
}
|
||
if (encryptMethod != 0)
|
||
{
|
||
SaLogger.Warn("_sadcp Invalid encryption method.");
|
||
return new SacsData();
|
||
}
|
||
|
||
// Decrypt data.
|
||
SacsData resultData = new SacsData();
|
||
// Method '0' - simple RC4 decryption.
|
||
if (encryptMethod == 0)
|
||
{
|
||
resultData = RC4(rawData.Skip(resultHeaderLength).ToArray(), keyData);
|
||
}
|
||
// Convert the data to the return type:
|
||
return resultData;
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
// E n c o d i n g
|
||
//-------------------------------------------------------------------
|
||
/// <summary>Encode data.</summary>
|
||
public static SacsData _saecd (byte[] pData)
|
||
{
|
||
// Prepare a buffer for the results.
|
||
int dataLength = pData.Length;
|
||
byte[] encodeBuffer = new byte[((dataLength + 2) / 3) * 4];
|
||
int iBufPos = 0;
|
||
// Data is encoded in lots of 3 bytes at a time.
|
||
// Each lot of 3 bytes becoms 4 bytes in the encoded data set:
|
||
for (uint i = 0; i < dataLength; i += 3)
|
||
{
|
||
byte byte0 = pData[i];
|
||
byte byte1 = (byte)(i < dataLength - 1 ? pData[i+1] : 0);
|
||
byte byte2 = (byte)(i < dataLength - 2 ? pData[i+2] : 0);
|
||
// The 3 data bytes are first converted to 4 6-bit values:
|
||
// Raw: abcdefgh ijklmnop qrstuvwx
|
||
// Conv: 00abcdef 00ghijkl 00mnopqr 00stuvwx
|
||
// Each of the resulting 4 bytes is then incremented by 0x20 (ascii code for a space):
|
||
encodeBuffer[iBufPos++] = ENC(byte0 >> 2); // Byte 1 bits 7-2
|
||
encodeBuffer[iBufPos++] = ENC((byte0 << 4) & 0x30 | (byte1 >> 4) & 0x0F); // Byte 1 bits 1-0 & Byte 2 bits 7-4
|
||
encodeBuffer[iBufPos++] = ENC((byte1 << 2) & 0x3C | (byte2 >> 6) & 0x03); // Byte 2 bits 3-0 & Byte 3 bits 7-6
|
||
encodeBuffer[iBufPos++] = ENC(byte2 & 0x3F); // Byte 3 bits 5-0
|
||
}
|
||
SacsData pdEncode = new SacsData(encodeBuffer);
|
||
return pdEncode;
|
||
}
|
||
private static byte ENC (int data)
|
||
{
|
||
return (byte)((data & 0x3F) + 0x20/*Space*/);
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
/// <summary>Decode data.</summary>
|
||
public static SacsData _sadcd (byte[] rawData)
|
||
{
|
||
// The encoded data must be a multiple of 4 bytes.
|
||
if (rawData == null || rawData.Length % 4 != 0)
|
||
{
|
||
SaLogger.Warn("_sadcd Data is not a multiple of 4 bytes. Length: " + (rawData?.Length ?? 0));
|
||
return new SacsData();
|
||
}
|
||
// Prepare a buffer for the results.
|
||
byte[] resultsBuffer = new byte[rawData.Length / 4 * 3];
|
||
int bufferIdx = 0;
|
||
// Iterate through the encoded data in blocks of four bytes.
|
||
for (uint idx = 0; idx < rawData.Length; idx += 4)
|
||
{
|
||
byte p = rawData[idx];
|
||
int c1 = (DEC(rawData[idx+0]) << 2) | (DEC(rawData[idx+1]) >> 4);
|
||
#if DEBUG
|
||
int z = rawData[idx+1];
|
||
z = DEC(z);
|
||
z = z << 4;
|
||
z = rawData[idx + 2];
|
||
z = DEC(z);
|
||
z = z >> 2;
|
||
z = (DEC(rawData[idx + 1]) << 4) | (DEC(rawData[idx + 2]) >> 2);
|
||
#endif
|
||
int c2 = (DEC(rawData[idx+1]) << 4) | (DEC(rawData[idx+2]) >> 2);
|
||
int c3 = (DEC(rawData[idx+2]) << 6) | DEC(rawData[idx+3]);
|
||
resultsBuffer[bufferIdx++] = (byte)c1;
|
||
resultsBuffer[bufferIdx++] = (byte)c2;
|
||
resultsBuffer[bufferIdx++] = (byte)c3;
|
||
}
|
||
return new SacsData(resultsBuffer);
|
||
}
|
||
private static byte DEC (int data)
|
||
{
|
||
#if DEBUG
|
||
int z = data - 0x20;
|
||
z = z & 0x3F;
|
||
#endif
|
||
return (byte)((data - 0x20/*Space*/) & 0x3F);
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
// R C 4 E n c r y p t i o n
|
||
//-------------------------------------------------------------------
|
||
// www.systweak.com & www.codeproject.com/cpp/crypt_routine.asp
|
||
// This Crypt routine uses an algorithm created by Rodney Thayer.
|
||
// This algorithm can use keys of various sizes.
|
||
// With a 40-bit key (5 characters) it can be freely exported from the U.S.
|
||
// The cipher is considered robust with 128 bits of key material but can use up to 2048 bits.
|
||
// It is compatible with RSA’s RC4 algorithm.
|
||
//---------------------------------------------------------------------------
|
||
// The only moral here is that we are generating a 256 bit key given from the user. To encrypt we always pick a random byte from the key and encrypt the text with that key. If we have a large pattern of repeating text with this algorithm the key repeat interval (distance between n repetitions) will be approximately of 21700 (2 to the 1700th power).
|
||
// For very good security I recommend using 128 bits (16 characters) of key material. If you want optimal security you could use 2048 bits (256 characters) of key material (but this is usually considered an overkill).
|
||
// This routine is constructed from a draft taken from Internet Engineering Task Force (IETF) at http://www.ietf.cnri.reston.va.us/home.htm
|
||
// I have tested the routine on a 27 MB file to test that it produce valid decrypted output from input and get the valid results.
|
||
// We do not enter the key so we will be using default key provide by the crypt routine:
|
||
// TCHAR HelloCrypt[] = "Hello Crypt";
|
||
// Encrypt: Crypt(HelloCrypt, _tcslen(HelloCrypt));
|
||
// Decrypt: Crypt(HelloCrypt, _tcslen(HelloCrypt));
|
||
// If we have to supply a key we have to use crypt as follows:
|
||
// TCHAR Data[] = "abcdefghijklmnopqrstuvwxyz"
|
||
// TCHAR Key[] = "Strong key";
|
||
// Encrypt: Crypt(Data, _tcslen(Data), Key, _tcslen(Data));
|
||
// Decrypt: Crypt(Data, _tcslen(Data), Key, _tcslen(Data));
|
||
//---------------------------------------------------------------------------
|
||
internal static SacsData RC4 (SacsData dataRaw, SacsData keyData)
|
||
{
|
||
return RC4(dataRaw.DataBuffer, keyData != null ? keyData.DataBuffer : null);
|
||
}
|
||
internal static SacsData RC4 (byte[] rawData, SacsData keyData)
|
||
{
|
||
return RC4(rawData, keyData != null ? keyData.DataBuffer : null);
|
||
}
|
||
public static SacsData RC4 (byte[] rawData, byte[] userKey = null)
|
||
{
|
||
// Key buffers (256 bytes = 1024 bits):
|
||
byte[] keyBuffer = new byte[257];
|
||
Array.Clear(keyBuffer, 0, keyBuffer.Length);
|
||
|
||
// Initialize the key buffer.
|
||
for (int keyIdx = 0; keyIdx <= 255U; keyIdx++)
|
||
keyBuffer[keyIdx] = (byte)keyIdx;
|
||
|
||
// Either use the specific key supplied, or the default (unsecured) key:
|
||
byte[] keyData = Encoding.ASCII.GetBytes(acknowledge);
|
||
int keyLength = StringUtil.GetLength(acknowledge);
|
||
if (userKey != null && userKey.Length > 0)
|
||
{
|
||
keyData = userKey;
|
||
keyLength = userKey.Length;
|
||
}
|
||
// Scramble this raw key into the key buffer.
|
||
uint rootKeyIdx = 0;
|
||
for (int dataIdx = 0; dataIdx < 256; dataIdx++)
|
||
{
|
||
// Position in the root key.
|
||
rootKeyIdx = (rootKeyIdx + (uint)keyBuffer[dataIdx] + keyData[dataIdx % keyLength]) % 256U;
|
||
byte temp = keyBuffer[dataIdx];
|
||
keyBuffer[dataIdx] = keyBuffer[rootKeyIdx];
|
||
keyBuffer[rootKeyIdx] = temp;
|
||
}
|
||
#if DEBUG_X
|
||
// Retrieve the contents of the key buffer in a readable form.
|
||
String key = "";
|
||
for (int keyBufferIdx = 0; keyBufferIdx <= 12; keyBufferIdx++) key += ((char)keyBuffer[keyBufferIdx]).ToString() + ".";
|
||
#endif
|
||
int i = 0;
|
||
int j = 0;
|
||
// Create a buffer for the encrypted data:
|
||
byte[]resultData = new byte [rawData.Length];
|
||
for (uint x = 0; x < rawData.Length; x++)
|
||
{
|
||
// Increment i.
|
||
i = (int)((i + 1U) % 256U);
|
||
// Increment j.
|
||
j = (int)((j + (uint)keyBuffer[i]) % 256U);
|
||
// Scramble pKeyBuf #1 further so encryption routine will only repeat itself at great interval.
|
||
byte temp = keyBuffer[i];
|
||
keyBuffer[i] = keyBuffer[j];
|
||
keyBuffer[j] = temp;
|
||
// Get ready to create pseudo random byte for encryption key.
|
||
int t = (int)(((int) keyBuffer[i] + (int)keyBuffer[j]) % 256U);
|
||
// Get the target byte:
|
||
byte k = keyBuffer[t];
|
||
// Xor with the data and done:
|
||
byte raw = rawData[x];
|
||
resultData[x] = (byte)(raw ^ k);
|
||
}
|
||
// Create the return value.
|
||
return new SacsData(resultData);
|
||
}
|
||
|
||
//-------------------------------------------------------------------
|
||
}
|
||
//=======================================================================
|
||
}
|