Skip to content

Commit 9478cd1

Browse files
committed
Build against local OpenSSL 1.0.2/PCRE1, add clipboard support (-c)
This code targets OpenSSL's pre-1.1 BIGNUM layout and the legacy PCRE1 API, neither of which is available as a system package anymore on current distros. Rather than rewrite the BIGNUM handling for opaque structs, point the Makefile at self-built OpenSSL 1.0.2u/PCRE 8.45 via overridable OPENSSL_PATH/PCRE_PATH variables (defaulting under $HOME). Also add -c, which copies the private key to the clipboard instead of printing it, clearing the clipboard automatically after 45 seconds if it still holds what we put there (same approach as pass). Prefers xclip when an X11 display is available, and falls back to the OSC 52 terminal escape sequence otherwise (e.g. plain SSH into a headless box, no X server needed), reporting which method was used. Falls back to printing the key if no clipboard mechanism is available, so the key is never lost.
1 parent cd1a728 commit 9478cd1

7 files changed

Lines changed: 238 additions & 17 deletions

File tree

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
LIBS=-lpcre -lcrypto -lm -lpthread
2-
CFLAGS=-ggdb -O3 -Wall
1+
OPENSSL_PATH ?= $(HOME)/development/crypto/openssl-1.0.2u
2+
PCRE_PATH ?= $(HOME)/development/crypto/pcre-8.45
3+
4+
LIBS=-lpcre -lcrypto -lm -lpthread -ldl
5+
CFLAGS=-ggdb -O3 -Wall -I$(OPENSSL_PATH)/include -L$(OPENSSL_PATH) -I$(PCRE_PATH) -L$(PCRE_PATH)/.libs
36
OBJS=vanitygen.o oclvanitygen.o oclvanityminer.o oclengine.o keyconv.o pattern.o util.o
47
PROGS=vanitygen keyconv oclvanitygen oclvanityminer
58

README

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,21 @@ CPU and does not use OpenCL or CUDA. Vanitygen is also a bit more
99
user-friendly in that it provides feedback on its rate of progress and
1010
how many keys it has checked.
1111

12-
Vanitygen is written in C, and is provided in source code form and
13-
pre-built Win32 binaries. At present, vanitygen can be built on Linux,
12+
Vanitygen is written in C, and is provided in source code form and
13+
pre-built Win32 binaries. At present, vanitygen can be built on Linux,
1414
and requires the openssl and pcre libraries.
1515

16+
Vanitygen targets the OpenSSL 1.0.x API and the legacy PCRE1 API. It will
17+
not build against OpenSSL 1.1+ (where BIGNUM became an opaque type) or
18+
PCRE2. On a system that only has newer versions installed, build OpenSSL
19+
1.0.2 and PCRE1 from source and point the Makefile at them:
20+
21+
$ make OPENSSL_PATH=/path/to/openssl-1.0.2u PCRE_PATH=/path/to/pcre-8.45
22+
23+
OPENSSL_PATH and PCRE_PATH default to $HOME/development/crypto/openssl-1.0.2u
24+
and $HOME/development/crypto/pcre-8.45, so a plain "make" picks them up
25+
automatically if built there.
26+
1627
Vanitygen can generate regular bitcoin addresses, namecoin addresses,
1728
and testnet addresses.
1829

@@ -32,10 +43,18 @@ lists of prefixes will have little effect on search rate. Searching
3243
for N regular expressions will have varied performance depending on the
3344
complexity of the expressions, but O(N) performance can be expected.
3445

35-
By default, vanitygen will spawn one worker thread for each CPU in your
36-
system. If you wish to limit the number of worker threads created by
46+
By default, vanitygen will spawn one worker thread for each CPU in your
47+
system. If you wish to limit the number of worker threads created by
3748
vanitygen, use the "-t" option.
3849

50+
For safety, the "-c" option copies the private key to the clipboard
51+
instead of printing it to the screen (the address is still printed, since
52+
it isn't sensitive). The clipboard is cleared automatically 45 seconds
53+
later, provided nothing else has been copied in the meantime -- the same
54+
approach used by "pass" (passwordstore.org). This requires "xclip" and a
55+
running X11 session; if either is unavailable, vanitygen falls back to
56+
printing the private key as usual.
57+
3958
The example below completed quicker than average, and took about 45 sec
4059
to finish, using both cores of my aging Core 2 Duo E6600:
4160

pattern.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,12 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern)
553553
vg_encode_privkey(pkey, vcp->vc_privtype, privkey_buf);
554554
}
555555

