diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index d128b5ab..3b32fa94 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -105,6 +105,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/mentos/inc/version.h ${CMAKE_SOURCE_DIR}/mentos/src/boot.c + ${CMAKE_SOURCE_DIR}/mentos/src/crypt/sha256.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.c @@ -193,6 +194,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/inc/bits/ioctls.h ${CMAKE_SOURCE_DIR}/libc/inc/bits/stat.h ${CMAKE_SOURCE_DIR}/libc/inc/bits/termios-struct.h + ${CMAKE_SOURCE_DIR}/libc/inc/crypt/sha256.h ${CMAKE_SOURCE_DIR}/libc/inc/ctype.h ${CMAKE_SOURCE_DIR}/libc/inc/fcntl.h ${CMAKE_SOURCE_DIR}/libc/inc/fcvt.h @@ -259,6 +261,7 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/libc/src/sys/utsname.c ${CMAKE_SOURCE_DIR}/libc/src/termios.c ${CMAKE_SOURCE_DIR}/libc/src/time.c + ${CMAKE_SOURCE_DIR}/libc/src/crypt/sha256.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/chdir.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/close.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/creat.c diff --git a/libc/inc/crypt/sha256.h b/libc/inc/crypt/sha256.h index f0a098c9..6225f6df 100644 --- a/libc/inc/crypt/sha256.h +++ b/libc/inc/crypt/sha256.h @@ -1,9 +1,8 @@ -/// @file sha256.c -/// @author Enrico Fraccaroli (enry.frak@gmail.com) +/// @file sha256.h /// @brief Implementation of the SHA-256 hashing algorithm. /// @details The original code was written by Brad Conte, and is available at: /// https://github.com/B-Con/crypto-algorithms -/// +/// /// SHA-256 is one of the three algorithms in the SHA2 /// specification. The others, SHA-384 and SHA-512, are not /// offered in this implementation. @@ -19,14 +18,33 @@ /// @brief SHA256 outputs a 32 byte digest. #define SHA256_BLOCK_SIZE 32 +/// @brief Structure that holds context information for SHA-256 operations. typedef struct { - uint8_t data[64]; - uint32_t datalen; - unsigned long long bitlen; - uint32_t state[8]; + uint8_t data[64]; ///< Input data block being processed (512 bits / 64 bytes). + uint32_t datalen; ///< Length of the current data in the buffer (in bytes). + unsigned long long bitlen; ///< Total length of the input in bits (for padding). + uint32_t state[8]; ///< Current hash state (256 bits / 8 * 32-bit words). } SHA256_ctx_t; +/// @brief Initializes the SHA-256 context. +/// @param ctx Pointer to the SHA-256 context to initialize. void sha256_init(SHA256_ctx_t *ctx); + +/// @brief Adds data to the SHA-256 context for hashing. +/// @param ctx Pointer to the SHA-256 context. +/// @param data Pointer to the data to be hashed. +/// @param len Length of the data to hash, in bytes. void sha256_update(SHA256_ctx_t *ctx, const uint8_t data[], size_t len); + +/// @brief Finalizes the hashing and produces the final SHA-256 digest. +/// @param ctx Pointer to the SHA-256 context. +/// @param hash Pointer to a buffer where the final hash will be stored (must be at least 32 bytes long). void sha256_final(SHA256_ctx_t *ctx, uint8_t hash[]); + +/// @brief Converts a byte array to its hexadecimal string representation. +/// @param src Pointer to the source byte array. +/// @param src_length Length of the source byte array. +/// @param out Pointer to the output buffer for the hexadecimal string. +/// @param out_length Length of the output buffer (must be at least 2 * src_length + 1). +/// @details The output string will be null-terminated if the buffer is large enough. void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length); diff --git a/libc/inc/readline.h b/libc/inc/readline.h index aeeda1d5..5fa3cb62 100644 --- a/libc/inc/readline.h +++ b/libc/inc/readline.h @@ -5,10 +5,12 @@ #include "stddef.h" -/// @brief Reads a line from the file. -/// @param fd the file descriptor. -/// @param buffer the buffer where we place the line. -/// @param buflen the length of the buffer. -/// @param readlen the amount we read, if negative, we did not encounter a newline. -/// @return 0 if we are done reading, 1 if we encountered a newline, -1 if otherwise. +/// @brief Reads a line from the given file descriptor into the buffer. +/// @param fd The file descriptor to read from. +/// @param buffer The buffer where the read line will be stored. Must not be NULL. +/// @param buflen The size of the buffer. +/// @param read_len A pointer to store the length of the read line. Can be NULL if not needed. +/// @return 1 if a newline was found and the line was read successfully, +/// 0 if the end of the file was reached, +/// -1 if no newline was found and partial data was read. int readline(int fd, char *buffer, size_t buflen, ssize_t *read_len); diff --git a/libc/inc/sys/list_head_algorithm.h b/libc/inc/sys/list_head_algorithm.h index e604f6db..e2dd8890 100644 --- a/libc/inc/sys/list_head_algorithm.h +++ b/libc/inc/sys/list_head_algorithm.h @@ -1,5 +1,4 @@ /// @file list_head_algorithm.h -/// @author Enrico Fraccaroli (enry.frak@gmail.com) /// @brief Some general algorithm that might come in handy while using list_head. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. diff --git a/libc/inc/sys/mman.h b/libc/inc/sys/mman.h index 15772d96..0dc897f0 100644 --- a/libc/inc/sys/mman.h +++ b/libc/inc/sys/mman.h @@ -1,5 +1,4 @@ /// @file mman.h -/// @author Enrico Fraccaroli (enry.frak@gmail.com) /// @brief Functions for managing mappings in virtual address space. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. diff --git a/libc/src/crypt/sha256.c b/libc/src/crypt/sha256.c index 8293fd0c..28e02f38 100644 --- a/libc/src/crypt/sha256.c +++ b/libc/src/crypt/sha256.c @@ -1,5 +1,4 @@ /// @file sha256.c -/// @author Enrico Fraccaroli (enry.frak@gmail.com) /// @brief Implementation of the SHA-256 hashing algorithm. /// @details The original code was written by Brad Conte, and is available at: /// https://github.com/B-Con/crypto-algorithms @@ -11,25 +10,65 @@ /// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf /// This implementation uses little endian byte order. +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SHA256]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. + #include "crypt/sha256.h" #include #include -/****************************** MACROS ******************************/ -#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) +/// @brief Rotate left operation on a 32-bit unsigned integer. +/// @param a The value to rotate. +/// @param b The number of positions to rotate. +/// @return The rotated value. +#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) + +/// @brief Rotate right operation on a 32-bit unsigned integer. +/// @param a The value to rotate. +/// @param b The number of positions to rotate. +/// @return The rotated value. #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) -#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +/// @brief Chooses bits from y if x is set, otherwise from z. +/// @param x, y, z Input values. +/// @return Result of CH function. +#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) + +/// @brief Majority function used in SHA-256. +/// @param x, y, z Input values. +/// @return Result of the majority function. #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) -#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) -#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) -#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) +/// @brief First expansion function for the working variables. +/// @param x Input value. +/// @return Result of EP0. +#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) + +/// @brief Second expansion function for the working variables. +/// @param x Input value. +/// @return Result of EP1. +#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) + +/// @brief First Sigma function for message scheduling. +/// @param x Input value. +/// @return Result of SIG0. +#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) + +/// @brief Second Sigma function for message scheduling. +/// @param x Input value. +/// @return Result of SIG1. +#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) + +/// Max data length for message scheduling expansion. #define SHA256_MAX_DATA_LENGTH (SHA256_BLOCK_SIZE * 2) -/**************************** VARIABLES *****************************/ +/// @brief The constants used in the SHA-256 algorithm, as defined by the +/// specification. These are the first 32 bits of the fractional parts of the +/// cube roots of the first 64 primes (2..311). static const uint32_t k[SHA256_MAX_DATA_LENGTH] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, @@ -44,35 +83,43 @@ static const uint32_t k[SHA256_MAX_DATA_LENGTH] = { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; -/*********************** FUNCTION DEFINITIONS ***********************/ - -void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length) +/// @brief Transforms the state of the SHA-256 context based on a block of input data. +/// @param ctx Pointer to the SHA-256 context. Must not be NULL. +/// @param data Input data block to process (64 bytes). Must not be NULL. +static inline void __sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) { - if (out_length >= (src_length * 2 + 1)) { - static const char look_up[] = "0123456789abcdef"; - for (size_t i = 0; i < src_length; ++i) { - *out++ = look_up[*src >> 4]; - *out++ = look_up[*src & 0x0F]; - src++; - } - *out = 0; + // Error checks: Ensure the input parameters are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; + } + if (!data) { + pr_err("Input data is NULL.\n"); + return; } -} -void sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) -{ - uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[SHA256_MAX_DATA_LENGTH]; + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2; + uint32_t m[SHA256_MAX_DATA_LENGTH]; // Message schedule array. + // Step 1: Prepare the message schedule (first 16 words are directly from + // the input data). for (i = 0, j = 0; i < 16; ++i, j += 4) { + // Each 32-bit word is constructed from 4 consecutive 8-bit bytes from + // the input data. m[i] = (uint32_t)(data[j] << 24) | (uint32_t)(data[j + 1] << 16) | (uint32_t)(data[j + 2] << 8) | (uint32_t)(data[j + 3]); } + + // Step 2: Extend the first 16 words into the remaining 48 words of the + // message schedule. Each word is computed based on the previous words using + // the SIG1 and SIG0 functions. for (; i < SHA256_MAX_DATA_LENGTH; ++i) { m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; } + // Step 3: Initialize the working variables with the current state values. a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; @@ -82,19 +129,24 @@ void sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) g = ctx->state[6]; h = ctx->state[7]; + // Step 4: Perform the main hash computation (64 rounds). for (i = 0; i < SHA256_MAX_DATA_LENGTH; ++i) { + // Calculate the temporary values. t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; t2 = EP0(a) + MAJ(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; + + // Rotate the working variables for the next round. + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; } + // Step 5: Add the resulting values back into the current state. ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; @@ -105,10 +157,47 @@ void sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) ctx->state[7] += h; } +/// @brief Converts a byte array into its hexadecimal string representation. +/// @param src Pointer to the source byte array. +/// @param src_length Length of the source byte array. +/// @param out Pointer to the output buffer for the hexadecimal string. +/// @param out_length Length of the output buffer (must be at least 2 * src_length + 1). +/// @details The output string will be null-terminated if the buffer is large enough. +void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length) +{ + // Check if the output buffer is large enough to hold the hex string + if (out_length < (src_length * 2 + 1)) { + pr_err("Output buffer is too small for the hex representation.\n"); + return; // Return early if the buffer is insufficient. + } + + // Lookup table for converting bytes to hex + static const char look_up[] = "0123456789abcdef"; + + // Convert each byte to its hex representation + for (size_t i = 0; i < src_length; ++i) { + *out++ = look_up[*src >> 4]; // Upper nibble + *out++ = look_up[*src & 0x0F]; // Lower nibble + src++; + } + *out = 0; // Null-terminate the output string +} + void sha256_init(SHA256_ctx_t *ctx) { - ctx->datalen = 0; - ctx->bitlen = 0; + // Error check: Ensure the input context is not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } + + // Initialize the length of the data (in bytes) to 0. + ctx->datalen = 0; + + // Initialize the total length of the input data (in bits) to 0. + ctx->bitlen = 0; + + // Initialize the state variables (hash values) to the SHA-256 initial constants. ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; @@ -121,14 +210,32 @@ void sha256_init(SHA256_ctx_t *ctx) void sha256_update(SHA256_ctx_t *ctx, const uint8_t data[], size_t len) { - uint32_t i; + // Error check: Ensure the input context and data are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } - for (i = 0; i < len; ++i) { + if (!data) { + pr_err("Input data is NULL.\n"); + return; // Return early if the data is NULL to prevent errors. + } + + // Iterate over the input data, processing it in chunks. + for (uint32_t i = 0; i < len; ++i) { + // Add data to the context's buffer. ctx->data[ctx->datalen] = data[i]; ctx->datalen++; + + // If the buffer is full, process the data and reset the buffer. if (ctx->datalen == SHA256_MAX_DATA_LENGTH) { - sha256_transform(ctx, ctx->data); + // Perform the SHA-256 transformation on the full data block. + __sha256_transform(ctx, ctx->data); + + // Update the total length of the data processed so far in bits. ctx->bitlen += 512; + + // Reset the buffer length to 0 for the next chunk of data. ctx->datalen = 0; } } @@ -136,28 +243,56 @@ void sha256_update(SHA256_ctx_t *ctx, const uint8_t data[], size_t len) void sha256_final(SHA256_ctx_t *ctx, uint8_t hash[]) { - uint32_t i; + // Error check: Ensure the input context and hash are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } + if (!hash) { + pr_err("Output hash buffer is NULL.\n"); + return; // Return early if the output buffer is NULL to prevent errors. + } + + // Get the current length of the data buffer. + uint32_t i = ctx->datalen; - i = ctx->datalen; + // Step 1: Pad whatever data is left in the buffer. - // Pad whatever data is left in the buffer. + // If there's enough space in the buffer (less than 56 bytes used), pad + // with 0x80 followed by zeros. if (ctx->datalen < 56) { + // Append the padding byte (0x80). ctx->data[i++] = 0x80; + + // Pad the buffer with zeros until we reach 56 bytes. while (i < 56) { ctx->data[i++] = 0x00; } - } else { + } + // If there's not enough space, pad the remaining buffer and process it. + else { + // Append the padding byte (0x80). ctx->data[i++] = 0x80; + + // Fill the rest of the buffer with zeros. while (i < SHA256_MAX_DATA_LENGTH) { ctx->data[i++] = 0x00; } - sha256_transform(ctx, ctx->data); - memset(ctx->data, 0, 56); + + // Process the full buffer. + __sha256_transform(ctx, ctx->data); + + // Reset the buffer for the next padding. + memset(ctx->data, 0, 56); } - // Append to the padding the total message's length in bits and transform. - ctx->bitlen += ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; + // Step 2: Append the total message length in bits and process the final block. + + // Convert the total length from bytes to bits. + ctx->bitlen += ctx->datalen * 8; + + // Append the lower 8 bits of the length. + ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; @@ -165,11 +300,14 @@ void sha256_final(SHA256_ctx_t *ctx, uint8_t hash[]) ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; - sha256_transform(ctx, ctx->data); - // Since this implementation uses little endian byte ordering and SHA uses big - // endian, reverse all the bytes when copying the final state to the output - // hash. + // Process the final block. + __sha256_transform(ctx, ctx->data); + + // Step 3: Copy the final state (hash) to the output buffer. + + // SHA-256 uses big-endian byte order, so we reverse the byte order when + // copying. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; diff --git a/libc/src/readline.c b/libc/src/readline.c index b66ce2b4..ae5286f2 100644 --- a/libc/src/readline.c +++ b/libc/src/readline.c @@ -1,11 +1,13 @@ /// @file readline.c -/// @author Enrico Fraccaroli (enry.frak@gmail.com) -/// @brief -/// @version 0.1 -/// @date 2023-08-30 -/// -/// @copyright Copyright (c) 2023 -/// +/// @brief Implementation of a function that reads a line from a file descriptor. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[RDLINE]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "readline.h" @@ -15,43 +17,73 @@ int readline(int fd, char *buffer, size_t buflen, ssize_t *read_len) { + // Error check: Ensure the buffer is not NULL and has sufficient length. + if (!buffer || buflen == 0) { + pr_err("Invalid buffer or buffer length.\n"); + return 0; // Invalid input, cannot proceed. + } + ssize_t length, rollback, num_read; - memset(buffer, 0, buflen); unsigned char found_newline = 1; - // Read from the file. + + // Initialize the buffer to ensure it starts empty. + memset(buffer, 0, buflen); + + // Read from the file descriptor into the buffer. num_read = read(fd, buffer, buflen); + + // Error check: If read() fails, return -1 to indicate an error. + if (num_read < 0) { + pr_err("Failed to read from file descriptor.\n"); + return -1; + } + + // If nothing was read, return 0 to indicate the end of the file. if (num_read == 0) { return 0; } - // Search for termination character. + + // Search for newline or termination character. char *newline = strchr(buffer, '\n'); if (newline == NULL) { found_newline = 0; - newline = strchr(buffer, EOF); + // Search for EOF. + newline = strchr(buffer, EOF); if (newline == NULL) { - newline = strchr(buffer, 0); + // Search for null terminator. + newline = strchr(buffer, '\0'); if (newline == NULL) { + // No termination character found. return 0; } } } - // Compute the length of the string. + + // Compute the length of the string up to the newline or termination + // character. length = (newline - buffer); if (length <= 0) { return 0; } - // Close the string. - buffer[length] = 0; - // Compute how much we need to rollback. + + // Close the string by adding a null terminator. + buffer[length] = '\0'; + + // Compute how much we need to rollback the file position after reading. rollback = length - num_read + 1; if (rollback > 1) { - return 0; + return 0; // Rollback value seems invalid. } - // Rollback the reading position in the file. + + // Adjust the file's read position using lseek to undo the extra read + // characters. lseek(fd, rollback, SEEK_CUR); - // Set how much we were able to read from the file. + + // Set the number of bytes read if the caller provided a pointer. if (read_len) { *read_len = length; } + + // Return 1 if a newline was found, -1 otherwise (partial data read). return (found_newline) ? 1 : -1; } diff --git a/libc/src/sys/mman.c b/libc/src/sys/mman.c index 17e7add8..f6433783 100644 --- a/libc/src/sys/mman.c +++ b/libc/src/sys/mman.c @@ -1,5 +1,4 @@ /// @file mman.c -/// @author Enrico Fraccaroli (enry.frak@gmail.com) /// @brief Functions for managing mappings in virtual address space. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index 18f9974c..280bca16 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -89,6 +89,7 @@ set(KERNEL_SOURCES ${CMAKE_SOURCE_DIR}/mentos/src/mem/vmem_map.c ${CMAKE_SOURCE_DIR}/mentos/src/mem/zone_allocator.c ${CMAKE_SOURCE_DIR}/mentos/src/elf/elf.c + ${CMAKE_SOURCE_DIR}/mentos/src/crypt/sha256.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.c ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.S ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.c diff --git a/mentos/inc/process/scheduler_feedback.h b/mentos/inc/process/scheduler_feedback.h index 99d1938a..aa477d34 100644 --- a/mentos/inc/process/scheduler_feedback.h +++ b/mentos/inc/process/scheduler_feedback.h @@ -1,6 +1,7 @@ /// @file scheduler_feedback.h -/// @author Enrico Fraccaroli (enry.frak@gmail.com) -/// @brief +/// @brief Scheduler feedback system for managing tasks and updating scheduling statistics. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. #pragma once @@ -8,21 +9,33 @@ #include "process/process.h" /// @brief Initialize the scheduler feedback system. +/// @details This function sets up the necessary data structures and mechanisms +/// for the scheduler feedback system. It must be called before any other +/// scheduler feedback operations are performed. /// @return 1 on success, 0 on failure. int scheduler_feedback_init(void); /// @brief Add the given task to the feedback system. -/// @param task the task we need to add. +/// @param task A pointer to the task_struct representing the task to be added. +/// @details This function adds a task to the scheduler feedback system for +/// monitoring and updating its scheduling statistics. void scheduler_feedback_task_add(task_struct *task); /// @brief Removes the given task from the feedback system. -/// @param pid the pid of the task we need to remove. +/// @param pid The process ID of the task to remove. +/// @details This function removes the task identified by the given pid from the +/// scheduler feedback system. It should be called when a task is terminated or +/// no longer needs to be monitored. void scheduler_feedback_task_remove(pid_t pid); /// @brief Updates the scheduling statistics for the given task. -/// @param task the task for which we update the statistics. +/// @param task A pointer to the task_struct representing the task to update. +/// @details This function updates the scheduling statistics for the given task +/// based on its recent behavior (e.g., execution time, priority changes). void scheduler_feedback_task_update(task_struct *task); -/// @brief Updates the scheduling statistics for the given task. -/// @param task the task for which we update the statistics. +/// @brief Updates the global scheduler feedback statistics. +/// @details This function is periodically called to update the overall +/// statistics of the scheduler feedback system, adjusting task priorities and +/// managing scheduling decisions for all tasks. void scheduler_feedback_update(void); diff --git a/mentos/src/crypt/sha256.c b/mentos/src/crypt/sha256.c new file mode 100644 index 00000000..a6530ae3 --- /dev/null +++ b/mentos/src/crypt/sha256.c @@ -0,0 +1,321 @@ +/// @file sha256.c +/// @brief Implementation of the SHA-256 hashing algorithm. +/// @details The original code was written by Brad Conte, and is available at: +/// https://github.com/B-Con/crypto-algorithms +/// +/// SHA-256 is one of the three algorithms in the SHA2 +/// specification. The others, SHA-384 and SHA-512, are not +/// offered in this implementation. +/// Algorithm specification can be found here: +/// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf +/// This implementation uses little endian byte order. + +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SHA256]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. + +#include "crypt/sha256.h" + +#include +#include + +/// @brief Rotate left operation on a 32-bit unsigned integer. +/// @param a The value to rotate. +/// @param b The number of positions to rotate. +/// @return The rotated value. +#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) + +/// @brief Rotate right operation on a 32-bit unsigned integer. +/// @param a The value to rotate. +/// @param b The number of positions to rotate. +/// @return The rotated value. +#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) + +/// @brief Chooses bits from y if x is set, otherwise from z. +/// @param x, y, z Input values. +/// @return Result of CH function. +#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) + +/// @brief Majority function used in SHA-256. +/// @param x, y, z Input values. +/// @return Result of the majority function. +#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/// @brief First expansion function for the working variables. +/// @param x Input value. +/// @return Result of EP0. +#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) + +/// @brief Second expansion function for the working variables. +/// @param x Input value. +/// @return Result of EP1. +#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) + +/// @brief First Sigma function for message scheduling. +/// @param x Input value. +/// @return Result of SIG0. +#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) + +/// @brief Second Sigma function for message scheduling. +/// @param x Input value. +/// @return Result of SIG1. +#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) + +/// Max data length for message scheduling expansion. +#define SHA256_MAX_DATA_LENGTH (SHA256_BLOCK_SIZE * 2) + +/// @brief The constants used in the SHA-256 algorithm, as defined by the +/// specification. These are the first 32 bits of the fractional parts of the +/// cube roots of the first 64 primes (2..311). +static const uint32_t k[SHA256_MAX_DATA_LENGTH] = { + 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 +}; + +/// @brief Transforms the state of the SHA-256 context based on a block of input data. +/// @param ctx Pointer to the SHA-256 context. Must not be NULL. +/// @param data Input data block to process (64 bytes). Must not be NULL. +static inline void __sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) +{ + // Error checks: Ensure the input parameters are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; + } + if (!data) { + pr_err("Input data is NULL.\n"); + return; + } + + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2; + uint32_t m[SHA256_MAX_DATA_LENGTH]; // Message schedule array. + + // Step 1: Prepare the message schedule (first 16 words are directly from + // the input data). + for (i = 0, j = 0; i < 16; ++i, j += 4) { + // Each 32-bit word is constructed from 4 consecutive 8-bit bytes from + // the input data. + m[i] = (uint32_t)(data[j] << 24) | + (uint32_t)(data[j + 1] << 16) | + (uint32_t)(data[j + 2] << 8) | + (uint32_t)(data[j + 3]); + } + + // Step 2: Extend the first 16 words into the remaining 48 words of the + // message schedule. Each word is computed based on the previous words using + // the SIG1 and SIG0 functions. + for (; i < SHA256_MAX_DATA_LENGTH; ++i) { + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + } + + // Step 3: Initialize the working variables with the current state values. + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + // Step 4: Perform the main hash computation (64 rounds). + for (i = 0; i < SHA256_MAX_DATA_LENGTH; ++i) { + // Calculate the temporary values. + t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a, b, c); + + // Rotate the working variables for the next round. + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Step 5: Add the resulting values back into the current state. + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +/// @brief Converts a byte array into its hexadecimal string representation. +/// @param src Pointer to the source byte array. +/// @param src_length Length of the source byte array. +/// @param out Pointer to the output buffer for the hexadecimal string. +/// @param out_length Length of the output buffer (must be at least 2 * src_length + 1). +/// @details The output string will be null-terminated if the buffer is large enough. +void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length) +{ + // Check if the output buffer is large enough to hold the hex string + if (out_length < (src_length * 2 + 1)) { + pr_err("Output buffer is too small for the hex representation.\n"); + return; // Return early if the buffer is insufficient. + } + + // Lookup table for converting bytes to hex + static const char look_up[] = "0123456789abcdef"; + + // Convert each byte to its hex representation + for (size_t i = 0; i < src_length; ++i) { + *out++ = look_up[*src >> 4]; // Upper nibble + *out++ = look_up[*src & 0x0F]; // Lower nibble + src++; + } + *out = 0; // Null-terminate the output string +} + +void sha256_init(SHA256_ctx_t *ctx) +{ + // Error check: Ensure the input context is not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } + + // Initialize the length of the data (in bytes) to 0. + ctx->datalen = 0; + + // Initialize the total length of the input data (in bits) to 0. + ctx->bitlen = 0; + + // Initialize the state variables (hash values) to the SHA-256 initial constants. + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_ctx_t *ctx, const uint8_t data[], size_t len) +{ + // Error check: Ensure the input context and data are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } + + if (!data) { + pr_err("Input data is NULL.\n"); + return; // Return early if the data is NULL to prevent errors. + } + + // Iterate over the input data, processing it in chunks. + for (uint32_t i = 0; i < len; ++i) { + // Add data to the context's buffer. + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + + // If the buffer is full, process the data and reset the buffer. + if (ctx->datalen == SHA256_MAX_DATA_LENGTH) { + // Perform the SHA-256 transformation on the full data block. + __sha256_transform(ctx, ctx->data); + + // Update the total length of the data processed so far in bits. + ctx->bitlen += 512; + + // Reset the buffer length to 0 for the next chunk of data. + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_ctx_t *ctx, uint8_t hash[]) +{ + // Error check: Ensure the input context and hash are not NULL. + if (!ctx) { + pr_err("SHA256 context is NULL.\n"); + return; // Return early if the context is NULL to prevent crashes. + } + if (!hash) { + pr_err("Output hash buffer is NULL.\n"); + return; // Return early if the output buffer is NULL to prevent errors. + } + + // Get the current length of the data buffer. + uint32_t i = ctx->datalen; + + // Step 1: Pad whatever data is left in the buffer. + + // If there's enough space in the buffer (less than 56 bytes used), pad + // with 0x80 followed by zeros. + if (ctx->datalen < 56) { + // Append the padding byte (0x80). + ctx->data[i++] = 0x80; + + // Pad the buffer with zeros until we reach 56 bytes. + while (i < 56) { + ctx->data[i++] = 0x00; + } + } + // If there's not enough space, pad the remaining buffer and process it. + else { + // Append the padding byte (0x80). + ctx->data[i++] = 0x80; + + // Fill the rest of the buffer with zeros. + while (i < SHA256_MAX_DATA_LENGTH) { + ctx->data[i++] = 0x00; + } + + // Process the full buffer. + __sha256_transform(ctx, ctx->data); + + // Reset the buffer for the next padding. + memset(ctx->data, 0, 56); + } + + // Step 2: Append the total message length in bits and process the final block. + + // Convert the total length from bytes to bits. + ctx->bitlen += ctx->datalen * 8; + + // Append the lower 8 bits of the length. + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + + // Process the final block. + __sha256_transform(ctx, ctx->data); + + // Step 3: Copy the final state (hash) to the output buffer. + + // SHA-256 uses big-endian byte order, so we reverse the byte order when + // copying. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +}