Skip to content

Commit dd3e11c

Browse files
committed
Build against local OpenSSL 1.0.2/PCRE1, add -c clipboard flag
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). Falls back to printing the key if xclip or an X11 display isn't available, so the key is never lost.
1 parent cd1a728 commit dd3e11c

8 files changed

Lines changed: 199 additions & 17 deletions

File tree

CLAUDE.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What this is
6+
7+
Vanitygen: a command-line vanity Bitcoin (and altcoin-family) address generator, written in C. Given a desired prefix or regex pattern, it brute-force searches EC keypairs until it finds an address matching the pattern. A CPU implementation (`vanitygen`) and an OpenCL/GPU implementation (`oclvanitygen`, `oclvanityminer`) share the same pattern-matching core.
8+
9+
## Build
10+
11+
```
12+
make # builds vanitygen and keyconv only (the "most" target, no OpenCL deps needed)
13+
make all # also builds oclvanitygen and oclvanityminer (requires OpenCL + libcurl)
14+
make clean
15+
```
16+
17+
Dependencies: OpenSSL (`libcrypto`), PCRE (`libpcre`), pthreads. OpenCL targets additionally need an OpenCL SDK/driver and `libcurl` (oclvanityminer only, for pool mining).
18+
19+
This code targets the OpenSSL 1.0.x API (`BIGNUM` as a transparent struct) and the legacy PCRE1 API (`<pcre.h>`), not OpenSSL 1.1+ (opaque `BIGNUM`) or PCRE2. On a system that only has newer versions installed system-wide, build OpenSSL 1.0.2 and PCRE1 from source under `$HOME/development/crypto/` and point the Makefile at them, e.g.:
20+
21+
```
22+
make OPENSSL_PATH=$HOME/development/crypto/openssl-1.0.2u PCRE_PATH=$HOME/development/crypto/pcre-8.45
23+
```
24+
25+
`OPENSSL_PATH`/`PCRE_PATH` default to those same paths under `$HOME`, so a plain `make` picks them up automatically if built there.
26+
27+
Windows build uses `Makefile.Win32` with `nmake` (MSVC); see `INSTALL` for required SDK paths (OpenSSL, pthreads-Win32, PCRE, AMD APP SDK/CUDA).
28+
29+
There is no test suite in this repository.
30+
31+
## Architecture
32+
33+
The codebase splits into a reusable pattern-matching/crypto core and multiple thin front ends:
34+
35+
- **`pattern.c`/`pattern.h`** — the core `vg_context_t` / `vg_exec_context_t` abstraction. A `vg_context_t` holds the search parameters (address type, patterns, output callbacks) and dispatches to one of two pattern engines via function pointers (`vc_add_patterns`, `vc_test`, `vc_hash160_sort`, ...):
36+
- **Prefix matching** (`vg_prefix_context_new`): builds a trie/AVL-based structure (see `avl.h`) over base58 prefixes for O(log N) matching against many patterns simultaneously, and can compute match difficulty.
37+
- **Regex matching** (`vg_regex_context_new`): compiles PCRE patterns and matches against generated addresses.
38+
Each worker thread owns a `vg_exec_context_t` (its own OpenSSL `BN_CTX`/`EC_KEY`/scratch bignums) and calls back into `vc_test` per candidate key. Thread coordination (locking, upgrade/downgrade, stop) lives here too.
39+
- **`util.c`/`util.h`** — base58check encode/decode, address/private-key encoding, private key protection/encryption (BIP38-like PBKDF2+AES), PKCS#8 import/export, password handling, and file reading helpers. Independent of the search engine.
40+
- **`vanitygen.c`** — CPU front end: parses CLI args, builds a `vg_context_t` (prefix or regex), spawns one pthread worker per CPU (or `-t` override), and calls into `pattern.c`.
41+
- **`oclengine.c`/`oclengine.h`** — the OpenCL execution engine (`vg_ocl_context_t`): device/platform enumeration, kernel compilation and dispatch, batched EC point arithmetic on the GPU. This is what makes GPU search fast — it computes many candidate public keys per kernel invocation rather than one key at a time.
42+
- **`calc_addrs.cl`** — the actual OpenCL kernel source (EC point math, hashing) compiled/loaded at runtime by `oclengine.c`.
43+
- **`oclvanitygen.c`** — GPU front end analogous to `vanitygen.c`, but driving `vg_ocl_context_t` instead of pthread workers.
44+
- **`oclvanityminer.c`** — a pool-mining-style variant of the OpenCL front end: pulls patterns from a remote server over HTTP (via libcurl) and reports matches back, instead of taking patterns from argv.
45+
- **`keyconv.c`** — standalone small utility to convert/reformat keys and addresses between formats (base58check, hex, PEM/PKCS#8), independent of the search engines.
46+
- **`winglue.c`/`winglue.h`** — Windows compatibility shims (pthreads/time/stat equivalents) used only when building with MSVC.
47+
48+
### Key extension points
49+
50+
- New coin/address types are just `addrtype`/`privtype` byte values passed into `vg_prefix_context_new`/`vg_regex_context_new` — see CLI handling in `vanitygen.c`/`oclvanitygen.c` for `-t`/address-version flags (Bitcoin, Namecoin, testnet variants).
51+
- Output is fully pluggable via `vc_output_match`/`vc_output_timing`/`vc_output_error` function pointers on `vg_context_t`; default console implementations are `vg_output_match_console`/`vg_output_timing_console` in `pattern.c`.
52+
- Both CPU and GPU front ends build the same `vg_context_t` and differ only in what drives `vc_test` (pthread loop vs. OpenCL kernel batches), so pattern-matching logic changes belong in `pattern.c`, not duplicated per front end.

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: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,9 @@ 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) : 0;
558+
556559
if (!vcp->vc_result_file || (vcp->vc_verbose > 0)) {
557560
printf("\r%79s\rPattern: %s\n", "", pattern);
558561
}
@@ -563,22 +566,34 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern)
563566
len = i2o_ECPublicKey(pkey, &pend);
564567
printf("Pubkey (hex): ");
565568
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);
569+
if (clipped) {
570+
printf("Privkey (hex): "
571+
"(copied to clipboard, "
572+
"clearing in 45s)\n");
573+
} else {
574+
printf("Privkey (hex): ");
575+
dumpbn(EC_KEY_get0_private_key(pkey));
576+
pend = key_buf;
577+
len = i2d_ECPrivateKey(pkey, &pend);
578+
printf("Privkey (ASN1): ");
579+
dumphex(key_buf, len);
580+
}
572581
}
573582

