mirror of
https://git.sr.ht/~sircmpwn/mkproof
synced 2024-05-06 04:36:09 +02:00
184 lines
3.6 KiB
C
184 lines
3.6 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include "argon2.h"
|
|
#include "random.h"
|
|
#include "util.h"
|
|
#include "proof.h"
|
|
|
|
|
|
#define MAX_PIDS 512
|
|
|
|
static void
|
|
die(int check, char *why)
|
|
{
|
|
if (check) {
|
|
fprintf(stderr, "Error: %s\n", why);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_spinner(void)
|
|
{
|
|
static size_t i = 0;
|
|
const char *spinners[] = {"|", "/", "-", "\\"};
|
|
size_t nspinners = sizeof(spinners) / sizeof(*spinners);
|
|
|
|
fprintf(stderr, "\rRunning... %s", spinners[i % nspinners]);
|
|
|
|
i++;
|
|
}
|
|
|
|
static void
|
|
run_mkproof(argon2_context context, int digits)
|
|
{
|
|
unsigned char password[16];
|
|
unsigned char hash[32];
|
|
|
|
context.out = hash;
|
|
context.outlen = sizeof(hash);
|
|
context.pwd = password;
|
|
context.pwdlen = sizeof(password);
|
|
|
|
int valid = 0;
|
|
while (!valid) {
|
|
ssize_t b = get_random_bytes(password, sizeof(password));
|
|
assert(b == sizeof(password));
|
|
|
|
int r = argon2id_ctx(&context);
|
|
die(r != 0, "argon2id failed\n");
|
|
|
|
valid = hash_msb(hash) >= digits;
|
|
}
|
|
|
|
if (isatty(STDERR_FILENO)) {
|
|
fprintf(stderr, "\rHere is your proof:\n");
|
|
}
|
|
|
|
char proof[33];
|
|
enchex(password, sizeof(password), proof, sizeof(proof));
|
|
printf("%s\n", proof);
|
|
_exit(0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
#ifdef _SC_NPROCESSORS_ONLN
|
|
long nprocs = sysconf(_SC_NPROCESSORS_ONLN) - 1;
|
|
#else
|
|
long nprocs = 2;
|
|
#endif
|
|
|
|
char *endptr;
|
|
int c;
|
|
while ((c = getopt(argc, argv, "j:")) != -1) {
|
|
switch (c) {
|
|
case 'j':
|
|
nprocs = strtol(optarg, &endptr, 10);
|
|
die(*endptr, "error parsing nprocs");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((argc - optind) != 1) {
|
|
fprintf(stderr, "Usage: %s [-j nprocs] <challenge>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (nprocs <= 0 || nprocs > MAX_PIDS) {
|
|
fprintf(stderr, "please specify nprocs value between 1 and "
|
|
"%d\n", MAX_PIDS);
|
|
return 1;
|
|
}
|
|
|
|
int iters, memory, digits;
|
|
unsigned char salt[16];
|
|
|
|
char *challenge = argv[optind];
|
|
char *algo = strtok(challenge, ":");
|
|
if (strcmp(algo, "argon2id") != 0) {
|
|
fprintf(stderr, "Error: unknown challenge type %s\n", algo);
|
|
return 1;
|
|
}
|
|
|
|
char *iterstr = strtok(NULL, ":");
|
|
iters = strtoul(iterstr, &endptr, 10);
|
|
die(*endptr, "Invalid challenge");
|
|
|
|
char *memorystr = strtok(NULL, ":");
|
|
memory = strtoul(memorystr, &endptr, 10);
|
|
die(*endptr, "Invalid challenge");
|
|
|
|
char *digitsstr = strtok(NULL, ":");
|
|
digits = strtoul(digitsstr, &endptr, 10);
|
|
die(*endptr, "Invalid challenge");
|
|
|
|
char *saltstr = strtok(NULL, ":");
|
|
die(strlen(saltstr) != 32, "Invalid challenge");
|
|
int r = dechex(saltstr, strlen(saltstr), salt, sizeof(salt));
|
|
die(r == -1, "Invalid challenge");
|
|
|
|
if (isatty(STDERR_FILENO)) {
|
|
// TODO: Provide a better estimate based on the difficulty
|
|
fprintf(stderr, "Generating a proof of work.\n");
|
|
fprintf(stderr, "This may take anywhere from several minutes to a few hours on slow computers.\n");
|
|
}
|
|
|
|
pid_t pids[MAX_PIDS];
|
|
assert(nprocs <= MAX_PIDS);
|
|
|
|
for (size_t i = 0; i < (size_t)nprocs; i++) {
|
|
pid_t pid = fork();
|
|
die(pid < 0, "fork()");
|
|
|
|
if (pid == 0) {
|
|
argon2_context context = {
|
|
.salt = salt,
|
|
.saltlen = sizeof(salt),
|
|
.t_cost = iters,
|
|
.m_cost = memory,
|
|
.lanes = 1,
|
|
.threads = 1,
|
|
.flags = ARGON2_DEFAULT_FLAGS,
|
|
.version = ARGON2_VERSION_NUMBER,
|
|
};
|
|
run_mkproof(context, digits);
|
|
// unreachable
|
|
assert(0);
|
|
}
|
|
|
|
pids[i] = pid;
|
|
}
|
|
|
|
pid_t pid;
|
|
while (1) {
|
|
int wstatus;
|
|
pid = waitpid(-1, &wstatus, WNOHANG);
|
|
if (pid > 0) {
|
|
break;
|
|
}
|
|
|
|
draw_spinner();
|
|
sleep(1);
|
|
}
|
|
|
|
for (size_t i = 0; i < (size_t)nprocs; i++) {
|
|
if (pids[i] == pid) {
|
|
continue;
|
|
}
|
|
|
|
r = kill(pids[i], SIGTERM);
|
|
die(r == -1, strerror(errno));
|
|
}
|
|
|
|
return 0;
|
|
}
|