From d46f19cb1dc3777b4c9905991ccc19fcfe2fd50e Mon Sep 17 00:00:00 2001 From: amr Date: Thu, 2 Oct 2025 21:13:08 +0300 Subject: [PATCH] Fix SHA256 implementation: safer rotate, finalize guard, fread loop, fclose on errors --- hash_file | Bin 0 -> 16912 bytes hash_file.c | 17 ++++++++++------ sha256.c | 55 ++++++++++++++++++++++++++++++--------------------- sha256_tests | Bin 0 -> 21024 bytes 4 files changed, 44 insertions(+), 28 deletions(-) create mode 100755 hash_file create mode 100755 sha256_tests diff --git a/hash_file b/hash_file new file mode 100755 index 0000000000000000000000000000000000000000..cb561f3180e8d77628c70010a51ba27dd097e608 GIT binary patch literal 16912 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 literal 0 HcmV?d00001 diff --git a/hash_file.c b/hash_file.c index 84636cd..1a9965e 100644 --- a/hash_file.c +++ b/hash_file.c @@ -10,26 +10,31 @@ int main(int argc, char** argv){ if (argc < 2){ - printf("Usage: %s [file]\n", argv[0]); - return 0; + fprintf(stderr, "Usage: %s [file]\n", argv[0]); + return 1; } FILE* file = fopen(argv[1], "rb"); if (!file){ - printf("Cannot open file\n"); - return 0; + perror(argv[1]); + return 1; } char buffer[1024]; size_t size; struct sha256_buff buff; sha256_init(&buff); - while (!feof(file)){ + while ((size = fread(buffer, 1, sizeof(buffer), file)) > 0){ /* Hash file by 1kb chunks, instead of loading into RAM at once */ - size = fread(buffer, 1, 1024, file); sha256_update(&buff, buffer, size); } + if (ferror(file)) { + perror("fread"); + fclose(file); + return 1; + } char hash[65] = {0}; /* hash[64] is null-byte */ sha256_finalize(&buff); sha256_read_hex(&buff, hash); printf("%s\n", hash); + fclose(file); return 0; } diff --git a/sha256.c b/sha256.c index 70196b1..497a971 100644 --- a/sha256.c +++ b/sha256.c @@ -25,6 +25,8 @@ /* Details of the implementation, etc can be found here: https://en.wikipedia.org/wiki/SHA-2 See sha256.h for short documentation on library usage */ +#include + #include "sha256.h" void sha256_init(struct sha256_buff* buff) { @@ -51,7 +53,10 @@ static const uint32_t k[64] = { 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; -#define rotate_r(val, bits) (val >> bits | val << (32 - bits)) +static inline uint32_t rotr(uint32_t x, unsigned n) { + n &= 31u; + return (x >> n) | (x << (32u - n)); +} static void sha256_calc_chunk(struct sha256_buff* buff, const uint8_t* chunk) { uint32_t w[64]; @@ -64,8 +69,8 @@ static void sha256_calc_chunk(struct sha256_buff* buff, const uint8_t* chunk) { } for (i=16; i<64; ++i){ - uint32_t s0 = rotate_r(w[i-15], 7) ^ rotate_r(w[i-15], 18) ^ (w[i-15] >> 3); - uint32_t s1 = rotate_r(w[i-2], 17) ^ rotate_r(w[i-2], 19) ^ (w[i-2] >> 10); + uint32_t s0 = rotr(w[i-15], 7) ^ rotr(w[i-15], 18) ^ (w[i-15] >> 3); + uint32_t s1 = rotr(w[i-2], 17) ^ rotr(w[i-2], 19) ^ (w[i-2] >> 10); w[i] = w[i-16] + s0 + w[i-7] + s1; } @@ -73,10 +78,10 @@ static void sha256_calc_chunk(struct sha256_buff* buff, const uint8_t* chunk) { tv[i] = buff->h[i]; for (i=0; i<64; ++i){ - uint32_t S1 = rotate_r(tv[4], 6) ^ rotate_r(tv[4], 11) ^ rotate_r(tv[4], 25); + uint32_t S1 = rotr(tv[4], 6) ^ rotr(tv[4], 11) ^ rotr(tv[4], 25); uint32_t ch = (tv[4] & tv[5]) ^ (~tv[4] & tv[6]); uint32_t temp1 = tv[7] + S1 + ch + k[i] + w[i]; - uint32_t S0 = rotate_r(tv[0], 2) ^ rotate_r(tv[0], 13) ^ rotate_r(tv[0], 22); + uint32_t S0 = rotr(tv[0], 2) ^ rotr(tv[0], 13) ^ rotr(tv[0], 22); uint32_t maj = (tv[0] & tv[1]) ^ (tv[0] & tv[2]) ^ (tv[1] & tv[2]); uint32_t temp2 = S0 + maj; @@ -96,7 +101,11 @@ static void sha256_calc_chunk(struct sha256_buff* buff, const uint8_t* chunk) { void sha256_update(struct sha256_buff* buff, const void* data, size_t size) { const uint8_t* ptr = (const uint8_t*)data; - buff->data_size += size; + if (size > UINT64_MAX - buff->data_size) { + buff->data_size = UINT64_MAX; + } else { + buff->data_size += size; + } /* If there is data left in buff, concatenate it to process as new chunk */ if (size + buff->chunk_size >= 64) { uint8_t tmp_chunk[64]; @@ -116,38 +125,40 @@ void sha256_update(struct sha256_buff* buff, const void* data, size_t size) { /* Save remaining data in buff, will be reused on next call or finalize */ memcpy(buff->last_chunk + buff->chunk_size, ptr, size); - buff->chunk_size += size; + buff->chunk_size = (uint8_t)(buff->chunk_size + size); } void sha256_finalize(struct sha256_buff* buff) { - buff->last_chunk[buff->chunk_size] = 0x80; - buff->chunk_size++; - memset(buff->last_chunk + buff->chunk_size, 0, 64 - buff->chunk_size); + if (buff->chunk_size == sizeof(buff->last_chunk)) { + sha256_calc_chunk(buff, buff->last_chunk); + buff->chunk_size = 0; + } + + buff->last_chunk[buff->chunk_size++] = 0x80; + memset(buff->last_chunk + buff->chunk_size, 0, sizeof(buff->last_chunk) - buff->chunk_size); /* If there isn't enough space to fit int64, pad chunk with zeroes and prepare next chunk */ if (buff->chunk_size > 56) { sha256_calc_chunk(buff, buff->last_chunk); - memset(buff->last_chunk, 0, 64); + memset(buff->last_chunk, 0, sizeof(buff->last_chunk)); } /* Add total size as big-endian int64 x8 */ - uint64_t size = buff->data_size * 8; - int i; - for (i = 8; i > 0; --i) { - buff->last_chunk[55+i] = size & 255; - size >>= 8; + uint64_t size_bits = buff->data_size << 3; + for (size_t i = 0; i < 8; ++i) { + buff->last_chunk[63 - i] = (uint8_t)(size_bits & 255U); + size_bits >>= 8; } sha256_calc_chunk(buff, buff->last_chunk); } void sha256_read(const struct sha256_buff* buff, uint8_t* hash) { - uint32_t i; - for (i = 0; i < 8; i++) { - hash[i*4] = (buff->h[i] >> 24) & 255; - hash[i*4 + 1] = (buff->h[i] >> 16) & 255; - hash[i*4 + 2] = (buff->h[i] >> 8) & 255; - hash[i*4 + 3] = buff->h[i] & 255; + for (size_t i = 0; i < 8; ++i) { + hash[i * 4] = (uint8_t)((buff->h[i] >> 24) & 255U); + hash[i * 4 + 1] = (uint8_t)((buff->h[i] >> 16) & 255U); + hash[i * 4 + 2] = (uint8_t)((buff->h[i] >> 8) & 255U); + hash[i * 4 + 3] = (uint8_t)(buff->h[i] & 255U); } } diff --git a/sha256_tests b/sha256_tests new file mode 100755 index 0000000000000000000000000000000000000000..6002ba0001c25f6344e90b3bd5a8e472512f56c9 GIT binary patch literal 21024 zcmeHP3v^WFoxhV1hzKSsSbS6m6@{wdzUFaNG$7H7f+CVuYwdVnLn3*ZnP6~N4aRsf z9nwZyT)MVBSlgA>ZV%g4OCMko6_C{y?F*~hWnFcLiY-*FSkc+v|9ebkhGg5*bM~A) zyZMrt|M&g=&+q>|?!7Z}?_CW`FRuuN6j>^jOB8C=AJ36`!3bX|Dj@YrT$zsduyTen z8SP07Tl@=Jn%la%FX)a(FNm6TsrEXZg+OtI@uE3(#np`p z%nT7G(x_DQ@V2x^ycvQ|@&n&r{_eUTU-;YA<|i9oKeYY5MF;ZpNQUAj9g?9!_T&_y zHr1!&MKU}e>rmqu(HDZBv=SY!oCU@SV=x~879Im9KV!+$x*Q9qn8w1d8V6rI4t~Qp z`1gQ^@hUGX0F34LW8>h%z{7Zzmk59}%Ry>)bfr^DB9Tfv-s*(cv^tS=np>2hyE(0- z(w!|q8lznT6_TO|Od-P{ULbOI9vR_(5l|syUSoI#({ctfjpzSn0Se0i3UD zZEq9dB)I69g6LcY=2Ip9Cb9QQw5m`eEClRkNXYf{rTZSh5LYR;2y8}kGp6GtrAxHC zPH8@dX6;>~{TP$4P_k%Kk^7+Bf5KwWdc-~_@qJ=H?k~f`8n;uuTu@FF+1`5%2WTGB z+?8ct5njBWaz!`-FO-8t__PA5D2I!1bi7bD3VaF>xd#w)QxQ(P;F&?aZro?`sE_uWZnU zv}f&s1rOl-Fh4T|(RY${HD}y|P9VVp3%(4*9;mvXh;=wU6RH0s)sgyR^80Ge=pnhM z1ciJvlaIJW=6z&m4%)-NyVM>&Txo}P*e`sPo&f`Mgn{b(KAsn;PxW=y*W$>n_3ZB;;=)g>~Gut*IVmtHrAc${gC(W75y3}x38`|0T z9e`xu%~m_xM-nwpC^b(E4ey(k84-xxI~0;=?J-4Yg+!~1(MpaUGm>S-GhgaENd9}7 z^+~-94E1g!$2Ds{$0MN2rTzQx#&vpgHJPF8JxpYDz!bCzFjab zO;wDV8mbvJRSZubHB|)Xrjn`WicHnQ6z@)!xd;^^m{M>;80ko!=zmGSFRi)vM9h(* z?9V~2X_&GbC@{qni8IV2rbU`ye#DYch3@SC6GGvFgwi>c$pk zoPccKYj~e6*zfJh3}^dtgzO@@$C%s!CPzBd4Z;SdRq%-Hff;=mC|Z&=r^5B5ih&s+ z&JrUQf|;$%0EIrhtAe89RhDR09b(D-aX~CQ@N=qf*o${<_iT!414H@d=l?06e|B_f z_deZ&jb)>q=^C^%js0b-8p?nhHiK@ovx@!R9{Zw({?v5V-l<^b<=@0MO3Yf2emjIk zjaG(Q8*84ZZ>o7>4`<-eb{IW;ng+atx6vN3=h_1eTkOnF?99$wB?26k82Vv7H0F)i z-it+`J+N$x9U2C*+0N{+Ge6Be|NhuoBX)>~&x08?H)792yoiK0JA0sU3)St+FIfm5 zpkE)ivyHst9Q+-{)!VSS-tKMf$J-tKm)`aYQyN%4m#FYyvtel@+cG${C^%T{=UM7s z-ZTiIlF`|5xO8+5gfTifNae`-#r0jZYv*k4F(<-Z$upK?JrSYi+P-{&%` z1?7{3GEJW0eU+F?2ko5=hspMvv$x`8Lb1=op(opTF!KV7cWK33d${#rH8OoTgd`uH zvS#lX9vyfbESkoYq)gX7X?y=BvP}KTHXhC{9;_bD&C1YpIb7IuvJLxeEW_RXG*ye_ zYK7eJz6zunNgPM6x&Y+zu^NrJ9Dvt^TH<@J{ZbPVgt4ZGNBh)7Wdkr-6V&gsy@!d8b9cL zwFcTurgtP%D%RU@B$RD9a#6!;oez{m(tDsD({C3tJ5;_g3^dZZ28%72GKG-Uc>8(%-I58u?!YD{y{#Lx%)}sQUt}nx)qV6-d7m$x^ z%}B@^uHc-~uLd81v+28tBZ1>p}YaTJT<;zHA>C>FWlwX8Qoi*aLkBL4Jr& z%@v-5+u#6b4>6Ib`w;h=^X!2E5)(L+7Ad3M*$XufkE^WPcH7+J9BSE_ zky0Z<|6bA`bvKjt?STzN&crzU4W{mnJ{9jsAm?ZMsIP**=MQY0K2@`Q1NAFgwG$uC z+u3bQWZ-dTFgrjASdw{P4&C3JNP&r=Yl8(0-Fx^FfI2=OO+IuRP%D`2AhTum#5^cA zHXAzGs>>kEhVnvGjvC6h4wVe$KvkMqpgHv@x0QQ?7VJRP!v)5#1>N<(#ptz<5V6Vw`b+HAl9` zF!6Z`mtu>78+?$_VL>d-nglF+l82Wua&$f`8o?@RPu+KpXpL{@JqD*99A_Ef{n8?6H z1|~8vk%5T}Ok`jp0}~mT$iPGf{*PoJRPyWMqdT>6{^u6mw>Wm|Pd^xX{F^U7zW>IN zPg;kLKXk)4?!NT?UwrnoM>l?Z%HGh8y(et%)6RbCj%khQo3?j;^iRL(d1Ue{52g=2 zI6U0^;A{6@vHkZuzB*j<+(XmOZa;tj1>e3WedR0P+VYuS_rJep#kH4gY~7)4-gR!} zmYsLq@Z{Ua?%Q$2eUJR&T5tE{RrBV}=$rcebK|p5tvk|s{<0IN$FEzNni~9Q>Md*P zo*h}&cYf#n59OZx`c=tOHg{yN8hQBImYpZ(?tkIz$zT3uU*x!VIyT)>d(!YXPOff= zoxb!o``pKWyv6+IX{T9NetYlL*X;hvjc>QtHT~}HFQnVOZ+6e<-gQrP#m@DA%5NWh zvt{`=-h24{?SJULb%(0yh8c;*;+Erjevp(Ix-N}h@vaJ|gH$?v)|}=r{Tev%XV0EJ zHPz(kk?7nr=cLY@7hVu1I<=v@BkpFn^2I zreS;BsMYQ{*M~n5o^yNGUl2R_FSS?deGFrTpP~kat9quXTk)i)d75cCNjGVFam$J( zT}wChm=l;LwG7j9OeHW?Gnv%2Xe=2GyukNO&GKC}>fy6SUTBukM4`EY|5f;J!hbXVZ^8d+ z{I}r075{DcZ^wTJ{%@7~UNYipQ70CQJIS~ar|7*PVg)f*H7(tUsjiNUXj*^{$DD}f zBpuI;s7B0527%?5#6iBehhh+9HE*qMVIYDb9>pX`J8!jjFc9$(52RC&6mO-{ z3{0iFWc+$Esi~@qsEv5ki$)wo=$TR7@gqi{N6eVz2A*YF@xWJA$B8(qiNT7SuBS%~ zRdb~szS{o!_@$kQswOQ{jq9Ejb6nliv^d7r@U5g7#e4{2hMPpRQB+jR@m-`-#hg?f zGwR1;nx^m-r$pn19*OA)+4W;yTu&NmG7>ReLo<9e5>q`Z;&`6x#(d9oBaRbjny#9j z8c!x+T6dLgJR$_)>AI&nLBvVw7+cHmUDq;A!*J99qYFjLS2Z&k4=~9ht{*`fH4nwN zBC4r8yZ-a^L8_|9VwM?=(p1whV>QiJWAV5a@uG%-8EV0VW~gz^G%Z!tle+0RMi5t% zs*409V=_O!8P{SM2h)m0b-46mu^{S3)u^V%R8x0d*FhK6cs%Y!l4i^?Vvzt#38RQD zG3ge@i$*%?VR=S1!-)7uMqqeRHKxVgh#JuY6S1l>J?cBA=W7NMWkwLSs~b?nqL%R_ zBTgjgo4SuhpsNPDiK&9c=3{s)xHSzd312sK6+@^7@xZa-hUQp$EP`;%LOc;ua}6vA z$5Aa;SFu4vl8zoTu&!O75~0O0N@n0;2MS`*i0{T@G4wa4YDV0N7&0DD52EpC90!58 zg$1vx8b(EpSZ*AlTL!upHLxc{eb0}h-zt`*rK#vs;P`$tFr}TO=|^=RgQX@7-?!Aj zfgd%Aq@lmbIA)E9*`#YRQ;kMc8ZImwOd(4(BG~a%X~);CSOmKl?TZE$8|I`N2~@{Q zqQ`-QBs);IuuG|K5co+~b)%L}JF4f!RlSgB&-Y?cBm;Y@NgHllb2Kj&=yB6WlCZyN zajXY3j(r!i9y1f`!LTq3u_koSFT{gAR6~9|fL_F~B3htPvao;0Ofv>S3|hp9x{eRN zhHU_WL}LNQ#5XAuvR@|VxMn$);rb@jla{VUl99lRng&)EvVgJiqEVOpnHceyuOog# z^RS#$w|Km;PsedkurOVbPbA!Tl19wJ(#5LNu@mX&kBN=P2oOXvzzPVGmWwfS+Nv+<(I8Aj}b1RPg;`Ej$ZtU30!>K^6NQ>;%IXP-$h>3;^Ve#kLJ0#OK0uPgAEg5L|iTf)Dg zz;6Nn8Spoh@Yfgk>%qSeV|hyn-zo6F1O7eW*MSz|tDIAa|2g3IW9gg;W?9{|1p zbKnFTdzs(LV=9;f@;Ood*JnW9_a^UillQgJkW;yty(`KzLFEm2@}9Hm*_?5*;LH2! z@C!y(u+&&NMNoNv9jz}alK<{U`F3h=p|yx2??sy{D)Jt*y@D@K;9utAioAymU#YS( zQ&35Sn$jcgI}_WLG9(0OAE6@eX~TE6tk?pOc!vl?#=Td_$$R#s|I-C7ndU*!o+SAAffwWBcNYI6Mc&t4Zd?_cZM~q&1-)6&4ncba9Taqv zpyl@dT)TQXm$>4x%PtJhZFIZZ(p_QAs59!+3!+`Dso$xab*ec}!2i8Pn#YrrL-}$k z8m|yI>&3(f3jzG7VoZ4&&kChh={Q~p3KAChrZSvP+(=%oD^p%5~Pr z@RJm|KBWBLqC$4$I+b`olb>0*k5%H2pfXl}hnV~cN>~+=!toX~$^OIQ{#SM^1U4e@ zd$0%UW;}udqrj*|g`!nEU`F)kipQ_9d{gw0g_X3|)hL4y3BTRmVvgaaH%F0-N zr=ou$WtLJtuTNzCK2eeP`%6>jF!_@U`-8+U27Utab3|-t>{tnA1H+}%D0P8{N1KJ( zS=eyG3X&=MD;al=W9JJDuT?(raW2V@!KnT{@LJfB^GDiwh~z7dSLA*y@khq7|HEec&#!|%pZ2H1?N-HU&`B^_(={Gb`AwPQ{WBafWgje!rMM>|6{ydfo=vDIdqjg#5eW0K(3#ApZk_e<;=?JLdzwo8iJ) z5qTLn^|##qe+oMf3OlaQc@KEFNMBZu$NqSxY!uND*{Q%;L2VI8)$6>@v{u)S+x{K5 zIgxf&DFE=nOfp&LDWi`FNTgd69z7(09%kUTCswtzyG~2OPq%lb5>8jQf=?McS^|7l zS*MOILC-U2PB@*N&e}xKmhN1uBs-nfAmMklwyuRrQ7Zw-bcrbc;1Ut>O2TtmJUmUI zt8KO7Hn%0x?TMzKTWRS^!@Cfn4=Yp9Ae!*nabT*`iW?N>V?aeTm~2q(MUAdf@?2cp(L(Ydd;GROB2^ze))9`D-$ahE?U}<;KuMA256K$!J%G0 zjN#8bYk?jPQIMr)G_YV@eAbFI2fmYb6qYvesE88L@@GmEn5KAAL;)bf{23A@kjN9| zTIQ^)13m-U>cnRe=o^K>9|s{GIx&`&!ow=cA0LtMQy3hl&Bvo9_@gTd>UdH`V#zfy z?PCbL@bNSH+~!N~_T z96bPI%%@tEK6|6Ypi-Aw+nRP<&~zuKO|phZa|E3oN?lufI;dOK)>YTh+1?R!rq>oh zZdWs&3)AciXyKwI7tnfR;?z;4&R^RG7o1{VG0vKxGu7PQR?Aoa(4t`W&nk>~xq zr6^HUiuKE%Z$zJi6c>5Ckr0exK92LI< zeY(by`tmwV(&dy~6pE3mvj6gWs2LcYYD;~2T_@?w;1w5Pt^m;&6LgIv_2qSuq;mXZ z{8CQRJE2e4T(T{%yCju|^YZv*00XE{PNlxQ4wO`02TK3t{r@|mzepIA*OihE37qul z)TmtlAz+jfX}@sX6~|w3dI1Su%E$jJfQ$9RLQm2`sVJym7u-BjroTl9NGj!}8QGR} zOPT(VU`W~{6$O=glK!Afzg}E#NxHM9)PU5JDE%V4xc~AzSyK6VhLkVQ-!npAj=wxF zlr%;cltm?6RHrJxZW^6G^7=#4^=0zq{%L(s{L=p}iz`SvO)`WXIe$b`c^#GG@Di81 zh3iK7JhF0m*_QMWbScGBUtXv7;7)BSQjWJq+Y*Vw44w7*cTEA4JUpWsR1y<}|eh$=gDHB(EI4QhKeM#xMu~@%wol?(Lq#tQO>dE%W zP$o>qFVD+s>$ya^e);&HF8r&~prBI!yfU0(rBY1GrU>KL*E6E@i_h8UvSY0Mho-lT ioon#-T@9R`U&fSvr7SJiG7-jl$L12}mMN4qmHz@oW}UbI literal 0 HcmV?d00001