вопрос по HMAC

 

Приветствую, коллеги.

Соорудил функцию для HMAC sha256, сами знаете для чего)

Ниже код, демонстрирующий работу и ошибку. В двух случаях результат совпадает с онлайн-сервисами (также ещё проверка - два примера взяты из документации по АПИ известной крипто биржи), в третьем нет.

Можно менять строку сообщения, результат будет то совпадать то нет, чаще вроде совпадает.

Никак не пойму как локализовать косяк и в чём вообще может быть затык) если была бы ошибка в алгоритме sha или hmac результат был бы неверный всегда, если бы не учитывался терминальный ноль и т.п. аналогично.

Как в зависимости от строки ошибка то проявляется то нет, голову сломал не пойму, помогите.

проверял тут https://www.liavaag.org/English/SHA-Generator/HMAC/

саму хеш-функцию sha256 проверял тут http://www.sha1-online.com/

string keystr="NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j";
string Message="symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559";               
uchar Result[];
void OnStart()
  {
//---
      string strhmac;
      strhmac=HMAC(Message,keystr);
      StringToCharArray(strhmac,Result,0,StringLen(strhmac));
      Print("for key "+keystr);
      Print(StringLen(Message)," ",Message+ "\r\nHMAC 256: "+ArrayToHex(Result));//+"\r\n"+strhmac);  
      Message = "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTCquantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559";  
      ArrayResize(Result,0);
      strhmac=HMAC(Message,keystr);
      StringToCharArray(strhmac,Result,0,StringLen(strhmac));
      Print(StringLen(Message)," ",Message+ "\r\nHMAC 256: "+ArrayToHex(Result));//+"\r\n"+strhmac);  
      Message = "symbol=LTCBTCside=BUY&type=LIMIT&timeInForce=GTCquantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559";        
      ArrayResize(Result,0);
      strhmac=HMAC(Message,keystr);
      StringToCharArray(strhmac,Result,0,StringLen(strhmac));
      Print(StringLen(Message)," ",Message+ "\r\nHMAC 256: "+ArrayToHex(Result));//+"\r\n"+strhmac);  
  }
//+------------------------------------------------------------------+
string ArrayToHex(uchar &arr[],int count=-1) 
  { 
   string res=""; 
//--- проверка размера 
   if(count<0 || count>ArraySize(arr)) 
      count=ArraySize(arr); 
//--- преобразование в шестнадцатиричную строку 
   for(int i=0; i<count; i++) 
      res+=StringFormat("%.2X",arr[i]); 
//--- 
   return(res); 
  } 
//+------------------------------------------------------------------+
string HMAC(string smsg, string skey)
        {
                uint BLOCKSIZE= 64;
                if ((uint)StringLen(skey)>BLOCKSIZE) 
                skey=sha256(skey);              
                uchar key[]; uint n=(uint)StringToCharArray(skey, key, 0, StringLen(skey));
                if (n<BLOCKSIZE) { ArrayResize(key, BLOCKSIZE); ArrayFill(key, n, BLOCKSIZE-n, 0); }
                
                uchar i_key_pad[]; ArrayCopy(i_key_pad, key); for(uint i=0; i<BLOCKSIZE; i++) i_key_pad[i]=key[i]^(uchar)0x36;
                uchar o_key_pad[]; ArrayCopy(o_key_pad, key); for(uint i=0; i<BLOCKSIZE; i++) o_key_pad[i]=key[i]^(uchar)0x5c;
        
                uchar msg[]; n=(uint)StringToCharArray(smsg, msg, 0, StringLen(smsg));
                uchar i_s[]; ArrayResize(i_s, BLOCKSIZE+n); ArrayCopy(i_s, i_key_pad); ArrayCopy(i_s, msg, BLOCKSIZE);
                uchar i_sha512[]; string is=sha256(i_s);StringToCharArray(is, i_sha512, 0, StringLen(is));
                uchar o_s[]; ArrayResize(o_s, BLOCKSIZE+ArraySize(i_sha512)); ArrayCopy(o_s, o_key_pad); ArrayCopy(o_s, i_sha512, BLOCKSIZE);
                string o_sha512=sha256(o_s);
        
                return o_sha512;
        } 
