|
19 | 19 | #include <stdio.h>
|
20 | 20 | #include <math.h>
|
21 | 21 |
|
22 |
| -#define FOSSIL_JELLYFISH_HASH_SIZE 16 |
23 | 22 | #define MAX_CUSTOM_FILTERS 64
|
24 | 23 |
|
25 | 24 | /** Lookup table for rot-brain words and their suggested replacements */
|
@@ -131,6 +130,55 @@ static const char *SKIP_WORDS[] = {
|
131 | 130 | NULL // Sentinel to mark the end
|
132 | 131 | };
|
133 | 132 |
|
| 133 | +#define FNV_PRIME 0x01000193 |
| 134 | +#define FNV_BASIS 0x811c9dc5 |
| 135 | + |
| 136 | +void fossil_io_soap_jellyfish_hash(const char *input, const char *output, uint8_t *hash_out) { |
| 137 | + uint32_t hash = FNV_BASIS; |
| 138 | + size_t in_len = strlen(input); |
| 139 | + size_t out_len = strlen(output); |
| 140 | + |
| 141 | + // Mix lengths |
| 142 | + hash ^= in_len; |
| 143 | + hash *= FNV_PRIME; |
| 144 | + hash ^= out_len; |
| 145 | + hash *= FNV_PRIME; |
| 146 | + |
| 147 | + // Mix input string |
| 148 | + for (size_t i = 0; i < in_len; ++i) { |
| 149 | + hash ^= (uint8_t)input[i]; |
| 150 | + hash *= FNV_PRIME; |
| 151 | + hash ^= (hash >> 5); |
| 152 | + } |
| 153 | + |
| 154 | + // Mix output string |
| 155 | + for (size_t i = 0; i < out_len; ++i) { |
| 156 | + hash ^= (uint8_t)output[i]; |
| 157 | + hash *= FNV_PRIME; |
| 158 | + hash ^= (hash >> 5); |
| 159 | + } |
| 160 | + |
| 161 | + // Final avalanche |
| 162 | + hash ^= (hash << 7); |
| 163 | + hash ^= (hash >> 3); |
| 164 | + |
| 165 | + // Expand to fixed size |
| 166 | + uint32_t h = hash; |
| 167 | + for (size_t i = 0; i < FOSSIL_JELLYFISH_HASH_SIZE; ++i) { |
| 168 | + h ^= (h >> 13); |
| 169 | + h *= FNV_PRIME; |
| 170 | + h ^= (h << 11); |
| 171 | + hash_out[i] = (uint8_t)((h >> (8 * (i % 4))) & 0xFF); |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +static void hash_to_hex(const uint8_t *hash, size_t len, char *out_hex) { |
| 176 | + for (size_t i = 0; i < len; ++i) { |
| 177 | + sprintf(out_hex + i * 2, "%02x", hash[i]); |
| 178 | + } |
| 179 | + out_hex[len * 2] = '\0'; |
| 180 | +} |
| 181 | + |
134 | 182 | /**
|
135 | 183 | * @brief Convert leetspeak to normal letters.
|
136 | 184 | */
|
@@ -427,25 +475,19 @@ char *fossil_io_soap_generate_audit_block(const char *text) {
|
427 | 475 | char *sanitized = fossil_io_soap_sanitize(text);
|
428 | 476 | if (!sanitized) return NULL;
|
429 | 477 |
|
430 |
| - // Compute Jellyfish-style hash |
431 |
| - uint8_t hash_out[FOSSIL_JELLYFISH_HASH_SIZE]; |
432 |
| - fossil_jellyfish_hash(text, sanitized, hash_out); |
| 478 | + uint8_t hash[FOSSIL_JELLYFISH_HASH_SIZE]; |
| 479 | + char hash_hex[FOSSIL_JELLYFISH_HASH_SIZE * 2 + 1]; |
433 | 480 |
|
434 |
| - // Convert hash to hex string |
435 |
| - char hash_hex[FOSSIL_JELLYFISH_HASH_SIZE * 2 + 1] = {0}; |
436 |
| - for (size_t i = 0; i < FOSSIL_JELLYFISH_HASH_SIZE; ++i) { |
437 |
| - sprintf(hash_hex + i * 2, "%02x", hash_out[i]); |
438 |
| - } |
| 481 | + fossil_io_soap_jellyfish_hash(text, sanitized, hash); |
| 482 | + hash_to_hex(hash, FOSSIL_JELLYFISH_HASH_SIZE, hash_hex); |
439 | 483 |
|
440 |
| - // Allocate JSON audit block |
441 |
| - size_t needed = strlen(text) + strlen(sanitized) + strlen(hash_hex) + 64; |
442 |
| - char *audit_block = malloc(needed); |
| 484 | + char *audit_block = malloc(1024); |
443 | 485 | if (!audit_block) {
|
444 | 486 | free(sanitized);
|
445 | 487 | return NULL;
|
446 | 488 | }
|
447 | 489 |
|
448 |
| - snprintf(audit_block, needed, |
| 490 | + snprintf(audit_block, 1024, |
449 | 491 | "{ \"original\": \"%s\", \"sanitized\": \"%s\", \"hash\": \"%s\" }",
|
450 | 492 | text, sanitized, hash_hex);
|
451 | 493 |
|
@@ -490,7 +532,7 @@ char *fossil_io_soap_flag_ethics(const char *text) {
|
490 | 532 | // Simulate simple filter
|
491 | 533 | const char *bad_words[] = {"stupid", "idiot", "hate", "kill", NULL};
|
492 | 534 |
|
493 |
| - char *flagged = strdup(text); |
| 535 | + char *flagged = fossil_io_cstring_dup(text); |
494 | 536 | if (!flagged) return NULL;
|
495 | 537 |
|
496 | 538 | for (size_t i = 0; bad_words[i]; i++) {
|
|
0 commit comments