//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2026, Niquel Mendoza | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza" #property link "https://www.mql5.com" #property strict #ifndef CRYPTOBYLEO_SRC_FERNET_MAIN_MQH #define CRYPTOBYLEO_SRC_FERNET_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Def.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Notas imporantes: - Los archivos como maximo pueden pesar menos bytes que INT_MAX lo ideal quizas 1.5gb a menos o 1.7gb la cosa e sque debe sobra algo de 64 bytes para el IV, KEY etc... Esto por limitaciones del lengauje en el numero de elemtnos INT_MAX sobre un array que podemos procesar - DecryptRaw y Encrypt RAW el array "out" su tamaño ArraySize() es ligeralmetne mas grande o igual que el tamaño "en si" Se le da la elecccion al usuario para que este pueda recortar su tamaño (trim) o quizas si trabaj con un "int size" pueda setearlo.. Aparte estas funciones retornan -1 en caso de error */ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CFernet { private: //--- CAes m_aes; CHMac m_hmac; //--- ENUM_CRYPTOBYLEO_FERNAT_ERR m_last_err; public: CFernet(void); ~CFernet(void) {} //--- Seteo de key bool Key(const uchar& key[]); // auto solo eliges el generador y ya se hace todo auto // CORRECCION INLINE POR BUG DEL COMPILADOR template void Key() { uchar key[CRYPTOBYLEO_FERNET_KEYBYTES]; TCryptoRandomFunc::RandomCryptoBytes(key, CRYPTOBYLEO_FERNET_KEYBYTES, 0); //--- // m_hmac.CheckKeyReserve(16); // opt: Dado que por defecto la reserva es de 128 no hace falta vamos d esobra m_hmac.m_key_s = ArrayCopy(m_hmac.m_key, key, 0, 0, CRYPTOBYLEO_FERNET_HMAC_KEY_BYTES); m_hmac.OnKeyUpdate(); // ahora toca copiar para la parte alta (empezamos desde el final de hmac) m_aes.Init(key, CRYPTOBYLEO_FERNET_HMAC_KEY_BYTES); } //--- EncryptRaw \ DecryptRaw (genericos basicos) template int EncryptRaw(const uchar& in[], uchar& token[], datetime time) { // La idea con este es esquema es zero copyes la copia de in es invetiable dado que neceistmao mutar en aes //--- const int data_t = ArraySize(in); //--- estimacion de tamaoño const int fs = 1 + 8 + AES_BLOCKLEN + data_t + AES_BLOCKLEN + CRYPTOBYLEO_SHA256_SIZET_FHASH; if(fs > ArraySize(token)) ArrayResize(token, fs); // armado int w = 0; token[w++] = CRYPTOBYLEO_FERNET_VER; w = CNumberUtils::SValueToBytesBE(time, token, w); // -1defautl exacto, timstap TCryptoRandomFunc::RandomCryptoBytes(m_aes.m_iv, AES_BLOCKLEN, 0); // El iv lo copiamos al CBC de aes w += ArrayCopy(token, m_aes.m_iv, w, 0, AES_BLOCKLEN); // y tambien lo copiamos el token // Copiamos los datos al final ArrayCopy(token, in, w, 0, data_t); // #1 copia de los datos //--- const int t = CAes::PKCS7_CorrectPadding(token, data_t, w) - w; // tamaño exacto m_aes.CbcCifrado(token, t, w); // ciframos [w(inicio de datos) : t (datos con el padding)] w += t; // movemos el puntero Print(w); m_hmac.Update(token, w); // hmac w += ArrayCopy(token, m_hmac.m_out, w, 0, CRYPTOBYLEO_SHA256_SIZET_FHASH); // Copiamos el digest luego (append al final) return w; // retorna cuando a encryptado (w) tamaño final } //--- // Retorna el tamaño final de msg int DecryptRaw(const uchar& in[], int t, uchar& msg[], int ttl); //--- Extra (Agregados para facilidad) template bool EncryptFile(const string& file_name, bool common_flag, bool delete_prev_file, datetime time, string ext_encrypt = ""); bool DecryptFile(const string& file_name, bool common_flag, bool delete_prev_file, int ttl, string ext_decrypt = ""); bool DecryptFile(const string& file_name, bool common_flag, bool delete_prev_file, uchar& res_data[], int ttl); //--- Extract timestap datetime ExtractTimestap(const uchar& token[]); //--- __forceinline ENUM_CRYPTOBYLEO_FERNAT_ERR LastErr() const { return m_last_err; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // AES: 128 // HMAC-256 (SHA2 256) CFernet::CFernet(void) : m_aes(AES_CRYPTH_128), m_hmac(CCryptoHashMeta::TSN_CRYPTO_HASH_TYPE_SHA256) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Se asume key de 32 bits bool CFernet::Key(const uchar &key[]) { if(ArraySize(key) < CRYPTOBYLEO_FERNET_KEYBYTES) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_KEY_INVALID_SIZE; return false; } //--- // m_hmac.CheckKeyReserve(16); // opt: Dado que por defecto la reserva es de 128 no hace falta vamos d esobra m_hmac.m_key_s = ArrayCopy(m_hmac.m_key, key, 0, 0, CRYPTOBYLEO_FERNET_HMAC_KEY_BYTES); m_hmac.OnKeyUpdate(); // ahora toca copiar para la parte alta (empezamos desde el final de hmac) m_aes.Init(key, CRYPTOBYLEO_FERNET_HMAC_KEY_BYTES); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CFernet::DecryptRaw(const uchar &in[], int t, uchar &msg[], int ttl) { int r = 0; if(in[r++] != CRYPTOBYLEO_FERNET_VER) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_INVALID_VER; return -1; } // tiempo datetime time; r = CNumberUtils::SVAlaueFromBytesBE(time, in, r); if(TimeGMT() - time >= ttl) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_EXPIRED; return -1; } // ahora obteenmos el HMAC de int msi = t - CRYPTOBYLEO_SHA256_SIZET_FHASH; m_hmac.Update(in, msi); // ahora si comparammos // msi tambien detecta el inicio if(!m_hmac.CompareRaw(in, CRYPTOBYLEO_SHA256_SIZET_FHASH, msi)) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_HMAC_CORRUPTED; return -1; } // descifrado r += ArrayCopy(m_aes.m_iv, in, 0, r, AES_BLOCKLEN); // ahora r apunta justo en chiper text msi -= AES_BLOCKLEN + 8 + 1; // (iv) - (datetime) - (version) // aqui ocurre el resize exacto ArrayCopy(msg, in, 0, r, msi); // solo copiamos desde [r:msi, osea desde donde empiza chiper hasta donde temrina] m_aes.CbcDescifra(msg, msi); //--- r = CAes::PKCS7_QuitarPadding(msg, msi); if(r == -1) // padding invalido { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_INVALID_PADDING; return -1; } return r; // termino } //+------------------------------------------------------------------+ datetime CFernet::ExtractTimestap(const uchar &token[]) { //--- if(token[0] != CRYPTOBYLEO_FERNET_VER) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_INVALID_VER; return 0; } // tiempo datetime time; CNumberUtils::SVAlaueFromBytesBE(time, token, 1); return time; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool CFernet::EncryptFile(const string &file_name, bool common_flag, bool delete_prev_file, datetime time, string ext_encrypt = "") { //----- file ::ResetLastError(); const int common_file_flag = (common_flag ? FILE_COMMON : 0); int fh = FileOpen(file_name, FILE_BIN | FILE_READ | common_file_flag); if(fh == INVALID_HANDLE) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_OPEN_FILE; return false; } //--- uchar token[]; const int data_t = (int)FileSize(fh); //----- fernat start //--- estimacion de tamaoño const int fs = 1 + 8 + AES_BLOCKLEN + data_t + AES_BLOCKLEN + CRYPTOBYLEO_SHA256_SIZET_FHASH; if(fs > ArraySize(token)) ArrayResize(token, fs); // armado int w = 0; token[w++] = CRYPTOBYLEO_FERNET_VER; w = CNumberUtils::SValueToBytesBE(time, token, w); // -1defautl exacto, timstap TCryptoRandomFunc::RandomCryptoBytes(m_aes.m_iv, AES_BLOCKLEN, 0); // El iv lo copiamos al CBC de aes w += ArrayCopy(token, m_aes.m_iv, w, 0, AES_BLOCKLEN); // y tambien lo copiamos el token // Copiamos los datos al final FileReadArray(fh, token, w, data_t); // #1 copia de los datos FileClose(fh); //--- const int t = CAes::PKCS7_CorrectPadding(token, data_t, w) - w; // tamaño exacto m_aes.CbcCifrado(token, t, w); // ciframos w += t; // movemos el puntero m_hmac.Update(token, w); // hmac w += ArrayCopy(token, m_hmac.m_out, w, 0, CRYPTOBYLEO_SHA256_SIZET_FHASH); // Copiamos el digest luego (append al final) //--- trim ArrayResize(token, w); //----- fin fermat const string fn_final = file_name + (ext_encrypt.Length() > 2 ? ext_encrypt : ""); if(!FileSave(fn_final, token, common_file_flag)) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_SAVE_CRYPTHF_ILE; return false; } //--- solo eleiminos si se nos pide y el archivo cambio de nombre no tiene sentido eliminiarlo // si se mantine (Eliminamso el encriptado) if(delete_prev_file && fn_final != file_name && !FileDelete(file_name, common_file_flag)) m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEL_FILE_IN_CRYPT; //--- ahora si return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFernet::DecryptFile(const string &file_name, bool common_flag, bool delete_prev_file, uchar &res_data[], int ttl) { uchar in[]; const int common_file_flag = (common_flag ? FILE_COMMON : 0); ::ResetLastError(); const int bytes = (int)FileLoad(file_name, in, common_file_flag); if(bytes == -1) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_FILE_LOAD_ERR; return false; } //--- if(DecryptRaw(in, bytes, res_data, ttl) == -1) return false; //--- ::ResetLastError(); if(delete_prev_file && !FileDelete(file_name, common_file_flag)) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_FILE_DEL_ERR; } //--- return true; } //+------------------------------------------------------------------+ //| Desencripta un archivo | //| file_name : ruta del archivo encriptado | //| common_flag : true = carpeta Common, false = carpeta Files\ | //| delete_prev_file: true = elimina el archivo encriptado original | //| ext_decrypt : controla el nombre del archivo de salida: | //| - "" = sobreescribe el mismo archivo | //| - ".ext" = agrega extension file.enc → file.enc.ext | //| - "-" = quita la ultima extension file.enc → file | //| - ".ext-" = reemplaza ultima ext file.enc → file.ext | //+------------------------------------------------------------------+ bool CFernet::DecryptFile(const string &file_name, bool common_flag, bool delete_prev_file, int ttl, string ext_decrypt = "") { uchar in[]; const int common_file_flag = (common_flag ? FILE_COMMON : 0); ::ResetLastError(); const int bytes = (int)FileLoad(file_name, in, common_file_flag); if(bytes == -1) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_FILE_LOAD_ERR; return false; } //--- uchar res_data[]; const int t = DecryptRaw(in, bytes, res_data, ttl); if(t == -1) return false; //--- ArrayResize(res_data, t); //--- Creamos el archivo destino (o sobreescribimos si ext = "") // si ext_decrypt contiene el . entonces es exntesion si no el nombre del archivo out string out_file_name = ""; if(StringFindCharWRef(ext_decrypt, '.')) { const uint len = (ext_decrypt.Length()); if(ext_decrypt[len - 1] == '-') // -. quiere decir elimina la extension previa y añade esta nueva { ext_decrypt.Truncate(len - 1); // quitamos el - final eg si tiewnmaos extdecurp como .txt- queda .txt out_file_name = FileRemoveExtension(file_name) + ext_decrypt; } else { out_file_name = file_name + ext_decrypt; } } else if(StringFindCharWRef(ext_decrypt, '-')) // quiere decir solo quita la extension que ya tenia { out_file_name = FileRemoveExtension(file_name); // Removes la primera extension (volvemos al extado original) } else { out_file_name = file_name; // lo mismo sobrescribe } //--- primero guardamos ::ResetLastError(); if(!FileSave(out_file_name, res_data, common_file_flag)) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_FILE_SAVE_ERR; return false; } //--- Ahora si eliminamos el archivo previo si es que se pide ::ResetLastError(); if(delete_prev_file && !FileDelete(file_name, common_file_flag)) { m_last_err = CRYPTOBYLEO_FERNAT_ERR_DEC_FILE_DEL_ERR; } //--- return true; } } //+------------------------------------------------------------------+ #endif // CRYPTOBYLEO_SRC_FERNET_MAIN_MQH