libluna+apps: Add a SHA256 hash implementation
All checks were successful
Build and test / build (push) Successful in 2m26s

This commit is contained in:
apio 2024-07-20 15:50:59 +02:00
parent 7345a952ca
commit db2f91b1fb
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 257 additions and 0 deletions

View File

@ -52,3 +52,4 @@ target_link_libraries(clock PUBLIC ui)
luna_app(startui.cpp startui)
luna_app(launch.cpp launch)
luna_app(run.cpp run)
luna_app(sha256sum.cpp sha256sum)

30
apps/sha256sum.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <luna/SHA.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
Result<int> luna_main(int argc, char** argv)
{
Vector<StringView> files;
os::ArgumentParser parser;
parser.add_description("Calculate SHA256 hashes for a series of files."_sv);
parser.add_system_program_info("sha256sum"_sv);
parser.set_vector_argument(files, "files"_sv, "-"_sv);
parser.parse(argc, argv);
for (auto path : files)
{
auto file = TRY(os::File::open_input_file(path));
auto data = TRY(file->read_all());
SHA256 sha;
TRY(sha.append(data.data(), data.size()));
auto hash = TRY(sha.digest());
auto string = TRY(sha.hash_to_string(hash));
os::println("%s %s", string.chars(), path.chars());
}
return 0;
}

View File

