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
This commit is contained in:
dzwdz 2023-05-31 11:22:39 +00:00
parent 7fd0b14f49
commit 98fb24d56a
3 changed files with 77 additions and 46 deletions

View file

@ -1,6 +1,8 @@
// includes the implicit NUL #define HEADER_BASESK "mkp224o base_sk\0"
#define HEADER_BASESK "mkp224o base_sk"
#define HEADER_BASESKLEN 16 #define HEADER_BASESKLEN 16
#define HEADER_BASEPK "mkp224o base_pk" #define HEADER_BASEPK "mkp224o base_pk\0"
#define HEADER_BASEPKLEN 16 #define HEADER_BASEPKLEN 16
#define HEADER_HALFKEY "mkp224o halfkey\0"
#define HEADER_HALFKEYLEN 16

68
main.c
View file

@ -139,7 +139,7 @@ static void printhelp(FILE *out,const char *progname)
" basekeys used\n" " basekeys used\n"
" --genbase base.priv base.pub\n" " --genbase base.priv base.pub\n"
" generate base keys for trustless mining\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" " combine a mined hs_secret key with base key(s)\n"
" -h, --help, --usage print help to stdout and quit\n" " -h, --help, --usage print help to stdout and quit\n"
" -V, --version print version information to stdout and exit\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) 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; FILE *fp;
const char *minedpath = argv[0]; const char *minedpath = argv[0];
@ -335,21 +336,21 @@ static void combine(int argc, char **argv)
fp = fopen(minedpath, "r"); fp = fopen(minedpath, "r");
if (fp == NULL) { if (fp == NULL) {
perror("failed to open hs_secret_key"); perror("failed to open halfkey");
exit(1); exit(1);
} }
if (fread(secret, 1, 96, fp) != 96) { if (fread(halfkey, sizeof halfkey, 1, fp) != 1) {
perror("failed to read hs_secret_key"); perror("failed to read hs_secret_key");
exit(1); exit(1);
} }
if (memcmp(secret, "== ed25519v1-secret: type0 ==\0\0\0", 32) != 0) { if (memcmp(halfkey, HEADER_HALFKEY, HEADER_HALFKEYLEN) != 0) {
fprintf(stderr, "invalid hs_secret_key format.\nare you sure you picked the right file?\n"); fprintf(stderr, "Invalid halfkey format. The halfkey must be the first argument.\n");
exit(1); exit(1);
} }
fclose(fp); fclose(fp);
sc25519 ALIGN(16) a; sc25519 ALIGN(16) a;
sc25519_from32bytes(&a, &secret[32]); sc25519_from32bytes(&a, &halfkey[HEADER_HALFKEYLEN]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
u8 base_sk[32], base_extsk[64]; u8 base_sk[32], base_extsk[64];
@ -363,7 +364,7 @@ static void combine(int argc, char **argv)
exit(1); exit(1);
} }
if (memcmp(base_sk, HEADER_BASESK, HEADER_BASESKLEN) != 0) { 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); exit(1);
} }
if (fread(base_sk, 1, sizeof base_sk, fp) != sizeof base_sk) { 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); ge25519_pack(pk, &A);
// Save secret scalar. // 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. // Compute the key's hash prefix.
// See "Pseudorandom generation of r.", page 8 of https://ed25519.cr.yp.to/ed25519-20110926.pdf // 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 // Usually it's generated together with the secret scalar using a hash
// we can't really do that here. As far as I can tell, it just needs to // function, but we can't do that here. As far as I can tell, it just
// be another secret value. // needs to be another secret value.
// In normal Ed25519 you never have a pair of keys with the same secret // I'm setting it to a hash of the secret scalar to prevent generating
// scalar but different hash prefixes. If I generated hash prefixes // multiple keys with the same secret scalar but different hash prefixes,
// independently from the secret scalar (such as by just using random // which never occurs in normal ed25519.
// bytes), you could get such a pair by running --combine multiple times. FIPS202_SHAKE256(&result[SKPREFIX_SIZE], 32, &result[64], 32);
// 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_scalarmult_base(&A, &a);
ge25519_pack(pk, &A); ge25519_pack(pk, &A);
printf("new pk: "); if (memcmp(pk, &halfkey[HEADER_HALFKEYLEN + SECRET_LEN], PUBLIC_LEN) != 0) {
for (size_t i = 0; i < sizeof(pk); i++) fprintf(stderr,"Didn't get the expected public key. You probably didn't use the right basekey(s).\n");
printf("%02x ", pk[i]); exit(1);
puts(""); }
char *newname = malloc(strlen(minedpath) + strlen(".fixed") + 1); char *newpath = malloc(strlen(minedpath) + strlen("hs_ed25519_secret_key") + 1);
strcpy(newname, minedpath); strcpy(newpath, minedpath);
strcat(newname, ".fixed"); char *slash = strrchr(newpath, '/');
printf("saving to %s\n", newname); 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) { if (!fp) {
perror("couldn't open"); perror("couldn't open");
exit(1); exit(1);
} }
if (fwrite(secret, 1, sizeof secret, fp) != sizeof secret) { if (fwrite(result, sizeof result, 1, fp) != 1) {
perror("failed to write fixed privkey"); perror("failed to write hs_ed25519_secret_key");
exit(1); exit(1);
} }
fclose(fp); fclose(fp);
@ -712,6 +711,11 @@ int main(int argc,char **argv)
exit(1); exit(1);
} }
if (yamloutput && 0 < basekeys) {
fprintf(stderr,"-y is incompatible with --basekey\n");
exit(1);
}
#ifdef PASSPHRASE #ifdef PASSPHRASE
if (checkpointfile && !deterministic) { if (checkpointfile && !deterministic) {
fprintf(stderr,"--checkpoint requires passphrase\n"); fprintf(stderr,"--checkpoint requires passphrase\n");

View file

@ -20,11 +20,15 @@
#include "ioutil.h" #include "ioutil.h"
#include "common.h" #include "common.h"
#include "yaml.h" #include "yaml.h"
#include "headers.h"
#include "worker.h" #include "worker.h"
#include "filters.h" #include "filters.h"
#include "ed25519/ed25519.h"
#include "ed25519/ed25519_impl_pre.h"
#ifndef _WIN32 #ifndef _WIN32
#define FSZ "%zu" #define FSZ "%zu"
#else #else
@ -51,6 +55,9 @@ size_t numneedgenerate = 0;
char *workdir = 0; char *workdir = 0;
size_t workdirlen = 0; size_t workdirlen = 0;
ge_p3 ALIGN(16) PUBKEY_BASE;
int pubkey_base_initialized = 0;
#ifdef PASSPHRASE #ifdef PASSPHRASE
// How many times we loop before a reseed // How many times we loop before a reseed
@ -101,11 +108,36 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion)
return; return;
} }
if (pubkey_base_initialized == 0) {
strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
writetofile(sname,secret,FORMATTED_SECRET_LEN,1); writetofile(sname,secret,FORMATTED_SECRET_LEN,1);
strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
writetofile(sname,pubonion,FORMATTED_PUBLIC_LEN,0); 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"); strcpy(&sname[onionendpos],"/hostname");
FILE *hfile = fopen(sname,"w"); FILE *hfile = fopen(sname,"w");
@ -206,13 +238,6 @@ static void reseedright(u8 sk[SECRET_LEN])
#define BATCHNUM 2048 #define BATCHNUM 2048
#endif #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 #include "worker_impl.inc.h" // uses those globals
void ed25519_pubkey_addbase(const u8 base_pk[32]) void ed25519_pubkey_addbase(const u8 base_pk[32])