2
0
Fork 0
mirror of https://git.sr.ht/~sircmpwn/mkproof synced 2024-05-06 04:36:09 +02:00
mkproof/src/mkproof.c
2020-12-26 19:24:50 -05:00

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;
}