@ -16,6 +16,7 @@ set(FREESTANDING_SOURCES
src/Bitmap.cpp
src/Buffer.cpp
src/Scanf.cpp
src/SHA.cpp
src/Stack.cpp
src/String.cpp
src/StringBuilder.cpp

View File

@ -0,0 +1,40 @@
/**
* @file SHA.h
* @author apio (cloudapio.eu)
* @brief SHA256 algorithm implementation.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#pragma once
#include <luna/Buffer.h>
#include <luna/String.h>
/**
* @brief A class to calculate a SHA256 hash.
*
*/
class SHA256
{
public:
/**
* @brief Add data to the hash.
*
* @param data The data to add.
* @param size The amount of bytes to add.
*/
Result<void> append(const u8* data, usize size);
/**
* @brief Calculate the final hash.
*
* @return Buffer The calculated hash.
*/
Result<Buffer> digest();
static Result<String> hash_to_string(const Buffer& buffer);
private:
Buffer m_message;
};

185
libluna/src/SHA.cpp Normal file
View File

@ -0,0 +1,185 @@
/**
* @file SHA.cpp
* @author apio (cloudapio.eu)
* @brief SHA256 algorithm implementation.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#include <luna/Alignment.h>
#include <luna/Common.h>
#include <luna/DebugLog.h>
#include <luna/SHA.h>
#include <luna/StringBuilder.h>
#include <string.h>
// Taken from https://gist.github.com/hak8or/8794351
#define ROTRIGHT(word, bits) (((word) >> (bits)) | ((word) << (32 - (bits))))
u32 k[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
u32 static H0 = 0x6a09e667;
u32 static H1 = 0xbb67ae85;
u32 static H2 = 0x3c6ef372;
u32 static H3 = 0xa54ff53a;
u32 static H4 = 0x510e527f;
u32 static H5 = 0x9b05688c;
u32 static H6 = 0x1f83d9ab;
u32 static H7 = 0x5be0cd19;
Result<void> SHA256::append(const u8* data, usize size)
{
return m_message.append_data(data, size);
}
// Thank you to https://sha256algorithm.com/ for showing me how the SHA256 algorithm works, and effectively, how to
// implement it!
Result<Buffer> SHA256::digest()
{
usize message_block_length = m_message.size() + 1 + sizeof(u64);
usize message_block_chunks = ceil_div(message_block_length, 64ul);
usize num_zeros = message_block_chunks * 64 - message_block_length;
// Prepare a message block and add the data into it.
Buffer message_block;
TRY(message_block.append_data(m_message.data(), m_message.size()));
// Add one bit at the end.
u8 one = 0b10000000;
TRY(message_block.append_data(&one, 1));
// Fill with zeros.
u8* slice = TRY(message_block.slice_at_end(num_zeros));
memset(slice, 0, num_zeros);
// Add the length of the original message (in bits), this has to be big-endian.
usize message_length = m_message.size() * 8;
message_length = __builtin_bswap64(message_length); // FIXME: We're assuming we're on a little-endian platform.
TRY(message_block.append_data((const u8*)&message_length, sizeof(usize)));
// The length of the message block should now be a multiple of 512 bits (64 bytes).
check(is_aligned<64>(message_block.size()));
u32 a, b, c, d, e, f, g, h; // working variables
u32 h0, h1, h2, h3, h4, h5, h6, h7; // hash values
// Initialize the hash values to their starting values.
h0 = H0;
h1 = H1;
h2 = H2;
h3 = H3;
h4 = H4;
h5 = H5;
h6 = H6;
h7 = H7;
// For each 512-bit (64-byte) chunk, perform the following operations.
for (usize i = 0; i < message_block_chunks; i++)
{
// Create a message schedule of 64 dwords, and copy the current chunk into the first 16 dwords.
u32 message_schedule[64];
memcpy(message_schedule, &message_block.data()[i * 64], 64);
for (int j = 0; j < 16; j++)
{
// SHA uses big-endian. FIXME: Don't assume little-endian.
message_schedule[j] = __builtin_bswap32(message_schedule[j]);
}
// Fill the rest of the message schedule.
for (int j = 0; j < 48; j++)
{
u32 w0 = message_schedule[j];
u32 w1 = message_schedule[j + 1];
u32 w14 = message_schedule[j + 14];
u32 w9 = message_schedule[j + 9];
u32 s0 = ROTRIGHT(w1, 7) ^ ROTRIGHT(w1, 18) ^ (w1 >> 3);
u32 s1 = ROTRIGHT(w14, 17) ^ ROTRIGHT(w14, 19) ^ (w14 >> 10);
message_schedule[j + 16] = w0 + s0 + w9 + s1;
}
// Initialize the working variables with the current hash values.
a = h0;
b = h1;
c = h2;
d = h3;
e = h4;
f = h5;
g = h6;
h = h7;
// For each dword in the message schedule, update the working variables, using this algorithm and the
// corresponding dword from the constant k[] array.
for (int j = 0; j < 64; j++)
{
u32 temp1, temp2, choice, majority;
choice = (e & f) ^ ((~e) & g);
majority = (a & b) ^ (a & c) ^ (b & c);
temp1 = h + (ROTRIGHT(e, 6) ^ ROTRIGHT(e, 11) ^ ROTRIGHT(e, 25)) + choice + k[j] + message_schedule[j];
temp2 = (ROTRIGHT(a, 2) ^ ROTRIGHT(a, 13) ^ ROTRIGHT(a, 22)) + majority;
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}
// Add the working variables to the hash values.
h0 += a;
h1 += b;
h2 += c;
h3 += d;
h4 += e;
h5 += f;
h6 += g;
h7 += h;
}
// FIXME: Again, this assumes little-endian.
h0 = __builtin_bswap32(h0);
h1 = __builtin_bswap32(h1);
h2 = __builtin_bswap32(h2);
h3 = __builtin_bswap32(h3);
h4 = __builtin_bswap32(h4);
h5 = __builtin_bswap32(h5);
h6 = __builtin_bswap32(h6);
h7 = __builtin_bswap32(h7);
Buffer result;
TRY(result.append_data((const u8*)&h0, sizeof(u32)));
TRY(result.append_data((const u8*)&h1, sizeof(u32)));
TRY(result.append_data((const u8*)&h2, sizeof(u32)));
TRY(result.append_data((const u8*)&h3, sizeof(u32)));
TRY(result.append_data((const u8*)&h4, sizeof(u32)));
TRY(result.append_data((const u8*)&h5, sizeof(u32)));
TRY(result.append_data((const u8*)&h6, sizeof(u32)));
TRY(result.append_data((const u8*)&h7, sizeof(u32)));
m_message.try_resize(0); // Don't need this anymore.
return result;
}
Result<String> SHA256::hash_to_string(const Buffer& buffer)
{
StringBuilder sb;
for (usize i = 0; i < buffer.size(); i++) { sb.format("%02x", buffer.data()[i]); }
return sb.string();
}