From bd3b8d588163b48b46992eae9a888b4498f00c2f Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sat, 5 Nov 2022 17:37:47 +0100 Subject: [PATCH 01/14] prototype of trustless mining --- README.md | 26 ++++++ main.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++ worker.c | 59 +++++++++++--- worker.h | 1 + worker_slow.inc.h | 2 +- 5 files changed, 277 insertions(+), 11 deletions(-) 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 @@ void *CRYPTO_NAMESPACE(worker_slow)(void *task) if (unlikely(endwork)) goto end; - ed25519_pubkey(pk,sk); + ed25519_pubkey_onbase(pk,sk); #ifdef STATISTICS ++st->numcalc.v; From 13166c0fa3cdc4e797bd567a44b02ee7e7454fc4 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sat, 5 Nov 2022 23:44:55 +0100 Subject: [PATCH 02/14] partial support for supercop amd64 --- README.md | 2 +- ed25519/ed25519_impl_pre.h | 2 ++ main.c | 10 ++++++++++ worker.c | 17 +++++++---------- worker_impl.inc.h | 2 +- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 99cdeca..3f67876 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ $ cp out/zzz*.onion/hs_ed25519_secret_key.fixed /var/lib/tor/hidden_service/hs_e #### 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 +* no support for ref10, partial support for supercop amd64 * only works with slow key generation (-Z) ### Requirements diff --git a/ed25519/ed25519_impl_pre.h b/ed25519/ed25519_impl_pre.h index bc84ad9..e10686c 100644 --- a/ed25519/ed25519_impl_pre.h +++ b/ed25519/ed25519_impl_pre.h @@ -79,6 +79,7 @@ inline static void ge_initeightpoint(void) {} #define ge_p3 ge25519_p3 #define ge_p1p1_to_p3 ge25519_p1p1_to_p3 #define ge_p3_tobytes ge25519_pack +#define ge_frombytes_negate_vartime ge25519_unpackneg_vartime #define ge_add ge25519_pnielsadd_p1p1 #define ge_p3_batchtobytes_destructive_1 ge25519_batchpack_destructive_1 @@ -190,6 +191,7 @@ static int ed25519_keypair(unsigned char *pk,unsigned char *sk) #define ge_p1p1_to_p3 ge25519_p1p1_to_full #define ge_p3_tobytes ge25519_pack +#define ge_frombytes_negate_vartime ge25519_unpack_negative_vartime #define ge_p3_batchtobytes_destructive_1 ge25519_batchpack_destructive_1 #define ge_p3_batchtobytes_destructive_finish ge25519_batchpack_destructive_finish diff --git a/main.c b/main.c index 1a9563c..c5417a8 100644 --- a/main.c +++ b/main.c @@ -274,6 +274,7 @@ enum worker_type { #include "ed25519/ed25519_impl_pre.h" static void genbase(const char *privpath, const char *pubpath) { +#ifdef ED25519_donna u8 base_sk[32]; u8 base_pk[32]; hash_512bits base_extsk; @@ -312,10 +313,15 @@ static void genbase(const char *privpath, const char *pubpath) fclose(fp); puts("done."); +#else + fprintf(stderr, "Please compile with ed25519-donna to use this flag.\n"); + exit(1); +#endif } static void combine(const char *privpath, const char *hs_secretkey) { +#ifdef ED25519_donna u8 base_sk[32], secret[96]; FILE *fp; @@ -414,6 +420,10 @@ static void combine(const char *privpath, const char *hs_secretkey) exit(1); } fclose(fp); +#else + fprintf(stderr, "Please compile with ed25519-donna to use this flag.\n"); + exit(1); +#endif } #include "ed25519/ed25519_impl_post.h" diff --git a/worker.c b/worker.c index 67a1da5..9ac975d 100644 --- a/worker.c +++ b/worker.c @@ -265,33 +265,30 @@ void worker_init(void) // so i just add them there // i don't understand how this codebase is organized :( -ge25519 ALIGN(16) PUBKEY_BASE = {0}; +ge_p3 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); + ge_frombytes_negate_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); + ge_p3_tobytes(tmp_pk, &PUBKEY_BASE); + ge_frombytes_negate_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; + ge_p3 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); + ge_scalarmult_base(&A, sk); ge25519_add(&A, &A, &PUBKEY_BASE); - ge25519_pack(pk,&A); + ge_p3_tobytes(pk,&A); return 0; } diff --git a/worker_impl.inc.h b/worker_impl.inc.h index fbd7b0f..111ee8d 100644 --- a/worker_impl.inc.h +++ b/worker_impl.inc.h @@ -12,4 +12,4 @@ static size_t CRYPTO_NAMESPACE(worker_batch_memuse)(void) #include "worker_batch.inc.h" #include "worker_batch_pass.inc.h" -#include "ed25519/ed25519_impl_post.h" +// #include "ed25519/ed25519_impl_post.h" From 656502b797c49c33af1e94163560fbdc42c14c35 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 6 Nov 2022 12:19:55 +0100 Subject: [PATCH 03/14] full supercop amd64 support --- README.md | 4 ++- ed25519/ed25519_impl_pre.h | 21 +++++++++++- main.c | 67 ++++++++------------------------------ 3 files changed, 36 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 3f67876..23df768 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,13 @@ saving to out/zzzkzmpje34nnp2yvgz7slr7rgpajzlpihsr3rpzgmekrjosnpprf2id.onion/hs_ $ cp out/zzz*.onion/hs_ed25519_secret_key.fixed /var/lib/tor/hidden_service/hs_ed25519_secret_key ``` +I recommend doing a test run with a short filter before mining "for real". Some settings are currently broken. #### 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 -* no support for ref10, partial support for supercop amd64 +* no support for ref10 +* no automated tests * only works with slow key generation (-Z) ### Requirements diff --git a/ed25519/ed25519_impl_pre.h b/ed25519/ed25519_impl_pre.h index e10686c..37cae16 100644 --- a/ed25519/ed25519_impl_pre.h +++ b/ed25519/ed25519_impl_pre.h @@ -186,6 +186,7 @@ static int ed25519_keypair(unsigned char *pk,unsigned char *sk) } #define fe bignum25519 +#define sc25519 bignum256modm #define ge_p1p1 ge25519_p1p1 #define ge_p3 ge25519 @@ -196,10 +197,28 @@ static int ed25519_keypair(unsigned char *pk,unsigned char *sk) #define ge_p3_batchtobytes_destructive_1 ge25519_batchpack_destructive_1 #define ge_p3_batchtobytes_destructive_finish ge25519_batchpack_destructive_finish - #define ge_add CRYPTO_NAMESPACE(ge_add) #define ge_scalarmult_base CRYPTO_NAMESPACE(ge_scalarmult_base) +static void sc25519_from32bytes(bignum256modm *r, const unsigned char x[32]) +{ + expand256_modm(*r, x, 32); +} + +static void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) +{ + contract256_modm(r, *x); +} + +static void sc25519_add(bignum256modm *r, const bignum256modm *x, const bignum256modm *y) +{ + add256_modm(*r, *x, *y); +} + +static void ge25519_scalarmult_base(ge25519 *r, const bignum256modm *s) +{ + ge25519_scalarmult_base_niels(r,ge25519_niels_base_multiples,*s); +} DONNA_INLINE static void ge_add(ge25519_p1p1 *r,const ge25519 *p,const ge25519_pniels *q) { diff --git a/main.c b/main.c index c5417a8..c2ab96c 100644 --- a/main.c +++ b/main.c @@ -274,18 +274,15 @@ enum worker_type { #include "ed25519/ed25519_impl_pre.h" static void genbase(const char *privpath, const char *pubpath) { -#ifdef ED25519_donna u8 base_sk[32]; u8 base_pk[32]; - hash_512bits base_extsk; - ge25519 ALIGN(16) A; - bignum256modm ALIGN(16) base; + u8 base_extsk[64]; + ge_p3 ALIGN(16) A; 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); + ge_scalarmult_base(&A, base_extsk); ge25519_pack(base_pk, &A); printf("writing private base key to '%s'\n", privpath); @@ -313,15 +310,10 @@ static void genbase(const char *privpath, const char *pubpath) fclose(fp); puts("done."); -#else - fprintf(stderr, "Please compile with ed25519-donna to use this flag.\n"); - exit(1); -#endif } static void combine(const char *privpath, const char *hs_secretkey) { -#ifdef ED25519_donna u8 base_sk[32], secret[96]; FILE *fp; @@ -351,53 +343,24 @@ static void combine(const char *privpath, const char *hs_secretkey) } fclose(fp); -#if 0 + u8 base_extsk[64];; + sc25519 ALIGN(16) a, b; + ge_p3 ALIGN(16) A; u8 pk[32]; - hash_512bits base_extsk; + sc25519_from32bytes(&a, &secret[32]); ed25519_seckey_expand(base_extsk, base_sk); + sc25519_from32bytes(&b, base_extsk); - bignum256modm ALIGN(16) base; - expand256_modm(base, base_extsk, 32); + sc25519_add(&a, &a, &b); - 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_scalarmult_base(&A, &a); 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]; + sc25519_to32bytes(&secret[32], &a); - 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); + sc25519_from32bytes(&a, &secret[32]); + ge25519_scalarmult_base(&A, &a); ge25519_pack(pk, &A); printf("new pk: "); @@ -420,10 +383,6 @@ static void combine(const char *privpath, const char *hs_secretkey) exit(1); } fclose(fp); -#else - fprintf(stderr, "Please compile with ed25519-donna to use this flag.\n"); - exit(1); -#endif } #include "ed25519/ed25519_impl_post.h" From e0244d41daa6f84166f68bebe8869eaff580fb0f Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 6 Nov 2022 14:31:43 +0100 Subject: [PATCH 04/14] --combine: regenerate the second half of the key i think that before this commit the miner could recover your private key after you set the hidden service up. whoops --- main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/main.c b/main.c index c2ab96c..e8235a1 100644 --- a/main.c +++ b/main.c @@ -357,8 +357,23 @@ static void combine(const char *privpath, const char *hs_secretkey) ge25519_scalarmult_base(&A, &a); ge25519_pack(pk, &A); + // Save secret scalar. sc25519_to32bytes(&secret[32], &a); + // Compute second half of the key. + // See "Pseudorandom generation of r.", page 8 of https://ed25519.cr.yp.to/ed25519-20110926.pdf + // You're supposed to generate it together with the secret scalar, but + // we can't really do that here. As far as I can tell, it just needs to + // be another secret value. + // In normal Ed25519 you never have a pair of keys with the same secret + // scalar but different second halves (I can't find the proper term for + // it...). If I generated them independently from the secret scalar, + // someone could run --combine and get such a pair of keys. + // I don't know if that would mess anything up, but to err on the side + // of caution, I'm setting it to a hash of the secret scalar and the + // original generated key. + FIPS202_SHAKE256(secret, sizeof secret, &secret[64], 32); + sc25519_from32bytes(&a, &secret[32]); ge25519_scalarmult_base(&A, &a); ge25519_pack(pk, &A); From 168249e60d2702db90a2dd9b1c9aeebb8d9b668d Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 6 Nov 2022 14:52:18 +0100 Subject: [PATCH 05/14] --combine: rewrite the comment explaining the hash prefix generation --- main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main.c b/main.c index e8235a1..d42a617 100644 --- a/main.c +++ b/main.c @@ -360,15 +360,15 @@ static void combine(const char *privpath, const char *hs_secretkey) // Save secret scalar. sc25519_to32bytes(&secret[32], &a); - // Compute second half of the key. + // Compute the key's hash prefix. // See "Pseudorandom generation of r.", page 8 of https://ed25519.cr.yp.to/ed25519-20110926.pdf // You're supposed to generate it together with the secret scalar, but // we can't really do that here. As far as I can tell, it just needs to // be another secret value. // In normal Ed25519 you never have a pair of keys with the same secret - // scalar but different second halves (I can't find the proper term for - // it...). If I generated them independently from the secret scalar, - // someone could run --combine and get such a pair of keys. + // scalar but different hash prefixes. If I generated hash prefixes + // independently from the secret scalar (such as by just using random + // bytes), you could get such a pair by running --combine multiple times. // I don't know if that would mess anything up, but to err on the side // of caution, I'm setting it to a hash of the secret scalar and the // original generated key. From 7d008081fdab41706212e6a3bbea5ae18e7c4a7a Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 6 Nov 2022 23:58:45 +0100 Subject: [PATCH 06/14] add trustless mining support to the batch/fast workers --- main.c | 5 +-- worker.c | 83 ++++++++++++++++++++++------------------------ worker_batch.inc.h | 5 ++- worker_fast.inc.h | 2 +- worker_impl.inc.h | 3 -- 5 files changed, 48 insertions(+), 50 deletions(-) diff --git a/main.c b/main.c index d42a617..c3284b5 100644 --- a/main.c +++ b/main.c @@ -659,8 +659,9 @@ 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 (deterministic) { + fprintf(stderr, "deterministic trustless mining isn't supported yet.\n"); + exit(1); } if (basekeyfile) { diff --git a/worker.c b/worker.c index 9ac975d..300a71a 100644 --- a/worker.c +++ b/worker.c @@ -208,8 +208,47 @@ static void reseedright(u8 sk[SECRET_LEN]) #include "ed25519/ed25519.h" +#include "ed25519/ed25519_impl_pre.h" -#include "worker_impl.inc.h" +ge_p3 ALIGN(16) PUBKEY_BASE; +int pubkey_base_initialized; + +#include "worker_impl.inc.h" // uses those globals + +void ed25519_pubkey_setbase(const u8 base_pk[32]) +{ + u8 tmp_pk[32]; + ge_frombytes_negate_vartime(&PUBKEY_BASE, base_pk); + // dumb hack: unpack flips the point. to get the original point + // back, i just pack and unpack it again + ge_p3_tobytes(tmp_pk, &PUBKEY_BASE); + ge_frombytes_negate_vartime(&PUBKEY_BASE, tmp_pk); + pubkey_base_initialized = 1; +} + +static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk) +{ + ge_p3 ALIGN(16) A; + + if (unlikely(pubkey_base_initialized == 0)) + abort(); + + ge_scalarmult_base(&A, sk); + ge25519_add(&A, &A, &PUBKEY_BASE); + ge_p3_tobytes(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(); + } +} size_t worker_batch_memuse(void) { @@ -260,45 +299,3 @@ 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 :( - -ge_p3 ALIGN(16) PUBKEY_BASE = {0}; -int pubkey_base_initialized; - -void ed25519_pubkey_setbase(const u8 base_pk[32]) -{ - u8 tmp_pk[32]; - ge_frombytes_negate_vartime(&PUBKEY_BASE, base_pk); - // dumb hack: unpack flips the point. to get the original point - // back, i just pack and unpack it again - ge_p3_tobytes(tmp_pk, &PUBKEY_BASE); - ge_frombytes_negate_vartime(&PUBKEY_BASE, tmp_pk); - pubkey_base_initialized = 1; -} - -static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk) -{ - ge_p3 ALIGN(16) A; - - if (unlikely(pubkey_base_initialized == 0)) - abort(); - - ge_scalarmult_base(&A, sk); - ge25519_add(&A, &A, &PUBKEY_BASE); - ge_p3_tobytes(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_batch.inc.h b/worker_batch.inc.h index 7eb5cd6..f7d5407 100644 --- a/worker_batch.inc.h +++ b/worker_batch.inc.h @@ -25,6 +25,9 @@ void *CRYPTO_NAMESPACE(worker_batch)(void *task) (void) task; #endif + if (unlikely(pubkey_base_initialized == 0)) + abort(); + PREFILTER memcpy(secret,skprefix,SKPREFIX_SIZE); @@ -46,8 +49,8 @@ void *CRYPTO_NAMESPACE(worker_batch)(void *task) randombytes(seed,sizeof(seed)); ed25519_seckey_expand(sk,seed); - ge_scalarmult_base(&ge_public,sk); + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); for (counter = 0;counter < SIZE_MAX-(8*BATCHNUM);counter += 8*BATCHNUM) { ge_p1p1 ALIGN(16) sum; diff --git a/worker_fast.inc.h b/worker_fast.inc.h index b9ec7a0..89608fb 100644 --- a/worker_fast.inc.h +++ b/worker_fast.inc.h @@ -41,8 +41,8 @@ void *CRYPTO_NAMESPACE(worker_fast)(void *task) randombytes(seed,sizeof(seed)); ed25519_seckey_expand(sk,seed); - ge_scalarmult_base(&ge_public,sk); + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); ge_p3_tobytes(pk,&ge_public); for (counter = 0;counter < SIZE_MAX-8;counter += 8) { diff --git a/worker_impl.inc.h b/worker_impl.inc.h index 111ee8d..e886a65 100644 --- a/worker_impl.inc.h +++ b/worker_impl.inc.h @@ -1,6 +1,4 @@ -#include "ed25519/ed25519_impl_pre.h" - static size_t CRYPTO_NAMESPACE(worker_batch_memuse)(void) { return (sizeof(ge_p3) + sizeof(fe) + sizeof(bytes32)) * BATCHNUM; @@ -12,4 +10,3 @@ static size_t CRYPTO_NAMESPACE(worker_batch_memuse)(void) #include "worker_batch.inc.h" #include "worker_batch_pass.inc.h" -// #include "ed25519/ed25519_impl_post.h" From 8d9b6d1398b193f6268af95be2551b9e4bcbe113 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 6 Nov 2022 23:59:56 +0100 Subject: [PATCH 07/14] delete the thing about only supporting -Z --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 23df768..cbedf04 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ I recommend doing a test run with a short filter before mining "for real". Some * horrible code organization - i'm not familiar with this style of codebases at all * no support for ref10 * no automated tests -* only works with slow key generation (-Z) ### Requirements From 90666cda8a92afeeaa2104e354b3aa00c6e01921 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Tue, 8 Nov 2022 09:41:31 +0100 Subject: [PATCH 08/14] trustless mining with multiple basekeys A really cool feature suggested by adapt-L. I'm still not certain the math checks out, not to mention the lack of any automated tests. --- README.md | 3 +- main.c | 105 ++++++++++++++++++++++++++++-------------------------- worker.c | 16 ++++++--- worker.h | 2 +- 4 files changed, 69 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index cbedf04..1f72f83 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,14 @@ done. $ ./mkp224o -n 1 -d out -Z --basekey out/base.pub zzz -$ ./mkp224o --combine out/base.priv out/zzz*.onion/hs_ed25519_secret_key +$ ./mkp224o --combine out/zzz*.onion/hs_ed25519_secret_key out/base.priv 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 ``` I recommend doing a test run with a short filter before mining "for real". Some settings are currently broken. +It's also possible to specify [multiple basekeys](https://github.com/cathugger/mkp224o/issues/60#issuecomment-1305033288). #### the ugly * i'm an amateur, the math might not check out diff --git a/main.c b/main.c index c3284b5..967d931 100644 --- a/main.c +++ b/main.c @@ -134,11 +134,12 @@ static void printhelp(FILE *out,const char *progname) " (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" + " to be --combine'd with thr private parts of all\n" + " basekeys used\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" + " --combine hs_secret_key base.priv..\n" + " combine a mined hs_secret key with base key(s)\n" " -h, --help, --usage print help to stdout and quit\n" " -V, --version print version information to stdout and exit\n" ,progname,progname); @@ -312,12 +313,18 @@ static void genbase(const char *privpath, const char *pubpath) puts("done."); } -static void combine(const char *privpath, const char *hs_secretkey) +static void combine(int argc, char **argv) { - u8 base_sk[32], secret[96]; + u8 secret[96]; FILE *fp; + const char *minedpath = argv[0]; + + if (argc < 2) { + fprintf(stderr, "--combine requires one or more base private keys after the mined key.\n"); + exit(1); + } - fp = fopen(hs_secretkey, "r"); + fp = fopen(minedpath, "r"); if (fp == NULL) { perror("failed to open hs_secret_key"); exit(1); @@ -332,29 +339,31 @@ static void combine(const char *privpath, const char *hs_secretkey) } 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); - - u8 base_extsk[64];; - sc25519 ALIGN(16) a, b; - ge_p3 ALIGN(16) A; - u8 pk[32]; - + sc25519 ALIGN(16) a; sc25519_from32bytes(&a, &secret[32]); - ed25519_seckey_expand(base_extsk, base_sk); - sc25519_from32bytes(&b, base_extsk); - sc25519_add(&a, &a, &b); + for (int i = 1; i < argc; i++) { + u8 base_sk[32], base_extsk[64]; + fp = fopen(argv[i], "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); + + sc25519 ALIGN(16) b; + ed25519_seckey_expand(base_extsk, base_sk); + sc25519_from32bytes(&b, base_extsk); + sc25519_add(&a, &a, &b); + } + ge_p3 ALIGN(16) A; ge25519_scalarmult_base(&A, &a); + u8 pk[32]; ge25519_pack(pk, &A); // Save secret scalar. @@ -383,8 +392,8 @@ static void combine(const char *privpath, const char *hs_secretkey) printf("%02x ", pk[i]); puts(""); - char *newname = malloc(strlen(hs_secretkey) + strlen(".fixed") + 1); - strcpy(newname, hs_secretkey); + char *newname = malloc(strlen(minedpath) + strlen(".fixed") + 1); + strcpy(newname, minedpath); strcat(newname, ".fixed"); printf("saving to %s\n", newname); @@ -406,7 +415,6 @@ 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; @@ -425,6 +433,7 @@ int main(int argc,char **argv) int realtimestats = 1; #endif int tret; + int basekeys = 0; if (sodium_init() < 0) { fprintf(stderr,"sodium_init() failed\n"); @@ -467,11 +476,7 @@ int main(int argc,char **argv) exit(0); } else if (!strcmp(arg,"combine")) { - if (argc != 2) { - printhelp(stdout,progname); - exit(0); - } - combine(argv[0],argv[1]); + combine(argc,argv); exit(0); } else if (!strcmp(arg,"genbase")) { @@ -483,10 +488,23 @@ int main(int argc,char **argv) exit(0); } else if (!strcmp(arg,"basekey")) { - if (argc--) - basekeyfile = *argv++; - else + if (argc--) { + u8 base_pk[32]; + FILE *fp = fopen(*argv++, "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_addbase(base_pk); + basekeys++; + } else { e_additional(); + } } else if (!strcmp(arg,"rawyaml")) yamlraw = 1; @@ -664,20 +682,7 @@ int main(int argc,char **argv) exit(1); } - 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 { + if (basekeys == 0) { fprintf(stderr, "This build requires using --basekey.\n"); exit(1); } diff --git a/worker.c b/worker.c index 300a71a..3d444e1 100644 --- a/worker.c +++ b/worker.c @@ -215,15 +215,21 @@ int pubkey_base_initialized; #include "worker_impl.inc.h" // uses those globals -void ed25519_pubkey_setbase(const u8 base_pk[32]) +void ed25519_pubkey_addbase(const u8 base_pk[32]) { + ge_p3 ALIGN(16) A; u8 tmp_pk[32]; - ge_frombytes_negate_vartime(&PUBKEY_BASE, base_pk); + ge_frombytes_negate_vartime(&A, base_pk); // dumb hack: unpack flips the point. to get the original point // back, i just pack and unpack it again - ge_p3_tobytes(tmp_pk, &PUBKEY_BASE); - ge_frombytes_negate_vartime(&PUBKEY_BASE, tmp_pk); - pubkey_base_initialized = 1; + ge_p3_tobytes(tmp_pk, &A); + ge_frombytes_negate_vartime(&A, tmp_pk); + if (!pubkey_base_initialized) { + pubkey_base_initialized = 1; + PUBKEY_BASE = A; // TODO use a proper cpy fn if any + } else { + ge25519_add(&PUBKEY_BASE, &PUBKEY_BASE, &A); + } } static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk) diff --git a/worker.h b/worker.h index f4d6764..24e5299 100644 --- a/worker.h +++ b/worker.h @@ -36,7 +36,7 @@ extern u8 determseed[SEED_LEN]; #endif extern void worker_init(void); -extern void ed25519_pubkey_setbase(const u8 base_pk[32]); +extern void ed25519_pubkey_addbase(const u8 base_pk[32]); extern char *makesname(void); extern size_t worker_batch_memuse(void); From 476d13574776e87ec6542d19e4d6f901e6a74baf Mon Sep 17 00:00:00 2001 From: dzwdz Date: Tue, 8 Nov 2022 09:51:30 +0100 Subject: [PATCH 09/14] fix typo in printhelp that's what i get for coding on my phone --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 967d931..8db9d42 100644 --- a/main.c +++ b/main.c @@ -134,7 +134,7 @@ static void printhelp(FILE *out,const char *progname) " (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 thr private parts of all\n" + " to be --combine'd with the private parts of all\n" " basekeys used\n" " --genbase base.priv base.pub\n" " generate base keys for trustless mining\n" From 532b61e4f98bc678467ddc3dabdf09d4a285cfb1 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Wed, 31 May 2023 09:50:08 +0000 Subject: [PATCH 10/14] Allow mining without --basekey again. --- main.c | 5 ----- worker.c | 23 ++++++++++++----------- worker_batch.inc.h | 7 +++---- worker_fast.inc.h | 4 +++- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/main.c b/main.c index 8db9d42..1fb1e06 100644 --- a/main.c +++ b/main.c @@ -682,11 +682,6 @@ int main(int argc,char **argv) exit(1); } - if (basekeys == 0) { - 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 3d444e1..cf8f7cf 100644 --- a/worker.c +++ b/worker.c @@ -211,7 +211,7 @@ static void reseedright(u8 sk[SECRET_LEN]) #include "ed25519/ed25519_impl_pre.h" ge_p3 ALIGN(16) PUBKEY_BASE; -int pubkey_base_initialized; +int pubkey_base_initialized = 0; #include "worker_impl.inc.h" // uses those globals @@ -219,14 +219,18 @@ void ed25519_pubkey_addbase(const u8 base_pk[32]) { ge_p3 ALIGN(16) A; u8 tmp_pk[32]; + ge_frombytes_negate_vartime(&A, base_pk); - // dumb hack: unpack flips the point. to get the original point - // back, i just pack and unpack it again + // dumb hack: The only available frombytes function flips the point. + // To get the original point back, I can just pack and unpack it again. ge_p3_tobytes(tmp_pk, &A); ge_frombytes_negate_vartime(&A, tmp_pk); + if (!pubkey_base_initialized) { + // note: PUBKEY_BASE could be initialized to the point at infinity + // to remove the need for pubkey_base_initialized. pubkey_base_initialized = 1; - PUBKEY_BASE = A; // TODO use a proper cpy fn if any + PUBKEY_BASE = A; } else { ge25519_add(&PUBKEY_BASE, &PUBKEY_BASE, &A); } @@ -235,14 +239,11 @@ void ed25519_pubkey_addbase(const u8 base_pk[32]) static int ed25519_pubkey_onbase(u8 *pk,const u8 *sk) { ge_p3 ALIGN(16) A; - - if (unlikely(pubkey_base_initialized == 0)) - abort(); - ge_scalarmult_base(&A, sk); - ge25519_add(&A, &A, &PUBKEY_BASE); + if (pubkey_base_initialized) { + ge25519_add(&A, &A, &PUBKEY_BASE); + } ge_p3_tobytes(pk,&A); - return 0; } @@ -251,7 +252,7 @@ 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"); + fprintf(stderr, "Sanity check failed. Please report this on Github, including the command line parameters you've used.\n"); abort(); } } diff --git a/worker_batch.inc.h b/worker_batch.inc.h index f7d5407..a6870e1 100644 --- a/worker_batch.inc.h +++ b/worker_batch.inc.h @@ -25,9 +25,6 @@ void *CRYPTO_NAMESPACE(worker_batch)(void *task) (void) task; #endif - if (unlikely(pubkey_base_initialized == 0)) - abort(); - PREFILTER memcpy(secret,skprefix,SKPREFIX_SIZE); @@ -50,7 +47,9 @@ void *CRYPTO_NAMESPACE(worker_batch)(void *task) ed25519_seckey_expand(sk,seed); ge_scalarmult_base(&ge_public,sk); - ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + if (pubkey_base_initialized) { + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + } for (counter = 0;counter < SIZE_MAX-(8*BATCHNUM);counter += 8*BATCHNUM) { ge_p1p1 ALIGN(16) sum; diff --git a/worker_fast.inc.h b/worker_fast.inc.h index 89608fb..0c23637 100644 --- a/worker_fast.inc.h +++ b/worker_fast.inc.h @@ -42,7 +42,9 @@ void *CRYPTO_NAMESPACE(worker_fast)(void *task) ed25519_seckey_expand(sk,seed); ge_scalarmult_base(&ge_public,sk); - ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + if (pubkey_base_initialized) { + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + } ge_p3_tobytes(pk,&ge_public); for (counter = 0;counter < SIZE_MAX-8;counter += 8) { From a1f7e9d9edf75ba4e76e88d51930ca466d2e3633 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Wed, 31 May 2023 10:11:35 +0000 Subject: [PATCH 11/14] Reenable deterministic mining. --- main.c | 5 ----- worker_batch_pass.inc.h | 3 +++ worker_fast_pass.inc.h | 3 +++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index 1fb1e06..82cfb4e 100644 --- a/main.c +++ b/main.c @@ -677,11 +677,6 @@ int main(int argc,char **argv) filters_add(arg); } - if (deterministic) { - fprintf(stderr, "deterministic trustless mining isn't supported yet.\n"); - exit(1); - } - if (yamlinput && yamloutput) { fprintf(stderr,"both -y and -Y does not make sense\n"); exit(1); diff --git a/worker_batch_pass.inc.h b/worker_batch_pass.inc.h index 0fa49ff..856f311 100644 --- a/worker_batch_pass.inc.h +++ b/worker_batch_pass.inc.h @@ -54,6 +54,9 @@ void *CRYPTO_NAMESPACE(worker_batch_pass)(void *task) ed25519_seckey_expand(sk,seed); ge_scalarmult_base(&ge_public,sk); + if (pubkey_base_initialized) { + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + } for (counter = oldcounter = 0;counter < DETERMINISTIC_LOOP_COUNT - (BATCHNUM - 1) * 8;counter += BATCHNUM * 8) { ge_p1p1 ALIGN(16) sum; diff --git a/worker_fast_pass.inc.h b/worker_fast_pass.inc.h index 793d600..ec90a6e 100644 --- a/worker_fast_pass.inc.h +++ b/worker_fast_pass.inc.h @@ -49,6 +49,9 @@ void *CRYPTO_NAMESPACE(worker_fast_pass)(void *task) ed25519_seckey_expand(sk,seed); ge_scalarmult_base(&ge_public,sk); + if (pubkey_base_initialized) { + ge25519_add(&ge_public, &ge_public, &PUBKEY_BASE); + } ge_p3_tobytes(pk,&ge_public); for (counter = oldcounter = 0;counter < DETERMINISTIC_LOOP_COUNT;counter += 8) { From 7fd0b14f4968bc191a44604edfc48d8ac8b05e88 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Wed, 31 May 2023 10:31:43 +0000 Subject: [PATCH 12/14] Change the basekey format, ensure the correct file is used. --- headers.h | 6 ++++++ main.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 headers.h diff --git a/headers.h b/headers.h new file mode 100644 index 0000000..21f4e43 --- /dev/null +++ b/headers.h @@ -0,0 +1,6 @@ +// includes the implicit NUL +#define HEADER_BASESK "mkp224o base_sk" +#define HEADER_BASESKLEN 16 + +#define HEADER_BASEPK "mkp224o base_pk" +#define HEADER_BASEPKLEN 16 diff --git a/main.c b/main.c index 82cfb4e..899de14 100644 --- a/main.c +++ b/main.c @@ -24,6 +24,7 @@ #include "ioutil.h" #include "common.h" #include "yaml.h" +#include "headers.h" #include "filters.h" @@ -292,6 +293,10 @@ static void genbase(const char *privpath, const char *pubpath) perror("couldn't open"); exit(1); } + if (fwrite(HEADER_BASESK, 1, HEADER_BASESKLEN, fp) != HEADER_BASESKLEN) { + perror("write"); + exit(1); + } if (fwrite(base_sk, 1, 32, fp) != 32) { perror("write"); exit(1); @@ -304,6 +309,10 @@ static void genbase(const char *privpath, const char *pubpath) perror("couldn't open"); exit(1); } + if (fwrite(HEADER_BASEPK, 1, HEADER_BASEPKLEN, fp) != HEADER_BASEPKLEN) { + perror("write"); + exit(1); + } if (fwrite(base_pk, 1, 32, fp) != 32) { perror("write"); exit(1); @@ -346,11 +355,19 @@ static void combine(int argc, char **argv) u8 base_sk[32], base_extsk[64]; fp = fopen(argv[i], "r"); if (fp == NULL) { - perror("failed to open base.priv"); + perror("couldn't open basekey"); + exit(1); + } + if (fread(base_sk, 1, HEADER_BASESKLEN, fp) != HEADER_BASESKLEN) { + perror("read"); + exit(1); + } + if (memcmp(base_sk, HEADER_BASESK, HEADER_BASESKLEN) != 0) { + fprintf(stderr, "\"%s\" isn't a valid base secret key.\n", argv[-1]); exit(1); } if (fread(base_sk, 1, sizeof base_sk, fp) != sizeof base_sk) { - perror("failed to read base.priv"); + perror("read"); exit(1); } fclose(fp); @@ -495,8 +512,16 @@ int main(int argc,char **argv) perror("couldn't open basekey"); exit(1); } + if (fread(base_pk, 1, HEADER_BASEPKLEN, fp) != HEADER_BASEPKLEN) { + perror("read"); + exit(1); + } + if (memcmp(base_pk, HEADER_BASEPK, HEADER_BASEPKLEN) != 0) { + fprintf(stderr, "\"%s\" isn't a valid base public key.\n", argv[-1]); + exit(1); + } if (fread(base_pk, 1, sizeof base_pk, fp) != sizeof base_pk) { - perror("incomplete read of base_pk"); + perror("read"); exit(1); } fclose(fp); From 98fb24d56a2bcea41a599299ec4008c43eb52238 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Wed, 31 May 2023 11:22:39 +0000 Subject: [PATCH 13/14] Use a different format for trustlessly mined "halfkeys". makes it a bit more foolproof, as I can check if they used the right keys etc. also requires you to actually combine it with your base key before slamming it into tor and wondering why it doesn't work --- headers.h | 8 ++++--- main.c | 68 +++++++++++++++++++++++++++++-------------------------- worker.c | 49 +++++++++++++++++++++++++++++---------- 3 files changed, 78 insertions(+), 47 deletions(-) diff --git a/headers.h b/headers.h index 21f4e43..df1bafc 100644 --- a/headers.h +++ b/headers.h @@ -1,6 +1,8 @@ -// includes the implicit NUL -#define HEADER_BASESK "mkp224o base_sk" +#define HEADER_BASESK "mkp224o base_sk\0" #define HEADER_BASESKLEN 16 -#define HEADER_BASEPK "mkp224o base_pk" +#define HEADER_BASEPK "mkp224o base_pk\0" #define HEADER_BASEPKLEN 16 + +#define HEADER_HALFKEY "mkp224o halfkey\0" +#define HEADER_HALFKEYLEN 16 diff --git a/main.c b/main.c index 899de14..b244519 100644 --- a/main.c +++ b/main.c @@ -139,7 +139,7 @@ static void printhelp(FILE *out,const char *progname) " basekeys used\n" " --genbase base.priv base.pub\n" " generate base keys for trustless mining\n" - " --combine hs_secret_key base.priv..\n" + " --combine halfkey base.priv..\n" " combine a mined hs_secret key with base key(s)\n" " -h, --help, --usage print help to stdout and quit\n" " -V, --version print version information to stdout and exit\n" @@ -324,7 +324,8 @@ static void genbase(const char *privpath, const char *pubpath) static void combine(int argc, char **argv) { - u8 secret[96]; + u8 halfkey[HEADER_HALFKEYLEN + SECRET_LEN + PUBLIC_LEN]; + u8 result[FORMATTED_SECRET_LEN]; FILE *fp; const char *minedpath = argv[0]; @@ -335,21 +336,21 @@ static void combine(int argc, char **argv) fp = fopen(minedpath, "r"); if (fp == NULL) { - perror("failed to open hs_secret_key"); + perror("failed to open halfkey"); exit(1); } - if (fread(secret, 1, 96, fp) != 96) { + if (fread(halfkey, sizeof halfkey, 1, fp) != 1) { 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"); + if (memcmp(halfkey, HEADER_HALFKEY, HEADER_HALFKEYLEN) != 0) { + fprintf(stderr, "Invalid halfkey format. The halfkey must be the first argument.\n"); exit(1); } fclose(fp); sc25519 ALIGN(16) a; - sc25519_from32bytes(&a, &secret[32]); + sc25519_from32bytes(&a, &halfkey[HEADER_HALFKEYLEN]); for (int i = 1; i < argc; i++) { u8 base_sk[32], base_extsk[64]; @@ -363,7 +364,7 @@ static void combine(int argc, char **argv) exit(1); } if (memcmp(base_sk, HEADER_BASESK, HEADER_BASESKLEN) != 0) { - fprintf(stderr, "\"%s\" isn't a valid base secret key.\n", argv[-1]); + fprintf(stderr, "\"%s\" isn't a valid base secret key.\n", argv[i]); exit(1); } if (fread(base_sk, 1, sizeof base_sk, fp) != sizeof base_sk) { @@ -384,43 +385,41 @@ static void combine(int argc, char **argv) ge25519_pack(pk, &A); // Save secret scalar. - sc25519_to32bytes(&secret[32], &a); + memcpy(result, "== ed25519v1-secret: type0 ==\0\0\0", SKPREFIX_SIZE); + sc25519_to32bytes(&result[SKPREFIX_SIZE], &a); // Compute the key's hash prefix. // See "Pseudorandom generation of r.", page 8 of https://ed25519.cr.yp.to/ed25519-20110926.pdf - // You're supposed to generate it together with the secret scalar, but - // we can't really do that here. As far as I can tell, it just needs to - // be another secret value. - // In normal Ed25519 you never have a pair of keys with the same secret - // scalar but different hash prefixes. If I generated hash prefixes - // independently from the secret scalar (such as by just using random - // bytes), you could get such a pair by running --combine multiple times. - // I don't know if that would mess anything up, but to err on the side - // of caution, I'm setting it to a hash of the secret scalar and the - // original generated key. - FIPS202_SHAKE256(secret, sizeof secret, &secret[64], 32); + // Usually it's generated together with the secret scalar using a hash + // function, but we can't do that here. As far as I can tell, it just + // needs to be another secret value. + // I'm setting it to a hash of the secret scalar to prevent generating + // multiple keys with the same secret scalar but different hash prefixes, + // which never occurs in normal ed25519. + FIPS202_SHAKE256(&result[SKPREFIX_SIZE], 32, &result[64], 32); - sc25519_from32bytes(&a, &secret[32]); ge25519_scalarmult_base(&A, &a); ge25519_pack(pk, &A); - printf("new pk: "); - for (size_t i = 0; i < sizeof(pk); i++) - printf("%02x ", pk[i]); - puts(""); + if (memcmp(pk, &halfkey[HEADER_HALFKEYLEN + SECRET_LEN], PUBLIC_LEN) != 0) { + fprintf(stderr,"Didn't get the expected public key. You probably didn't use the right basekey(s).\n"); + exit(1); + } - char *newname = malloc(strlen(minedpath) + strlen(".fixed") + 1); - strcpy(newname, minedpath); - strcat(newname, ".fixed"); - printf("saving to %s\n", newname); + char *newpath = malloc(strlen(minedpath) + strlen("hs_ed25519_secret_key") + 1); + strcpy(newpath, minedpath); + char *slash = strrchr(newpath, '/'); + slash = slash ? slash + 1 : newpath; + strcpy(slash, "hs_ed25519_secret_key"); + printf("saving to %s\n", newpath); - fp = fopen(newname, "w"); + fp = fopen(newpath, "w"); if (!fp) { perror("couldn't open"); exit(1); } - if (fwrite(secret, 1, sizeof secret, fp) != sizeof secret) { - perror("failed to write fixed privkey"); + if (fwrite(result, sizeof result, 1, fp) != 1) { + perror("failed to write hs_ed25519_secret_key"); exit(1); } fclose(fp); @@ -712,6 +711,11 @@ int main(int argc,char **argv) exit(1); } + if (yamloutput && 0 < basekeys) { + fprintf(stderr,"-y is incompatible with --basekey\n"); + exit(1); + } + #ifdef PASSPHRASE if (checkpointfile && !deterministic) { fprintf(stderr,"--checkpoint requires passphrase\n"); diff --git a/worker.c b/worker.c index cf8f7cf..2db6ab8 100644 --- a/worker.c +++ b/worker.c @@ -20,11 +20,15 @@ #include "ioutil.h" #include "common.h" #include "yaml.h" +#include "headers.h" #include "worker.h" #include "filters.h" +#include "ed25519/ed25519.h" +#include "ed25519/ed25519_impl_pre.h" + #ifndef _WIN32 #define FSZ "%zu" #else @@ -51,6 +55,9 @@ size_t numneedgenerate = 0; char *workdir = 0; size_t workdirlen = 0; +ge_p3 ALIGN(16) PUBKEY_BASE; +int pubkey_base_initialized = 0; + #ifdef PASSPHRASE // How many times we loop before a reseed @@ -101,11 +108,36 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) return; } - strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); - writetofile(sname,secret,FORMATTED_SECRET_LEN,1); - - strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); - writetofile(sname,pubonion,FORMATTED_PUBLIC_LEN,0); + if (pubkey_base_initialized == 0) { + strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); + writetofile(sname,secret,FORMATTED_SECRET_LEN,1); + + strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); + writetofile(sname,pubonion,FORMATTED_PUBLIC_LEN,0); + } else { + strcpy(&sname[onionendpos],"/halfkey"); + FILE *fp = fopen(sname,"w"); + if (!fp) { + perror("couldn't create output file"); + return; + } + if (fwrite(HEADER_HALFKEY,HEADER_HALFKEYLEN,1,fp) != 1) { + perror("couldn't write to output file"); + fclose(fp); + return; + } + if (fwrite(&secret[SKPREFIX_SIZE],SECRET_LEN,1,fp) != 1) { + perror("couldn't write to output file"); + fclose(fp); + return; + } + if (fwrite(&pubonion[PKPREFIX_SIZE],PUBLIC_LEN,1,fp) != 1) { + perror("couldn't write to output file"); + fclose(fp); + return; + } + fclose(fp); + } strcpy(&sname[onionendpos],"/hostname"); FILE *hfile = fopen(sname,"w"); @@ -206,13 +238,6 @@ static void reseedright(u8 sk[SECRET_LEN]) #define BATCHNUM 2048 #endif - -#include "ed25519/ed25519.h" -#include "ed25519/ed25519_impl_pre.h" - -ge_p3 ALIGN(16) PUBKEY_BASE; -int pubkey_base_initialized = 0; - #include "worker_impl.inc.h" // uses those globals void ed25519_pubkey_addbase(const u8 base_pk[32]) From e17e26786cb1e8073d85417968a2a69173ff2919 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Wed, 31 May 2023 11:35:45 +0000 Subject: [PATCH 14/14] restore normal README / disable ref10 --- README.md | 55 ++++++++++++++++++++++++++-------------------------- configure.ac | 1 + 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 1f72f83..198c7cf 100644 --- a/README.md +++ b/README.md @@ -3,34 +3,6 @@ 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/zzz*.onion/hs_ed25519_secret_key out/base.priv -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 -``` -I recommend doing a test run with a short filter before mining "for real". Some settings are currently broken. -It's also possible to specify [multiple basekeys](https://github.com/cathugger/mkp224o/issues/60#issuecomment-1305033288). - -#### 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 -* no support for ref10 -* no automated tests - ### Requirements * C99 compatible compiler (gcc and clang should work) @@ -75,6 +47,33 @@ Use `-h` switch to obtain all available options. I highly recommend reading [OPTIMISATION.txt][OPTIMISATION] for performance-related tips. +#### Trustless mining +[Trustless mining](https://github.com/cathugger/mkp224o/issues/60) lets someone +else safely mine a vanity .onion for you without letting them get its private +key. Beware that it's an experimental feature - it's not yet completely tested, +audited, nor does it work with all configurations. + +``` +# example usage +$ ./mkp224o --genbase base.priv base.pub +writing private base key to 'base.priv' +writing public base key to 'base.pub' +done. + +$ ./mkp224o --basekey base.pub -d ~/keys -n 1 neko +set workdir: /home/dzwdz/keys/ +sorting filters... done. +filters: + neko +in total, 1 filter +using 1 thread +neko3q2neskgkol2gyg2hia46xnavwt4yfy2nvj2pulmvc7lxk6yfkad.onion +waiting for threads to finish... done. + +$ ./mkp224o --combine ~/keys/neko3q2nes*.onion/halfkey base.priv +saving to ~/keys/neko3q2neskgkol2gyg2hia46xnavwt4yfy2nvj2pulmvc7lxk6yfkad.onion/hs_ed25519_secret_key +``` + ### FAQ and other useful info * How do I generate address? diff --git a/configure.ac b/configure.ac index a2053c8..dde93a2 100644 --- a/configure.ac +++ b/configure.ac @@ -83,6 +83,7 @@ AC_ARG_ENABLE([ref10], [AS_HELP_STRING([--enable-ref10], [use SUPERCOP ref10 ed25519 implementation @<:@default=no@:>@])], [ + AC_MSG_ERROR(ref10 is temporarily unsupported. try using donna instead) AS_IF([test x"$ed25519impl" != x"" -a "$ed25519impl" != "ref10"], [AC_MSG_ERROR(only one ed25519 implementation can be defined)]) ed25519impl="ref10"