556+
int clipped = vcp->vc_clip ?
557+
vg_clipboard_copy_secret(privkey_buf, 45) : VG_CLIPBOARD_NONE;
558+
const char *clip_method =
559+
(clipped == VG_CLIPBOARD_XCLIP) ? "xclip" :
560+
(clipped == VG_CLIPBOARD_OSC52) ? "terminal (OSC 52)" : "";
561+
556562
if (!vcp->vc_result_file || (vcp->vc_verbose > 0)) {
557563
printf("\r%79s\rPattern: %s\n", "", pattern);
558564
}
@@ -563,22 +569,35 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern)
563569
len = i2o_ECPublicKey(pkey, &pend);
564570
printf("Pubkey (hex): ");
565571
dumphex(key_buf, len);
566-
printf("Privkey (hex): ");
567-
dumpbn(EC_KEY_get0_private_key(pkey));
568-
pend = key_buf;
569-
len = i2d_ECPrivateKey(pkey, &pend);
570-
printf("Privkey (ASN1): ");
571-
dumphex(key_buf, len);
572+
if (clipped) {
573+
printf("Privkey (hex): "
574+
"(copied to clipboard via %s, "
575+
"clearing in 45s)\n", clip_method);
576+
} else {
577+
printf("Privkey (hex): ");
578+
dumpbn(EC_KEY_get0_private_key(pkey));
579+
pend = key_buf;
580+
len = i2d_ECPrivateKey(pkey, &pend);
581+
printf("Privkey (ASN1): ");
582+
dumphex(key_buf, len);
583+
}
572584
}
573585

574586
}
575587

576588
if (!vcp->vc_result_file || (vcp->vc_verbose > 0)) {
577589
if (isscript)
578590
printf("P2SHAddress: %s\n", addr2_buf);
579-
printf("Address: %s\n"
580-
"%s: %s\n",
581-
addr_buf, keytype, privkey_buf);
591+
if (clipped) {
592+
printf("Address: %s\n"
593+
"%s: (copied to clipboard via %s, "
594+
"clearing in 45s)\n",
595+
addr_buf, keytype, clip_method);
596+
} else {
597+
printf("Address: %s\n"
598+
"%s: %s\n",
599+
addr_buf, keytype, privkey_buf);
600+
}
582601
}
583602

