13 changed files with 675 additions and 58 deletions
@ -0,0 +1,158 @@
|
||||
/*********************************************************************
|
||||
* Filename: sha256.c |
||||
* Author: Brad Conte (brad AT bradconte.com) |
||||
* Copyright: |
||||
* Disclaimer: This code is presented "as is" without any guarantees. |
||||
* Details: Implementation of the SHA-256 hashing algorithm. |
||||
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. |
||||
*********************************************************************/ |
||||
|
||||
/*************************** HEADER FILES ***************************/ |
||||
#include <stdlib.h> |
||||
#include <memory.h> |
||||
#include "sha256.h" |
||||
|
||||
/****************************** MACROS ******************************/ |
||||
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) |
||||
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) |
||||
|
||||
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) |
||||
#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)) |
||||
|
||||
/**************************** VARIABLES *****************************/ |
||||
static const WORD 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 |
||||
}; |
||||
|
||||
/*********************** FUNCTION DEFINITIONS ***********************/ |
||||
void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) |
||||
{ |
||||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; |
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 4) |
||||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); |
||||
for ( ; i < 64; ++i) |
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; |
||||
|
||||
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]; |
||||
|
||||
for (i = 0; i < 64; ++i) { |
||||
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; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
void sha256_init(SHA256_CTX *ctx) |
||||
{ |
||||
ctx->datalen = 0; |
||||
ctx->bitlen = 0; |
||||
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 *ctx, const BYTE data[], size_t len) |
||||
{ |
||||
WORD i; |
||||
|
||||
for (i = 0; i < len; ++i) { |
||||
ctx->data[ctx->datalen] = data[i]; |
||||
ctx->datalen++; |
||||
if (ctx->datalen == 64) { |
||||
sha256_transform(ctx, ctx->data); |
||||
ctx->bitlen += 512; |
||||
ctx->datalen = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]) |
||||
{ |
||||
WORD i; |
||||
|
||||
i = ctx->datalen; |
||||
|
||||
// Pad whatever data is left in the buffer.
|
||||
if (ctx->datalen < 56) { |
||||
ctx->data[i++] = 0x80; |
||||
while (i < 56) |
||||
ctx->data[i++] = 0x00; |
||||
} |
||||
else { |
||||
ctx->data[i++] = 0x80; |
||||
while (i < 64) |
||||
ctx->data[i++] = 0x00; |
||||
sha256_transform(ctx, ctx->data); |
||||
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; |
||||
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; |
||||
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.
|
||||
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; |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@
|
||||
/*********************************************************************
|
||||
* Filename: sha256.h |
||||
* Author: Brad Conte (brad AT bradconte.com) |
||||
* Copyright: |
||||
* Disclaimer: This code is presented "as is" without any guarantees. |
||||
* Details: Defines the API for the corresponding SHA1 implementation. |
||||
*********************************************************************/ |
||||
|
||||
#ifndef SHA256_H |
||||
#define SHA256_H |
||||
|
||||
/*************************** HEADER FILES ***************************/ |
||||
#include <stddef.h> |
||||
|
||||
/****************************** MACROS ******************************/ |
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
|
||||
/**************************** DATA TYPES ****************************/ |
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
|
||||
typedef struct { |
||||
BYTE data[64]; |
||||
WORD datalen; |
||||
unsigned long long bitlen; |
||||
WORD state[8]; |
||||
} SHA256_CTX; |
||||
|
||||
/*********************** FUNCTION DECLARATIONS **********************/ |
||||
void sha256_init(SHA256_CTX *ctx); |
||||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); |
||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]); |
||||
|
||||
#endif // SHA256_H
|
||||
@ -0,0 +1,293 @@
|
||||
// config_updater.c - Configuration file updater implementation
|
||||
#define _POSIX_C_SOURCE 200809L |
||||
#include "config_updater.h" |
||||
#include "config_parser.h" |
||||
#include "secure_channel.h" |
||||
#include "debug_config.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <ctype.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#define HEXKEY_LEN 64 // 32 bytes * 2 hex chars
|
||||
#define HEXNODEID_LEN 16 // 8 bytes * 2 hex chars
|
||||
#define MAX_LINE_LEN 1024 |
||||
|
||||
static int is_valid_hex_key(const char *key) { |
||||
if (!key || strlen(key) != HEXKEY_LEN) return 0; |
||||
for (int i = 0; i < HEXKEY_LEN; i++) { |
||||
if (!isxdigit((unsigned char)key[i])) return 0; |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
static int is_valid_node_id(uint64_t node_id) { |
||||
return node_id != 0; |
||||
} |
||||
|
||||
static int read_file_to_mem(const char *filename, char **buffer, size_t *size) { |
||||
FILE *fp = fopen(filename, "r"); |
||||
if (!fp) return -1; |
||||
|
||||
fseek(fp, 0, SEEK_END); |
||||
long file_size = ftell(fp); |
||||
if (file_size < 0) { |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
fseek(fp, 0, SEEK_SET); |
||||
|
||||
*buffer = malloc(file_size + 1); |
||||
if (!*buffer) { |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
size_t read_size = fread(*buffer, 1, file_size, fp); |
||||
if (read_size != (size_t)file_size) { |
||||
free(*buffer); |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
(*buffer)[file_size] = '\0'; |
||||
*size = file_size; |
||||
fclose(fp); |
||||
return 0; |
||||
} |
||||
|
||||
static int write_mem_to_file(const char *filename, const char *buffer, size_t size) { |
||||
FILE *fp = fopen(filename, "w"); |
||||
if (!fp) return -1; |
||||
|
||||
size_t written = fwrite(buffer, 1, size, fp); |
||||
if (written != size) { |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
fclose(fp); |
||||
return 0; |
||||
} |
||||
|
||||
static char* find_section_global(char *buf, size_t buf_len) { |
||||
char *p = buf; |
||||
while (p < buf + buf_len) { |
||||
if (p[0] == '[' && strncmp(p + 1, "global", 6) == 0) { |
||||
char *end = strchr(p, ']'); |
||||
if (end) return end + 1; |
||||
} |
||||
p = strchr(p, '\n'); |
||||
if (!p) break; |
||||
p++; // skip '\n'
|
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
static char* find_option(char *buf, size_t buf_len, const char *option) { |
||||
char *p = buf; |
||||
while (p < buf + buf_len) { |
||||
if (strncmp(p, option, strlen(option)) == 0) { |
||||
char *after_key = p + strlen(option); |
||||
if (after_key[0] == '=') return p; |
||||
} |
||||
p = strchr(p, '\n'); |
||||
if (!p) break; |
||||
p++; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_capacity, const char *option, const char *value) { |
||||
if (!buf || !*buf || !buf_len || !buf_capacity || !option || !value) return -1; |
||||
|
||||
// Check if option already exists
|
||||
char *opt_pos = find_option(*buf, *buf_len, option); |
||||
if (opt_pos) { |
||||
// Find option line end
|
||||
char *line_end = strchr(opt_pos, '\n'); |
||||
if (!line_end) line_end = *buf + *buf_len; |
||||
|
||||
// Prepare new line
|
||||
char new_line[MAX_LINE_LEN]; |
||||
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value); |
||||
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1; |
||||
|
||||
// Calculate length difference
|
||||
size_t old_line_len = line_end - opt_pos + 1; |
||||
long len_diff = new_len - old_line_len; |
||||
|
||||
// Ensure buffer capacity
|
||||
if (*buf_len + len_diff + 1 > *buf_capacity) { |
||||
*buf_capacity = *buf_len + len_diff + 1024; |
||||
char *new_buf = realloc(*buf, *buf_capacity); |
||||
if (!new_buf) return -1; |
||||
*buf = new_buf; |
||||
// Recalculate positions after realloc
|
||||
opt_pos = find_option(*buf, *buf_len, option); |
||||
if (!opt_pos) return -1; |
||||
line_end = strchr(opt_pos, '\n'); |
||||
if (!line_end) line_end = *buf + *buf_len; |
||||
} |
||||
|
||||
// Move content and insert new line
|
||||
memmove(opt_pos + new_len, line_end, *buf_len - (line_end - *buf) + 1); |
||||
memcpy(opt_pos, new_line, new_len); |
||||
*buf_len += len_diff; |
||||
|
||||
} else { |
||||
// Insert after [global] section
|
||||
char *section_end = find_section_global(*buf, *buf_len); |
||||
if (!section_end) return -1; |
||||
|
||||
// Find end of global section (next [ or end of buffer)
|
||||
char *insert_pos = strchr(section_end, '['); |
||||
if (!insert_pos) insert_pos = *buf + *buf_len; |
||||
else { |
||||
// Move to beginning of next line
|
||||
char *prev_nl = insert_pos; |
||||
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--; |
||||
if (*prev_nl == '\n') insert_pos = prev_nl + 1; |
||||
} |
||||
|
||||
// Prepare new line
|
||||
char new_line[MAX_LINE_LEN]; |
||||
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value); |
||||
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1; |
||||
|
||||
// Ensure buffer capacity
|
||||
if (*buf_len + new_len + 1 > *buf_capacity) { |
||||
*buf_capacity = *buf_len + new_len + 1024; |
||||
char *new_buf = realloc(*buf, *buf_capacity); |
||||
if (!new_buf) return -1; |
||||
*buf = new_buf; |
||||
// Recalculate insert position after realloc
|
||||
section_end = find_section_global(*buf, *buf_len); |
||||
if (!section_end) return -1; |
||||
insert_pos = strchr(section_end, '['); |
||||
if (!insert_pos) insert_pos = *buf + *buf_len; |
||||
else { |
||||
char *prev_nl = insert_pos; |
||||
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--; |
||||
if (*prev_nl == '\n') insert_pos = prev_nl + 1; |
||||
} |
||||
} |
||||
|
||||
// Move content and insert new line
|
||||
memmove(insert_pos + new_len, insert_pos, *buf_len - (insert_pos - *buf) + 1); |
||||
memcpy(insert_pos, new_line, new_len); |
||||
*buf_len += new_len; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int config_ensure_keys_and_node_id(const char *filename) { |
||||
struct utun_config *config = parse_config(filename); |
||||
if (!config) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse config: %s", filename); |
||||
return -1; |
||||
} |
||||
|
||||
struct global_config *global = &config->global; |
||||
|
||||
// Check if we need to generate anything
|
||||
int need_priv_key = !is_valid_hex_key(global->my_private_key_hex); |
||||
int need_pub_key = !is_valid_hex_key(global->my_public_key_hex); |
||||
int need_node_id = !is_valid_node_id(global->my_node_id); |
||||
|
||||
if (!need_priv_key && !need_pub_key && !need_node_id) { |
||||
free_config(config); |
||||
return 0; |
||||
} |
||||
|
||||
// Generate keys if needed
|
||||
char new_priv_key[HEXKEY_LEN + 1] = {0}; |
||||
char new_pub_key[HEXKEY_LEN + 1] = {0}; |
||||
uint64_t new_node_id = 0; |
||||
|
||||
if (need_priv_key || need_pub_key) { |
||||
struct secure_channel ctx; |
||||
if (sc_generate_keypair(&ctx) != SC_OK) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair"); |
||||
free_config(config); |
||||
return -1; |
||||
} |
||||
bytes_to_hex(ctx.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key)); |
||||
bytes_to_hex(ctx.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key)); |
||||
} |
||||
|
||||
if (need_node_id) { |
||||
int fd = open("/dev/urandom", O_RDONLY); |
||||
if (fd < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to open /dev/urandom"); |
||||
free_config(config); |
||||
return -1; |
||||
} |
||||
if (read(fd, &new_node_id, sizeof(new_node_id)) != sizeof(new_node_id)) { |
||||
close(fd); |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read random bytes for node_id"); |
||||
free_config(config); |
||||
return -1; |
||||
} |
||||
close(fd); |
||||
new_node_id &= 0x7FFFFFFFFFFFFFFF; |
||||
} |
||||
|
||||
free_config(config); |
||||
|
||||
// Read entire config file to memory
|
||||
char *file_buf = NULL; |
||||
size_t file_size = 0; |
||||
if (read_file_to_mem(filename, &file_buf, &file_size) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read config file: %s", filename); |
||||
return -1; |
||||
} |
||||
|
||||
// Update config file
|
||||
size_t buf_capacity = file_size + 1024; |
||||
char *work_buf = malloc(buf_capacity); |
||||
if (!work_buf) { |
||||
free(file_buf); |
||||
return -1; |
||||
} |
||||
memcpy(work_buf, file_buf, file_size); |
||||
size_t work_len = file_size; |
||||
|
||||
int ret = 0; |
||||
|
||||
if (need_priv_key) { |
||||
char node_id_hex[HEXNODEID_LEN + 1]; |
||||
snprintf(node_id_hex, sizeof(node_id_hex), "%llx", (unsigned long long)new_node_id); |
||||
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) { |
||||
ret = -1; |
||||
} |
||||
} |
||||
|
||||
if (need_priv_key && ret == 0) { |
||||
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) { |
||||
ret = -1; |
||||
} |
||||
} |
||||
|
||||
if (need_pub_key && ret == 0) { |
||||
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) { |
||||
ret = -1; |
||||
} |
||||
} |
||||
|
||||
if (ret == 0) { |
||||
if (write_mem_to_file(filename, work_buf, work_len) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to write updated config file: %s", filename); |
||||
ret = -1; |
||||
} |
||||
} |
||||
|
||||
free(file_buf); |
||||
free(work_buf); |
||||
|
||||
return ret; |
||||
} |
||||
@ -0,0 +1,18 @@
|
||||
// config_updater.h - Configuration file updater
|
||||
#ifndef CONFIG_UPDATER_H |
||||
#define CONFIG_UPDATER_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
// Ensures config file has valid my_private_key, my_public_key, and my_node_id
|
||||
// If any are missing or invalid, generates and updates them
|
||||
// Returns 0 on success, -1 on error
|
||||
int config_ensure_keys_and_node_id(const char *filename); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif // CONFIG_UPDATER_H
|
||||
Loading…
Reference in new issue