MQL5Book/Scripts/p7/CryptDecode.mq5
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

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)
*/
//+------------------------------------------------------------------+