584603
if (vcp->vc_result_file) {

pattern.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ struct _vg_context_s {
100100
int vc_remove_on_match;
101101
int vc_only_one;
102102
int vc_verbose;
103+
int vc_clip;
103104
enum vg_format vc_format;
104105
int vc_pubkeytype;
105106
EC_POINT *vc_pubkey_base;

util.c

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
#include "pattern.h"
4040
#include "util.h"
4141

42+
#if !defined(_WIN32)
43+
#include <unistd.h>
44+
#include <sys/wait.h>
45+
#endif
46+
4247
const char *vg_b58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4348

4449
const signed char vg_b58_reverse_map[256] = {
@@ -1086,3 +1091,163 @@ vg_read_file(FILE *fp, char ***result, int *rescount)
10861091

10871092
return ret;
10881093
}
1094+
1095+
1096+
#if !defined(_WIN32)
1097+
/*
1098+
* Copy a secret to the X11 clipboard using xclip, and schedule it to be
1099+
* cleared after timeout_secs -- but only if the clipboard still holds
1100+
* exactly what we put there, so we don't clobber something else the user
1101+
* copied in the meantime. Modeled after the clipboard handling in "pass"
1102+
* (passwordstore.org).
1103+
*
1104+
* Returns 1 on success, 0 if xclip is unavailable or failed.
1105+
*/
1106+
static int
1107+
vg_clipboard_copy_xclip(const char *secret, int timeout_secs)
1108+
{
1109+
FILE *fp;
1110+
pid_t pid;
1111+
char cmd[256];
1112+
int status;
1113+
1114+
if (system("command -v xclip >/dev/null 2>&1") != 0)
1115+
return 0;
1116+
1117+
fp = popen("xclip -selection clipboard 2>/dev/null", "w");
1118+
if (!fp)
1119+
return 0;
1120+
fputs(secret, fp);
1121+
status = pclose(fp);
1122+
if (status != 0)
1123+
return 0;
1124+
1125+
pid = fork();
1126+
if (pid < 0)
1127+
/* Copy succeeded even though we can't schedule the clear */
1128+
return 1;
1129+
1130+
if (pid == 0) {
1131+
/* Detach a grandchild so the parent doesn't have to wait */
1132+
if (fork() > 0)
1133+
_exit(0);
1134+
setsid();
1135+
close(0);
1136+
close(1);
1137+
close(2);
1138+
1139+
if (setenv("VG_CLIP_SECRET", secret, 1) == 0) {
1140+
snprintf(cmd, sizeof(cmd),
1141+
"sleep %d; "
1142+
"[ \"$(xclip -selection clipboard -o "
1143+
"2>/dev/null)\" = \"$VG_CLIP_SECRET\" ] && "
1144+
"printf '' | xclip -selection clipboard",
1145+
timeout_secs);
1146+
system(cmd);
1147+
}
1148+
_exit(0);
1149+
}
1150+
1151+
/* Reap the intermediate child, which exits immediately */
1152+
waitpid(pid, NULL, 0);
1153+
return 1;
1154+
}
1155+
1156+
/*
1157+
* Copy a secret to the terminal's clipboard using the OSC 52 escape
1158+
* sequence, understood by most modern terminal emulators (Windows
1159+
* Terminal, iTerm2, kitty, xterm, mintty, ...) without requiring an X11
1160+
* server -- this is what makes it work over a plain SSH session into a
1161+
* headless box. Scheduled clearing has no readback to compare against
1162+
* (OSC 52 has no reliable, portable query), so it unconditionally sends
1163+
* an empty payload after timeout_secs, which terminals interpret as
1164+
* "clear the clipboard".
1165+
*
1166+
* Returns 1 on success, 0 if no terminal is attached.
1167+
*/
1168+
static int
1169+
vg_clipboard_copy_osc52(const char *secret, int timeout_secs)
1170+
{
1171+
FILE *tty;
1172+
char *b64;
1173+
int b64_len;
1174+
pid_t pid;
1175+
1176+
tty = fopen("/dev/tty", "w");
1177+
if (!tty)
1178+
return 0;
1179+
1180+
b64_len = 4 * ((strlen(secret) + 2) / 3) + 1;
1181+
b64 = malloc(b64_len);
1182+
if (!b64) {
1183+
fclose(tty);
1184+
return 0;
1185+
}
1186+
EVP_EncodeBlock((unsigned char *)b64,
1187+
(const unsigned char *)secret, strlen(secret));
1188+
1189+
fprintf(tty, "\x1b]52;c;%s\x07", b64);
1190+
fflush(tty);
1191+
fclose(tty);
1192+
free(b64);
1193+
1194+
pid = fork();
1195+
if (pid < 0)
1196+
/* Copy succeeded even though we can't schedule the clear */
1197+
return 1;
1198+
1199+
if (pid == 0) {
1200+
/* Detach a grandchild so the parent doesn't have to wait */
1201+
if (fork() > 0)
1202+
_exit(0);
1203+
setsid();
1204+
close(0);
1205+
close(1);
1206+
close(2);
1207+
1208+
sleep(timeout_secs);
1209+
tty = fopen("/dev/tty", "w");
1210+
if (tty) {
1211+
fprintf(tty, "\x1b]52;c;\x07");
1212+
fflush(tty);
1213+
fclose(tty);
1214+
}
1215+
_exit(0);
1216+
}
1217+
1218+
/* Reap the intermediate child, which exits immediately */
1219+
waitpid(pid, NULL, 0);
1220+
return 1;
1221+
}
1222+
#endif /* !defined(_WIN32) */
1223+
1224+
/*
1225+
* Copy a secret (e.g. a private key) to the clipboard, preferring the X11
1226+
* clipboard via xclip when a display is available, and falling back to
1227+
* the OSC 52 terminal escape sequence otherwise (e.g. over a plain SSH
1228+
* session with no X server).
1229+
*
1230+
* Returns VG_CLIPBOARD_XCLIP or VG_CLIPBOARD_OSC52 if the secret was
1231+
* copied to the clipboard, VG_CLIPBOARD_NONE if no clipboard mechanism
1232+
* was available (in which case the caller should fall back to printing
1233+
* the secret).
1234+
*/
1235+
int
1236+
vg_clipboard_copy_secret(const char *secret, int timeout_secs)
1237+
{
1238+
#if defined(_WIN32)
1239+
return VG_CLIPBOARD_NONE;
1240+
#else
1241+
if (getenv("DISPLAY") &&
1242+
vg_clipboard_copy_xclip(secret, timeout_secs))
1243+
return VG_CLIPBOARD_XCLIP;
1244+
1245+
if (vg_clipboard_copy_osc52(secret, timeout_secs))
1246+
return VG_CLIPBOARD_OSC52;
1247+
1248+
fprintf(stderr,
1249+
"vanitygen: no clipboard available, "
1250+
"printing private key instead\n");
1251+
return VG_CLIPBOARD_NONE;
1252+
#endif
1253+
}

util.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,10 @@ extern int vg_check_password_complexity(const char *pass, int verbose);
7575

7676
extern int vg_read_file(FILE *fp, char ***result, int *rescount);
7777

78+
#define VG_CLIPBOARD_NONE 0
79+
#define VG_CLIPBOARD_XCLIP 1
80+
#define VG_CLIPBOARD_OSC52 2
81+
82+
extern int vg_clipboard_copy_secret(const char *secret, int timeout_secs);
83+
7884
#endif /* !defined (__VG_UTIL_H__) */

vanitygen.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ usage(const char *name)
295295
{
296296
fprintf(stderr,
297297
"Vanitygen %s (" OPENSSL_VERSION_TEXT ")\n"
298-
"Usage: %s [-vqnrik1NT] [-t <threads>] [-f <filename>|-] [<pattern>...]\n"
298+
"Usage: %s [-vqnrik1cNT] [-t <threads>] [-f <filename>|-] [<pattern>...]\n"
299299
"Generates a bitcoin receiving address matching <pattern>, and outputs the\n"
300300
"address and associated private key. The private key may be stored in a safe\n"
301301
"location or imported into a bitcoin client to spend any balance received on\n"
@@ -311,6 +311,9 @@ usage(const char *name)
311311
"-i Case-insensitive prefix search\n"
312312
"-k Keep pattern and continue search after finding a match\n"
313313
"-1 Stop after first match\n"
314+
"-c Copy private key to the clipboard instead of printing it\n"
315+
" (cleared automatically after 45 seconds; uses xclip if an\n"
316+
" X display is available, otherwise the terminal clipboard)\n"
314317
"-N Generate namecoin address\n"
315318
"-T Generate bitcoin testnet address\n"
316319
"-X <version> Generate address with the given version\n"
@@ -342,6 +345,7 @@ main(int argc, char **argv)
342345
int simulate = 0;
343346
int remove_on_match = 1;
344347
int only_one = 0;
348+
int clip = 0;
345349
int prompt_password = 0;
346350
int opt;
347351
char *seedfile = NULL;
@@ -361,7 +365,7 @@ main(int argc, char **argv)
361365

362366
int i;
363367

364-
while ((opt = getopt(argc, argv, "vqnrik1eE:P:NTX:F:t:h?f:o:s:")) != -1) {
368+
while ((opt = getopt(argc, argv, "vqnrik1ceE:P:NTX:F:t:h?f:o:s:")) != -1) {
365369
switch (opt) {
366370
case 'v':
367371
verbose = 2;
@@ -384,6 +388,9 @@ main(int argc, char **argv)
384388
case '1':
385389
only_one = 1;
386390
break;
391+
case 'c':
392+
clip = 1;
393+
break;
387394
case 'N':
388395
addrtype = 52;
389396
privtype = 180;
@@ -551,6 +558,7 @@ main(int argc, char **argv)
551558
vcp->vc_format = format;
552559
vcp->vc_pubkeytype = pubkeytype;
553560
vcp->vc_pubkey_base = pubkey_base;
561+
vcp->vc_clip = clip;
554562

555563
vcp->vc_output_match = vg_output_match_console;
556564
vcp->vc_output_timing = vg_output_timing_console;

0 commit comments

Comments
 (0)