From 206ee88a266df03ab41908c284cb2132519e26ca Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Tue, 7 Feb 2023 21:19:24 -1000 Subject: [PATCH] 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. --- ch04.asciidoc | 166 ++++++++++-------------------------------- code/vanity-miner.cpp | 73 ------------------- 2 files changed, 37 insertions(+), 202 deletions(-) delete mode 100644 code/vanity-miner.cpp diff --git a/ch04.asciidoc b/ch04.asciidoc index a249eb6d..f2cd23ec 100644 --- a/ch04.asciidoc +++ b/ch04.asciidoc @@ -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 following sections we will look at advanced forms of keys and addresses, -such as encrypted private keys, script and multisignature addresses, -vanity addresses, and paper wallets. +such as vanity addresses and paper wallets. ==== Vanity Addresses @@ -1596,7 +1595,7 @@ addresses that contain human-readable messages. For example, +1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33+ is a valid address that contains the letters forming the word "Love" as the first four Base-58 letters. 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 algorithm, the process essentially involves picking a private key at 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 factor of 58. Patterns with more than seven characters are usually found by specialized hardware, such as custom-built desktops with multiple -GPUs. These are often repurposed bitcoin mining "rigs" that are no -longer profitable for bitcoin mining but can be used to find vanity -addresses. Vanity searches on GPU systems are many orders of magnitude +GPUs. +Vanity searches on GPU systems are many orders of magnitude faster than on a general-purpose CPU. Another way to find a vanity address is to outsource the work to a pool -of vanity miners, such as the pool at -http://vanitypool.appspot.com[Vanity Pool]. A pool is a service that +of vanity miners. A pool is a service that allows those with GPU hardware to earn bitcoin searching for vanity -addresses for others. For a small payment (0.01 bitcoin or approximately -$5 at the time of this writing), Eugenia can outsource the search for a +addresses for others. For a fee, Eugenia can outsource the search for a seven-character pattern vanity address and get results in a few hours instead of having to run a CPU search for months. 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, -repeat until successful. <> shows an example of a -"vanity miner," a program designed to find vanity addresses, written in -C++. The example uses the libbitcoin library, which we introduced in -<>. - -[[vanity_miner_code]] -.Vanity address miner -==== -[source,cpp] ----- -include::code/vanity-miner.cpp[] ----- -==== - -[NOTE] -==== -<> uses +std::random_device+. Depending on the -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 -against the libbitcoin library (which must be first installed on that -system). To run the example, run the ++vanity-miner++ executable with no -parameters (see <>) and it will attempt to find a -vanity address starting with "1kid." - -[[vanity_miner_run]] -.Compiling and running the vanity-miner example -==== -[source,bash] ----- -$ # Compile the code with g++ -$ g++ -o vanity-miner vanity-miner.cpp $(pkg-config --cflags --libs libbitcoin) -$ # Run the example -$ ./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 -user 0m8.828s -sys 0m0.035s ----- -==== - -The example code will take a few seconds to find a match for the -three-character pattern "kid," as we can see when we use the +time+ Unix -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]] -.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 × 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"))) +repeat until successful. + +===== Vanity address security and privacy + +((("security", "vanity addresses")))Vanity addresses were popular in the +early years of Bitcoin but have almost entirely disappeared from use as +of 2023. There are two likely causes for this trend: + +1. Deterministic wallets: as we saw in <>, it's possible to +backup every key in most modern wallets by simply writing down a few +words or characters. This is achieved by deriving every key in the +wallet from those words or characters using a deterministic algorithm. +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. + +2. Avoiding address reuse: using a vanity address to receive multiple +payments to the same address creates a link between all of those +payments. This might be acceptable to Eugenia if her non-profit needs +to report its income and expenditures to a tax authority anyway. +However, it also reduces the privacy of people who either pay Eugenia or +receive payments from her. For example, Alice may want to donate +anonymously and Bob may not want his other customers to know that he +gives discount pricing to Eugenia. + +// https://github.com/MakisChristou/vanitybech + +Given those problems, we don't expect to see many vanity addresses in +the future, although there will probably always be some. [[paper_wallets]] ==== Paper Wallets diff --git a/code/vanity-miner.cpp b/code/vanity-miner.cpp deleted file mode 100644 index 6c3ab412..00000000 --- a/code/vanity-miner.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include - -// 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::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; -} -