diff --git a/README.md b/README.md index 7ba4f09..99cdeca 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,32 @@ This tool generates vanity ed25519 ([hidden service version 3][v3], formely known as proposal 224) onion addresses. +### What is this fork? +This is my shot at implementing [trustless mining](https://github.com/cathugger/mkp224o/issues/60). +It's a garbage implementation, but it (kinda?) works. + +#### Usage +``` +$ ./mkp224o --genbase out/base.priv out/base.pub +writing private base key to 'out/base.priv' +writing public base key to 'out/base.pub' +done. + +$ ./mkp224o -n 1 -d out -Z --basekey out/base.pub zzz + +$ ./mkp224o --combine out/base.priv out/zzz*.onion/hs_ed25519_secret_key +new pk: [...] +saving to out/zzzkzmpje34nnp2yvgz7slr7rgpajzlpihsr3rpzgmekrjosnpprf2id.onion/hs_ed25519_secret_key.fixed + +$ cp out/zzz*.onion/hs_ed25519_secret_key.fixed /var/lib/tor/hidden_service/hs_ed25519_secret_key +``` + +#### the ugly +* i'm an amateur, the math might not check out +* horrible code organization - i'm not familiar with this style of codebases at all +* depends on ed25519-donna +* only works with slow key generation (-Z) + ### Requirements * C99 compatible compiler (gcc and clang should work) diff --git a/main.c b/main.c index 1943817..1a9563c 100644 --- a/main.c +++ b/main.c @@ -132,6 +132,13 @@ static void printhelp(FILE *out,const char *progname) #endif " --rawyaml raw (unprefixed) public/secret keys for -y/-Y\n" " (may be useful for tor controller API)\n" + " --basekey base.pub\n" + " trustless mining: the private keys found will need\n" + " to be --combine'd with base.priv before use\n" + " --genbase base.priv base.pub\n" + " generate base keys for trustless mining\n" + " --combine base.priv hs_secret_key\n" + " combine a mined hs_secret key with a base key\n" " -h, --help, --usage print help to stdout and quit\n" " -V, --version print version information to stdout and exit\n" ,progname,progname); @@ -262,11 +269,160 @@ enum worker_type { WT_BATCH, }; +// i'm so sorry for including an implementation header +// i didn't find another way to get access to the functions +#include "ed25519/ed25519_impl_pre.h" +static void genbase(const char *privpath, const char *pubpath) +{ + u8 base_sk[32]; + u8 base_pk[32]; + hash_512bits base_extsk; + ge25519 ALIGN(16) A; + bignum256modm ALIGN(16) base; + FILE *fp; + + randombytes(base_sk, sizeof base_sk); + ed25519_seckey_expand(base_extsk, base_sk); + expand256_modm(base, base_extsk, 32); + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, base); + ge25519_pack(base_pk, &A); + + printf("writing private base key to '%s'\n", privpath); + fp = fopen(privpath, "w"); + if (!fp) { + perror("couldn't open"); + exit(1); + } + if (fwrite(base_sk, 1, 32, fp) != 32) { + perror("write"); + exit(1); + } + fclose(fp); + + printf("writing public base key to '%s'\n", pubpath); + fp = fopen(pubpath, "w"); + if (!fp) { + perror("couldn't open"); + exit(1); + } + if (fwrite(base_pk, 1, 32, fp) != 32) { + perror("write"); + exit(1); + } + fclose(fp); + + puts("done."); +} + +static void combine(const char *privpath, const char *hs_secretkey) +{ + u8 base_sk[32], secret[96]; + FILE *fp; + + fp = fopen(hs_secretkey, "r"); + if (fp == NULL) { + perror("failed to open hs_secret_key"); + exit(1); + } + if (fread(secret, 1, 96, fp) != 96) { + perror("failed to read hs_secret_key"); + exit(1); + } + if (memcmp(secret, "== ed25519v1-secret: type0 ==\0\0\0", 32) != 0) { + fprintf(stderr, "invalid hs_secret_key format.\nare you sure you picked the right file?\n"); + exit(1); + } + fclose(fp); + + fp = fopen(privpath, "r"); + if (fp == NULL) { + perror("failed to open base.priv"); + exit(1); + } + if (fread(base_sk, 1, sizeof base_sk, fp) != sizeof base_sk) { + perror("failed to read base.priv"); + exit(1); + } + fclose(fp); + +#if 0 + u8 pk[32]; + + hash_512bits base_extsk; + ed25519_seckey_expand(base_extsk, base_sk); + + bignum256modm ALIGN(16) base; + expand256_modm(base, base_extsk, 32); + + ge25519 ALIGN(16) A, B; + ge25519_scalarmult_base_niels(&B, ge25519_niels_base_multiples, base); + u8 base_pk[32]; + ge25519_pack(base_pk, &B); + ge25519_unpack_negative_vartime(&B, base_pk); + ge25519_pack(base_pk, &B); + ge25519_unpack_negative_vartime(&B, base_pk); + + bignum256modm ALIGN(16) a; + expand256_modm(a, &secret[SKPREFIX_SIZE], 32); + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_add(&A, &A, &B); + ge25519_pack(pk, &A); + + printf("pk from public: "); + for (size_t i = 0; i < sizeof(pk); i++) + printf("%02x ", pk[i]); + puts(""); +#endif + + hash_512bits base_extsk; + bignum256modm ALIGN(16) a, b; + ge25519 ALIGN(16) A; + u8 pk[32]; + + expand256_modm(a, &secret[32], 32); + ed25519_seckey_expand(base_extsk, base_sk); + expand256_modm(b, base_extsk, 32); + + add256_modm(a, a, b); + + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_pack(pk, &A); + + contract256_modm(&secret[32], a); + + expand256_modm(a, &secret[32], 32); + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_pack(pk, &A); + + printf("new pk: "); + for (size_t i = 0; i < sizeof(pk); i++) + printf("%02x ", pk[i]); + puts(""); + + char *newname = malloc(strlen(hs_secretkey) + strlen(".fixed") + 1); + strcpy(newname, hs_secretkey); + strcat(newname, ".fixed"); + printf("saving to %s\n", newname); + + fp = fopen(newname, "w"); + if (!fp) { + perror("couldn't open"); + exit(1); + } + if (fwrite(secret, 1, sizeof secret, fp) != sizeof secret) { + perror("failed to write fixed privkey"); + exit(1); + } + fclose(fp); +} +#include "ed25519/ed25519_impl_post.h" + int main(int argc,char **argv) { const char *outfile = 0; const char *infile = 0; const char *onehostname = 0; + const char *basekeyfile = 0; const char *arg; int ignoreargs = 0; int dirnameflag = 0; @@ -326,6 +482,28 @@ int main(int argc,char **argv) printversion(); exit(0); } + else if (!strcmp(arg,"combine")) { + if (argc != 2) { + printhelp(stdout,progname); + exit(0); + } + combine(argv[0],argv[1]); + exit(0); + } + else if (!strcmp(arg,"genbase")) { + if (argc != 2) { + printhelp(stdout,progname); + exit(0); + } + genbase(argv[0],argv[1]); + exit(0); + } + else if (!strcmp(arg,"basekey")) { + if (argc--) + basekeyfile = *argv++; + else + e_additional(); + } else if (!strcmp(arg,"rawyaml")) yamlraw = 1; #ifdef PASSPHRASE @@ -497,6 +675,28 @@ int main(int argc,char **argv) filters_add(arg); } + if (wt != WT_SLOW) { + fprintf(stderr,"you're not using -Z. this will probably break."); + } + + if (basekeyfile) { + u8 base_pk[32]; + FILE *fp = fopen(basekeyfile, "r"); + if (!fp) { + perror("couldn't open basekey"); + exit(1); + } + if (fread(base_pk, 1, sizeof base_pk, fp) != sizeof base_pk) { + perror("incomplete read of base_pk"); + exit(1); + } + fclose(fp); + ed25519_pubkey_setbase(base_pk); + } else { + fprintf(stderr, "This build requires using --basekey.\n"); + exit(1); + } + if (yamlinput && yamloutput) { fprintf(stderr,"both -y and -Y does not make sense\n"); exit(1); diff --git a/worker.c b/worker.c index b9a6476..67a1da5 100644 --- a/worker.c +++ b/worker.c @@ -60,6 +60,9 @@ pthread_mutex_t determseed_mutex; u8 determseed[SEED_LEN]; #endif +static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk); +static void sanitycheck(const u8 *sk, const u8 *pk); + char *makesname(void) { @@ -88,16 +91,7 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) pthread_mutex_unlock(&keysgenerated_mutex); } - // disabled as this was never ever triggered as far as I'm aware -#if 0 - // Sanity check that the public key matches the private one. - ge_p3 ALIGN(16) point; - u8 testpk[PUBLIC_LEN]; - ge_scalarmult_base(&point,&secret[SKPREFIX_SIZE]); - ge_p3_tobytes(testpk,&point); - if (memcmp(testpk,&pubonion[PKPREFIX_SIZE],PUBLIC_LEN) != 0) - abort(); -#endif + sanitycheck(&secret[SKPREFIX_SIZE], &pubonion[PKPREFIX_SIZE]); if (!yamloutput) { if (createdir(sname,1) != 0) { @@ -266,3 +260,48 @@ void worker_init(void) crypto_sign_ed25519_donna_ge_initeightpoint(); #endif } + +// there's not really any good place to add ed25519 functions +// so i just add them there +// i don't understand how this codebase is organized :( + +ge25519 ALIGN(16) PUBKEY_BASE = {0}; +int pubkey_base_initialized; + +void ed25519_pubkey_setbase(const u8 base_pk[32]) +{ + u8 tmp_pk[32]; + ge25519_unpack_negative_vartime(&PUBKEY_BASE, base_pk); + // dumb hack: unpack flips the point. to get the original point + // back, i just pack and unpack it again + ge25519_pack(tmp_pk, &PUBKEY_BASE); + ge25519_unpack_negative_vartime(&PUBKEY_BASE, tmp_pk); + pubkey_base_initialized = 1; +} + +static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk) +{ + bignum256modm a; + ge25519 ALIGN(16) A; + + if (unlikely(pubkey_base_initialized == 0)) + abort(); + + // ge_scalarmult_base(&A, sk); + expand256_modm(a,sk,32); + ge25519_scalarmult_base_niels(&A,ge25519_niels_base_multiples,a); + ge25519_add(&A, &A, &PUBKEY_BASE); + ge25519_pack(pk,&A); + + return 0; +} + + +static void sanitycheck(const u8 *sk, const u8 *pk) { + u8 testpk[PUBLIC_LEN]; + ed25519_pubkey_onbase(testpk, sk); + if (memcmp(testpk,pk,PUBLIC_LEN) != 0) { + fprintf(stderr, "Sanity check failed. Either I fucked something up, or you're using an unsupported combination of options. Probably both.\n"); + abort(); + } +} diff --git a/worker.h b/worker.h index 36912a0..f4d6764 100644 --- a/worker.h +++ b/worker.h @@ -36,6 +36,7 @@ extern u8 determseed[SEED_LEN]; #endif extern void worker_init(void); +extern void ed25519_pubkey_setbase(const u8 base_pk[32]); extern char *makesname(void); extern size_t worker_batch_memuse(void); diff --git a/worker_slow.inc.h b/worker_slow.inc.h index ab75ef2..4ade24c 100644 --- a/worker_slow.inc.h +++ b/worker_slow.inc.h @@ -42,7 +42,7 @@ again: if (unlikely(endwork)) goto end; - ed25519_pubkey(pk,sk); + ed25519_pubkey_onbase(pk,sk); #ifdef STATISTICS ++st->numcalc.v;