Skip to content

Conversation

manuel5975p
Copy link
Contributor

Here a correct Sha256 implementation.

If you want to verify, here is a test against sha256sum:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#define RL_CALLOC calloc
#define RL_FREE free
/* sha256_single.c
 * C99 implementation of SHA-256 with this API:
 *   unsigned int *ComputeSHA256(unsigned char *data, int dataSize);
 *
 * Returns pointer to 8 unsigned ints (32-bit words) containing the digest
 * (h0..h7). Caller must free() the returned pointer. Returns NULL on error.
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>


unsigned int *ComputeSHA256(unsigned char *data, int dataSize) {
    #define SHA256_ROTR_32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
    static unsigned int result[8];
    if (!data || dataSize < 0){
        return NULL;
    }

    unsigned int h[8] = {
        0x6a09e667u, 0xbb67ae85u, 0x3c6ef372u, 0xa54ff53au,
        0x510e527fu, 0x9b05688cu, 0x1f83d9abu, 0x5be0cd19u
    };

    const unsigned int k[64] = {
        0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u, 0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
        0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u, 0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
        0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu, 0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
        0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u, 0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
        0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u, 0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
        0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u, 0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
        0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u, 0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
        0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u, 0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u
    };

    uint64_t bit_len = (uint64_t)dataSize * 8ULL;
    int pad_len = (int)((56 - ((dataSize + 1) % 64) + 64) % 64);
    size_t total_len = (size_t)dataSize + 1 + pad_len + 8;
    unsigned char *buf = (unsigned char *)RL_CALLOC(total_len, 1);
    if (!buf){
        return NULL;
    }

    memcpy(buf, data, (size_t)dataSize);
    buf[dataSize] = 0x80u;
    for (int i = 0; i < 8; ++i){
        buf[dataSize + 1 + pad_len + i] = (unsigned char)((bit_len >> (56 - 8 * i)) & 0xFFu);
    }

    size_t num_blocks = total_len / 64;
    for (size_t blk = 0; blk < num_blocks; ++blk) {
        const unsigned char *chunk = buf + blk * 64;
        unsigned int w[64];
        for (int t = 0; t < 16; ++t){
            w[t] = ((unsigned int)chunk[t * 4 + 0] << 24) | ((unsigned int)chunk[t * 4 + 1] << 16) |
                   ((unsigned int)chunk[t * 4 + 2] << 8) | ((unsigned int)chunk[t * 4 + 3]);
        }
        for (int t = 16; t < 64; ++t) {
            unsigned int s0 = SHA256_ROTR_32(w[t - 15], 7) ^ SHA256_ROTR_32(w[t - 15], 18) ^ (w[t - 15] >> 3);
            unsigned int s1 = SHA256_ROTR_32(w[t - 2], 17) ^ SHA256_ROTR_32(w[t - 2], 19) ^ (w[t - 2] >> 10);
            w[t] = w[t - 16] + s0 + w[t - 7] + s1;
        }
        unsigned int a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
        for (int t = 0; t < 64; ++t) {
            unsigned int S1 = SHA256_ROTR_32(e, 6) ^ SHA256_ROTR_32(e, 11) ^ SHA256_ROTR_32(e, 25);
            unsigned int ch = (e & f) ^ ((~e) & g);
            unsigned int temp1 = hh + S1 + ch + k[t] + w[t];
            unsigned int S0 = SHA256_ROTR_32(a, 2) ^ SHA256_ROTR_32(a, 13) ^ SHA256_ROTR_32(a, 22);
            unsigned int maj = (a & b) ^ (a & c) ^ (b & c);
            unsigned int temp2 = S0 + maj;
            hh = g;
            g = f;
            f = e;
            e = d + temp1;
            d = c;
            c = b;
            b = a;
            a = temp1 + temp2;
        }
        h[0] += a;
        h[1] += b;
        h[2] += c;
        h[3] += d;
        h[4] += e;
        h[5] += f;
        h[6] += g;
        h[7] += hh;
    }

    RL_FREE(buf);
    for (int i = 0; i < 8; ++i){
        result[i] = (unsigned int)h[i];
    }
    return result;
    #undef SHA256_ROTR_32
}


std::string get_sha256sum(const std::string &input) {
    const char *tmp_input_file = "/tmp/sha256_test_input.txt";
    const char *tmp_output_file = "/tmp/sha256_sum_output.txt";

    std::ofstream out(tmp_input_file, std::ios::binary);
    out.write(input.c_str(), input.length());
    out.close();

    std::string command = "sha256sum ";
    command += tmp_input_file;
    command += " > ";
    command += tmp_output_file;
    system(command.c_str());

    std::ifstream in(tmp_output_file);
    std::string hash_str;
    in >> hash_str;
    in.close();

    remove(tmp_input_file);
    remove(tmp_output_file);

    return hash_str;
}

std::string format_computed_hash(const unsigned int *hash) {
    if (!hash) {
        return "null";
    }
    std::stringstream ss;
    ss << std::hex << std::setfill('0');
    for (int i = 0; i < 8; ++i) {
        ss << std::setw(8) << hash[i];
    }
    return ss.str();
}

int main() {
    std::vector<std::string> test_cases = {
        "",
        "hello world",
        "The quick brown fox jumps over the lazy dog",
        "The quick brown fox jumps over the lazy dog.",
        "1234567890123456789012345678901234567890123456789012345",
        "1234567890123456789012345678901234567890123456789012345678901234",
        std::string(1000, 'a')};

    int tests_passed = 0;
    for (const auto &test_str : test_cases) {
        std::cout << "================================================" << std::endl;
        std::cout << "Testing string: \"" << test_str.substr(0, 60) << (test_str.length() > 60 ? "..." : "") << "\"" << std::endl;
        std::cout << "Length: " << test_str.length() << " bytes" << std::endl;
        std::cout << "------------------------------------------------" << std::endl;

        std::string reference_hash_str = get_sha256sum(test_str);
        std::cout << "sha256sum (expected): " << reference_hash_str << std::endl;

        unsigned int *computed_hash_ptr = ComputeSHA256(
            (unsigned char *)test_str.c_str(),
            test_str.length());
        std::string computed_hash_str = format_computed_hash(computed_hash_ptr);
        std::cout << "Your function output: " << computed_hash_str << std::endl;

        if (reference_hash_str == computed_hash_str) {
            std::cout << "\n>>> TEST PASSED" << std::endl;
            tests_passed++;
        } else {
            std::cout << "\n>>> TEST FAILED" << std::endl;
        }

        std::cout << "================================================" << std::endl
                  << std::endl;
    }

    std::cout << "Testing complete. " << tests_passed << "/" << test_cases.size() << " tests passed." << std::endl;

    return 0;
}

@CrackedPixel
Copy link
Contributor

the provided example/test is verbose, but i was able to successfully verify accurate results with a minimal example (using known inputs+outputs):

#include <stdio.h>
#include <string.h>
#include "raylib.h"

int main() {
   unsigned char* input = "hello, world!";
   unsigned int* hash = ComputeSHA256(input, strlen(input));

    printf("SHA-256 hash of \"%s\" with size %d:\n", input, strlen(input));
    for (int i = 0; i < 8; i++) {
        printf("%08x", hash[i]);
    }
    printf("\n");

   return 0;
}

@raysan5
Copy link
Owner

raysan5 commented Oct 14, 2025

@manuel5975p thanks for the new implementation! Note that now we have to PRs adding the same function, I'd like to merge the first one, if the inconsistencies can be solved. But definitely a nice alternative function for verification! Thanks!

@raysan5 raysan5 changed the title Add Sha256 function [rcore] ComputeSHA256() (alternative implementation) Oct 14, 2025
@raysan5 raysan5 added the duplicate This is a duplicate issue label Oct 14, 2025
@manuel5975p
Copy link
Contributor Author

Yeah feel free to close it, just one thing that was kinda goofy was making it work with the signed int as a size argument. One would expect it to work for all positive int32 values so the multiplication by 8 and the padding need to happen in 64 bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This is a duplicate issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants