diff options
Diffstat (limited to 'static/solver.c')
-rw-r--r-- | static/solver.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/static/solver.c b/static/solver.c new file mode 100644 index 0000000..d76d020 --- /dev/null +++ b/static/solver.c @@ -0,0 +1,182 @@ +// This is a reference implementation of the proof of work solver in C. +// For security reasons, it is recommended that you read and understand the +// entire program first if you actually want to run it. +// +// You need to have OpenSSL, and link with -lcrypto + +#include <openssl/evp.h> +#include <openssl/bio.h> +#include <openssl/buffer.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +bool validate_bit_zeros(const unsigned char *bs, unsigned long n) +{ + unsigned long q = n / CHAR_BIT; + unsigned char r = n % CHAR_BIT; + + for (unsigned long i = 0; i < q; i++) { + if (bs[i] != 0) + return false; + } + + if (r > 0) { + unsigned char mask = + (unsigned char)(UCHAR_MAX << (CHAR_BIT - r)); + if (bs[q] & mask) + return false; + } + + return true; +} + +int main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "usage: %s <base64_data> <difficulty>\n", + argv[0]); + return 1; + } + + size_t base64_data_len = strlen(argv[1]); + unsigned char *base64_data = malloc(base64_data_len); + if (!base64_data) { + perror("malloc"); + return 1; + } + memcpy(base64_data, argv[1], base64_data_len); + + char *endptr = NULL; + errno = 0; + unsigned long difficulty = strtoul(argv[2], &endptr, 10); + if ((difficulty == ULONG_MAX && errno == ERANGE) || *endptr != '\0' + || difficulty > 256) { + fprintf(stderr, "invalid difficulty value\n"); + free(base64_data); + return 1; + } + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *bmem = BIO_new_mem_buf(base64_data, (int)base64_data_len); + if (!b64 || !bmem) { + fprintf(stderr, "BIO_new/BIO_new_mem_buf\n"); + free(base64_data); + return 1; + } + + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + b64 = BIO_push(b64, bmem); + + size_t decoded_cap = base64_data_len; + unsigned char *decoded = malloc(decoded_cap); + if (!decoded) { + perror("malloc"); + BIO_free_all(b64); + free(base64_data); + return 1; + } + + int decoded_len = BIO_read(b64, decoded, (int)decoded_cap); + if (decoded_len < 0) { + fprintf(stderr, "BIO_read\n"); + BIO_free_all(b64); + free(base64_data); + free(decoded); + return 1; + } + BIO_free_all(b64); + free(base64_data); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + fprintf(stderr, "EVP_MD_CTX_new\n"); + free(decoded); + return 1; + } + + size_t len = EVP_MD_size(EVP_sha256()); + unsigned char digest[len]; + size_t next = 0; + + while (1) { + if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) { + fprintf(stderr, "EVP_DigestInit_ex\n"); + EVP_MD_CTX_free(mdctx); + free(decoded); + return 1; + } + if (EVP_DigestUpdate(mdctx, decoded, decoded_len) != 1) { + fprintf(stderr, "EVP_DigestUpdate(data)\n"); + EVP_MD_CTX_free(mdctx); + free(decoded); + return 1; + } + if (EVP_DigestUpdate(mdctx, &next, sizeof(next)) != 1) { + fprintf(stderr, "EVP_DigestUpdate(next)\n"); + EVP_MD_CTX_free(mdctx); + free(decoded); + return 1; + } + if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1) { + fprintf(stderr, "EVP_DigestFinal_ex\n"); + EVP_MD_CTX_free(mdctx); + free(decoded); + return 1; + } + if (validate_bit_zeros(digest, difficulty)) { + break; + } + next++; + if (!next) { + fprintf(stderr, "unsigned integer overflow\n"); + EVP_MD_CTX_free(mdctx); + free(decoded); + return 1; + } + } + EVP_MD_CTX_free(mdctx); + free(decoded); + + BIO *b64_out = BIO_new(BIO_f_base64()); + BIO *bmem_out = BIO_new(BIO_s_mem()); + if (!b64_out || !bmem_out) { + fprintf(stderr, "BIO_new\n"); + if (b64_out) + BIO_free_all(b64_out); + if (bmem_out) + BIO_free(bmem_out); + return 1; + } + BIO_set_flags(b64_out, BIO_FLAGS_BASE64_NO_NL); + b64_out = BIO_push(b64_out, bmem_out); + + if (BIO_write(b64_out, &next, sizeof(next)) < 0) { + fprintf(stderr, "BIO_write\n"); + BIO_free_all(b64_out); + return 1; + } + if (BIO_flush(b64_out) < 1) { + fprintf(stderr, "BIO_flush\n"); + BIO_free_all(b64_out); + return 1; + } + + BUF_MEM *bptr = NULL; + BIO_get_mem_ptr(b64_out, &bptr); + if (!bptr || !bptr->data) { + fprintf(stderr, "BIO_get_mem_ptr\n"); + BIO_free_all(b64_out); + return 1; + } + + write(STDOUT_FILENO, bptr->data, bptr->length); + write(STDOUT_FILENO, "\n", 1); + + BIO_free_all(b64_out); + return 0; +} |