forked from nique_372/CryptoByLeo
411 lines
13 KiB
MQL5
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
|