Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trustless mining #98

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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?
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
23 changes: 22 additions & 1 deletion ed25519/ed25519_impl_pre.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -185,19 +186,39 @@ 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

#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


#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)
{
Expand Down
8 changes: 8 additions & 0 deletions headers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#define HEADER_BASESK "mkp224o base_sk\0"
#define HEADER_BASESKLEN 16

#define HEADER_BASEPK "mkp224o base_pk\0"
#define HEADER_BASEPKLEN 16

#define HEADER_HALFKEY "mkp224o halfkey\0"
#define HEADER_HALFKEYLEN 16
209 changes: 209 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "ioutil.h"
#include "common.h"
#include "yaml.h"
#include "headers.h"

#include "filters.h"

Expand Down Expand Up @@ -132,6 +133,14 @@ 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 the private parts of all\n"
" basekeys used\n"
" --genbase base.priv base.pub\n"
" generate base keys for trustless mining\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"
,progname,progname);
Expand Down Expand Up @@ -262,6 +271,161 @@ 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];
u8 base_extsk[64];
ge_p3 ALIGN(16) A;
FILE *fp;

randombytes(base_sk, sizeof base_sk);
ed25519_seckey_expand(base_extsk, base_sk);
ge_scalarmult_base(&A, base_extsk);
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(HEADER_BASESK, 1, HEADER_BASESKLEN, fp) != HEADER_BASESKLEN) {
perror("write");
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(HEADER_BASEPK, 1, HEADER_BASEPKLEN, fp) != HEADER_BASEPKLEN) {
perror("write");
exit(1);
}
if (fwrite(base_pk, 1, 32, fp) != 32) {
perror("write");
exit(1);
}
fclose(fp);

puts("done.");
}

static void combine(int argc, char **argv)
{
u8 halfkey[HEADER_HALFKEYLEN + SECRET_LEN + PUBLIC_LEN];
u8 result[FORMATTED_SECRET_LEN];
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(minedpath, "r");
if (fp == NULL) {
perror("failed to open halfkey");
exit(1);
}
if (fread(halfkey, sizeof halfkey, 1, fp) != 1) {
perror("failed to read hs_secret_key");
exit(1);
}
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, &halfkey[HEADER_HALFKEYLEN]);

for (int i = 1; i < argc; i++) {
u8 base_sk[32], base_extsk[64];
fp = fopen(argv[i], "r");
if (fp == NULL) {
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[i]);
exit(1);
}
if (fread(base_sk, 1, sizeof base_sk, fp) != sizeof base_sk) {
perror("read");
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.
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
// 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);

ge25519_scalarmult_base(&A, &a);
ge25519_pack(pk, &A);

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 *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(newpath, "w");
if (!fp) {
perror("couldn't open");
exit(1);
}
if (fwrite(result, sizeof result, 1, fp) != 1) {
perror("failed to write hs_ed25519_secret_key");
exit(1);
}
fclose(fp);
}
#include "ed25519/ed25519_impl_post.h"

int main(int argc,char **argv)
{
const char *outfile = 0;
Expand All @@ -285,6 +449,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");
Expand Down Expand Up @@ -326,6 +491,45 @@ int main(int argc,char **argv)
printversion();
exit(0);
}
else if (!strcmp(arg,"combine")) {
combine(argc,argv);
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--) {
u8 base_pk[32];
FILE *fp = fopen(*argv++, "r");
if (!fp) {
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("read");
exit(1);
}
fclose(fp);
ed25519_pubkey_addbase(base_pk);
basekeys++;
} else {
e_additional();
}
}
else if (!strcmp(arg,"rawyaml"))
yamlraw = 1;
#ifdef PASSPHRASE
Expand Down Expand Up @@ -507,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");
Expand Down
Loading