Lainaa
Re: Matikkaa tietokoneella
Korjailin aikani kuluksi pahimmat bugit, muutin suolan 512 bittiseksi ja otin käyttöön sen lupaamani SHA3-512:n. Tärkein muutos liittyy tietoturvaan eli avainten pyyhintään. Jaksoin myös ajaa dieharder testin kokonaan läpi. Eiköhän tämä ollut nyt tässä ellei yleisö välttämättä halua lisää.

Koodi: Valitse kaikki

#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/crypto.h> // OPENSSL_cleanse
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sys/file.h> // flock
#define BLOCK_SIZE 16 // AES-lohkon koko tavuina
#define CHUNK_BLOCKS 16384 // 16k * 16 = 262 144 B = 256 KB per erä
#define RESEED_INTERVAL (4096) // 4096 * 256 KB = 1 048 576 KB = 1 GB
#define SEED_SIZE 32 // 256-bittinen siemen
#define SALT_SIZE 64 // 512-bittinen suola
#define RDSEED_TIMEOUT_NS 10000000000 // 10 s aikaraja RDSEED:lle per 64 bittiä
#define LOG_FILE "aes_ctr_drbg.log" // Lokitiedoston nimi
static volatile sig_atomic_t keep_running = 1;
static EVP_CIPHER_CTX *global_ctx = NULL;
static FILE *log_file = NULL;
// Lokitusfunktio virheille (kirjoittaa lokiin ja stderr:iin)
void log_message(const char *message) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&ts.tv_sec));
flock(fileno(log_file), LOCK_EX);
fprintf(log_file, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fprintf(stderr, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fflush(log_file);
flock(fileno(log_file), LOCK_UN);
}
// Lokitusfunktio tapahtumille (kirjoittaa vain lokiin)
void log_event(const char *message) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&ts.tv_sec));
flock(fileno(log_file), LOCK_EX);
fprintf(log_file, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fflush(log_file);
flock(fileno(log_file), LOCK_UN);
}
// Signaalinkäsittelijä Ctrl+C:lle
void handle_sigint(int sig) {
(void)sig;
keep_running = 0;
}
// Generoi 64-bittinen satunnaisluku RDSEED:llä
int get_random_64(unsigned long long *val) {
struct timespec start, now;
clock_gettime(CLOCK_REALTIME, &start);
while (1) {
if (_rdseed64_step(val) == 1) {
return 1;
}
clock_gettime(CLOCK_REALTIME, &now);
long elapsed_ns = (now.tv_sec - start.tv_sec) * 1000000000L + (now.tv_nsec - start.tv_nsec);
if (elapsed_ns > RDSEED_TIMEOUT_NS) {
log_message("RDSEED timeout exceeded");
return 0;
}
nanosleep(&(struct timespec){.tv_nsec = 1000000}, NULL); // 1 ms tauko
}
}
// Generoi 256-bittinen siemen ja 512-bittinen suola RDSEED:llä
int generate_seed_and_salt(uint8_t *seed, uint8_t *salt) {
for (int i = 0; i < SEED_SIZE / sizeof(unsigned long long); i++) {
unsigned long long val;
if (!get_random_64(&val)) {
log_message("Failed to generate seed");
return 0;
}
memcpy(seed + i * sizeof(unsigned long long), &val, sizeof(unsigned long long));
}
for (int i = 0; i < SALT_SIZE / sizeof(unsigned long long); i++) {
unsigned long long val;
if (!get_random_64(&val)) {
log_message("Failed to generate salt");
return 0;
}
memcpy(salt + i * sizeof(unsigned long long), &val, sizeof(unsigned long long));
}
return 1;
}
// Aseta uusi siemen ja alusta laskuri
int reseed(EVP_CIPHER_CTX *ctx, uint8_t *iv) {
uint8_t seed[SEED_SIZE];
uint8_t salt[SALT_SIZE];
uint8_t key[SEED_SIZE];
int ret = 0;
if (!generate_seed_and_salt(seed, salt)) {
goto cleanup;
}
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "HKDF", NULL);
if (!kdf) {
log_message("EVP_KDF_fetch failed");
goto cleanup;
}
EVP_KDF_CTX *kdf_ctx = EVP_KDF_CTX_new(kdf);
EVP_KDF_free(kdf);
if (!kdf_ctx) {
log_message("EVP_KDF_CTX_new failed");
goto cleanup;
}
OSSL_PARAM params = {
OSSL_PARAM_construct_utf8_string("digest", "SHA3-512", 0),
OSSL_PARAM_construct_octet_string("salt", salt, SALT_SIZE),
OSSL_PARAM_construct_octet_string("key", seed, SEED_SIZE),
OSSL_PARAM_construct_end()
};
if (EVP_KDF_derive(kdf_ctx, key, SEED_SIZE, params) != 1) {
log_message("EVP_KDF_derive failed");
EVP_KDF_CTX_free(kdf_ctx);
goto cleanup;
}
EVP_KDF_CTX_free(kdf_ctx);
memset(iv, 0, BLOCK_SIZE);
if (EVP_CIPHER_CTX_reset(ctx) != 1) {
log_message("EVP_CIPHER_CTX_reset failed");
goto cleanup;
}
if (EVP_EncryptInit_ex2(ctx, EVP_aes_256_ctr(), key, iv, NULL) != 1) {
log_message("EVP_EncryptInit_ex2 failed");
goto cleanup;
}
ret = 1;
cleanup:
OPENSSL_cleanse(seed, SEED_SIZE);
OPENSSL_cleanse(salt, SALT_SIZE);
OPENSSL_cleanse(key, SEED_SIZE);
return ret;
}
int main() {
signal(SIGPIPE, SIG_IGN);
log_file = fopen(LOG_FILE, "a");
if (!log_file) {
fprintf(stderr, "Failed to open log file " LOG_FILE "\n");
return 1;
}
log_event("Program started");
signal(SIGINT, handle_sigint);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
log_message("EVP_CIPHER_CTX_new failed");
fclose(log_file);
return 1;
}
global_ctx = ctx;
uint8_t iv[BLOCK_SIZE] = {0};
uint8_t buffer[CHUNK_BLOCKS * BLOCK_SIZE]; // 256 KB
uint8_t zero_buffer[CHUNK_BLOCKS * BLOCK_SIZE] = {0}; // 256 KB nollapuskuri
uint64_t chunk_counter = 0;
if (!reseed(ctx, iv)) {
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
// Main loop: Generate random data in 256 KB chunks using AES-256-CTR,
// reseed every 1 GB, and write to stdout until interrupted or pipe closes.
while (keep_running) {
int outlen;
if (EVP_EncryptUpdate(ctx, buffer, &outlen, zero_buffer, CHUNK_BLOCKS * BLOCK_SIZE) != 1) {
log_message("EVP_EncryptUpdate failed");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
if (outlen != CHUNK_BLOCKS * BLOCK_SIZE) {
log_message("Unexpected output length");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
if (fwrite(buffer, 1, outlen, stdout) != (size_t)outlen) {
if (errno == EPIPE) {
log_event("Output pipe closed, shutting down");
keep_running = 0;
} else {
log_message("fwrite failed");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
} else {
fflush(stdout);
chunk_counter++;
if (chunk_counter % RESEED_INTERVAL == 0) {
if (!reseed(ctx, iv)) {
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
char msg[64];
uint64_t gb_generated = chunk_counter / RESEED_INTERVAL; // Kokonaisluku GB
snprintf(msg, sizeof(msg), "Reseeded after %ld GB", gb_generated);
log_event(msg);
}
}
}
char msg[64];
uint64_t gb_generated = chunk_counter * CHUNK_BLOCKS * BLOCK_SIZE / (1024LL * 1024 * 1024);
snprintf(msg, sizeof(msg), "Total data generated: %ld GB", gb_generated);
log_event(msg);
EVP_CIPHER_CTX_free(ctx);
log_event("Program terminated");
fclose(log_file);
global_ctx = NULL;
return 0;
}

Koodi: Valitse kaikki

 ./aes_ctr_drbg_new | dieharder -a -g 200
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 5.97e+07 |1115297810|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.43543630| PASSED
diehard_operm5| 0| 1000000| 100|0.78361743| PASSED
diehard_rank_32x32| 0| 40000| 100|0.31692885| PASSED
diehard_rank_6x8| 0| 100000| 100|0.99171666| PASSED
diehard_bitstream| 0| 2097152| 100|0.72565079| PASSED
diehard_opso| 0| 2097152| 100|0.99991762| WEAK
diehard_oqso| 0| 2097152| 100|0.04022020| PASSED
diehard_dna| 0| 2097152| 100|0.64703788| PASSED
diehard_count_1s_str| 0| 256000| 100|0.71883437| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.86836429| PASSED
diehard_parking_lot| 0| 12000| 100|0.79252967| PASSED
diehard_2dsphere| 2| 8000| 100|0.49861982| PASSED
diehard_3dsphere| 3| 4000| 100|0.29861975| PASSED
diehard_squeeze| 0| 100000| 100|0.11465672| PASSED
diehard_sums| 0| 100| 100|0.06486198| PASSED
diehard_runs| 0| 100000| 100|0.35574496| PASSED
diehard_runs| 0| 100000| 100|0.32528130| PASSED
diehard_craps| 0| 200000| 100|0.18823885| PASSED
diehard_craps| 0| 200000| 100|0.52891535| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.96210375| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.25112654| PASSED
sts_monobit| 1| 100000| 100|0.98529115| PASSED
sts_runs| 2| 100000| 100|0.62428014| PASSED
sts_serial| 1| 100000| 100|0.91093142| PASSED
sts_serial| 2| 100000| 100|0.69339930| PASSED
sts_serial| 3| 100000| 100|0.20406129| PASSED
sts_serial| 3| 100000| 100|0.01815481| PASSED
sts_serial| 4| 100000| 100|0.02411009| PASSED
sts_serial| 4| 100000| 100|0.06759275| PASSED
sts_serial| 5| 100000| 100|0.05452092| PASSED
sts_serial| 5| 100000| 100|0.16006190| PASSED
sts_serial| 6| 100000| 100|0.01524816| PASSED
sts_serial| 6| 100000| 100|0.41673603| PASSED
sts_serial| 7| 100000| 100|0.55377725| PASSED
sts_serial| 7| 100000| 100|0.89397256| PASSED
sts_serial| 8| 100000| 100|0.16507489| PASSED
sts_serial| 8| 100000| 100|0.59537615| PASSED
sts_serial| 9| 100000| 100|0.67552873| PASSED
sts_serial| 9| 100000| 100|0.26031571| PASSED
sts_serial| 10| 100000| 100|0.99999975| FAILED
sts_serial| 10| 100000| 100|0.56633487| PASSED
sts_serial| 11| 100000| 100|0.81544127| PASSED
sts_serial| 11| 100000| 100|0.54121691| PASSED
sts_serial| 12| 100000| 100|0.90719346| PASSED
sts_serial| 12| 100000| 100|0.53679143| PASSED
sts_serial| 13| 100000| 100|0.93000961| PASSED
sts_serial| 13| 100000| 100|0.49730862| PASSED
sts_serial| 14| 100000| 100|0.18158707| PASSED
sts_serial| 14| 100000| 100|0.52665864| PASSED
sts_serial| 15| 100000| 100|0.71076563| PASSED
sts_serial| 15| 100000| 100|0.29000691| PASSED
sts_serial| 16| 100000| 100|0.89369003| PASSED
sts_serial| 16| 100000| 100|0.97384299| PASSED
rgb_bitdist| 1| 100000| 100|0.63346740| PASSED
rgb_bitdist| 2| 100000| 100|0.53480556| PASSED
rgb_bitdist| 3| 100000| 100|0.05411541| PASSED
rgb_bitdist| 4| 100000| 100|0.65614719| PASSED
rgb_bitdist| 5| 100000| 100|0.47350279| PASSED
rgb_bitdist| 6| 100000| 100|0.12222463| PASSED
rgb_bitdist| 7| 100000| 100|0.53464355| PASSED
rgb_bitdist| 8| 100000| 100|0.13057620| PASSED
rgb_bitdist| 9| 100000| 100|0.43533794| PASSED
rgb_bitdist| 10| 100000| 100|0.39422938| PASSED
rgb_bitdist| 11| 100000| 100|0.91978077| PASSED
rgb_bitdist| 12| 100000| 100|0.21919621| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.08090195| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.02079021| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.29876387| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.46438021| PASSED
rgb_permutations| 2| 100000| 100|0.87353070| PASSED
rgb_permutations| 3| 100000| 100|0.52552605| PASSED
rgb_permutations| 4| 100000| 100|0.91774575| PASSED
rgb_permutations| 5| 100000| 100|0.86219339| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.97810078| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.36232370| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.28509842| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.91208516| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.12288336| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.66442364| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.64970350| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.48952773| PASSED
rgb_lagged_sum| 8| 1000000| 100|0.88901015| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.48118278| PASSED
rgb_lagged_sum| 10| 1000000| 100|0.69794978| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.10677097| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.72159961| PASSED
rgb_lagged_sum| 13| 1000000| 100|0.63723486| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.31263455| PASSED
rgb_lagged_sum| 15| 1000000| 100|0.76732644| PASSED
rgb_lagged_sum| 16| 1000000| 100|0.25360204| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.91180734| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.98559464| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.82360676| PASSED
rgb_lagged_sum| 20| 1000000| 100|0.05047740| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.52650681| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.44660144| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.66982129| PASSED
rgb_lagged_sum| 24| 1000000| 100|0.46483777| PASSED
rgb_lagged_sum| 25| 1000000| 100|0.51657381| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.04200830| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.67524992| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.95076360| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.65131173| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.23827590| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.29841371| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.79204731| PASSED
rgb_kstest_test| 0| 10000| 1000|0.67597377| PASSED
dab_bytedistrib| 0| 51200000| 1|0.82307693| PASSED
dab_dct| 256| 50000| 1|0.12245871| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.86454674| PASSED
dab_filltree| 32| 15000000| 1|0.15622413| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.47355166| PASSED
dab_filltree2| 1| 5000000| 1|0.25204152| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.64480429| PASSED
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Avatar
Lainaa
Re: Matikkaa tietokoneella
Näyttää hienolta, ja muistin pyyhintäkin mukana. Detaljeja en pysty analysoimaan, mutta idealtaan käsittääkseni mallikas.
Lainaa
Re: Matikkaa tietokoneella
QS kirjoitti: 19.5.2025, 09:23 Näyttää hienolta, ja muistin pyyhintäkin mukana. Detaljeja en pysty analysoimaan, mutta idealtaan käsittääkseni mallikas.
Siellä yksi testi (diehard_opso) saa arvion heikko, mutta se on todennäköisesti pienestä datamäärästä johtuvaa ja saattaisi seuraavassa ajossa mennä läpi. Sama juttu (sts_serial k=10) joka epäonnistui kerran mutta meni toisella kertaa läpi eli tilastollinen poikkeama veikkaan.
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Lainaa
Re: Matikkaa tietokoneella
Pakko oli vielä ottaa yksi kierros kun jäi takaraivoon kummittelemaan. Muutin koodia vielä niin, että IV (Initialization Vector) käydään hakemassa RDSEED:llä. Aikaisemmin se oli tyylikkäästi aina nolla. Dieharder näyttää nyt paremmalle, mutta se voi olla sattumaakin.🤣 Nyt kyllä lupaan jättää tämän käsistäni ettei foorumi muutu blogiksi.

Koodi: Valitse kaikki

#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/crypto.h> // OPENSSL_cleanse
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sys/file.h> // flock
#define BLOCK_SIZE 16 // AES-lohkon koko tavuina
#define CHUNK_BLOCKS 16384 // 16k * 16 = 262 144 B = 256 KB per erä
#define RESEED_INTERVAL (4096) // 4096 * 256 KB = 1 048 576 KB = 1 GB
#define SEED_SIZE 32 // 256-bittinen siemen
#define SALT_SIZE 64 // 256-bittinen suola
#define IV_SIZE 16 // 128-bittinen IV AES-CTR:lle
#define RDSEED_TIMEOUT_NS 10000000000 // 10 s aikaraja RDSEED:lle per 64 bittiä
#define LOG_FILE "aes_ctr_drbg.log" // Lokitiedoston nimi
static volatile sig_atomic_t keep_running = 1;
static EVP_CIPHER_CTX *global_ctx = NULL;
static FILE *log_file = NULL;
// Lokitusfunktio virheille (kirjoittaa lokiin ja stderr:iin)
void log_message(const char *message) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&ts.tv_sec));
flock(fileno(log_file), LOCK_EX);
fprintf(log_file, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fprintf(stderr, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fflush(log_file);
flock(fileno(log_file), LOCK_UN);
}
// Lokitusfunktio tapahtumille (kirjoittaa vain lokiin)
void log_event(const char *message) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&ts.tv_sec));
flock(fileno(log_file), LOCK_EX);
fprintf(log_file, "[%s.%03ld] %s\n", timestamp, ts.tv_nsec / 1000000, message);
fflush(log_file);
flock(fileno(log_file), LOCK_UN);
}
// Signaalinkäsittelijä Ctrl+C:lle
void handle_sigint(int sig) {
(void)sig;
keep_running = 0;
}
// Generoi 64-bittinen satunnaisluku RDSEED:llä
int get_random_64(unsigned long long *val) {
struct timespec start, now;
clock_gettime(CLOCK_REALTIME, &start);
while (1) {
if (_rdseed64_step(val) == 1) {
return 1;
}
clock_gettime(CLOCK_REALTIME, &now);
long elapsed_ns = (now.tv_sec - start.tv_sec) * 1000000000L + (now.tv_nsec - start.tv_nsec);
if (elapsed_ns > RDSEED_TIMEOUT_NS) {
log_message("RDSEED timeout exceeded");
return 0;
}
nanosleep(&(struct timespec){.tv_nsec = 1000000}, NULL); // 1 ms tauko
}
}
// Generoi 256-bittinen siemen ja 512-bittinen suola RDSEED:llä
int generate_seed_and_salt(uint8_t *seed, uint8_t *salt, uint8_t *iv) {
for (int i = 0; i < SEED_SIZE / sizeof(unsigned long long); i++) {
unsigned long long val;
if (!get_random_64(&val)) {
log_message("Failed to generate seed");
return 0;
}
memcpy(seed + i * sizeof(unsigned long long), &val, sizeof(unsigned long long));
}
for (int i = 0; i < SALT_SIZE / sizeof(unsigned long long); i++) {
unsigned long long val;
if (!get_random_64(&val)) {
log_message("Failed to generate salt");
return 0;
}
memcpy(salt + i * sizeof(unsigned long long), &val, sizeof(unsigned long long));
}
for (int i = 0; i < IV_SIZE / sizeof(unsigned long long); i++) {
unsigned long long val;
if (!get_random_64(&val)) {
log_message("Failed to generate IV");
return 0;
}
memcpy(iv + i * sizeof(unsigned long long), &val, sizeof(unsigned long long));
}
return 1;
}
// Aseta uusi siemen ja alusta laskuri
int reseed(EVP_CIPHER_CTX *ctx) {
uint8_t seed[SEED_SIZE];
uint8_t salt[SALT_SIZE];
uint8_t key[SEED_SIZE];
uint8_t iv[IV_SIZE];
int ret = 0;
if (!generate_seed_and_salt(seed, salt, iv)) {
goto cleanup;
}
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "HKDF", NULL);
if (!kdf) {
log_message("EVP_KDF_fetch failed");
goto cleanup;
}
EVP_KDF_CTX *kdf_ctx = EVP_KDF_CTX_new(kdf);
EVP_KDF_free(kdf);
if (!kdf_ctx) {
log_message("EVP_KDF_CTX_new failed");
goto cleanup;
}
OSSL_PARAM params = {
OSSL_PARAM_construct_utf8_string("digest", "SHA3-512", 0),
OSSL_PARAM_construct_octet_string("salt", salt, SALT_SIZE),
OSSL_PARAM_construct_octet_string("key", seed, SEED_SIZE),
OSSL_PARAM_construct_end()
};
if (EVP_KDF_derive(kdf_ctx, key, SEED_SIZE, params) != 1) {
log_message("EVP_KDF_derive failed");
EVP_KDF_CTX_free(kdf_ctx);
goto cleanup;
}
EVP_KDF_CTX_free(kdf_ctx);
if (EVP_CIPHER_CTX_reset(ctx) != 1) {
log_message("EVP_CIPHER_CTX_reset failed");
goto cleanup;
}
if (EVP_EncryptInit_ex2(ctx, EVP_aes_256_ctr(), key, iv, NULL) != 1) {
log_message("EVP_EncryptInit_ex2 failed");
goto cleanup;
}
ret = 1;
cleanup:
OPENSSL_cleanse(seed, SEED_SIZE);
OPENSSL_cleanse(salt, SALT_SIZE);
OPENSSL_cleanse(key, SEED_SIZE);
OPENSSL_cleanse(iv, IV_SIZE); // ← lisäys
return ret;
}
int main() {
signal(SIGPIPE, SIG_IGN);
log_file = fopen(LOG_FILE, "a");
if (!log_file) {
fprintf(stderr, "Failed to open log file " LOG_FILE "\n");
return 1;
}
log_event("Program started");
signal(SIGINT, handle_sigint);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
log_message("EVP_CIPHER_CTX_new failed");
fclose(log_file);
return 1;
}
global_ctx = ctx;
uint8_t buffer[CHUNK_BLOCKS * BLOCK_SIZE]; // 256 KB
uint8_t zero_buffer[CHUNK_BLOCKS * BLOCK_SIZE] = {0}; // 256 KB nollapuskuri
uint64_t chunk_counter = 0;
if (!reseed(ctx)) {
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
// Main loop: Generate random data in 256 KB chunks using AES-256-CTR,
// reseed every 1 GB, and write to stdout until interrupted or pipe closes.
while (keep_running) {
int outlen;
if (EVP_EncryptUpdate(ctx, buffer, &outlen, zero_buffer, CHUNK_BLOCKS * BLOCK_SIZE) != 1) {
log_message("EVP_EncryptUpdate failed");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
if (outlen != CHUNK_BLOCKS * BLOCK_SIZE) {
log_message("Unexpected output length");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
if (fwrite(buffer, 1, outlen, stdout) != (size_t)outlen) {
if (errno == EPIPE) {
log_event("Output pipe closed, shutting down");
keep_running = 0;
} else {
log_message("fwrite failed");
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
} else {
fflush(stdout);
chunk_counter++;
if (chunk_counter % RESEED_INTERVAL == 0) {
if (!reseed(ctx)) {
EVP_CIPHER_CTX_free(ctx);
fclose(log_file);
return 1;
}
char msg[64];
uint64_t gb_generated = chunk_counter / RESEED_INTERVAL; // Kokonaisluku GB
snprintf(msg, sizeof(msg), "Reseeded after %ld GB", gb_generated);
log_event(msg);
}
}
}
char msg[64];
uint64_t gb_generated = chunk_counter * CHUNK_BLOCKS * BLOCK_SIZE / (1024LL * 1024 * 1024);
snprintf(msg, sizeof(msg), "Total data generated: %ld GB", gb_generated);
log_event(msg);
EVP_CIPHER_CTX_free(ctx);
log_event("Program terminated");
fclose(log_file);
global_ctx = NULL;
return 0;
}

Koodi: Valitse kaikki

./aes_ctr_drbg_new | dieharder -a -g 200
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 5.84e+07 |3147049193|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.47516901| PASSED
diehard_operm5| 0| 1000000| 100|0.02078930| PASSED
diehard_rank_32x32| 0| 40000| 100|0.67687128| PASSED
diehard_rank_6x8| 0| 100000| 100|0.39549952| PASSED
diehard_bitstream| 0| 2097152| 100|0.87999126| PASSED
diehard_opso| 0| 2097152| 100|0.21508778| PASSED
diehard_oqso| 0| 2097152| 100|0.98259761| PASSED
diehard_dna| 0| 2097152| 100|0.43376816| PASSED
diehard_count_1s_str| 0| 256000| 100|0.17957854| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.74896929| PASSED
diehard_parking_lot| 0| 12000| 100|0.51744758| PASSED
diehard_2dsphere| 2| 8000| 100|0.06389207| PASSED
diehard_3dsphere| 3| 4000| 100|0.37234646| PASSED
diehard_squeeze| 0| 100000| 100|0.54956884| PASSED
diehard_sums| 0| 100| 100|0.96995991| PASSED
diehard_runs| 0| 100000| 100|0.75479214| PASSED
diehard_runs| 0| 100000| 100|0.79028476| PASSED
diehard_craps| 0| 200000| 100|0.22655665| PASSED
diehard_craps| 0| 200000| 100|0.98965949| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.99244805| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.14393355| PASSED
sts_monobit| 1| 100000| 100|0.09414226| PASSED
sts_runs| 2| 100000| 100|0.65725942| PASSED
sts_serial| 1| 100000| 100|0.64321634| PASSED
sts_serial| 2| 100000| 100|0.51531010| PASSED
sts_serial| 3| 100000| 100|0.15610998| PASSED
sts_serial| 3| 100000| 100|0.14283228| PASSED
sts_serial| 4| 100000| 100|0.17890319| PASSED
sts_serial| 4| 100000| 100|0.97009290| PASSED
sts_serial| 5| 100000| 100|0.13370220| PASSED
sts_serial| 5| 100000| 100|0.97824567| PASSED
sts_serial| 6| 100000| 100|0.57171788| PASSED
sts_serial| 6| 100000| 100|0.70477793| PASSED
sts_serial| 7| 100000| 100|0.51395228| PASSED
sts_serial| 7| 100000| 100|0.74330655| PASSED
sts_serial| 8| 100000| 100|0.91737357| PASSED
sts_serial| 8| 100000| 100|0.52794739| PASSED
sts_serial| 9| 100000| 100|0.44794243| PASSED
sts_serial| 9| 100000| 100|0.56969177| PASSED
sts_serial| 10| 100000| 100|0.98432977| PASSED
sts_serial| 10| 100000| 100|0.58300818| PASSED
sts_serial| 11| 100000| 100|0.63421346| PASSED
sts_serial| 11| 100000| 100|0.43790724| PASSED
sts_serial| 12| 100000| 100|0.60413658| PASSED
sts_serial| 12| 100000| 100|0.99914320| WEAK
sts_serial| 13| 100000| 100|0.50619944| PASSED
sts_serial| 13| 100000| 100|0.25934429| PASSED
sts_serial| 14| 100000| 100|0.36643264| PASSED
sts_serial| 14| 100000| 100|0.75761362| PASSED
sts_serial| 15| 100000| 100|0.71763605| PASSED
sts_serial| 15| 100000| 100|0.80351811| PASSED
sts_serial| 16| 100000| 100|0.56443638| PASSED
sts_serial| 16| 100000| 100|0.47530996| PASSED
rgb_bitdist| 1| 100000| 100|0.72017289| PASSED
rgb_bitdist| 2| 100000| 100|0.93813223| PASSED
rgb_bitdist| 3| 100000| 100|0.70984808| PASSED
rgb_bitdist| 4| 100000| 100|0.52386872| PASSED
rgb_bitdist| 5| 100000| 100|0.84256520| PASSED
rgb_bitdist| 6| 100000| 100|0.77894915| PASSED
rgb_bitdist| 7| 100000| 100|0.51667455| PASSED
rgb_bitdist| 8| 100000| 100|0.76637854| PASSED
rgb_bitdist| 9| 100000| 100|0.84299089| PASSED
rgb_bitdist| 10| 100000| 100|0.96711699| PASSED
rgb_bitdist| 11| 100000| 100|0.81460252| PASSED
rgb_bitdist| 12| 100000| 100|0.98647042| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.68275828| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.11677097| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.13911512| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.40767985| PASSED
rgb_permutations| 2| 100000| 100|0.81547443| PASSED
rgb_permutations| 3| 100000| 100|0.62729268| PASSED
rgb_permutations| 4| 100000| 100|0.92629894| PASSED
rgb_permutations| 5| 100000| 100|0.23137417| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.36774770| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.58680391| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.87096554| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.30527352| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.84922365| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.88172634| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.08400529| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.79472242| PASSED
rgb_lagged_sum| 8| 1000000| 100|0.80778957| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.57857079| PASSED
rgb_lagged_sum| 10| 1000000| 100|0.24825466| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.74583264| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.28062854| PASSED
rgb_lagged_sum| 13| 1000000| 100|0.80665627| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.17051732| PASSED
rgb_lagged_sum| 15| 1000000| 100|0.86164480| PASSED
rgb_lagged_sum| 16| 1000000| 100|0.37276346| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.85721407| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.67262295| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.64483282| PASSED
rgb_lagged_sum| 20| 1000000| 100|0.59431272| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.78923065| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.97356715| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.36815241| PASSED
rgb_lagged_sum| 24| 1000000| 100|0.94242426| PASSED
rgb_lagged_sum| 25| 1000000| 100|0.48143648| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.10129223| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.89288246| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.78452908| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.95909359| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.63893999| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.92322370| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.63357626| PASSED
rgb_kstest_test| 0| 10000| 1000|0.51893864| PASSED
dab_bytedistrib| 0| 51200000| 1|0.94751798| PASSED
dab_dct| 256| 50000| 1|0.19608202| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.52123064| PASSED
dab_filltree| 32| 15000000| 1|0.46636143| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.92550341| PASSED
dab_filltree2| 1| 5000000| 1|0.79231481| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.13746188| PASSED
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Avatar
Lainaa
Re: Matikkaa tietokoneella
Mites muuten sen alkuperäinen juttu eli eri lähteiden XOR, oliko se tuossa toteutettu?
Lainaa
Re: Matikkaa tietokoneella
QS kirjoitti: 19.5.2025, 13:36 Mites muuten sen alkuperäinen juttu eli eri lähteiden XOR, oliko se tuossa toteutettu?
Sitä ei ole tässä. Odottelen kolmatta lähdettä saapuvaksi, niin sitten tehdään kolmella lähteellä oikein viimeisen päälle satunnaislukuja ja uusi ohjelma niitä varten. Tavoitteena on se, että tämä noppaskripti (muokattuna) toimii luotettavasti.🤣

Koodi: Valitse kaikki

#!/bin/bash

# Tarkista että parametri on oikeanmuotoinen
if [[ ! "$1" =~ ^([1-9][0-9]*)[dD]([1-9][0-9]*)$ ]]; then
echo "Käyttö: $0 <XdY> (esim. 3D20 tai 4d100)"
exit 1
fi

NUM_DICE=${BASH_REMATCH[1]}
NUM_SIDES=${BASH_REMATCH[2]}

# Varmistetaan, että /dev/hwrng on luettavissa
if [ ! -r /dev/hwrng ]; then
echo "/dev/hwrng ei ole luettavissa"
exit 1
fi

echo "Heitetään $NUM_DICE D$NUM_SIDES noppaa:"
for ((i = 1; i <= NUM_DICE; i++)); do
# Luetaan 2 tavua = 16-bittinen luku
RAND=$(od -An -N2 -tu2 /dev/hwrng | tr -d ' ')
# Muutetaan luku noppaväliin
RESULT=$(( (RAND % NUM_SIDES) + 1 ))
echo "Heitto $i: $RESULT"
done
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Lainaa
Re: Matikkaa tietokoneella
QS kirjoitti: 19.5.2025, 13:36 Mites muuten sen alkuperäinen juttu eli eri lähteiden XOR, oliko se tuossa toteutettu?
Kun kysyit, niin pakkohan se oli tehdä.🤣
Tässä se nyt on ja testasin sen verran, että se kääntyy virheittä. Kunhan se Infinite Noise TRNG tulee niin sitten päästään testaan. Veikkaan että vaatii monta korjausta ennen kun alkaa toimimaan ja TPM sirun takia tulee olemaan todella hidas. En kehdannut pelkkää XOR:ia käyttää, vaan sekoitus tapahtuu SHAKE-256 algoritmilla.

Koodi: Valitse kaikki

/*
* Satunnaislukugeneraattori, joka yhdistää TPM 2.0:n, RDSEED:n ja Infinite Noise TRNG:n.
* Tulostaa 32 tavua per iteraatio stdout:iin, sopii rngtest- tai dieharder-testaukseen.
*
* Käännös:
* gcc -O3 -mrdrnd -mrdseed -o random_mix random_mix.c -lssl -lcrypto -ltss2-esys -ltss2-tcti-device
*
* Käyttö:
* ./random_mix [/dev/ttyACM0] | rngtest -c 1000
* ./random_mix [/dev/ttyACM0] | dieharder -a -g 200
*
* Testausvaatimukset:
* - rngtest: Vaatii noin 100 kt dataa perustesteihin.
* - dieharder: Vaatii useita megatavuja kattaviin testeihin; voi kestää TPM/TRNG:n hitauden vuoksi.
* - Varmista, että /dev/ttyACM0 on oikea TRNG-laitteen polku (tarkista: ls -l /dev/ttyACM*).
* - Tarkista RDSEED-tuki: cat /proc/cpuinfo | grep rdseed
*
* Huomautukset:
* - Paina Ctrl+C keskeyttääksesi ohjelman siististi (SIGINT/SIGTERM).
* - Virheilmoitukset rajoitettu 10:een per lähde stderr-tulvan välttämiseksi.
* - Herkät puskurit tyhjennetään explicit_bzero:lla jokaisen iteraation jälkeen.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <tss2/tss2_esys.h>
#include <signal.h>

// Määritä koot
#define BYTES_PER_SOURCE 32 // Tavuja luetaan kustakin lähteestä
#define OUTPUT_BYTES 32 // Tulostettavien tavujen määrä per iteraatio
#define MAX_ERRORS_PER_SOURCE 10 // Enimmäismäärä virheilmoituksia per lähde
#define MAX_ERRORS_PER_ITERATION 5 // Enimmäismäärä virheitä per lähde per iteraatio
#define RDSEED_RETRIES 10 // RDSEED:n yritysten enimmäismäärä per 64-bittinen luku
#define TRNG_RETRIES 100 // TRNG:n yritysten enimmäismäärä (noin 1 s, 10 ms viive)

// Virhelaskurit
static int tpm_errors = 0, rdseed_errors = 0, trng_errors = 0;
static int tpm_iter_errors = 0, rdseed_iter_errors = 0, trng_iter_errors = 0;

// Signaalikäsittely
static volatile sig_atomic_t terminate = 0;
static ESYS_CONTEXT *global_ctx = NULL;
static unsigned char *tpm_data = NULL, *rdseed_data = NULL, *trng_data = NULL;
static unsigned char *input = NULL, *output = NULL;

// Signaalikäsittelijä SIGINT:lle ja SIGTERM:lle
static void handle_signal(int sig) {
(void)sig; // Estä varoitus käyttämättömästä parametrista
terminate = 1;
}

// Siivousfunktio herkille tiedoille ja resursseille
static void cleanup(void) {
if (tpm_data) explicit_bzero(tpm_data, BYTES_PER_SOURCE);
if (rdseed_data) explicit_bzero(rdseed_data, BYTES_PER_SOURCE);
if (trng_data) explicit_bzero(trng_data, BYTES_PER_SOURCE);
if (input) explicit_bzero(input, 3 * BYTES_PER_SOURCE);
if (output) explicit_bzero(output, OUTPUT_BYTES);
if (global_ctx) Esys_Finalize(&global_ctx);
}

// Funktio RDSEED-tuen tarkistamiseen
static int check_rdseed_support(void) {
unsigned int eax, ebx, ecx, edx;
asm volatile(
"cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(7), "c"(0)
);
return (ebx & (1 << 18)) != 0;
}

// Funktio RDSEED:n lukemiseen inline-assemblyllä ja uudelleenyrityksillä
static int read_from_rdseed(unsigned char *buf, size_t len) {
size_t i = 0;
while (i < len) {
unsigned long long rand64;
int retries = 0;
unsigned char success;
do {
asm volatile(
"rdseed %1; "
"setc %0"
: "=qm"(success), "=r"(rand64)
:
: "cc"
);
if (success) {
break;
}
retries++;
if (retries >= RDSEED_RETRIES) {
if (rdseed_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "RDSEED epäonnistui %d yrityksen jälkeen\n", RDSEED_RETRIES);
rdseed_errors++;
}
rdseed_iter_errors++;
return -1;
}
usleep(10000); // 10 ms viive
} while (1);
size_t to_copy = (len - i >= 8) ? 8 : (len - i);
memcpy(buf + i, &rand64, to_copy);
i += to_copy;
}
return 0;
}

// Funktio TPM:stä lukemiseen
static int read_from_tpm(ESYS_CONTEXT *ctx, unsigned char *buf, size_t len) {
TPM2B_DIGEST *random_bytes = NULL;
TSS2_RC rc = Esys_GetRandom(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, len, &random_bytes);
if (rc != TSS2_RC_SUCCESS || random_bytes->size != len) {
if (tpm_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "Ei voitu lukea %zu tavua TPM:stä: RC=0x%x\n", len, rc);
tpm_errors++;
}
free(random_bytes);
tpm_iter_errors++;
return -1;
}
memcpy(buf, random_bytes->buffer, len);
free(random_bytes);
return 0;
}

// Funktio Infinite Noise TRNG:stä lukemiseen ei-blokkaavalla tavalla ja uudelleenyrityksillä
static int read_from_trng(unsigned char *buf, size_t len, const char *device) {
int fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
if (trng_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "TRNG-laitteen %s avaaminen epäonnistui: %s\n", device, strerror(errno));
trng_errors++;
}
trng_iter_errors++;
return -1;
}

ssize_t bytes_read = 0;
int retries = 0;
while (bytes_read < len && !terminate) {
ssize_t n = read(fd, buf + bytes_read, len - bytes_read);
if (n > 0) {
bytes_read += n;
retries = 0;
} else if (n == 0) {
if (trng_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "TRNG-laitteen tiedosto päättyi\n");
trng_errors++;
}
close(fd);
trng_iter_errors++;
return -1;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (retries >= TRNG_RETRIES) {
if (trng_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "TRNG-lukeminen aikakatkaistiin %d yrityksen jälkeen\n", TRNG_RETRIES);
trng_errors++;
}
close(fd);
trng_iter_errors++;
return -1;
}
usleep(10000); // 10 ms viive
retries++;
} else {
if (trng_errors < MAX_ERRORS_PER_SOURCE) {
fprintf(stderr, "TRNG:stä lukeminen epäonnistui: %s\n", strerror(errno));
trng_errors++;
}
close(fd);
trng_iter_errors++;
return -1;
}
}
}
close(fd);
return terminate ? -1 : 0;
}

int main(int argc, char *argv) {
// Aseta TRNG-laitteen polku (oletus: /dev/ttyACM0)
const char *trng_device = (argc > 1) ? argv[1] : "/dev/ttyACM0";

// Tarkista RDSEED-tuki
if (!check_rdseed_support()) {
fprintf(stderr, "RDSEED ei ole tuettu tässä prosessorissa\n");
return 1;
}

// Aseta signaalikäsittelijät SIGINT:lle ja SIGTERM:lle
struct sigaction sa;
sa.sa_handler = handle_signal;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) {
fprintf(stderr, "Signaalikäsittelijän asettaminen epäonnistui\n");
return 1;
}

// Alusta TPM-konteksti
TSS2_RC rc = Esys_Initialize(&global_ctx, NULL, NULL);
if (rc != TSS2_RC_SUCCESS) {
fprintf(stderr, "TPM:n alustaminen epäonnistui: RC=0x%x\n", rc);
return 1;
}

// Aseta stdout puskuroimattomaksi
setbuf(stdout, NULL);

// Varaa puskurit
tpm_data = malloc(BYTES_PER_SOURCE);
rdseed_data = malloc(BYTES_PER_SOURCE);
trng_data = malloc(BYTES_PER_SOURCE);
input = malloc(3 * BYTES_PER_SOURCE);
output = malloc(OUTPUT_BYTES);
if (!tpm_data || !rdseed_data || !trng_data || !input || !output) {
fprintf(stderr, "Muistin varaus epäonnistui\n");
cleanup();
return 1;
}

while (!terminate) {
// Nollaa iteraation virhelaskurit
tpm_iter_errors = 0;
rdseed_iter_errors = 0;
trng_iter_errors = 0;

// Lue kaikista lähteistä
if (read_from_tpm(global_ctx, tpm_data, BYTES_PER_SOURCE) != 0) {
if (tpm_iter_errors >= MAX_ERRORS_PER_ITERATION) {
fprintf(stderr, "Liian monta TPM-virhettä yhdessä iteraatiossa\n");
break;
}
usleep(100000); // 100 ms viive
continue;
}
if (read_from_rdseed(rdseed_data, BYTES_PER_SOURCE) != 0) {
if (rdseed_iter_errors >= MAX_ERRORS_PER_ITERATION) {
fprintf(stderr, "Liian monta RDSEED-virhettä yhdessä iteraatiossa\n");
break;
}
usleep(100000); // 100 ms viive
continue;
}
if (read_from_trng(trng_data, BYTES_PER_SOURCE, trng_device) != 0) {
if (trng_iter_errors >= MAX_ERRORS_PER_ITERATION) {
fprintf(stderr, "Liian monta TRNG-virhettä yhdessä iteraatiossa\n");
break;
}
usleep(100000); // 100 ms viive
continue;
}

// Yhdistä data
memcpy(input, tpm_data, BYTES_PER_SOURCE);
memcpy(input + BYTES_PER_SOURCE, rdseed_data, BYTES_PER_SOURCE);
memcpy(input + 2 * BYTES_PER_SOURCE, trng_data, BYTES_PER_SOURCE);

// Laske SHAKE-256
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (!md_ctx || EVP_DigestInit_ex(md_ctx, EVP_shake256(), NULL) != 1 ||
EVP_DigestUpdate(md_ctx, input, 3 * BYTES_PER_SOURCE) != 1 ||
EVP_DigestFinalXOF(md_ctx, output, OUTPUT_BYTES) != 1) {
fprintf(stderr, "SHAKE-256-laskenta epäonnistui\n");
if (md_ctx) EVP_MD_CTX_free(md_ctx);
usleep(100000); // 100 ms viive
continue;
}
EVP_MD_CTX_free(md_ctx);

// Kirjoita stdout:iin ja tyhjennä puskuri suurille testeille
fwrite(output, 1, OUTPUT_BYTES, stdout);
fflush(stdout); // Varmista datan kirjoitus testityökaluille

// Tyhjennä herkät tiedot jokaisen iteraation jälkeen
explicit_bzero(tpm_data, BYTES_PER_SOURCE);
explicit_bzero(rdseed_data, BYTES_PER_SOURCE);
explicit_bzero(trng_data, BYTES_PER_SOURCE);
explicit_bzero(input, 3 * BYTES_PER_SOURCE);
explicit_bzero(output, OUTPUT_BYTES);

// Lyhyt viive CPU-kuorman vähentämiseksi
usleep(1000); // 1 ms
}

// Lopullinen siivous
cleanup();
free(tpm_data);
free(rdseed_data);
free(trng_data);
free(input);
free(output);

// Tulosta virheyhteenveto
fprintf(stderr, "Ohjelma päättynyt. Virheyhteenveto:\n");
fprintf(stderr, "TPM-virheitä: %d\n", tpm_errors);
fprintf(stderr, "RDSEED-virheitä: %d\n", rdseed_errors);
fprintf(stderr, "TRNG-virheitä: %d\n", trng_errors);
fprintf(stderr, "Ohjelma keskeytetty %s-signaalilla\n", terminate ? "SIGINT/SIGTERM" : "virheen vuoksi");

return terminate ? 0 : 1;
}
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Lainaa
Re: Matikkaa tietokoneella
En tiennytkään, että Yubico YubiKey 5 sisältää raudalla toteutetun satunnaislukugeneraattorin jota voi komentaa käyttiksestä käsin. Sen toki tiesin että sen sisällä jonkinmoinen pitää olla kun se osaa tehdä suojausavaimia. No Linux maailmassa se menee esimerkiksi näin:ykman openpgp generate-random --bytes 16 kunhan on asentanut tarpeelliset softat. Niitä saa sieltä 0.5–2 kB/s parhaimmillaan, mutta yleensä vähemmän eli helkutin hitaasti. No mutta mulla on ylimääräinen Yubico YubiKey 5 NFC niin tästä saadaan neljäs lähde.🤣 Testailin noita Linux vakio satunnaislähteitäkin (/dev/urandom sekä /dev/random) ja ne tuntuu toimivan paremmin kun mun omat viritykset, mutta parannetaan omia. Käänsin NIST STS:n niin alkaa olla testaustyökalut kohdillaan.🤣

Siinä on vissiin NXP A700x -sarjan salauspiiri mikä on aika hyvä kun speksejä katsoo.
https://media.digikey.com/pdf/Data%20Sh ... Rev3.1.pdf
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Lainaa
Re: Matikkaa tietokoneella
Siinä se nyt on. Katsotaan mitä saadaan aikaiseksi. Kukaan ei ole vielä kysynyt, että mihin näitä satunnaislukuja tarvitaan paitsi virtuaaliseen nopan heittoon. No vastaan vaikka ei kysyttykään. Asia selviää oheisesta ohjelmasta.🤣

Koodi: Valitse kaikki

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

int main(int argc, char *argv[]) {
    // Määritä korttien lukumäärä ja korttitaulukko paikallisesti
    static const int NUM_CARDS = 78;
    static const char *tarot_cards[] = {
        "Narri", "Taikuri", "Ylipapitar", "Keisarinna", "Keisari",
        "Ylipappi", "Rakastavaiset", "Vaunut", "Voima", "Erakko",
        "Onnenpyörä", "Oikeus", "Hirtetty", "Kuolema", "Kohtuus",
        "Paholainen", "Torni", "Tähti", "Kuu", "Aurinko",
        "Tuomio", "Maailma",
        "Sauvojen Ässä", "Sauvojen Kakkonen", "Sauvojen Kolmonen", "Sauvojen Nelonen", "Sauvojen Vitonen",
        "Sauvojen Kutonen", "Sauvojen Seiska", "Sauvojen Kasi", "Sauvojen Ysi", "Sauvojen Kymppi",
        "Sauvojen Lähetti", "Sauvojen Ritari", "Sauvojen Kuningatar", "Sauvojen Kuningas",
        "Maljojen Ässä", "Maljojen Kakkonen", "Maljojen Kolmonen", "Maljojen Nelonen", "Maljojen Vitonen",
        "Maljojen Kutonen", "Maljojen Seiska", "Maljojen Kasi", "Maljojen Ysi", "Maljojen Kymppi",
        "Maljojen Lähetti", "Maljojen Ritari", "Maljojen Kuningatar", "Maljojen Kuningas",
        "Miekkojen Ässä", "Miekkojen Kakkonen", "Miekkojen Kolmonen", "Miekkojen Nelonen", "Miekkojen Vitonen",
        "Miekkojen Kutonen", "Miekkojen Seiska", "Miekkojen Kasi", "Miekkojen Ysi", "Miekkojen Kymppi",
        "Miekkojen Lähetti", "Miekkojen Ritari", "Miekkojen Kuningatar", "Miekkojen Kuningas",
        "Lanttien Ässä", "Lanttien Kakkonen", "Lanttien Kolmonen", "Lanttien Nelonen", "Lanttien Vitonen",
        "Lanttien Kutonen", "Lanttien Seiska", "Lanttien Kasi", "Lanttien Ysi", "Lanttien Kymppi",
        "Lanttien Lähetti", "Lanttien Ritari", "Lanttien Kuningatar", "Lanttien Kuningas"
    };

    // Tarkista komentorivin argumentit
    if (argc != 2) {
        fprintf(stderr, "Käyttö: %s <korttien_lukumäärä>\n", argv[0]);
        return 1;
    }

    // Muunna argumentti kokonaisluvuksi
    char *endptr;
    long num_cards = strtol(argv[1], &endptr, 10);
    if (*endptr != '\0' || num_cards <= 0 || num_cards > NUM_CARDS) {
        fprintf(stderr, "Virhe: Korttien lukumäärän on oltava positiivinen kokonaisluku <= %d\n", NUM_CARDS);
        return 1;
    }

    // Avaa /dev/random
    int fd = open("/dev/random", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "Virhe avattaessa /dev/random: %s\n", strerror(errno));
        return 1;
    }

    // Alusta korttipakka
    int deck[NUM_CARDS];
    for (int i = 0; i < NUM_CARDS; i++) {
        deck[i] = i;
    }

    // Fisher-Yates-sekoitus
    for (int i = NUM_CARDS - 1; i > 0; i--) {
        unsigned int random_value;
        ssize_t bytes_read;
        unsigned int current_max = UINT_MAX - (UINT_MAX % (i + 1)); // Bias-korjaus jokaiselle i:lle

        // Lue satunnaislukuja, kunnes saadaan kelvollinen arvo
        while (1) {
            bytes_read = read(fd, &random_value, sizeof(random_value));
            if (bytes_read < 0 && errno == EINTR) continue; // Uudelleenyritys keskeytyksen jälkeen
            if (bytes_read != sizeof(random_value)) {
                fprintf(stderr, "Virhe luettaessa /dev/random: %s\n", strerror(errno));
                close(fd);
                return 1;
            }
            if (random_value <= current_max) break; // Kelvollinen arvo
        }

        // Määritä satunnaisluku indeksiksi (0..i)
        unsigned int j = random_value % (i + 1);

        // Vaihda kortit
        int temp = deck[i];
        deck[i] = deck[j];
        deck[j] = temp;
    }

    // Tulosta ensimmäiset num_cards korttia
    for (int i = 0; i < num_cards; i++) {
        printf("%s\n", tarot_cards[deck[i]]);
    }

    // Sulje /dev/random
    close(fd);
    return 0;
}
20250521_102722.jpg
20250521_102722.jpg (71.09 KiB) Katsottu 291 kertaa
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Lainaa
Re: Matikkaa tietokoneella
Wikipediasta huomasin, että siellä on jokaisesta tarot-kortista kuva ja vieläpä siitä ainoasta oikeasta Rider–Waite pakasta. No minähän kopioin kuvat itselleni ja muokkasin virtuaalisen tarot-pakan tulostamaan pakasta nostetut kortit sixel grafiikkana suoraan konsoliin.🤣

Käytän Fisher Yates algoritmia jonka ei pitäisi tuottaa biasta, mutta auditoikaa toki jos muuta epäilette.

Koodi: Valitse kaikki

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/stat.h>
#include <termios.h>

// Tarot-korttien tiedot
typedef struct {
const char *name_fi; // Kortin nimi suomeksi
char *image_file; // Kuvatiedoston absoluuttinen polku
} Card;

// Staattiset muuttujat resurssien hallintaan
static int random_fd = -1;
static Card *tarot_cards = NULL;
static int total_cards = 0;
static char **temp_files = NULL;
static int temp_files_count = 0;

// Siivoa resurssit
static void cleanup(void) {
if (random_fd >= 0) {
close(random_fd);
random_fd = -1;
}
if (tarot_cards) {
for (int i = 0; i < total_cards; i++) {
free(tarot_cards[i].image_file);
tarot_cards[i].image_file = NULL;
}
}
if (temp_files) {
for (int i = 0; i < temp_files_count; i++) {
if (temp_files[i]) {
unlink(temp_files[i]);
free(temp_files[i]);
}
}
free(temp_files);
temp_files = NULL;
temp_files_count = 0;
}
}

// Käsittele keskeytyssignaalit
static void cleanup_and_exit(int signo) {
cleanup();
const char msg = "\nOhjelma keskeytettiin. Resurssit siivottu.\n";
write(STDERR_FILENO, msg, sizeof(msg) - 1);
_exit(EXIT_FAILURE);
}

// Tarkista, onko komento saatavilla PATH-ympäristömuuttujassa
static int check_command(const char *cmd) {
const char *path = getenv("PATH");
if (!path || !*path) {
fprintf(stderr, "Virhe: PATH-ympäristömuuttuja on tyhjä\n");
return 0;
}
char *paths = strdup(path);
if (!paths) {
fprintf(stderr, "Virhe: Muistin varaus epäonnistui PATH-tarkistuksessa\n");
return 0;
}
char *dir = strtok(paths, ":");
while (dir) {
if (dir[0] == '.' || strstr(dir, "..") || !*dir || strchr(dir, '\n')) {
dir = strtok(NULL, ":");
continue;
}
char full_path[PATH_MAX];
if (snprintf(full_path, sizeof(full_path), "%s/%s", dir, cmd) >= sizeof(full_path)) {
dir = strtok(NULL, ":");
continue;
}
if (access(full_path, X_OK) == 0) {
free(paths);
return 1;
}
dir = strtok(NULL, ":");
}
free(paths);
return 0;
}

// Luo väliaikainen tiedosto
static char *create_temp_file(void) {
char *template = strdup("temp_row_XXXXXX.jpg");
if (!template) {
fprintf(stderr, "Virhe: Muistin varaus väliaikaiselle tiedostolle epäonnistui\n");
return NULL;
}
int fd = mkstemps(template, 4);
if (fd == -1) {
fprintf(stderr, "Virhe: Väliaikaisen tiedoston luonti epäonnistui: %s\n", strerror(errno));
free(template);
return NULL;
}
close(fd);
temp_files = realloc(temp_files, (temp_files_count + 1) * sizeof(char *));
if (!temp_files) {
fprintf(stderr, "Virhe: Muistin varaus väliaikaisille tiedostoille epäonnistui\n");
unlink(template);
free(template);
return NULL;
}
temp_files[temp_files_count++] = template;
return template;
}

int main(int argc, char *argv) {
// Aseta signaalinkäsittelijät
struct sigaction sa;
sa.sa_handler = cleanup_and_exit;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGINT, &sa, NULL) == -1 ||
sigaction(SIGTERM, &sa, NULL) == -1 ||
sigaction(SIGHUP, &sa, NULL) == -1) {
fprintf(stderr, "Virhe: Signaalinkäsittelijän asettaminen epäonnistui: %s\n", strerror(errno));
return 1;
}

// Tallenna terminaalin asetukset
struct termios original_termios;
int termios_saved = 0;
#ifdef POSIX
if (isatty(fileno(stdin))) {
if (tcgetattr(fileno(stdin), &original_termios) == 0) {
termios_saved = 1;
} else {
fprintf(stderr, "Varoitus: Terminaalin asetusten tallentaminen epäonnistui: %s\n", strerror(errno));
}
}
#endif

// Määritä tarot-kortit
Card local_tarot_cards = {
{"Narri", NULL}, {"Taikuri", NULL}, {"Ylipapitar", NULL}, {"Keisarinna", NULL},
{"Keisari", NULL}, {"Ylipappi", NULL}, {"Rakastavaiset", NULL}, {"Vaunut", NULL},
{"Voima", NULL}, {"Erakko", NULL}, {"Onnenpyörä", NULL}, {"Oikeus", NULL},
{"Hirtetty", NULL}, {"Kuolema", NULL}, {"Kohtuus", NULL}, {"Paholainen", NULL},
{"Torni", NULL}, {"Tähti", NULL}, {"Kuu", NULL}, {"Aurinko", NULL},
{"Tuomio", NULL}, {"Maailma", NULL}, {"Sauvojen Ässä", NULL}, {"Sauvojen Kakkonen", NULL},
{"Sauvojen Kolmonen", NULL}, {"Sauvojen Nelonen", NULL}, {"Sauvojen Vitonen", NULL},
{"Sauvojen Kutonen", NULL}, {"Sauvojen Seiska", NULL}, {"Sauvojen Kasi", NULL},
{"Sauvojen Ysi", NULL}, {"Sauvojen Kymppi", NULL}, {"Sauvojen Lähetti", NULL},
{"Sauvojen Ritari", NULL}, {"Sauvojen Kuningatar", NULL}, {"Sauvojen Kuningas", NULL},
{"Maljojen Ässä", NULL}, {"Maljojen Kakkonen", NULL}, {"Maljojen Kolmonen", NULL},
{"Maljojen Nelonen", NULL}, {"Maljojen Vitonen", NULL}, {"Maljojen Kutonen", NULL},
{"Maljojen Seiska", NULL}, {"Maljojen Kasi", NULL}, {"Maljojen Ysi", NULL},
{"Maljojen Kymppi", NULL}, {"Maljojen Lähetti", NULL}, {"Maljojen Ritari", NULL},
{"Maljojen Kuningatar", NULL}, {"Maljojen Kuningas", NULL}, {"Miekkojen Ässä", NULL},
{"Miekkojen Kakkonen", NULL}, {"Miekkojen Kolmonen", NULL}, {"Miekkojen Nelonen", NULL},
{"Miekkojen Vitonen", NULL}, {"Miekkojen Kutonen", NULL}, {"Miekkojen Seiska", NULL},
{"Miekkojen Kasi", NULL}, {"Miekkojen Ysi", NULL}, {"Miekkojen Kymppi", NULL},
{"Miekkojen Lähetti", NULL}, {"Miekkojen Ritari", NULL}, {"Miekkojen Kuningatar", NULL},
{"Miekkojen Kuningas", NULL}, {"Lanttien Ässä", NULL}, {"Lanttien Kakkonen", NULL},
{"Lanttien Kolmonen", NULL}, {"Lanttien Nelonen", NULL}, {"Lanttien Vitonen", NULL},
{"Lanttien Kutonen", NULL}, {"Lanttien Seiska", NULL}, {"Lanttien Kasi", NULL},
{"Lanttien Ysi", NULL}, {"Lanttien Kymppi", NULL}, {"Lanttien Lähetti", NULL},
{"Lanttien Ritari", NULL}, {"Lanttien Kuningatar", NULL}, {"Lanttien Kuningas", NULL}
};
total_cards = sizeof(local_tarot_cards) / sizeof(local_tarot_cards[0]);
tarot_cards = local_tarot_cards;

// Hae images-hakemisto
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) == NULL) {
fprintf(stderr, "Virhe: Nykyisen hakemiston hakeminen epäonnistui: %s\n", strerror(errno));
cleanup();
return 1;
}
char images_dir[PATH_MAX];
if (snprintf(images_dir, sizeof(images_dir), "%s/images", cwd) >= sizeof(images_dir)) {
fprintf(stderr, "Virhe: Kuvatiedostojen hakemistopolku on liian pitkä\n");
cleanup();
return 1;
}
struct stat st;
if (stat(images_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
fprintf(stderr, "Virhe: Kuvatiedostojen hakemistoa ei löydy tai se ei ole hakemisto: %s\n", images_dir);
cleanup();
return 1;
}

// Alusta kuvatiedostojen polut
const char *image_files = {
"RWS_Tarot_00_Fool.jpg", "RWS_Tarot_01_Magician.jpg", "RWS_Tarot_02_High_Priestess.jpg",
"RWS_Tarot_03_Empress.jpg", "RWS_Tarot_04_Emperor.jpg", "RWS_Tarot_05_Hierophant.jpg",
"RWS_Tarot_06_Lovers.jpg", "RWS_Tarot_07_Chariot.jpg", "RWS_Tarot_08_Strength.jpg",
"RWS_Tarot_09_Hermit.jpg", "RWS_Tarot_10_Wheel_of_Fortune.jpg", "RWS_Tarot_11_Justice.jpg",
"RWS_Tarot_12_Hanged_Man.jpg", "RWS_Tarot_13_Death.jpg", "RWS_Tarot_14_Temperance.jpg",
"RWS_Tarot_15_Devil.jpg", "RWS_Tarot_16_Tower.jpg", "RWS_Tarot_17_Star.jpg",
"RWS_Tarot_18_Moon.jpg", "RWS_Tarot_19_Sun.jpg", "RWS_Tarot_20_Judgement.jpg",
"RWS_Tarot_21_World.jpg", "Wands01.jpg", "Wands02.jpg", "Wands03.jpg", "Wands04.jpg",
"Wands05.jpg", "Wands06.jpg", "Wands07.jpg", "Wands08.jpg", "Wands09.jpg", "Wands10.jpg",
"Wands11.jpg", "Wands12.jpg", "Wands13.jpg", "Wands14.jpg", "Cups01.jpg", "Cups02.jpg",
"Cups03.jpg", "Cups04.jpg", "Cups05.jpg", "Cups06.jpg", "Cups07.jpg", "Cups08.jpg",
"Cups09.jpg", "Cups10.jpg", "Cups11.jpg", "Cups12.jpg", "Cups13.jpg", "Cups14.jpg",
"Swords01.jpg", "Swords02.jpg", "Swords03.jpg", "Swords04.jpg", "Swords05.jpg", "Swords06.jpg",
"Swords07.jpg", "Swords08.jpg", "Swords09.jpg", "Swords10.jpg", "Swords11.jpg", "Swords12.jpg",
"Swords13.jpg", "Swords14.jpg", "Pents01.jpg", "Pents02.jpg", "Pents03.jpg", "Pents04.jpg",
"Pents05.jpg", "Pents06.jpg", "Pents07.jpg", "Pents08.jpg", "Pents09.jpg", "Pents10.jpg",
"Pents11.jpg", "Pents12.jpg", "Pents13.jpg", "Pents14.jpg"
};
if (sizeof(image_files) / sizeof(image_files[0]) != total_cards) {
fprintf(stderr, "Virhe: Kuvatiedostojen määrä (%zu) ei vastaa korttien määrää (%d)\n",
sizeof(image_files) / sizeof(image_files[0]), total_cards);
cleanup();
return 1;
}
for (int i = 0; i < total_cards; i++) {
size_t dir_len = strlen(images_dir);
size_t file_len = strlen(image_files[i]);
if (dir_len + file_len + 2 > PATH_MAX) {
fprintf(stderr, "Virhe: Kuvatiedoston polku kortille %d on liian pitkä\n", i + 1);
cleanup();
return 1;
}
tarot_cards[i].image_file = malloc(dir_len + file_len + 2);
if (!tarot_cards[i].image_file) {
fprintf(stderr, "Virhe: Muistin varaus epäonnistui kortin %d kuvatiedostopolulle\n", i + 1);
cleanup();
return 1;
}
snprintf(tarot_cards[i].image_file, dir_len + file_len + 2, "%s/%s", images_dir, image_files[i]);
}

// Tarkista komentorivin argumentit
int num_cards = 3; // Oletusarvo 3 korttia
if (argc > 1) {
char *endptr;
errno = 0;
long long_value = strtol(argv[1], &endptr, 10);
if (*endptr != '\0' || errno != 0 || long_value < 1 || long_value > 6) {
fprintf(stderr, "Virhe: Korttien määrän tulee olla kokonaisluku väliltä 1-6\n");
cleanup();
return 1;
}
num_cards = (int)long_value;
}

// Tarkista tarvittavat komennot
if (!check_command("montage")) {
fprintf(stderr, "Virhe: montage-ohjelmaa ei ole asennettuna\n");
cleanup();
return 1;
}
if (!check_command("img2sixel")) {
fprintf(stderr, "Virhe: img2sixel-ohjelmaa ei ole asennettuna\n");
cleanup();
return 1;
}

// Avaa /dev/random satunnaislukuja varten
random_fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
if (random_fd < 0) {
fprintf(stderr, "Virhe: /dev/random-tiedoston avaaminen epäonnistui: %s\n", strerror(errno));
cleanup();
return 1;
}
struct stat random_stat;
if (fstat(random_fd, &random_stat) == -1) {
fprintf(stderr, "Virhe: /dev/random-tiedoston tilan tarkistus epäonnistui: %s\n", strerror(errno));
cleanup();
return 1;
}

// Alusta korttipakka sekoitusta varten
int deck[total_cards];
for (int i = 0; i < total_cards; i++) {
deck[i] = i;
}

// Sekoita pakka Fisher-Yates-algoritmilla
for (int i = total_cards - 1; i > 0; i--) {
unsigned int random_value;
ssize_t bytes_read;
unsigned int current_max = UINT_MAX - (UINT_MAX % (unsigned int)(i + 1));
while (1) {
bytes_read = read(random_fd, &random_value, sizeof(random_value));
if (bytes_read < 0 && errno == EINTR) continue;
if (bytes_read != sizeof(random_value)) {
fprintf(stderr, "Virhe: /dev/random-tiedoston lukeminen epäonnistui: %s\n", strerror(errno));
cleanup();
return 1;
}
if (random_value <= current_max) break;
}
unsigned int j = random_value % (unsigned int)(i + 1);
int temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}

// Tarkista kuvatiedostojen saatavuus
for (int i = 0; i < num_cards; i++) {
int image_fd = open(tarot_cards[deck[i]].image_file, O_RDONLY | O_CLOEXEC);
if (image_fd == -1) {
fprintf(stderr, "Virhe: Kuvatiedostoa %s ei voitu avata: %s\n",
tarot_cards[deck[i]].image_file, strerror(errno));
cleanup();
return 1;
}
close(image_fd);
}

// Tulosta kaikkien korttien nimet
printf("Nostetut kortit:\n");
for (int i = 0; i < num_cards; i++) {
printf("%s\n", tarot_cards[deck[i]].name_fi);
}
printf("\n");

// Määritä asettelu
int rows;
int *cards_per_row = NULL;
if (num_cards <= 3) {
rows = 1;
cards_per_row = malloc(sizeof(int));
if (!cards_per_row) {
fprintf(stderr, "Virhe: Muistin varaus epäonnistui\n");
cleanup();
return 1;
}
cards_per_row[0] = num_cards;
} else {
rows = 2;
cards_per_row = malloc(2 * sizeof(int));
if (!cards_per_row) {
fprintf(stderr, "Virhe: Muistin varaus epäonnistui\n");
cleanup();
return 1;
}
cards_per_row[0] = (num_cards + 1) / 2; // Ensimmäiselle riville puolet kortteja (pyöristettynä ylös)
cards_per_row[1] = num_cards - cards_per_row[0]; // Loput toiselle riville
}

// Näytä kortit riveittäin
int card_index = 0;
for (int row = 0; row < rows; row++) {
int num_cards_in_row = cards_per_row[row];

// Luo väliaikainen tiedosto riville
char *temp_file = create_temp_file();
if (!temp_file) {
free(cards_per_row);
cleanup();
return 1;
}

// Rakenna montage-komento
char tile[16];
snprintf(tile, sizeof(tile), "%dx1", num_cards_in_row);
char *montage_args[32];
int arg_count = 0;
montage_args[arg_count++] = "montage";
for (int i = 0; i < num_cards_in_row; i++) {
montage_args[arg_count++] = tarot_cards[deck[card_index + i]].image_file;
}
montage_args[arg_count++] = "-geometry";
montage_args[arg_count++] = "+5+5";
montage_args[arg_count++] = "-tile";
montage_args[arg_count++] = tile;
montage_args[arg_count++] = temp_file;
montage_args[arg_count++] = NULL;

// Suorita montage
pid_t pid = fork();
if (pid == -1) {
fprintf(stderr, "Virhe: Prosessin luonti epäonnistui: %s\n", strerror(errno));
free(cards_per_row);
cleanup();
return 1;
} else if (pid == 0) {
execvp("montage", montage_args);
fprintf(stderr, "Virhe: montage-komennon suoritus epäonnistui: %s\n", strerror(errno));
cleanup();
_exit(EXIT_FAILURE);
} else {
int status;
while (waitpid(pid, &status, 0) == -1 && errno == EINTR);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fprintf(stderr, "Virhe: montage epäonnistui riville %d (paluukoodi %d)\n", row + 1, WEXITSTATUS(status));
free(cards_per_row);
cleanup();
return 1;
}
}

// Näytä yhdistetty kuva img2sixel-komennolla
pid = fork();
if (pid == -1) {
fprintf(stderr, "Virhe: Prosessin luonti epäonnistui: %s\n", strerror(errno));
free(cards_per_row);
cleanup();
return 1;
} else if (pid == 0) {
char *args = {"img2sixel", temp_file, NULL};
execvp("img2sixel", args);
fprintf(stderr, "Virhe: img2sixel-komennon suoritus epäonnistui: %s\n", strerror(errno));
cleanup();
_exit(EXIT_FAILURE);
} else {
int status;
while (waitpid(pid, &status, 0) == -1 && errno == EINTR);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fprintf(stderr, "Virhe: img2sixel epäonnistui riville %d (paluukoodi %d)\n", row + 1, WEXITSTATUS(status));
free(cards_per_row);
cleanup();
return 1;
}
}
card_index += num_cards_in_row;
printf("\n");
}
free(cards_per_row);

// Palauta terminaalin asetukset
#ifdef POSIX
if (termios_saved) {
if (tcsetattr(fileno(stdin), TCSANOW, &original_termios) != 0) {
fprintf(stderr, "Varoitus: Terminaalin asetusten palauttaminen epäonnistui: %s\n", strerror(errno));
}
}
#endif

// Odota käyttäjän syötettä
if (isatty(fileno(stdin))) {
printf("Paina Enter jatkaaksesi...\n");
int c;
while ((c = getchar()) != EOF && c != '\n');
}

// Siivoa resurssit ja lopeta
cleanup();
return 0;
}
tarot.jpg
tarot.jpg (682.06 KiB) Katsottu 275 kertaa
[/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i]
Abezethibou·daemon unimanus et unialis·abyssorum legatus·cuius nomen terram scindit. In tenebris lucet·in luce obscuratur. Per fractas alas suadet·per manum perditam ligat.
Per sigillum Beelzebub·Abezethibou inferorum·per sanguinem et ignem·responde mihi!
Vastaa Viestiin