forked from airat77786/MQL5Book
208 lines
7.2 KiB
MQL5
208 lines
7.2 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| CryptDecode.mq5 |
| |||
//| Copyright 2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright 2022, MetaQuotes Ltd."
| |||
#property link "https://www.mql5.com"
| |||
#property description "Use CryptDecode to restore protected data from given cipher, using different methods."
| |||
#property script_show_inputs
| |||
| |||
#include "..\..\Include\PRTF.mqh"
| |||
#include "..\..\Include\EnumToArray.mqh"
| |||
#include "..\..\Include\ArrayUtils.mqh"
| |||
| |||
#define KEY_REQUIRED(C) ((C) == CRYPT_DES || (C) == CRYPT_AES128 || (C) == CRYPT_AES256)
| |||
#define IS_HASH(C) ((C) == CRYPT_HASH_MD5 || (C) == CRYPT_HASH_SHA1 || (C) == CRYPT_HASH_SHA256)
| |||
| |||
enum ENUM_CRYPT_METHOD_EXT
| |||
{
| |||
_CRYPT_DES = CRYPT_DES, // DES
| |||
_CRYPT_AES128 = CRYPT_AES128, // AES128
| |||
_CRYPT_AES256 = CRYPT_AES256, // AES256
| |||
_CRYPT_HASH_MD5 = CRYPT_HASH_MD5, // MD5 (irreversible)
| |||
_CRYPT_HASH_SHA1 = CRYPT_HASH_SHA1, // SHA1 (irreversible)
| |||
_CRYPT_HASH_SHA256 = CRYPT_HASH_SHA256, // SHA256 (irreversible)
| |||
_CRYPT_ARCH_ZIP = CRYPT_ARCH_ZIP, // ZIP
| |||
_CRYPT_BASE64 = CRYPT_BASE64, // BASE64
| |||
};
| |||
| |||
enum DUMMY_KEY_LENGTH
| |||
{
| |||
DUMMY_KEY_0 = 0, // 0 bytes (no key)
| |||
DUMMY_KEY_7 = 7, // 7 bytes (sufficient for DES)
| |||
DUMMY_KEY_16 = 16, // 16 bytes (sufficient for AES128)
| |||
DUMMY_KEY_32 = 32, // 32 bytes (sufficient for AES256)
| |||
DUMMY_KEY_CUSTOM, // use CustomKey
| |||
};
| |||
| |||
input string Text; // Text (base64, or empty to process File)
| |||
input string File = "MQL5Book/clock10.htm.BASE64";
| |||
input ENUM_CRYPT_METHOD_EXT Method = _CRYPT_BASE64;
| |||
input DUMMY_KEY_LENGTH GenerateKey = DUMMY_KEY_CUSTOM; // GenerateKey (length, or take from CustomKey)
| |||
input string CustomKey = "My top secret key is very strong"; // CustomKey
| |||
input bool DisableCRCinZIP = false;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Script program start function |
| |||
//+------------------------------------------------------------------+
| |||
void OnStart()
| |||
{
| |||
ENUM_CRYPT_METHOD method = 0;
| |||
int methods[];
| |||
uchar key[] = {}; // key is empty by default: ok for zip, base64
| |||
uchar data[], result[];
| |||
uchar zip[], opt[] = {1, 0, 0, 0};
| |||
| |||
if(!StringLen(Text) && !StringLen(File))
| |||
{
| |||
Alert("Please specify either Text or File to decode");
| |||
return;
| |||
}
| |||
| |||
if(GenerateKey == DUMMY_KEY_CUSTOM)
| |||
{
| |||
if(StringLen(CustomKey))
| |||
{
| |||
PRTF(CustomKey);
| |||
StringToCharArray(CustomKey, key, 0, -1, CP_UTF8);
| |||
ArrayResize(key, ArraySize(key) - 1);
| |||
}
| |||
}
| |||
else if(GenerateKey != DUMMY_KEY_0)
| |||
{
| |||
// normally the key should be a secret, hard to guess, random selected token,
| |||
// here we generate it in straightforward manner for your easy testing
| |||
ArrayResize(key, GenerateKey);
| |||
for(int i = 0; i < GenerateKey; ++i) key[i] = (uchar)i;
| |||
}
| |||
| |||
if(ArraySize(key))
| |||
{
| |||
Print("Key (bytes):");
| |||
ByteArrayPrint(key);
| |||
}
| |||
else
| |||
{
| |||
Print("Key is not provided");
| |||
}
| |||
| |||
method = (ENUM_CRYPT_METHOD)Method;
| |||
Print("- ", EnumToString(method), ", key required: ", KEY_REQUIRED(method));
| |||
| |||
if(StringLen(Text))
| |||
{
| |||
if(method != CRYPT_BASE64)
| |||
{
| |||
// Since all methods except for Base64 produce binary results,
| |||
// it was additionally converted to Base64 in CryptEncode.mq5,
| |||
// hence we need to restore binary data from text input
| |||
// before deciphering it
| |||
uchar base64[];
| |||
const uchar dummy[] = {};
| |||
PRTF(Text);
| |||
PRTF(StringToCharArray(Text, base64, 0, -1, CP_UTF8));
| |||
ArrayResize(base64, ArraySize(base64) - 1);
| |||
Print("Text (bytes):");
| |||
ByteArrayPrint(base64);
| |||
if(!PRTF(CryptDecode(CRYPT_BASE64, base64, dummy, data)))
| |||
{
| |||
return; // error
| |||
}
| |||
| |||
Print("Raw data to decipher (after de-base64):");
| |||
ByteArrayPrint(data);
| |||
}
| |||
else
| |||
{
| |||
PRTF(StringToCharArray(Text, data, 0, -1, CP_UTF8));
| |||
ArrayResize(data, ArraySize(data) - 1);
| |||
}
| |||
}
| |||
else if(StringLen(File))
| |||
{
| |||
PRTF(File);
| |||
if(PRTF(FileLoad(File, data)) <= 0)
| |||
{
| |||
return; // error
| |||
}
| |||
}
| |||
| |||
if(IS_HASH(method))
| |||
{
| |||
Print("WARNING: hashes can not be used to restore data! CryptDecode will fail.");
| |||
}
| |||
| |||
if(method == CRYPT_ARCH_ZIP)
| |||
{
| |||
// a special key is supported for CRYPT_ARCH_ZIP:
| |||
// at least 4 bytes with 1-st '1' is used to disable specific to MQL5
| |||
// Adler32 checksum embedding into the compressed data,
| |||
// needed for compatibility with some standards such as ZIP archives
| |||
if(DisableCRCinZIP)
| |||
{
| |||
ArrayCopy(zip, opt); // make dynamic copy of array (dynamic needed for ArraySwap)
| |||
}
| |||
ArraySwap(key, zip); // substitute the key with empty or optional
| |||
}
| |||
| |||
ResetLastError();
| |||
if(PRTF(CryptDecode(method, data, key, result)))
| |||
{
| |||
if(StringLen(Text))
| |||
{
| |||
Print("Text restored:");
| |||
Print(CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8));
| |||
}
| |||
else // File
| |||
{
| |||
const string filename = File + ".dec";
| |||
if(PRTF(FileSave(filename, result)))
| |||
{
| |||
Print("File saved: ", filename);
| |||
}
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
/*
| |||
| |||
- CRYPT_BASE64, key required: false
| |||
File=MQL5Book/clock10.htm.BASE64 / ok
| |||
FileLoad(File,data)=1320 / ok
| |||
CryptDecode(method,data,key,result)=988 / ok
| |||
FileSave(filename,result)=true / ok
| |||
File saved: MQL5Book/clock10.htm.BASE64.dec
| |||
| |||
...
| |||
| |||
CustomKey=My top secret key is very strong / ok
| |||
Key (bytes):
| |||
[00] 4D | 79 | 20 | 74 | 6F | 70 | 20 | 73 | 65 | 63 | 72 | 65 | 74 | 20 | 6B | 65 |
| |||
[16] 79 | 20 | 69 | 73 | 20 | 76 | 65 | 72 | 79 | 20 | 73 | 74 | 72 | 6F | 6E | 67 |
| |||
- CRYPT_AES128, key required: true
| |||
Text=AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E= / ok
| |||
StringToCharArray(Text,base64,0,-1,CP_UTF8)=44 / ok
| |||
Text (bytes):
| |||
[00] 41 | 51 | 75 | 76 | 56 | 43 | 6F | 53 | 79 | 31 | 73 | 7A | 61 | 4E | 38 | 4F |
| |||
[16] 77 | 79 | 38 | 74 | 51 | 78 | 6C | 39 | 72 | 49 | 72 | 52 | 6A | 39 | 68 | 4F |
| |||
[32] 71 | 4B | 37 | 4B | 67 | 59 | 59 | 47 | 68 | 39 | 45 | 3D |
| |||
CryptDecode(CRYPT_BASE64,base64,dummy,data)=32 / ok
| |||
Raw data to decipher (after de-base64):
| |||
[00] 01 | 0B | AF | 54 | 2A | 12 | CB | 5B | 33 | 68 | DF | 0E | C3 | 2F | 2D | 43 |
| |||
[16] 19 | 7D | AC | 8A | D1 | 8F | D8 | 4E | A8 | AE | CA | 81 | 86 | 06 | 87 | D1 |
| |||
CryptDecode(method,data,key,result)=32 / ok
| |||
Text restored:
| |||
Let's encrypt this message
| |||
| |||
...
| |||
| |||
- CRYPT_HASH_MD5, key required: false
| |||
File=MQL5Book/clock10.htm.MD5 / ok
| |||
FileLoad(File,data)=16 / ok
| |||
WARNING: hashes can not be used to restore data! CryptDecode will fail.
| |||
CryptDecode(method,data,key,result)=0 / INVALID_PARAMETER(4003)
| |||
| |||
*/
| |||
//+------------------------------------------------------------------+
|