CryptoByLeo/Src/Hash/SHA3.mqh
2026-08-20 22:15:18 -05:00

536 lines
18 KiB
MQL5
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+------------------------------------------------------------------+
//| SHA3.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_HASH_SHA3_MQH
#define CRYPTOBYLEO_SRC_HASH_SHA3_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "Base.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//---
#define SHA3_DELIM_SHA3 (0x06)
#define SHA3_DELIM_SHAKE (0x1F)
//---
#define SHA3_256_OUT_BSIZE (32)
#define SHA3_256_BLOCK_SIZE (136)
//---
#define SHA3_512_OUT_BSIZE (64)
#define SHA3_512_BLOCK_SIZE (72)
//---
#define SHA3_384_OUT_BSIZE (48)
#define SHA3_384_BLOCK_SIZE (104)
//---
#define SHA3_SHAKE_128_BLOCK_SIZE (168)
#define SHA3_SHAKE_256_BLOCK_SIZE (136)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define SHA3_LOADINLINE(idx) \
s[idx] ^= ((uint64_t)in[pos++]) | \
((uint64_t)in[pos++] << 8) | \
((uint64_t)in[pos++] << 16) | \
((uint64_t)in[pos++] << 24) | \
((uint64_t)in[pos++] << 32) | \
((uint64_t)in[pos++] << 40) | \
((uint64_t)in[pos++] << 48) | \
((uint64_t)in[pos++] << 56);
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CCryptoHashSHA3
{
private:
static void SHA3_KECCAK_F(ulong &s[]);
public:
CCryptoHashSHA3(void) {}
~CCryptoHashSHA3(void) {}
//---
union SHA3_UN
{
uchar block[200];
ulong uv[25];
};
//--- Por limitaciones del compilador de que no se permite tempaltes medio raras
// en static class lo hare inline
//---
// TChunkBytes = tamaño del bloquje en bytes que se procesar en cada it
// ins= tmaño del inpunt (in)
// in= input
// out=salida
// outl= tamaño de salida
template <typename TChunkBytes, typename TDelimBytes>
static void SHA3_HASH(const uchar &in[], int ins, uchar &out[], int outl, int o_s)
{
//--- Iniciales
ulong s[25] = {0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL,
0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL
};
const int rho[25] = {0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14};
const int pi[25] = {0, 10, 20, 5, 15, 16, 1, 11, 21, 6, 7, 17, 2, 12, 22, 23, 8, 18, 3, 13, 14, 24, 9, 19, 4};
const ulong rc[24] =
{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
//--- paso 1 absorcion
int pos = 0;
while(ins - pos >= sizeof(TChunkBytes))
{
//---- carga inline (72 bytes iniciales el minimo comun entre todos.. de ahi algunos cargan mas..)
SHA3_LOADINLINE(0)
SHA3_LOADINLINE(1)
SHA3_LOADINLINE(2)
SHA3_LOADINLINE(3)
SHA3_LOADINLINE(4)
SHA3_LOADINLINE(5)
SHA3_LOADINLINE(6)
SHA3_LOADINLINE(7)
SHA3_LOADINLINE(8)
//---- carga manual
for(int idx = 9; idx < (sizeof(TChunkBytes) >> 3); idx++)
{
s[idx] ^= ((uint64_t)in[pos++]) |
((uint64_t)in[pos++] << 8) |
((uint64_t)in[pos++] << 16) |
((uint64_t)in[pos++] << 24) |
((uint64_t)in[pos++] << 32) |
((uint64_t)in[pos++] << 40) |
((uint64_t)in[pos++] << 48) |
((uint64_t)in[pos++] << 56);
}
//---
// HOT PATH inline aqui mismo
//------- statart of mixing
// Nuestro "valor" en el cual operamos..
/*
mapa=
5 filas 5 columnas y cada valor es un lane de 64 bits
*/
//--- Temp
ulong c[5], d[5];
ulong b[25];
//--- Rondas.
for(int r = 0; r < 24; r++)
{
//--- theta
/* wiki
Calcula la paridad de cada una de las 5 w (320, cuando w = 64 )
columnas de 5 bits y realiza la operación OR exclusiva con dos columnas cercanas siguiendo un patrón regular.
Para ser precisos, a [ i ][ j ][ k ] ← a [ i ][ j ][ k ] ⊕ paridad(a[0...4][ j -1][ k ]) ⊕ paridad(a[0...4][ j +1][ k −1] )
*/
// Por cada columna
/*for(int x = 0; x < 5; x++)
c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
*/ // Denserollado:
c[0] = s[0] ^ s[5] ^ s[10] ^ s[15] ^ s[20];
c[1] = s[1] ^ s[6] ^ s[11] ^ s[16] ^ s[21];
c[2] = s[2] ^ s[7] ^ s[12] ^ s[17] ^ s[22];
c[3] = s[3] ^ s[8] ^ s[13] ^ s[18] ^ s[23];
c[4] = s[4] ^ s[9] ^ s[14] ^ s[19] ^ s[24];
// Final
//for(int x = 0; x < 5; x++)
/*{
const ulong cr = c[(x + 1) % 5];
d[x] = c[(x + 4) % 5] ^ ((cr << 1) | (cr >> 63));
}*/
// Denserollado
{
const ulong cr = c[1];
d[0] = c[4] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[2];
d[1] = c[0] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[3];
d[2] = c[1] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[4];
d[3] = c[2] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[0];
d[4] = c[3] ^ ((cr << 1) | (cr >> 63));
}
// unrrolled
s[0] ^= d[0];
s[1] ^= d[1];
s[2] ^= d[2];
s[3] ^= d[3];
s[4] ^= d[4];
s[5] ^= d[0];
s[6] ^= d[1];
s[7] ^= d[2];
s[8] ^= d[3];
s[9] ^= d[4];
s[10] ^= d[0];
s[11] ^= d[1];
s[12] ^= d[2];
s[13] ^= d[3];
s[14] ^= d[4];
s[15] ^= d[0];
s[16] ^= d[1];
s[17] ^= d[2];
s[18] ^= d[3];
s[19] ^= d[4];
s[20] ^= d[0];
s[21] ^= d[1];
s[22] ^= d[2];
s[23] ^= d[3];
s[24] ^= d[4];
//--- RHO+PI
for(int i = 0; i < 25; i++)
{
/*
Gire bit a bit cada una de las 25 palabras por un número triangular diferente 0, 1, 3, 6, 10, 15, .... (rho[])
Para ser precisos, a [0][0] no se rota, y para todo 0 ≤ t < 24 ,
a [ i ][ j ][ k ] ← a [ i ][ j ][ k −( t +1)( t +2)/2] , donde
*/
const int n = rho[i];
b[pi[i]] = (n == 0) ? s[i] : ((s[i] << n) | (s[i] >> (64 - n)));
}
//--- CHI fila a fila
for(int y = 0; y < 5; y++)
{
/*
Combinación bit a bit a lo largo de las filas, usando x ← x ⊕ (¬ y & z ) . Para ser precisos, a
[ i ][ j ][ k ] ← a [ i ][ j ][ k ] ⊕ (¬ a [ i ][ j + 1 ][ k ] &a a
[ i ][ j + 2 ][ k ]) . Esta es la única operación no lineal en SHA-3.
*/
//---
ulong row0 = b[5 * y], row1 = b[1 + 5 * y], row2 = b[2 + 5 * y], row3 = b[3 + 5 * y], row4 = b[4 + 5 * y];
//---
s[5 * y] = row0 ^ ((~row1) & row2);
s[1 + 5 * y] = row1 ^ ((~row2) & row3);
s[2 + 5 * y] = row2 ^ ((~row3) & row4);
s[3 + 5 * y] = row3 ^ ((~row4) & row0);
s[4 + 5 * y] = row4 ^ ((~row0) & row1);
}
//--- iota
s[0] ^= rc[r];
}
//-----
//------- end of mixing
//pos += sizeof(TChunkBytes);
}
// Lo demas ya no inline dado que no da una ganancia sinficativa por lo visto
// dado qeu se llama poco...
//--- ultimo bloque parcial + padding (0x06 o 0x1F ... 0x80)
SHA3_UN v;
ArrayInitialize(v.block, 0);
//---
const int rem = ins - pos;
//---
for(int i = 0; i < rem; i++)
v.block[i] = in[pos + i];
//---
v.block[rem] = sizeof(TDelimBytes);
v.block[sizeof(TChunkBytes) - 1] |= 0x80;
//--- Ahora lo que sobra..
const int fin = (sizeof(TChunkBytes) >> 3);
for(int i = 0; i < fin; i++)
{
/*const ulong w = ((ulong)block[i]) | ((ulong)block[i + 1] << 8) |
((ulong)block[i + 2] << 16) | ((ulong)block[i + 3] << 24) |
((ulong)block[i + 4] << 32) | ((ulong)block[i + 5] << 40) |
((ulong)block[i + 6] << 48) | ((ulong)block[i + 7] << 56);*/
s[i] ^= v.uv[i];
}
SHA3_KECCAK_F(s);
//---
// Print("hola", sizeof(TDelimBytes));
if(sizeof(TDelimBytes) == SHA3_DELIM_SHA3)
{
const int fin = (outl >> 3); // / 8
for(int k = 0; k < fin;)
{
const ulong t = s[k++];
out[o_s++] = uchar(t);
out[o_s++] = uchar(t >> 8);
out[o_s++] = uchar(t >> 16);
out[o_s++] = uchar(t >> 24);
out[o_s++] = uchar(t >> 32);
out[o_s++] = uchar(t >> 40);
out[o_s++] = uchar(t >> 48);
out[o_s++] = uchar(t >> 56);
}
}
else // dinamico
{
outl += o_s;
while(o_s < outl)
{
//---
int t = outl - o_s; // tomado
if(t > sizeof(TChunkBytes))
t = sizeof(TChunkBytes); // clamp a lo maximo a tomar..
//---
for(int i = 0; i < t; i++)
{
// en que lane
// const int w = ; // i / 8
const int sh = (i & 7) << 3; // i % 8 * 8 - cuanto demzpasmroes del lane de 64 desde cuando se mrpiza
out[o_s + i] = (uchar)(s[i >> 3] >> sh); // escribimos
}
o_s += t; // aumentaos lo que ya tenemos
//---
if(o_s < outl) // volvemos.. se requier mas de lo normal asi qeu aplicanos denuevo para la nueva salida
SHA3_KECCAK_F(s);
}
}
}
//---- Especificos
static __forceinline void SHA3_256(const uchar &in[], int ins, uchar &out[], const int o_s);
static __forceinline void SHA3_384(const uchar &in[], int ins, uchar &out[], const int o_s);
static __forceinline void SHA3_512(const uchar &in[], int ins, uchar &out[], const int o_s);
static __forceinline void SHAKE128(const uchar &in[], int ins, uchar &out[], int outl, const int o_s);
static __forceinline void SHAKE256(const uchar &in[], int ins, uchar &out[], int outl, const int o_s);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//--- keccak-f[1600] funcion base
// s =arrya de entarad (25) size ya con
static void CCryptoHashSHA3::SHA3_KECCAK_F(ulong &s[])
{
//--- Constatnes
static const int rho[25] = {0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14};
static const int pi[25] = {0, 10, 20, 5, 15, 16, 1, 11, 21, 6, 7, 17, 2, 12, 22, 23, 8, 18, 3, 13, 14, 24, 9, 19, 4};
static const ulong rc[24] =
{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
// Nuestro "valor" en el cual operamos..
/*
mapa=
5 filas 5 columnas y cada valor es un lane de 64 bits
*/
//--- Temp
ulong c[5], d[5];
ulong b[25];
//--- Rondas.
for(int r = 0; r < 24; r++)
{
//--- theta
/* wiki
Calcula la paridad de cada una de las 5 w (320, cuando w = 64 )
columnas de 5 bits y realiza la operación OR exclusiva con dos columnas cercanas siguiendo un patrón regular.
Para ser precisos, a [ i ][ j ][ k ] ← a [ i ][ j ][ k ] ⊕ paridad(a[0...4][ j -1][ k ]) ⊕ paridad(a[0...4][ j +1][ k −1] )
*/
// Por cada columna
/*for(int x = 0; x < 5; x++)
c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
*/ // Denserollado:
c[0] = s[0] ^ s[5] ^ s[10] ^ s[15] ^ s[20];
c[1] = s[1] ^ s[6] ^ s[11] ^ s[16] ^ s[21];
c[2] = s[2] ^ s[7] ^ s[12] ^ s[17] ^ s[22];
c[3] = s[3] ^ s[8] ^ s[13] ^ s[18] ^ s[23];
c[4] = s[4] ^ s[9] ^ s[14] ^ s[19] ^ s[24];
// Final
//for(int x = 0; x < 5; x++)
/*{
const ulong cr = c[(x + 1) % 5];
d[x] = c[(x + 4) % 5] ^ ((cr << 1) | (cr >> 63));
}*/
// Denserollado
{
const ulong cr = c[1];
d[0] = c[4] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[2];
d[1] = c[0] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[3];
d[2] = c[1] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[4];
d[3] = c[2] ^ ((cr << 1) | (cr >> 63));
}
{
const ulong cr = c[0];
d[4] = c[3] ^ ((cr << 1) | (cr >> 63));
}
// unrrolled
s[0] ^= d[0];
s[1] ^= d[1];
s[2] ^= d[2];
s[3] ^= d[3];
s[4] ^= d[4];
s[5] ^= d[0];
s[6] ^= d[1];
s[7] ^= d[2];
s[8] ^= d[3];
s[9] ^= d[4];
s[10] ^= d[0];
s[11] ^= d[1];
s[12] ^= d[2];
s[13] ^= d[3];
s[14] ^= d[4];
s[15] ^= d[0];
s[16] ^= d[1];
s[17] ^= d[2];
s[18] ^= d[3];
s[19] ^= d[4];
s[20] ^= d[0];
s[21] ^= d[1];
s[22] ^= d[2];
s[23] ^= d[3];
s[24] ^= d[4];
//--- RHO+PI
for(int i = 0; i < 25; i++)
{
/*
Gire bit a bit cada una de las 25 palabras por un número triangular diferente 0, 1, 3, 6, 10, 15, .... (rho[])
Para ser precisos, a [0][0] no se rota, y para todo 0 ≤ t < 24 ,
a [ i ][ j ][ k ] ← a [ i ][ j ][ k −( t +1)( t +2)/2] , donde
*/
const int n = rho[i];
b[pi[i]] = (n == 0) ? s[i] : ((s[i] << n) | (s[i] >> (64 - n)));
}
//--- CHI fila a fila
for(int y = 0; y < 5; y++)
{
/*
Combinación bit a bit a lo largo de las filas, usando x ← x ⊕ (¬ y & z ) . Para ser precisos, a
[ i ][ j ][ k ] ← a [ i ][ j ][ k ] ⊕ (¬ a [ i ][ j + 1 ][ k ] &a a
[ i ][ j + 2 ][ k ]) . Esta es la única operación no lineal en SHA-3.
*/
//---
ulong row0 = b[5 * y], row1 = b[1 + 5 * y], row2 = b[2 + 5 * y], row3 = b[3 + 5 * y], row4 = b[4 + 5 * y];
//---
s[5 * y] = row0 ^ ((~row1) & row2);
s[1 + 5 * y] = row1 ^ ((~row2) & row3);
s[2 + 5 * y] = row2 ^ ((~row3) & row4);
s[3 + 5 * y] = row3 ^ ((~row4) & row0);
s[4 + 5 * y] = row4 ^ ((~row0) & row1);
}
//--- iota
s[0] ^= rc[r];
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define SHA3_CHUNKT_IMP(BYTES, DLIM, P) \
struct SHA3_CHUNKT_BUFFER_##P \
{ \
const uchar data[BYTES]; \
}; \
struct SHA3_DLIM_BUFFER_##P \
{ \
const uchar data[DLIM]; \
};
//---
SHA3_CHUNKT_IMP(SHA3_256_BLOCK_SIZE, SHA3_DELIM_SHA3, SHA3_256)
SHA3_CHUNKT_IMP(SHA3_384_BLOCK_SIZE, SHA3_DELIM_SHA3, SHA3_384)
SHA3_CHUNKT_IMP(SHA3_512_BLOCK_SIZE, SHA3_DELIM_SHA3, SHA3_512)
SHA3_CHUNKT_IMP(SHA3_SHAKE_128_BLOCK_SIZE, SHA3_DELIM_SHAKE, SHA3_SHAKE_128)
SHA3_CHUNKT_IMP(SHA3_SHAKE_256_BLOCK_SIZE, SHA3_DELIM_SHAKE, SHA3_SHAKE_256)
//+------------------------------------------------------------------+
//| Esepcific implemtantion |
//+------------------------------------------------------------------+
static __forceinline void CCryptoHashSHA3::SHA3_256(const uchar &in[], int ins, uchar &out[], const int o_s)
{
CCryptoHashSHA3::SHA3_HASH<SHA3_CHUNKT_BUFFER_SHA3_256, SHA3_DLIM_BUFFER_SHA3_256>(in, ins, out, SHA3_256_OUT_BSIZE, o_s);
}
static __forceinline void CCryptoHashSHA3::SHA3_384(const uchar &in[], int ins, uchar &out[], const int o_s)
{
CCryptoHashSHA3::SHA3_HASH<SHA3_CHUNKT_BUFFER_SHA3_384, SHA3_DLIM_BUFFER_SHA3_384>(in, ins, out, SHA3_384_OUT_BSIZE, o_s);
}
static __forceinline void CCryptoHashSHA3::SHA3_512(const uchar &in[], int ins, uchar &out[], const int o_s)
{
CCryptoHashSHA3::SHA3_HASH<SHA3_CHUNKT_BUFFER_SHA3_512, SHA3_DLIM_BUFFER_SHA3_512>(in, ins, out, SHA3_512_OUT_BSIZE, o_s);
}
static __forceinline void CCryptoHashSHA3::SHAKE128(const uchar &in[], int ins, uchar &out[], int outl, const int o_s)
{
CCryptoHashSHA3::SHA3_HASH<SHA3_CHUNKT_BUFFER_SHA3_SHAKE_128, SHA3_DLIM_BUFFER_SHA3_SHAKE_128>(in, ins, out, outl, o_s);
}
static __forceinline void CCryptoHashSHA3::SHAKE256(const uchar &in[], int ins, uchar &out[], int outl, const int o_s)
{
CCryptoHashSHA3::SHA3_HASH<SHA3_CHUNKT_BUFFER_SHA3_SHAKE_256, SHA3_DLIM_BUFFER_SHA3_SHAKE_256>(in, ins, out, outl, o_s);
}
//---
}
//---
#endif // CRYPTOBYLEO_SRC_HASH_SHA3_MQH
//+------------------------------------------------------------------+