574583
}
575584

576585
if (!vcp->vc_result_file || (vcp->vc_verbose > 0)) {
577586
if (isscript)
578587
printf("P2SHAddress: %s\n", addr2_buf);
579-
printf("Address: %s\n"
580-
"%s: %s\n",
581-
addr_buf, keytype, privkey_buf);
588+
if (clipped) {
589+
printf("Address: %s\n"
590+
"%s: (copied to clipboard, clearing in 45s)\n",
591+
addr_buf, keytype);
592+
} else {
593+
printf("Address: %s\n"
594+
"%s: %s\n",
595+
addr_buf, keytype, privkey_buf);
596+
}
582597
}
583598

584599
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: 83 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,81 @@ vg_read_file(FILE *fp, char ***result, int *rescount)
10861091

10871092
return ret;
10881093
}
1094+
1095+
1096+
/*
1097+
* Copy a secret (e.g. a private key) to the X11 clipboard using xclip,
1098+
* and schedule it to be cleared after timeout_secs -- but only if the
1099+
* clipboard still holds exactly what we put there, so we don't clobber
1100+
* something else the user copied in the meantime. Modeled after the
1101+
* clipboard handling in "pass" (passwordstore.org).
1102+
*
1103+
* Returns 1 if the secret was copied to the clipboard, 0 if no clipboard
1104+
* tool was available (in which case the caller should fall back to
1105+
* printing the secret).
1106+
*/
1107+
int
1108+
vg_clipboard_copy_secret(const char *secret, int timeout_secs)
1109+
{
1110+
#if defined(_WIN32)
1111+
return 0;
1112+
#else
1113+
FILE *fp;
1114+
pid_t pid;
1115+
char cmd[256];
1116+
int status;
1117+
1118+
if (system("command -v xclip >/dev/null 2>&1") != 0) {
1119+
fprintf(stderr,
1120+
"vanitygen: xclip not found in PATH, "
1121+
"printing private key instead\n");
1122+
return 0;
1123+
}
1124+
1125+
fp = popen("xclip -selection clipboard", "w");
1126+
if (!fp) {
1127+
fprintf(stderr,
1128+
"vanitygen: could not run xclip (%s), "
1129+
"printing private key instead\n", strerror(errno));
1130+
return 0;
1131+
}
1132+
fputs(secret, fp);
1133+
status = pclose(fp);
1134+
if (status != 0) {
1135+
fprintf(stderr,
1136+
"vanitygen: xclip exited with status %d, "
1137+
"printing private key instead\n", status);
1138+
return 0;
1139+
}
1140+
1141+
pid = fork();
1142+
if (pid < 0)
1143+
/* Copy succeeded even though we can't schedule the clear */
1144+
return 1;
1145+
1146+
if (pid == 0) {
1147+
/* Detach a grandchild so the parent doesn't have to wait */
1148+
if (fork() > 0)
1149+
_exit(0);
1150+
setsid();
1151+
close(0);
1152+
close(1);
1153+
close(2);
1154+
1155+
if (setenv("VG_CLIP_SECRET", secret, 1) == 0) {
1156+
snprintf(cmd, sizeof(cmd),
1157+
"sleep %d; "
1158+
"[ \"$(xclip -selection clipboard -o "
1159+
"2>/dev/null)\" = \"$VG_CLIP_SECRET\" ] && "
1160+
"printf '' | xclip -selection clipboard",
1161+
timeout_secs);
1162+
system(cmd);
1163+
}
1164+
_exit(0);
1165+
}
1166+
1167+
/* Reap the intermediate child, which exits immediately */
1168+
waitpid(pid, NULL, 0);
1169+
return 1;
1170+
#endif
1171+
}

