1
0
mirror of https://github.com/bitcoinbook/bitcoinbook synced 2024-11-22 08:08:11 +00:00

CH04::vanity addresses: update, drop code, clarify security/privacy

- Explain why almost nobody uses vanity addresses any more---HD wallets
  killed them, plus they suck for privacy.

- Remove example code.  It's only useful for base58check addresses, but
  those are no longer recommended and (as mentioned above) almost nobody
  uses vanity addresses any more, so there's not much point in updating
  it for bech32(m).

- Remove vanity address security section with unvetted security claims.

- Replace outdated claim about miners using GPUs.

- Remove specific amount for cost of vanity address pooling and URL for
  a pool.  That pool doesn't work, I don't know of any others, and I
  have no idea what the pricing would be even if there were existing
  pools.
This commit is contained in:
David A. Harding 2023-02-07 21:19:24 -10:00
parent 132094b670
commit 206ee88a26
2 changed files with 31 additions and 196 deletions

View File

@ -1583,8 +1583,7 @@ because it has the added +01+ suffix to distinguish it from an
((("keys and addresses", "advanced forms", id="KAadvanced04")))In the ((("keys and addresses", "advanced forms", id="KAadvanced04")))In the
following sections we will look at advanced forms of keys and addresses, following sections we will look at advanced forms of keys and addresses,
such as encrypted private keys, script and multisignature addresses, such as vanity addresses and paper wallets.
vanity addresses, and paper wallets.
==== Vanity Addresses ==== Vanity Addresses
@ -1596,7 +1595,7 @@ addresses that contain human-readable messages. For example,
+1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33+ is a valid address that contains +1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33+ is a valid address that contains
the letters forming the word "Love" as the first four Base-58 letters. the letters forming the word "Love" as the first four Base-58 letters.
Vanity addresses require generating and testing billions of candidate Vanity addresses require generating and testing billions of candidate
private keys, until a bitcoin address with the desired pattern is found. private keys, until a Bitcoin address with the desired pattern is found.
Although there are some optimizations in the vanity generation Although there are some optimizations in the vanity generation
algorithm, the process essentially involves picking a private key at algorithm, the process essentially involves picking a private key at
random, deriving the public key, deriving the Bitcoin address, and random, deriving the public key, deriving the Bitcoin address, and
@ -1669,141 +1668,50 @@ As you can see, Eugenia won't be creating the vanity address
computers. Each additional character increases the difficulty by a computers. Each additional character increases the difficulty by a
factor of 58. Patterns with more than seven characters are usually found factor of 58. Patterns with more than seven characters are usually found
by specialized hardware, such as custom-built desktops with multiple by specialized hardware, such as custom-built desktops with multiple
GPUs. These are often repurposed bitcoin mining "rigs" that are no GPUs.
longer profitable for bitcoin mining but can be used to find vanity Vanity searches on GPU systems are many orders of magnitude
addresses. Vanity searches on GPU systems are many orders of magnitude
faster than on a general-purpose CPU. faster than on a general-purpose CPU.
Another way to find a vanity address is to outsource the work to a pool Another way to find a vanity address is to outsource the work to a pool
of vanity miners, such as the pool at of vanity miners. A pool is a service that
http://vanitypool.appspot.com[Vanity Pool]. A pool is a service that
allows those with GPU hardware to earn bitcoin searching for vanity allows those with GPU hardware to earn bitcoin searching for vanity
addresses for others. For a small payment (0.01 bitcoin or approximately addresses for others. For a fee, Eugenia can outsource the search for a
$5 at the time of this writing), Eugenia can outsource the search for a
seven-character pattern vanity address and get results in a few hours seven-character pattern vanity address and get results in a few hours
instead of having to run a CPU search for months. instead of having to run a CPU search for months.
Generating a vanity address is a brute-force exercise: try a random key, Generating a vanity address is a brute-force exercise: try a random key,
check the resulting address to see if it matches the desired pattern, check the resulting address to see if it matches the desired pattern,
repeat until successful. <<vanity_miner_code>> shows an example of a repeat until successful.
"vanity miner," a program designed to find vanity addresses, written in
C++. The example uses the libbitcoin library, which we introduced in
<<alt_libraries>>.
[[vanity_miner_code]] ===== Vanity address security and privacy
.Vanity address miner
====
[source,cpp]
----
include::code/vanity-miner.cpp[]
----
====
[NOTE] ((("security", "vanity addresses")))Vanity addresses were popular in the
==== early years of Bitcoin but have almost entirely disappeared from use as
<<vanity_miner_run>> uses +std::random_device+. Depending on the of 2023. There are two likely causes for this trend:
implementation it may reflect a CSRNG provided by the underlying
operating system. In the case of a Unix-like operating system such as
Linux, it draws from +/dev/urandom+. The random number generator used
here is for demonstration purposes, and it is _not_ appropriate for
generating production-quality bitcoin keys as it is not implemented with
sufficient security.
====
The example code must be compiled using a pass:[C++] compiler and linked 1. Deterministic wallets: as we saw in <<recovery_code_intro>>, it's possible to
against the libbitcoin library (which must be first installed on that backup every key in most modern wallets by simply writing down a few
system). To run the example, run the ++vanity-miner++ executable with no words or characters. This is achieved by deriving every key in the
parameters (see <<vanity_miner_run>>) and it will attempt to find a wallet from those words or characters using a deterministic algorithm.
vanity address starting with "1kid." It's not possible to use vanity addresses with a deterministic wallet
unless the user backs up additional data for every vanity address they
create. More practically, most wallets using deterministic key
generation simply don't allow importing a private key or key tweak from
a vanity generator.
[[vanity_miner_run]] 2. Avoiding address reuse: using a vanity address to receive multiple
.Compiling and running the vanity-miner example payments to the same address creates a link between all of those
==== payments. This might be acceptable to Eugenia if her non-profit needs
[source,bash] to report its income and expenditures to a tax authority anyway.
---- However, it also reduces the privacy of people who either pay Eugenia or
$ # Compile the code with g++ receive payments from her. For example, Alice may want to donate
$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin) anonymously and Bob may not want his other customers to know that he
$ # Run the example gives discount pricing to Eugenia.
$ ./vanity-miner
Found vanity address! 1KiDzkG4MxmovZryZRj8tK81oQRhbZ46YT
Secret: 57cc268a05f83a23ac9d930bc8565bac4e277055f4794cbd1a39e5e71c038f3f
$ # Run it again for a different result
$ ./vanity-miner
Found vanity address! 1Kidxr3wsmMzzouwXibKfwTYs5Pau8TUFn
Secret: 7f65bbbbe6d8caae74a0c6a0d2d7b5c6663d71b60337299a1a2cf34c04b2a623
# Use "time" to see how long it takes to find a result
$ time ./vanity-miner
Found vanity address! 1KidPWhKgGRQWD5PP5TAnGfDyfWp5yceXM
Secret: 2a802e7a53d8aa237cd059377b616d2bfcfa4b0140bc85fa008f2d3d4b225349
real 0m8.868s // https://github.com/MakisChristou/vanitybech
user 0m8.828s
sys 0m0.035s
----
====
The example code will take a few seconds to find a match for the Given those problems, we don't expect to see many vanity addresses in
three-character pattern "kid," as we can see when we use the +time+ Unix the future, although there will probably always be some.
command to measure the execution time. Change the +search+ pattern in
the source code and see how much longer it takes for four- or
five-character patterns!
===== Vanity address security
((("security", "vanity addresses")))Vanity addresses can be used to
enhance _and_ to defeat security measures; they are truly a double-edged
sword. Used to improve security, a distinctive address makes it harder
for adversaries to substitute their own address and fool your customers
into paying them instead of you. Unfortunately, vanity addresses also
make it possible for anyone to create an address that _resembles_ any
random address, or even another vanity address, thereby fooling your
customers.
Eugenia could advertise a randomly generated address (e.g.,
+1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy+) to which people can send their
donations. Or, she could generate a vanity address that starts with
1Kids, to make it more distinctive.
In both cases, one of the risks of using a single fixed address (rather
than a separate dynamic address per donor) is that a thief might be able
to infiltrate your website and replace it with his own address, thereby
diverting donations to himself. If you have advertised your donation
address in a number of different places, your users may visually inspect
the address before making a payment to ensure it is the same one they
saw on your website, on your email, and on your flyer. In the case of a
random address like +1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy+, the average
user will perhaps inspect the first few characters "1J7mdg" and be
satisfied that the address matches. Using a vanity address generator,
someone with the intent to steal by substituting a similar-looking
address can quickly generate addresses that match the first few
characters, as shown in <<table_4-13>>.
[[table_4-13]]
.Generating vanity addresses to match a random address
|=======
| *Original Random Address* | 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
| *Vanity (4-character match)* | 1J7md1QqU4LpctBetHS2ZoyLV5d6dShhEy
| *Vanity (5-character match)* | 1J7mdgYqyNd4ya3UEcq31Q7sqRMXw2XZ6n
| *Vanity (6-character match)* | 1J7mdg5WxGENmwyJP9xuGhG5KRzu99BBCX
|=======
So does a vanity address increase security? If Eugenia generates the
vanity address +1Kids33q44erFfpeXrmDSz7zEqG2FesZEN+, users are likely to
look at the vanity pattern word _and a few characters beyond_, for
example noticing the "1Kids33" part of the address. That would force an
attacker to generate a vanity address matching at least six characters
(two more), expending an effort that is 3,364 times (58 &#x00D7; 58)
higher than the effort Eugenia expended for her 4-character vanity.
Essentially, the effort Eugenia expends (or pays a vanity pool for)
"pushes" the attacker into having to produce a longer pattern vanity. If
Eugenia pays a pool to generate an 8-character vanity address, the
attacker would be pushed into the realm of 10 characters, which is
infeasible on a personal computer and expensive even with a custom
vanity-mining rig or vanity pool. What is affordable for Eugenia becomes
unaffordable for the attacker, especially if the potential reward of
fraud is not high enough to cover the cost of the vanity address
generation.((("", startref="Avanity04")))((("",
startref="vanity04")))((("", startref="eugeniafour")))
[[paper_wallets]] [[paper_wallets]]
==== Paper Wallets ==== Paper Wallets

View File

@ -1,73 +0,0 @@
#include <random>
#include <bitcoin/bitcoin.hpp>
// The string we are searching for
const std::string search = "1kid";
// Generate a random secret key. A random 32 bytes.
bc::ec_secret random_secret(std::default_random_engine& engine);
// Extract the Bitcoin address from an EC secret.
std::string bitcoin_address(const bc::ec_secret& secret);
// Case insensitive comparison with the search string.
bool match_found(const std::string& address);
int main()
{
// random_device on Linux uses "/dev/urandom"
// CAUTION: Depending on implementation this RNG may not be secure enough!
// Do not use vanity keys generated by this example in production
std::random_device random;
std::default_random_engine engine(random());
// Loop continuously...
while (true)
{
// Generate a random secret.
bc::ec_secret secret = random_secret(engine);
// Get the address.
std::string address = bitcoin_address(secret);
// Does it match our search string? (1kid)
if (match_found(address))
{
// Success!
std::cout << "Found vanity address! " << address << std::endl;
std::cout << "Secret: " << bc::encode_base16(secret) << std::endl;
return 0;
}
}
// Should never reach here!
return 0;
}
bc::ec_secret random_secret(std::default_random_engine& engine)
{
// Create new secret...
bc::ec_secret secret;
// Iterate through every byte setting a random value...
for (uint8_t& byte: secret)
byte = engine() % std::numeric_limits<uint8_t>::max();
// Return result.
return secret;
}
std::string bitcoin_address(const bc::ec_secret& secret)
{
// Convert secret to payment address
bc::wallet::ec_private private_key(secret);
bc::wallet::payment_address payaddr(private_key);
// Return encoded form.
return payaddr.encoded();
}
bool match_found(const std::string& address)
{
auto addr_it = address.begin();
// Loop through the search string comparing it to the lower case
// character of the supplied address.
for (auto it = search.begin(); it != search.end(); ++it, ++addr_it)
if (*it != std::tolower(*addr_it))
return false;
// Reached end of search string, so address matches.
return true;
}