//+------------------------------------------------------------------+ //| 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_SIMETRIC_AES_MAIN_MQH #define CRYPTOBYLEO_SRC_SIMETRIC_AES_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Defines.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CAes { public: // se peude modifcar directamtne fuera.. uint8_t m_iv[AES_BLOCKLEN]; // vecotr de inicizlaicion uint m_ks; // desde donde inica el buffer pasado para cifrar\descifrar private: //_-- ENUM_AES_TYPE_CRYPTH m_method; ENUM_AES_LAST_ERR m_last_err; //--- int m_ivs; bool m_save_last_ivs_in_cbc; //--- int m_round_key_s; // size uint8_t m_round_key[AES_MAX_KEY_FINAL_S]; // round key //--- uint m_ncol; // nuymeo de columnas en la clave (4 = clavelen\4) (Nk) uint m_ncol_last; // m_ncol-1 (para fast %) uint m_nr; // numero de rondas (nk+6) int m_expected_len_key; // tamaƱo de clave que se espera //--- void AddRoundKey(uint round, uint8_t& state[]) const; __forceinline uint8_t xtime(uint8_t x) const; public: CAes(void); ~CAes(void) {} //--- void KeyExpansion(const uint8_t& key[]); //--- void Method(ENUM_AES_TYPE_CRYPTH tcrypht); //--- bool Init(const uint8_t &key[]); bool Init(const uint8_t& key[], const uint8_t& iv[]); //--- Base / ECB void CifradoInverso(uint8_t& state[]) const; void Cifrar(uint8_t& state[]) const; //--- Cbc con IV secuencial void CbcCifrado(uint8_t &buf[], uint length); void CbcDescifra(uint8_t& buf[], uint length); __forceinline bool CbcSaveLastIv() const { return m_save_last_ivs_in_cbc; } void CbcSaveLastIv(bool v) { m_save_last_ivs_in_cbc = v; } //--- CTR void CtrCryptX(uint8_t& buf[], uint length); //--- static void CorrectPadding(uchar& in[]); static bool QuitarPadding(uchar& in[]); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CAes::CAes(void) : m_save_last_ivs_in_cbc(true) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CAes::KeyExpansion(const uint8_t & key[]) { unsigned int i, j, k; uint8_t tempa[4]; // Used for the column/row operations //--- Copiamos. // Primera pasada copiamos por cada columa 4 valores.. // [] .... m_col // [] // [] // [] for(i = 0; i < m_ncol; ++i) { m_round_key[(i * 4) + 0] = key[(i * 4) + 0]; m_round_key[(i * 4) + 1] = key[(i * 4) + 1]; m_round_key[(i * 4) + 2] = key[(i * 4) + 2]; m_round_key[(i * 4) + 3] = key[(i * 4) + 3]; } //--- // luego empzamos en la utliam fila hasta el numero de rondas * 4 +1 for(i = m_ncol; i < AES_COL_S * (m_nr + 1); ++i) { { // Obtenemos palabra k = (i - 1) * 4; tempa[0] = m_round_key[k + 0]; tempa[1] = m_round_key[k + 1]; tempa[2] = m_round_key[k + 2]; tempa[3] = m_round_key[k + 3]; } if((i & m_ncol_last) == 0) // % mncol { // This function shifts the 4 bytes in a word to the left once. // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] // Function RotWord() { const uint8_t u8tmp = tempa[0]; tempa[0] = tempa[1]; tempa[1] = tempa[2]; tempa[2] = tempa[3]; tempa[3] = u8tmp; } // SubWord() is a function that takes a four-byte input word and // applies the S-box to each of the four bytes to produce an output word. // Function Subword() { tempa[0] = getSBoxValue(tempa[0]); tempa[1] = getSBoxValue(tempa[1]); tempa[2] = getSBoxValue(tempa[2]); tempa[3] = getSBoxValue(tempa[3]); } tempa[0] = tempa[0] ^ g_aes_rcon[i / m_ncol]; } //#if defined(AES256) && (AES256 == 1) if(m_method == AES_CRYPTH_256 && (i & m_ncol_last) == 4) { // Function Subword() { tempa[0] = getSBoxValue(tempa[0]); tempa[1] = getSBoxValue(tempa[1]); tempa[2] = getSBoxValue(tempa[2]); tempa[3] = getSBoxValue(tempa[3]); } } //#endif j = i * 4; k = (i - m_ncol) * 4; m_round_key[j + 0] = m_round_key[k + 0] ^ tempa[0]; m_round_key[j + 1] = m_round_key[k + 1] ^ tempa[1]; m_round_key[j + 2] = m_round_key[k + 2] ^ tempa[2]; m_round_key[j + 3] = m_round_key[k + 3] ^ tempa[3]; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CAes::Method(ENUM_AES_TYPE_CRYPTH tcrypht) { m_method = tcrypht; switch(tcrypht) { case AES_CRYPTH_128: { m_round_key_s = 176; m_expected_len_key = 16; m_ncol = 4; m_ncol_last = 3; m_nr = 10; break; } case AES_CRYPTH_256: { m_round_key_s = 240; m_expected_len_key = 32; m_ncol = 8; m_ncol_last = 7; m_nr = 14; break; } default: { // fllabkac a 128 break; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CAes::Init(const uint8_t &key[]) { if(ArraySize(key) != m_expected_len_key) { m_last_err = AES_LAST_ERR_INVALID_KEY_LEN; return false; } KeyExpansion(key); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CAes::Init(const uint8_t &key[], const uint8_t &iv[]) { if(ArraySize(key) != m_expected_len_key) { m_last_err = AES_LAST_ERR_INVALID_KEY_LEN; return false; } KeyExpansion(key); //--- const int t = ArraySize(iv); if(t != AES_BLOCKLEN) { m_last_err = AES_LAST_ERR_INVALID_IV_SIZE; return false; } //--- for(int i = 0; i < AES_BLOCKLEN; i++) { m_iv[i] = iv[i]; } //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // con el buffer acutal aplicamos un xor con el key void CAes::AddRoundKey(uint round, uint8_t& state[]) const { uint8_t i; for(i = 0; i < 4; ++i) { // unrolled AesStateAcces(i, 0) ^= m_round_key[(round * AES_COL_S * 4) + (i * AES_COL_S) + 0]; AesStateAcces(i, 1) ^= m_round_key[(round * AES_COL_S * 4) + (i * AES_COL_S) + 1]; AesStateAcces(i, 2) ^= m_round_key[(round * AES_COL_S * 4) + (i * AES_COL_S) + 2]; AesStateAcces(i, 3) ^= m_round_key[(round * AES_COL_S * 4) + (i * AES_COL_S) + 3]; } } //+------------------------------------------------------------------+ // Suma corregida por lo visto en mapa finito de 256 __forceinline uint8_t CAes::xtime(uint8_t x) const { return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // por cda elemento lo sutitlmis con la posicion en que cae en la talba sbox /* void CAes::SubBytes(uint8_t &state[]) const { uint8_t i, j; for(i = 0; i < 4; ++i) { AesStateAcces(0, i) = getSBoxValue(AesStateAcces(0, i)); AesStateAcces(1, i) = getSBoxValue(AesStateAcces(1, i)); AesStateAcces(2, i) = getSBoxValue(AesStateAcces(2, i)); AesStateAcces(3, i) = getSBoxValue(AesStateAcces(3, i)); } } //+------------------------------------------------------------------+ void CAes::ShiftRows(uint8_t &state[]) const { uint8_t temp; // Rotate first row 1 columns to left temp = AesStateAcces(0, 1); AesStateAcces(0, 1) = AesStateAcces(1, 1); AesStateAcces(1, 1) = AesStateAcces(2, 1); AesStateAcces(2, 1) = AesStateAcces(3, 1); AesStateAcces(3, 1) = temp; // Rotate second row 2 columns to left temp = AesStateAcces(0, 2); AesStateAcces(0, 2) = AesStateAcces(2, 2); AesStateAcces(2, 2) = temp; temp = AesStateAcces(1, 2); AesStateAcces(1, 2) = AesStateAcces(3, 2); AesStateAcces(3, 2) = temp; // Rotate third row 3 columns to left temp = AesStateAcces(0, 3); AesStateAcces(0, 3) = AesStateAcces(3, 3); AesStateAcces(3, 3) = AesStateAcces(2, 3); AesStateAcces(2, 3) = AesStateAcces(1, 3); AesStateAcces(1, 3) = temp; }*/ /* //+------------------------------------------------------------------+ void CAes::MixColumns(uint8_t &state[]) const { uint8_t temp = AesStateAcces(0, 1); AesStateAcces(0, 1) = AesStateAcces(1, 1); AesStateAcces(1, 1) = AesStateAcces(2, 1); AesStateAcces(2, 1) = AesStateAcces(3, 1); AesStateAcces(3, 1) = temp; // Rotate second row 2 columns to left temp = AesStateAcces(0, 2); AesStateAcces(0, 2) = AesStateAcces(2, 2); AesStateAcces(2, 2) = temp; temp = AesStateAcces(1, 2); AesStateAcces(1, 2) = AesStateAcces(3, 2); AesStateAcces(3, 2) = temp; // Rotate third row 3 columns to left temp = AesStateAcces(0, 3); AesStateAcces(0, 3) = AesStateAcces(3, 3); AesStateAcces(3, 3) = AesStateAcces(2, 3); AesStateAcces(2, 3) = AesStateAcces(1, 3); AesStateAcces(1, 3) = temp; } */ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* void CAes::InvMixColumns(uint8_t &state[]) const { int i; uint8_t a, b, c, d; for(i = 0; i < 4; ++i) { a = AesStateAcces(i, 0); b = AesStateAcces(i, 1); c = AesStateAcces(i, 2); d = AesStateAcces(i, 3); AesStateAcces(i, 0) = AesMultiply(a, 0x0e) ^ AesMultiply(b, 0x0b) ^ AesMultiply(c, 0x0d) ^ AesMultiply(d, 0x09); AesStateAcces(i, 1) = AesMultiply(a, 0x09) ^ AesMultiply(b, 0x0e) ^ AesMultiply(c, 0x0b) ^ AesMultiply(d, 0x0d); AesStateAcces(i, 2) = AesMultiply(a, 0x0d) ^ AesMultiply(b, 0x09) ^ AesMultiply(c, 0x0e) ^ AesMultiply(d, 0x0b); AesStateAcces(i, 3) = AesMultiply(a, 0x0b) ^ AesMultiply(b, 0x0d) ^ AesMultiply(c, 0x09) ^ AesMultiply(d, 0x0e); } } //+------------------------------------------------------------------+ void CAes::InvSubBytes(uint8_t &state[]) const { uint8_t i; for(i = 0; i < 4; ++i) { AesStateAcces(0, i) = getSBoxInvert(AesStateAcces(0, i)); AesStateAcces(1, i) = getSBoxInvert(AesStateAcces(1, i)); AesStateAcces(2, i) = getSBoxInvert(AesStateAcces(2, i)); AesStateAcces(3, i) = getSBoxInvert(AesStateAcces(3, i)); } } //+------------------------------------------------------------------+ void CAes::InvShiftRows(uint8_t &state[]) const { // Rotate first row 1 columns to right uint8_t temp = AesStateAcces(3, 1); AesStateAcces(3, 1) = AesStateAcces(2, 1); AesStateAcces(2, 1) = AesStateAcces(1, 1); AesStateAcces(1, 1) = AesStateAcces(0, 1); AesStateAcces(0, 1) = temp; // Rotate second row 2 columns to right temp = AesStateAcces(0, 2); AesStateAcces(0, 2) = AesStateAcces(2, 2); AesStateAcces(2, 2) = temp; temp = AesStateAcces(1, 2); AesStateAcces(1, 2) = AesStateAcces(3, 2); AesStateAcces(3, 2) = temp; // Rotate third row 3 columns to right temp = AesStateAcces(0, 3); AesStateAcces(0, 3) = AesStateAcces(1, 3); AesStateAcces(1, 3) = AesStateAcces(2, 3); AesStateAcces(2, 3) = AesStateAcces(3, 3); AesStateAcces(3, 3) = temp; } */ //+------------------------------------------------------------------+ //| Cifrado\Descifrado de un block de 16 exactos | //+------------------------------------------------------------------+ void CAes::Cifrar(uint8_t& state[]) const { uint8_t round = 0; // Add the First round key to the buf before starting the rounds. AddRoundKey(0, state); // There will be m_nr rounds. // The first m_nr-1 rounds are identical. // These m_nr rounds are executed in the loop below. // Last one without MixColumns() for(round = 1; ; ++round) { //--- Subbytes // SubBytes(state); uint8_t i; for(i = 0; i < 4; ++i) { // unrrolled AesStateAcces(0, i) = getSBoxValue(AesStateAcces(0, i)); AesStateAcces(1, i) = getSBoxValue(AesStateAcces(1, i)); AesStateAcces(2, i) = getSBoxValue(AesStateAcces(2, i)); AesStateAcces(3, i) = getSBoxValue(AesStateAcces(3, i)); } //--- Shift rows // Inline //ShiftRows(state); // temp; // Rotate first row 1 columns to left uint8_t temp = AesStateAcces(0, 1); AesStateAcces(0, 1) = AesStateAcces(1, 1); AesStateAcces(1, 1) = AesStateAcces(2, 1); AesStateAcces(2, 1) = AesStateAcces(3, 1); AesStateAcces(3, 1) = temp; // Rotate second row 2 columns to left temp = AesStateAcces(0, 2); AesStateAcces(0, 2) = AesStateAcces(2, 2); AesStateAcces(2, 2) = temp; temp = AesStateAcces(1, 2); AesStateAcces(1, 2) = AesStateAcces(3, 2); AesStateAcces(3, 2) = temp; // Rotate third row 3 columns to left temp = AesStateAcces(0, 3); AesStateAcces(0, 3) = AesStateAcces(3, 3); AesStateAcces(3, 3) = AesStateAcces(2, 3); AesStateAcces(2, 3) = AesStateAcces(1, 3); AesStateAcces(1, 3) = temp; //--- if(round == m_nr) { break; } //--- Mix columns //MixColumns(state); Inline uint8_t Tmp, Tm, t; //--- for(i = 0; i < 4; ++i) { t = AesStateAcces(i, 0); Tm = t ^ AesStateAcces(i, 1); Tmp = Tm ^ AesStateAcces(i, 2) ^ AesStateAcces(i, 3); //--- Tm = xtime(Tm); AesStateAcces(i, 0) ^= Tm ^ Tmp ; Tm = AesStateAcces(i, 1) ^ AesStateAcces(i, 2) ; Tm = xtime(Tm); AesStateAcces(i, 1) ^= Tm ^ Tmp ; Tm = AesStateAcces(i, 2) ^ AesStateAcces(i, 3) ; Tm = xtime(Tm); AesStateAcces(i, 2) ^= Tm ^ Tmp ; Tm = AesStateAcces(i, 3) ^ t ; Tm = xtime(Tm); AesStateAcces(i, 3) ^= Tm ^ Tmp ; } //--- AddRoundKey(round, state); } //--- // Add round key to last round AddRoundKey(m_nr, state); } //+------------------------------------------------------------------+ void CAes::CifradoInverso(uint8_t& state[]) const { //--- // Add the First round key to the buf before starting the rounds. AddRoundKey(m_nr, state); // There will be m_nr rounds. // The first m_nr-1 rounds are identical. // These m_nr rounds are executed in the loop below. // Last one without InvMixColumn() for(uint8_t round = uchar(m_nr - 1); ; --round) { //--- // InvShiftRows // Rotate first row 1 columns to right uint8_t temp = AesStateAcces(3, 1); AesStateAcces(3, 1) = AesStateAcces(2, 1); AesStateAcces(2, 1) = AesStateAcces(1, 1); AesStateAcces(1, 1) = AesStateAcces(0, 1); AesStateAcces(0, 1) = temp; // Rotate second row 2 columns to right temp = AesStateAcces(0, 2); AesStateAcces(0, 2) = AesStateAcces(2, 2); AesStateAcces(2, 2) = temp; temp = AesStateAcces(1, 2); AesStateAcces(1, 2) = AesStateAcces(3, 2); AesStateAcces(3, 2) = temp; // Rotate third row 3 columns to right temp = AesStateAcces(0, 3); AesStateAcces(0, 3) = AesStateAcces(1, 3); AesStateAcces(1, 3) = AesStateAcces(2, 3); AesStateAcces(2, 3) = AesStateAcces(3, 3); AesStateAcces(3, 3) = temp; //--- SubBytes // InvSubBytes(state); for(uint8_t i = 0; i < 4; ++i) { AesStateAcces(0, i) = getSBoxInvert(AesStateAcces(0, i)); AesStateAcces(1, i) = getSBoxInvert(AesStateAcces(1, i)); AesStateAcces(2, i) = getSBoxInvert(AesStateAcces(2, i)); AesStateAcces(3, i) = getSBoxInvert(AesStateAcces(3, i)); } //--- AddRoundKey(round, state); if(round == 0) { break; } //--- // InvMixColumns(state); uint8_t a, b, c, d; for(int i = 0; i < 4; ++i) { a = AesStateAcces(i, 0); b = AesStateAcces(i, 1); c = AesStateAcces(i, 2); d = AesStateAcces(i, 3); AesStateAcces(i, 0) = AesMultiply(a, 0x0e) ^ AesMultiply(b, 0x0b) ^ AesMultiply(c, 0x0d) ^ AesMultiply(d, 0x09); AesStateAcces(i, 1) = AesMultiply(a, 0x09) ^ AesMultiply(b, 0x0e) ^ AesMultiply(c, 0x0b) ^ AesMultiply(d, 0x0d); AesStateAcces(i, 2) = AesMultiply(a, 0x0d) ^ AesMultiply(b, 0x09) ^ AesMultiply(c, 0x0e) ^ AesMultiply(d, 0x0b); AesStateAcces(i, 3) = AesMultiply(a, 0x0b) ^ AesMultiply(b, 0x0d) ^ AesMultiply(c, 0x09) ^ AesMultiply(d, 0x0e); } } } //+------------------------------------------------------------------+ //| Cifrado\Descifrado CBC | //+------------------------------------------------------------------+ // buf = entrada se cifra // lenght multiplde blockslen (si no esta ajusta padding con len % BLOCK len eso te da lo que falta lo sumas y rellenas) void CAes::CbcCifrado(uint8_t &buf[], uint length) { //--- m_ivs = -1; for(m_ks = 0; m_ks < length; m_ks += AES_BLOCKLEN) { //--- Xor // The block in AES is always 128bit no matter the key size if(m_ivs == -1) { for(uint k = 0; k < AES_BLOCKLEN; ++k) buf[m_ks + k] ^= m_iv[k]; } else { for(uint k = 0; k < AES_BLOCKLEN; ++k) buf[m_ks + k] ^= buf[m_ivs + k]; } //--- // cifra desde m_ks Cifrar(buf); //--- m_ivs = (int)m_ks; // Ahora punta ahi.. } //--- /* store Iv in ctx for next call */ if(m_save_last_ivs_in_cbc) { ArrayCopy(m_iv, buf, 0, m_ivs, AES_BLOCKLEN); // memcpy(ctx->Iv, Iv, AES_BLOCKLEN); } } //+------------------------------------------------------------------+ void CAes::CbcDescifra(uchar &buf[], uint length) { //--- m_ivs = -1; uint8_t storeNextIv[AES_BLOCKLEN]; //--- for(m_ks = 0; m_ks < length; m_ks += AES_BLOCKLEN) { //--- Copiamos en el temporaƱ //memcpy(storeNextIv, buf, AES_BLOCKLEN); for(int i = 0; i < AES_BLOCKLEN; i++) storeNextIv[i] = buf[m_ks + i]; //--- CifradoInverso(buf); //--- //XorWithIv(buf, ctx->Iv); //--- Xor // The block in AES is always 128bit no matter the key size if(m_ivs == -1) { for(uint k = 0; k < AES_BLOCKLEN; ++k) buf[m_ks + k] ^= m_iv[k]; } else { for(uint k = 0; k < AES_BLOCKLEN; ++k) buf[m_ks + k] ^= buf[m_ivs + k]; } //--- // memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);, for(int i = 0; i < AES_BLOCKLEN; i++) { m_iv[i] = storeNextIv[i]; } // buf += AES_BLOCKLEN; ya se hace en el foir } // Nota que nosotros no hacesmos suma de putneros para iterar // Si no que tenemso un integer que es como nuestro puntero de lecutra // ENtonces nosotros modificamos eso.. } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CAes::CtrCryptX(uint8_t& buf[], uint length) { //--- uint8_t buffer[AES_BLOCKLEN]; //--- uint i; int bi; m_ks = 0; // start en 0 // La idea aqui es ir cifrando iv y aplicar xor con el buffer //--- for(i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) { if(bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ { // memcpy(buffer, m_iv, AES_BLOCKLEN); for(int k = 0; k < AES_BLOCKLEN; k++) { buffer[k] = m_iv[k]; } //--- Cifrar(buffer); //--- // Sumar en 1 (base 256) /* Increment Iv and handle overflow */ for(bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) { /* inc will overflow */ if(m_iv[bi] == 255) { m_iv[bi] = 0; continue; } m_iv[bi] += 1; break; } bi = 0; } //-- xor buf[i] = (buf[i] ^ buffer[bi]); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CAes::CorrectPadding(uchar &in[]) { //--- const int t = ArraySize(in); uchar ext = uchar(t % AES_BLOCKLEN); if(ext == 0) ext = 16; //---- const int fs = t + ext; ArrayResize(in, fs); //--- for(int i = t; i < fs; i++) { in[i] = ext; } } //+------------------------------------------------------------------+ static bool CAes::QuitarPadding(uchar &in[]) { //--- const int t = ArraySize(in); if(t < 1) return true; // raro no hay nada.. asi que no hayt errores const uchar last = in[t - 1]; //--- Check de range if(last < 1 || last > AES_BLOCKLEN) return false; //--- const int fs = int(t - last); for(int i = fs; i < t; i++) { if(in[i] != last) return false; } //--- ArrayResize(in, fs); //--- return true; } } //+------------------------------------------------------------------+ #endif // CRYPTOBYLEO_SRC_SIMETRIC_AES_MAIN_MQH