//+------------------------------------------------------------------+ //| 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_PADDING_PSS_MAIN_MQH #define CRYPTOBYLEO_SRC_PADDING_PSS_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\..\\MGF1\\Main.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // https://datatracker.ietf.org/doc/html/rfc8017#section-9.1 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // maximo tamaño de salt en esta implemtacion (max HLEN * 4) // mas de eso no tiene mucho sentido #define PSS_MAX_SIZE_SALT (CRYPTOBYLEO_HASHFUNC_FIXED_MAX_OL<<2) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CPSS { public: CPSS(void) {} ~CPSS(void) {} //--- // msg= array de bytes // slen= len del salt // salt= datos random // em_bits=(modBits - 1) osea longitud del modulo (n) en bits -1 // f=funcion de hasheo // out=array de salida static int Encode(const uchar &msg[], int slen, const uchar& salt[], int em_bits, TSN::CCryptoHashMeta::ENUM_TSN_CRYPTO_HASH f, uchar &out[]); //--- // msg= original // em = mensaje codificado (firmado) // em_bits=(modBits - 1) osea longitud del modulo (n) en bits -1 // f=funcion de hash // slen=lent del salt static bool Verify(const uchar &msg[], const uchar &em[], int em_bits, TSN::CCryptoHashMeta::ENUM_TSN_CRYPTO_HASH f, int slen); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* +-----------+ | M | +-----------+ | V Hash | V +--------+----------+----------+ M' = |Padding1| mHash | salt | +--------+----------+----------+ | +--------+----------+ V DB = |Padding2| salt | Hash +--------+----------+ | | | V | xor <--- MGF <---| | | | | V V +-------------------+----------+--+ EM = | maskedDB | H |bc| +-------------------+----------+--+ */ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static int CPSS::Encode(const uchar &msg[], int slen, const uchar& salt[], int em_bits, TSN::CCryptoHashMeta::ENUM_TSN_CRYPTO_HASH f, uchar &out[]) { //--- const int hlen = TSN::CCryptoHashMeta::g_crytpo_hash_meta[f].outl; // tamañao de salida const int ml = ArraySize(msg); //--- const int em_len = (em_bits + 7) >> 3; if(em_len < (hlen + slen + 2)) return -1; //--- Tamaños /* // parte 1 const int p1s = 8 + hlen + slen; luego se le aplica un H hash asi que euqda hlen // parte 2 const int ps = (em_len - slen - hlen - 2); const int dbs = ps + 1 + slen; // ems = dbs + hlen + 1 */ //---- const int req_s = ((em_len - 1) << 1) - hlen; if(req_s > ArraySize(out)) ArrayResize(out, req_s); //--- const TSN::CCryptoHashMeta::TsnCryptoByLeoHashFuncFixed func = TSN::CCryptoHashMeta::g_crypto_fhash_fixed[f]; //---- Calcullo de los tamaños que necesitaremos reservar // // DB = PS + b"\x01" + salt // // dbs = (em_len - slen - hlen - 2) + 1 + slen // dbs = em_len - hlen - 2 + 1 // dbs = em_len - hlen - 1 // final_size = dbs + hlen + 1 // final_size = em_len - hlen - 1 + hlen + 1 // final_size = em_len (total) // // mprima_size = (8 + hlen + slen) // +1=BC // // max_size = dbs + (8 + hlen + slen) + 1 ; (maximo tamaño que llega alcanzar) // max_size = em_len - hlen - 1 + 8 + hlen + slen + 1 // max_size = em_len + 8 + slen // // En nuestro caso no llegaremos a usar eso si no: // = dbs + hlen + dbs // porque: // - dbs: parte inicial del layout // - hlen: H Prima inicial // - dbs (2): Neceistaremos un temproal para poder almacenar el tema del mask para DB // Asi que simplifiquemos lo que podamos // = dbs + hlen + dbs // = em_len - hlen - 1 + hlen + dbs // = em_len - 1 + dbs // nuestro tamaño final queda como: // = em_len - 1 + em_len - 1 - hlen // digamos que: x = em_len - 1 // = x + x - hlen // = 2x - hlen // asi qeu usaremos eso para cualar el tamaño requerido para nuestras operaciones //--- parte 1 // padding inicial const int ps = (em_len - slen - hlen - 2); int i = 0; for(; i < ps; i++) out[i] = 0; // 1 out[i++] = 1; // salt for(int k = 0; k < slen; k++) out[i + k] = salt[k]; i += slen; const int dbs = i; //----- parte 2 // M_prima = b"\x00" * 8 + mHash + salt const int mprima_s = 8 + hlen + slen; uchar MPrima[8 + CRYPTOBYLEO_HASHFUNC_FIXED_MAX_OL + PSS_MAX_SIZE_SALT]; //- Padding inicial MPrima[0] = 0; MPrima[1] = 0; MPrima[2] = 0; MPrima[3] = 0; MPrima[4] = 0; MPrima[5] = 0; MPrima[6] = 0; MPrima[7] = 0; //- Hash del mensaje func(msg, ml, MPrima, 8); //- concatenamos salt for(int k = 0; k < slen; k++) MPrima[8 + hlen + k] = salt[k]; //---- Ahora le aplicamos otro hahs a la parte 2 // tenemos que aplicarle la funcion hash a MPrima // el resultado va en [i ... hlen bytes] = H func(MPrima, mprima_s, out, i); //--- ahora toca crear la mascara const int i_mask = i + hlen; // inicio de donde estara la mascara // entonces: // tomamos: [i ... hlen bytes] // le aplicamos mgf1 // resultado en: [i_mask ... dbs bytes] MGF1(out, i, hlen, out, i_mask, dbs, f); // reiniciamos i i = 0; // aplicamos un xor entre (DB Mask XOR DB) for(; i < dbs; i++) out[i] ^= out[i_mask + i]; //--- por ultimos ponemos // pamaos a em len a bits const int bits_sob = (em_len << 3) - em_bits; if(bits_sob > 0) { // en caso haya bits sobrantes el byte 0 de masked DB (resultado db mask DB) // le aplicamos la mascara de bits para ponerlkos en 0 (and de 0 y x da 0) const uchar mask = uchar(0xff >> bits_sob); out[0] &= mask; } //--- BC // el final final (dbs + hlen) justo aqui lo pnemos con un valor de BC out[i_mask] = 0xBC; //terminamso //--- retornamos return em_len; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static bool CPSS::Verify(const uchar &msg[], const uchar &em[], int em_bits, TSN::CCryptoHashMeta::ENUM_TSN_CRYPTO_HASH f, int slen) { const int hlen = TSN::CCryptoHashMeta::g_crytpo_hash_meta[f].outl; // tamañao de salida const int em_len = (em_bits + 7) >> 3; // /8 mathceil(em_bits / 8) //--- cheke de len del enconden message (creo qeu esto es por ps dado qeu si fuera menor el padding seria invalido) const int min = hlen + slen + 2; if(em_len < min) return false; //--- if(ArraySize(em) != em_len) return false;// no es igual a lo esperado //--- if(em[em_len - 1] != 0xbc) return false;// layout invalido //--- const int dbs = em_len - hlen - 1; //--- const int bits_sob = (em_len << 3) - em_bits; uchar mask = 0xff; if(bits_sob > 0) { // ahora la mascara que tenamis apagamos x bits ahora el tema es que // digmoas si tnemis la mascra 00110011 // y por lo tanto el inverso 00110011 = 11001100 // entonces un and entre ambos debe de dar 0 // x and ~x = 0 // pero si x (el nuevo) se modificdo dara > 0 mask >>= uchar(bits_sob); if((em[0] & (~mask)) != 0) return false; } //--- // empezmaos a leer desde [dbs ... hlen bytes] (Osea donde se ubicaaba H real) uchar db[]; ArrayResize(db, em_len + 6 + dbs); //--- const int i_db = min + 6; // empiza justo donde terminar m_prima (hlen+slen+8) MGF1(em, dbs, hlen, db, i_db, dbs, f); //--- // obteenmmos la mascara de db ahora la corregiremos for(int i = 0; i < dbs; i++) db[i_db + i] ^= em[i]; // aplicamos la mascara al primer elemento db[i_db] &= mask; //--- Ahora chekeamos DB // padding de zeros // ps = (em_len - slen - hlen - 2) const int fin = i_db + (em_len - slen - hlen - 2); // fin apunta al final de del padding int ip = i_db; // ahora ip esta justo en i_db for(; ip < fin; ip++) if(db[ip] != 0x0) return false; // byte 0x1 luego del padding if(db[ip++] != 0x1) return false; // ip arhao mismo apuinta a la prima posicion de salt //--- ahora verficiameso el slen // (dbs + i_db) nos da el fin real en db del array DB if(slen != ((dbs + i_db) - ip)) // (dbs - ip) nos da el tamaño del slen real return false; // no coincidio //--- ahora m_prima // Ahora el pro qeu agrege al buffer dbs un hlen: // em_len > hlen + slen + 2 // dbs = em_len - hlen - 1 // como dbs no es 0 entonces como minimo es mayor a slen // dado qeu emlen es mayot a hlen+slen+2 eso quiere deicr qeu si le restamos hlen y 1 // entonces aun nos queda slen+1 como minimo // em_len - hlen - 1 > slen + 1 // asi que: dbs > slen + 1 // si garanzamis el peor caso=slen 0 // entonces dbs>1 // pero hlen como minimo (pro el minimo hash) hlen>=31 // si totamos un caso borde // hlen=32, slen=0 // emlen como minoimo > 34 // por lo que el minimo valo rde dbs = 35 - 32 - 1 = 2 (cumple mayor que 1) // debido a esto.. nuestro peor caso tnemos un len de dbs=2 que no cumple // con la condicino de ser mayor o igual a hlen // asi que debemos de sumar hlen aparte como lo reutilizamos para armar prima // tiene que ser mayor a > 8 + hlen + slen // en nuestro caso borde no lo seria.. asi que aumentos + 8 tambien para cumplir eso // bueno estuve dando rodeos a este tema pero note algo // partimso de que emlen > hlen + slen + 2 // entonces tambien sabes que emlen > dbs // asi qeu para que peuda cumplir que m_prima_size < em_len // summeos 6 a ambos lados de la inecuaion // emlen + 6 > hlen + slen + 2 + 6 // ahora si em_len + 6 > hlen + slen + 8 // por lo qeu ahora mismo tenemos que si reservmaos con eso nuestro buffer sera mayor a: // - m_prima // - dbs // entonces esto es lo suficiente para nuestro uso.. //--- bueno ahroa si obtengamos m-prima // ahora para poder usar 8+hlen bytes sin pisar el salt previo tenemso qeu verificar qeu // ps+1 > 8 + hlen // ps = (em_len - slen - hlen - 2) | + 1 // v = em_len - slen - hlen - 1 // para empzar ps!=0 dado qeu em_len>hlen + slen + 2 // asi qeu el minimo valor que toma esta exprecion sera de 2 (1+1) // por lo qeu tampoco... es candidatara a reutlizacion // Bueno hagamos algo // db es este // [......][....] // lo partiremos en dos partes la prienmra es donde pondre M Prima (parte baja) // La parte alta ahi pondre todo el tema de DB // entonces para tener un len adecuado seria // dbs+m_len+6 con eso cubro un m_prima+dbs //---- MPrima // ahora si empezmoas con el padding de zeros db[0] = 0; db[1] = 0; db[2] = 0; db[3] = 0; db[4] = 0; db[5] = 0; db[6] = 0; db[7] = 0; // Hasheos el mensaje primero (lo añadimos) const CCryptoHashMeta::TsnCryptoByLeoHashFuncFixed func = TSN::CCryptoHashMeta::g_crypto_fhash_fixed[f]; func(msg, ArraySize(msg), db, 8); // ahora añdimos el salt (qeu esta en la parte alta justo en ip apartirt de ahi iterarmos) for(int i = 0; i < slen; i++) { // se posiciona justo donde ira el salt lo copiamos ahi db[i + 8 + hlen] = db[ip + i]; } //--- parte final lo mutamos para uqe ahora tenga el H real func(db, i_db, db, 0); // 0= empiza a escribir en 0 //---- por ultimo comparamos este H obteneido con el H real del em uchar v = 0; for(int i = 0; i < hlen; i++) { v |= db[i] ^ em[i + dbs]; } return v == 0; } } #endif // CRYPTOBYLEO_SRC_PADDING_PSS_MAIN_MQH