CryptoByLeo/Src/Padding/PSS/Main.mqh
2026-08-23 19:36:33 -05:00

411 lines
13 KiB
MQL5

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