util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,6 @@ 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+
extern int vg_clipboard_copy_secret(const char *secret, int timeout_secs);
79+
7880
#endif /* !defined (__VG_UTIL_H__) */

vanitygen.c

Lines changed: 9 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,8 @@ 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; requires xclip)\n"
314316
"-N Generate namecoin address\n"
315317
"-T Generate bitcoin testnet address\n"
316318
"-X <version> Generate address with the given version\n"
@@ -342,6 +344,7 @@ main(int argc, char **argv)
342344
int simulate = 0;
343345
int remove_on_match = 1;
344346
int only_one = 0;
347+
int clip = 0;
345348
int prompt_password = 0;
346349
int opt;
347350
char *seedfile = NULL;
@@ -361,7 +364,7 @@ main(int argc, char **argv)
361364

362365
int i;
363366

364-
while ((opt = getopt(argc, argv, "vqnrik1eE:P:NTX:F:t:h?f:o:s:")) != -1) {
367+
while ((opt = getopt(argc, argv, "vqnrik1ceE:P:NTX:F:t:h?f:o:s:")) != -1) {
365368
switch (opt) {
366369
case 'v':
367370
verbose = 2;
@@ -384,6 +387,9 @@ main(int argc, char **argv)
384387
case '1':
385388
only_one = 1;
386389
break;
390+
case 'c':
391+
clip = 1;
392+
break;
387393
case 'N':
388394
addrtype = 52;
389395
privtype = 180;
@@ -551,6 +557,7 @@ main(int argc, char **argv)
551557
vcp->vc_format = format;
552558
vcp->vc_pubkeytype = pubkeytype;
553559
vcp->vc_pubkey_base = pubkey_base;
560+
vcp->vc_clip = clip;
554561

555562
vcp->vc_output_match = vg_output_match_console;
556563
vcp->vc_output_timing = vg_output_timing_console;

0 commit comments

Comments
 (0)