332 lines
9.8 KiB
MQL5
332 lines
9.8 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Public.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_RSA_KEYS_PUBLIC_MQH
|
|
#define CRYPTOBYLEO_SRC_RSA_KEYS_PUBLIC_MQH
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "H.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
namespace TSN
|
|
{
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CRsaPublicKey::Load(BigUInteger &n, const ulong e, int totaln_bits)
|
|
{
|
|
m_n.RobarDe(n);
|
|
m_e = e;
|
|
m_total_sbits = totaln_bits;
|
|
m_total_sbytes = totaln_bits >> 3;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CRsaPublicKey::Verify(const uchar &firma[], const uchar &data[])
|
|
{
|
|
/*
|
|
switch(m_sing_padder)
|
|
{
|
|
case RSA_SING_PADDER_PSS:
|
|
{
|
|
// m_sing_fhash uso del hash base para este firmado (se usa en pss y en mgf1 dos en uno)
|
|
//return CPSS::Verify(data, firma, m_total_sbits - 1, m_sing_fhash, m_sing_data.salt_len);
|
|
}
|
|
case RSA_SING_PADDER_PKCS1V15:
|
|
{
|
|
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}*/
|
|
return true;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TRandomFunc>
|
|
bool CRsaPublicKey::Cifrar(const uchar &raw[], uchar &out[])
|
|
{
|
|
//---
|
|
uchar em[];
|
|
TRandomFunc::RandomCryptoBytes(m_cip_random_bytes, TSN::CCryptoHashMeta::g_crytpo_hash_meta[m_cip_fhash].outl);
|
|
if(COAEP::Encode(raw, m_total_sbytes, m_cip_random_bytes, m_cip_fhash, em) == -1)
|
|
return false;
|
|
|
|
//---
|
|
m_base.FromBytesBE(em);
|
|
m_base.PowMod(m_e, m_n);
|
|
|
|
//---
|
|
m_base.ToBytesBE(out, 0, m_total_sbytes);
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
/*
|
|
SubjectPublicKeyInfo ::= SEQUENCE {
|
|
algorithm AlgorithmIdentifier,
|
|
subjectPublicKey BIT STRING }
|
|
|
|
AlgorithmIdentifier ::= SEQUENCE {
|
|
algorithm OBJECT IDENTIFIER, -- rsaEncryption: 1.2.840.113549.1.1.1
|
|
parameters NULL }
|
|
|
|
-- El contenido del BIT STRING es a su vez:
|
|
RSAPublicKey ::= SEQUENCE {
|
|
modulus INTEGER, -- n
|
|
publicExponent INTEGER -- e
|
|
}
|
|
*/
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CRsaPublicKey::ToSubjectPublicKeyInfo(uchar &buf[], int w = 0) const
|
|
{
|
|
//--- Empezamosres claculasl el len de todo
|
|
//-- De abajo ahcia arriab emepzoas con los numeros
|
|
//- E
|
|
int e_k;
|
|
uchar e_wl;
|
|
bool e_he;
|
|
int e_len = CNumberDERUtils::DEREstimateLen(m_e, e_k, e_he, e_wl);
|
|
//-- N
|
|
bool n_he;
|
|
int n_k;
|
|
uchar n_wl;
|
|
int n_len = m_n.DerEstimateLen(n_he, n_k, n_wl);
|
|
//-- Nivel mas arriba BIT STRING
|
|
uchar rsa_lenw;
|
|
int rsa_len = CASN1Utils::ContentTotalBytes(n_len + e_len, rsa_lenw);
|
|
// 1 = unnuseds bits
|
|
uchar bits_lenw;
|
|
int bits_total = CASN1Utils::ContentTotalBytes((1 + rsa_len), bits_lenw);
|
|
//--- parte 2 (AlgorithmIdentifier + BIT
|
|
uchar total_lenw;
|
|
int total = CASN1Utils::ContentTotalBytes(RSA_ALG_OID_TSIZE + bits_total, total_lenw);
|
|
|
|
//--- Ahora siahora mismo estamos en el "top" ahora bajamos
|
|
if(total + w > ArraySize(buf))
|
|
ArrayResize(buf, total + w);
|
|
|
|
//--- Inicaimos
|
|
// SubjectPublicKeyInfo
|
|
buf[w++] = CBL_ASN1_TAG_SEQUENCE;
|
|
CBL_ASN1_U_WRITELEN(buf, w, total_lenw, total)
|
|
// Ahora copy tal cual
|
|
w += ArrayCopy(buf, g_rsa_alg_id, w, 0, RSA_ALG_OID_TSIZE);
|
|
|
|
//-- seguimso con bit string
|
|
buf[w++] = CBL_ASN1_TAG_BITSTRING;
|
|
CBL_ASN1_U_WRITELEN(buf, w, bits_lenw, bits_total)
|
|
buf[w++] = CBL_ASN1_V_UNUSED_BITS;
|
|
|
|
//-- rsa
|
|
buf[w++] = CBL_ASN1_TAG_SEQUENCE;
|
|
CBL_ASN1_U_WRITELEN(buf, w, rsa_lenw, rsa_len)
|
|
// n
|
|
BIGUINTEGERBYLEO_DER_WRITE(buf, w, m_n, n_he, n_k, n_wl, n_len)
|
|
// e
|
|
NUMBERUTILS_DER_WRITE_NUMBER(buf, w, m_e, ulong, e_he, e_k, e_wl, e_len)
|
|
return w;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// Aqui elegi este diseño dado qeu noramlte cuando hay una deciion de disñeo como esta
|
|
// me pongo a testear y hacer benchs, la cosa es que habai tres candidasots
|
|
// 1. B64 Manual + \r\n cada 48 bytes
|
|
// 2. Cada 48 bytes callear a b64 natvo y insertar \r\n
|
|
// 3. Bulk masivo de b64 y luego copiar trozos e insetar \r\n
|
|
// Al final testenado el 3. ha sido mas rapido.... y el 1. como segundo mas rapido
|
|
// (pero no era muha difenica 10%~ aprox) esto en parte se debe a que el b64 nativo
|
|
// me da qeu no usa algo rapido por dbeajo tipo una lib con simd o algo si no mas bien
|
|
// una imepltacion funcional en cpp puro sin los ckeos de rango a cada acceso...
|
|
// dado que la version en MQL5 puro en mi caso es 160mb\s mas lento que la version nativa
|
|
// Lo otro el ArrayCopy intemante tampco hace ckehs (solo una vez) por lo qeu tambien
|
|
// el costo que se nos cva en allocar un buffer entero ya quizas lo cubren esos dos factores..
|
|
// tema de acceso.
|
|
// Por cierto ese material del los bench estan en (DiseñoBenchs)
|
|
int CRsaPublicKey::ToSubjectPublicKeyInfoPem(uchar &buf[], int w = 0) const
|
|
{
|
|
//---
|
|
// inplace (sin otro buffer hasta ahora 2 buffers en juego)
|
|
uchar raw[];
|
|
ToSubjectPublicKeyInfo(raw);
|
|
|
|
//---
|
|
// NOTA quizas cambie esto dado qeu no se puede hacer eso....
|
|
//
|
|
const int n = CryptEncode(CRYPT_BASE64, raw, raw, raw);
|
|
const int lines = (n + 63) >> 6; // ceil(n/64)
|
|
|
|
//---
|
|
const int final_s = w + RSA_PUBKEY_PEMHEADER_SIZE + (n + (lines << 1)) + RSA_PUBKEY_PEMFOOTER_SIZE;
|
|
if(final_s > ArraySize(buf))
|
|
ArrayResize(buf, final_s);
|
|
|
|
//--- header
|
|
w += ArrayCopy(buf, g_rsa_pubkey_pem_header, w, 0, RSA_PUBKEY_PEMHEADER_SIZE);
|
|
|
|
//--- in
|
|
int pos = 0;
|
|
while(n - pos >= RSA_PUBKEY_PEM_LINESIZE)
|
|
{
|
|
w += ArrayCopy(buf, raw, w, pos, RSA_PUBKEY_PEM_LINESIZE);
|
|
buf[w++] = '\r';
|
|
buf[w++] = '\n';
|
|
pos += RSA_PUBKEY_PEM_LINESIZE;
|
|
}
|
|
if(pos < n)
|
|
{
|
|
const int fin = n - pos;
|
|
w += ArrayCopy(buf, raw, w, pos, n);
|
|
buf[w++] = '\r';
|
|
buf[w++] = '\n';
|
|
}
|
|
|
|
//--- footer
|
|
w += ArrayCopy(buf, g_rsa_pubkey_pem_footer, w, 0, RSA_PUBKEY_PEMFOOTER_SIZE);
|
|
return w;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CRsaPublicKey::LoadSubjectPublicKeyInfo(const uchar &buf[], int r = 0)
|
|
{
|
|
if(buf[r++] != CBL_ASN1_TAG_SEQUENCE)
|
|
return false; // err
|
|
// saltamos len
|
|
CBL_ASN1_U_SALTAR_LEN(buf, r)
|
|
// ahora si chekeamos OID (tiene que ser RSA)
|
|
for(int i = 0; i < RSA_ALG_OID_TSIZE; i++)
|
|
if(buf[r++] != g_rsa_alg_id[i])
|
|
return false;
|
|
// ahora bit string
|
|
if(buf[r++] != CBL_ASN1_TAG_BITSTRING)
|
|
return false;
|
|
// len bit string
|
|
CBL_ASN1_U_SALTAR_LEN(buf, r)
|
|
// unused bit
|
|
r++;
|
|
|
|
//-- rsa
|
|
if(buf[r++] != CBL_ASN1_TAG_SEQUENCE) // tag sequencue
|
|
return false;
|
|
//--
|
|
CBL_ASN1_U_SALTAR_LEN(buf, r)
|
|
//--- ahora leemos numero
|
|
r = m_n.FromDER(buf, r);
|
|
if(r == -1)
|
|
return false;
|
|
//--- terminamso con el exponente
|
|
return CNumberDERUtils::DERRead(buf, r, m_e) >= r;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// Diseño para el decode elegido en BE64_PUB_DEC
|
|
bool CRsaPublicKey::LoadSubjectPublicKeyInfoPem(const uchar &buf[], int r = 0)
|
|
{
|
|
//---
|
|
const int t = ArraySize(buf); // tamaño del buffer
|
|
|
|
//--- Buscar el inicio
|
|
r += RSA_PUBKEY_PEMHEADER_SIZE;
|
|
//- saltar ws
|
|
while(true)
|
|
{
|
|
if(buf[r] > 32)
|
|
break;
|
|
r++;
|
|
if(r >= t)
|
|
return false;
|
|
}
|
|
// ahora mismo en el primer carcater no ws
|
|
|
|
//--- Ahora buscamos el fin
|
|
int k = t - 1;
|
|
//- saltar ws
|
|
while(true)
|
|
{
|
|
if(buf[k] > 32)
|
|
break;
|
|
k--;
|
|
if(k < 0)
|
|
return false;
|
|
}
|
|
k -= RSA_PUBKEY_PEMFOOTER_CLEAN; // ahora restamos
|
|
// saltamos ws que encontremos..
|
|
while(true)
|
|
{
|
|
if(buf[k] > 32)
|
|
break;
|
|
k--;
|
|
if(r >= t)
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
// Justo en el inicio de b64
|
|
uchar out[];
|
|
const int rlen = (k - r) + 1;
|
|
ArrayResize(out, ((rlen >> 2) + 1) * 3);
|
|
|
|
|
|
//--- ahora si decodeamos
|
|
int w = 0;
|
|
uint acc = 0;
|
|
int bits = 0;
|
|
|
|
//---
|
|
for(; r <= k; r++)
|
|
{
|
|
//---
|
|
const uchar c = buf[r];
|
|
if(c < 33 || c == '=')
|
|
continue;
|
|
//---
|
|
acc = (acc << 6) | g_b64_decode[c];
|
|
bits += 6;
|
|
if(bits >= 8)
|
|
{
|
|
bits -= 8;
|
|
out[w++] = uchar(acc >> bits);
|
|
}
|
|
}
|
|
|
|
//---
|
|
// Ahora si cargamos ya con la data verdadera
|
|
return LoadSubjectPublicKeyInfo(out);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
}
|
|
|
|
|
|
//---
|
|
#endif // CRYPTOBYLEO_SRC_RSA_KEYS_PUBLIC_MQH
|