//---
string sha256(uchar &message[])  
{
                uchar result[],key[]; string res;
                //StringToCharArray(smsg,message,0,StringLen(keystr),CP_UTF8);
                int c=CryptEncode(CRYPT_HASH_SHA256,message,key,result); 
                res=CharArrayToString(result,0,ArraySize(result),CP_ACP);
                //Print(__LINE__+res);
                return res;
}
/*Вывод:
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   for key NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   110 symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   HMAC 256: C8DB56825AE71D6D79447849E617115F4A920FA2ACDCAB2B053C4B2838BD6B71
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   109 symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTCquantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   HMAC 256: 811DAB3E88527429FBCEA39D76AB9C516AE3F177239B66D5443A1ACD261D9DF6   
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   108 symbol=LTCBTCside=BUY&type=LIMIT&timeInForce=GTCquantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559
2021.12.12 16:21:35.021 test_HMAC (LTCBTC,H1)   HMAC 256: E04F1DCF24FBE2B010E7BBFD0466B93898D607175C2E389303DD44E58E5990BC
*/
HMAC Generator — Online Hash Encryption
HMAC Generator — Online Hash Encryption
  • Morten Rene Liavaag Stroemsborg
  • www.liavaag.org
Here is an HMAC (keyed-hash message authentication code) online generator that generates a cryptographic hash function in combination with a secret encryption key.
 

бросается в глаза CP_ACP, когда сервера классически считают что должен быть UTF8..хотя для первых 128 ASCII разницы вроде-бы нет

и между & вы пропускаете со страшной силой :-)

 
Maxim Kuznetsov #:

бросается в глаза CP_ACP, когда сервера классически считают что должен быть UTF8..хотя для первых 128 ASCII разницы вроде-бы нет

и между & вы пропускаете со страшной силой :-)

1. как понял выделенное, имеется ввиду символ & в строке сообщений, это как раз для наглядности-простоты примера, первые две строки - там один раз пропущено &,так требует отправлять параметры биржа, если часть в url , а часть в теле запроса, а во второй это я от балды удалил & чтобы создать третий пример :-)

2. насчёт   CP_ACP спасибо, думал в эту сторону, ох и не люблю эти вещи, неужели нельзя было подумать чуть наперёд и придумать юникод сразу в 50-х, умные вроде дядьки же были)))

3. а ошибку нашёл и пофиксил уже, после поста осенило какие ещё тесты сделать и докопался. Может кстати и связано с  UTF8, до конца не понял, см.п.2. :-) 

 
Aleksey Mavrin #:

1. как понял выделенное, имеется ввиду символ & в строке сообщений, это как раз для наглядности-простоты примера, первые две строки - там один раз пропущено &,так требует отправлять параметры биржа, если часть в url , а часть в теле запроса, а во второй это я от балды удалил & чтобы создать третий пример :-)

2. насчёт   CP_ACP спасибо, думал в эту сторону, ох и не люблю эти вещи, неужели нельзя было подумать чуть наперёд и придумать юникод сразу в 50-х, умные вроде дядьки же были)))

3. а ошибку нашёл и пофиксил уже, после поста осенило какие ещё тесты сделать и докопался. Может кстати и связано с  UTF8, до конца не понял, см.п.2. :-) 

помнится есть ещё проблемы с завершающим \0 - он включается в результат StringToCharArray, но в подсчёт HMAC не должен попадать

 
Maxim Kuznetsov #:

помнится есть ещё проблемы с завершающим \0 - он включается в результат StringToCharArray, но в подсчёт HMAC не должен попадать

верно, это я ранее выяснил, кстати я бы на месте разработчиков в справку по функции это прям дописал, важный же момент.

а в моём случае оказалось тоже всё просто - Sha256 может выдавать 0 в хеше, поэтому конвертация в строку неприемлема, только в хХХ  hex вид в самом конце.

Причина обращения: