Last active
November 19, 2017 20:58
-
-
Save bertm/3a50d0c32520dfb4b34b to your computer and use it in GitHub Desktop.
Freenet vanity key generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Link to libcrypto and libgmp. Only tested on a 64 bit machine. | |
This program may disclose your private key, harm your computer or kill your cat. Use only if you | |
understand what it does. I accept no responsibility for any use of this program. | |
*/ | |
#include <openssl/evp.h> | |
#include <gmp.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <inttypes.h> | |
// SHA256 contexts | |
EVP_MD_CTX *mdctx; | |
EVP_MD_CTX *mdctx_clean; | |
const EVP_MD *md; | |
// DSA group info | |
const uint8_t P[] = "8608ac4f55361337f2a3e38ab1864ff3c98d66411d8d2afc9c526320c541f650" | |
"78e86bc78494a5d73e4a9a67583f941f2993ed6c97dbc795dd88f0915c9cfbff" | |
"c7e5373cde13e3c7ca9073b9106eb31bf82272ed0057f984a870a19f8a83bfa7" | |
"07d16440c382e62d3890473ea79e9d50c4ac6b1f1d30b10c32a02f685833c627" | |
"8fc29eb3439c5333885614a115219b3808c92a37a0f365cd5e61b5861761dad9" | |
"eff0ce23250f558848f8db932b87a3bd8d7a2f7cf99c75822bdc2fb7c1a1d78d" | |
"0bcf81488ae0de5269ff853ab8b8f1f2bf3e6c0564573f612808f68dbfef49d5" | |
"c9b4a705794cf7a424cd4eb1e0260552e67bfc1fa37b4a1f78b757ef185e86e9"; | |
const uint8_t Q[] = "b143368abcd51f58d6440d5417399339a4d15bef096a2c5d8e6df44f52d6d379"; | |
const uint8_t G[] = "51a45ab670c1c9fd10bd395a6805d33339f5675e4b0d35defc9fa03aa5c2bf4c" | |
"e9cfcdc256781291bfff6d546e67d47ae4e160f804ca72ec3c5492709f5f80f6" | |
"9e6346dd8d3e3d8433b6eeef63bce7f98574185c6aff161c9b536d76f8731373" | |
"65a4246cf414bfe8049ee11e31373cd0a6558e2950ef095320ce86218f992551" | |
"cc292224114f3b60146d22dd51f8125c9da0c028126ffa85efd4f4bfea2c1044" | |
"53329cc1268a97e9a835c14e4a9a43c6a1886580e35ad8f1de230e1af32208ef" | |
"9337f1924702a4514e95dc16f30f0c11e714a112ee84a9d8d6c9bc9e74e33656" | |
"0bb5cd4e91eabf6dad26bf0ca04807f8c31a2fc18ea7d45baab7cc997b53c356"; | |
const uint8_t group[] = { // pre-encoded because I'm too lazy to code it myself | |
0x08, 0x00, 0x00, 0x86, 0x08, 0xac, 0x4f, 0x55, 0x36, 0x13, 0x37, 0xf2, 0xa3, 0xe3, 0x8a, 0xb1, | |
0x86, 0x4f, 0xf3, 0xc9, 0x8d, 0x66, 0x41, 0x1d, 0x8d, 0x2a, 0xfc, 0x9c, 0x52, 0x63, 0x20, 0xc5, | |
0x41, 0xf6, 0x50, 0x78, 0xe8, 0x6b, 0xc7, 0x84, 0x94, 0xa5, 0xd7, 0x3e, 0x4a, 0x9a, 0x67, 0x58, | |
0x3f, 0x94, 0x1f, 0x29, 0x93, 0xed, 0x6c, 0x97, 0xdb, 0xc7, 0x95, 0xdd, 0x88, 0xf0, 0x91, 0x5c, | |
0x9c, 0xfb, 0xff, 0xc7, 0xe5, 0x37, 0x3c, 0xde, 0x13, 0xe3, 0xc7, 0xca, 0x90, 0x73, 0xb9, 0x10, | |
0x6e, 0xb3, 0x1b, 0xf8, 0x22, 0x72, 0xed, 0x00, 0x57, 0xf9, 0x84, 0xa8, 0x70, 0xa1, 0x9f, 0x8a, | |
0x83, 0xbf, 0xa7, 0x07, 0xd1, 0x64, 0x40, 0xc3, 0x82, 0xe6, 0x2d, 0x38, 0x90, 0x47, 0x3e, 0xa7, | |
0x9e, 0x9d, 0x50, 0xc4, 0xac, 0x6b, 0x1f, 0x1d, 0x30, 0xb1, 0x0c, 0x32, 0xa0, 0x2f, 0x68, 0x58, | |
0x33, 0xc6, 0x27, 0x8f, 0xc2, 0x9e, 0xb3, 0x43, 0x9c, 0x53, 0x33, 0x88, 0x56, 0x14, 0xa1, 0x15, | |
0x21, 0x9b, 0x38, 0x08, 0xc9, 0x2a, 0x37, 0xa0, 0xf3, 0x65, 0xcd, 0x5e, 0x61, 0xb5, 0x86, 0x17, | |
0x61, 0xda, 0xd9, 0xef, 0xf0, 0xce, 0x23, 0x25, 0x0f, 0x55, 0x88, 0x48, 0xf8, 0xdb, 0x93, 0x2b, | |
0x87, 0xa3, 0xbd, 0x8d, 0x7a, 0x2f, 0x7c, 0xf9, 0x9c, 0x75, 0x82, 0x2b, 0xdc, 0x2f, 0xb7, 0xc1, | |
0xa1, 0xd7, 0x8d, 0x0b, 0xcf, 0x81, 0x48, 0x8a, 0xe0, 0xde, 0x52, 0x69, 0xff, 0x85, 0x3a, 0xb8, | |
0xb8, 0xf1, 0xf2, 0xbf, 0x3e, 0x6c, 0x05, 0x64, 0x57, 0x3f, 0x61, 0x28, 0x08, 0xf6, 0x8d, 0xbf, | |
0xef, 0x49, 0xd5, 0xc9, 0xb4, 0xa7, 0x05, 0x79, 0x4c, 0xf7, 0xa4, 0x24, 0xcd, 0x4e, 0xb1, 0xe0, | |
0x26, 0x05, 0x52, 0xe6, 0x7b, 0xfc, 0x1f, 0xa3, 0x7b, 0x4a, 0x1f, 0x78, 0xb7, 0x57, 0xef, 0x18, | |
0x5e, 0x86, 0xe9, 0x01, 0x00, 0x00, 0xb1, 0x43, 0x36, 0x8a, 0xbc, 0xd5, 0x1f, 0x58, 0xd6, 0x44, | |
0x0d, 0x54, 0x17, 0x39, 0x93, 0x39, 0xa4, 0xd1, 0x5b, 0xef, 0x09, 0x6a, 0x2c, 0x5d, 0x8e, 0x6d, | |
0xf4, 0x4f, 0x52, 0xd6, 0xd3, 0x79, 0x07, 0xff, 0x51, 0xa4, 0x5a, 0xb6, 0x70, 0xc1, 0xc9, 0xfd, | |
0x10, 0xbd, 0x39, 0x5a, 0x68, 0x05, 0xd3, 0x33, 0x39, 0xf5, 0x67, 0x5e, 0x4b, 0x0d, 0x35, 0xde, | |
0xfc, 0x9f, 0xa0, 0x3a, 0xa5, 0xc2, 0xbf, 0x4c, 0xe9, 0xcf, 0xcd, 0xc2, 0x56, 0x78, 0x12, 0x91, | |
0xbf, 0xff, 0x6d, 0x54, 0x6e, 0x67, 0xd4, 0x7a, 0xe4, 0xe1, 0x60, 0xf8, 0x04, 0xca, 0x72, 0xec, | |
0x3c, 0x54, 0x92, 0x70, 0x9f, 0x5f, 0x80, 0xf6, 0x9e, 0x63, 0x46, 0xdd, 0x8d, 0x3e, 0x3d, 0x84, | |
0x33, 0xb6, 0xee, 0xef, 0x63, 0xbc, 0xe7, 0xf9, 0x85, 0x74, 0x18, 0x5c, 0x6a, 0xff, 0x16, 0x1c, | |
0x9b, 0x53, 0x6d, 0x76, 0xf8, 0x73, 0x13, 0x73, 0x65, 0xa4, 0x24, 0x6c, 0xf4, 0x14, 0xbf, 0xe8, | |
0x04, 0x9e, 0xe1, 0x1e, 0x31, 0x37, 0x3c, 0xd0, 0xa6, 0x55, 0x8e, 0x29, 0x50, 0xef, 0x09, 0x53, | |
0x20, 0xce, 0x86, 0x21, 0x8f, 0x99, 0x25, 0x51, 0xcc, 0x29, 0x22, 0x24, 0x11, 0x4f, 0x3b, 0x60, | |
0x14, 0x6d, 0x22, 0xdd, 0x51, 0xf8, 0x12, 0x5c, 0x9d, 0xa0, 0xc0, 0x28, 0x12, 0x6f, 0xfa, 0x85, | |
0xef, 0xd4, 0xf4, 0xbf, 0xea, 0x2c, 0x10, 0x44, 0x53, 0x32, 0x9c, 0xc1, 0x26, 0x8a, 0x97, 0xe9, | |
0xa8, 0x35, 0xc1, 0x4e, 0x4a, 0x9a, 0x43, 0xc6, 0xa1, 0x88, 0x65, 0x80, 0xe3, 0x5a, 0xd8, 0xf1, | |
0xde, 0x23, 0x0e, 0x1a, 0xf3, 0x22, 0x08, 0xef, 0x93, 0x37, 0xf1, 0x92, 0x47, 0x02, 0xa4, 0x51, | |
0x4e, 0x95, 0xdc, 0x16, 0xf3, 0x0f, 0x0c, 0x11, 0xe7, 0x14, 0xa1, 0x12, 0xee, 0x84, 0xa9, 0xd8, | |
0xd6, 0xc9, 0xbc, 0x9e, 0x74, 0xe3, 0x36, 0x56, 0x0b, 0xb5, 0xcd, 0x4e, 0x91, 0xea, 0xbf, 0x6d, | |
0xad, 0x26, 0xbf, 0x0c, 0xa0, 0x48, 0x07, 0xf8, 0xc3, 0x1a, 0x2f, 0xc1, 0x8e, 0xa7, 0xd4, 0x5b, | |
0xaa, 0xb7, 0xcc, 0x99, 0x7b, 0x53, 0xc3, 0x56 }; | |
void digest_init() | |
{ | |
OpenSSL_add_all_digests(); | |
md = EVP_get_digestbyname("SHA256"); | |
if(!md) { | |
printf("Unknown message digest SHA256\n"); | |
exit(1); | |
} | |
mdctx_clean = EVP_MD_CTX_create(); | |
mdctx = EVP_MD_CTX_create(); | |
EVP_DigestInit_ex(mdctx_clean, md, NULL); | |
EVP_DigestUpdate(mdctx_clean, group, sizeof(group)); | |
} | |
// calculates SHA256(group + message) in hash | |
void sha256_hash(const char *message, const size_t len, char hash[32]) { | |
int md_len; | |
EVP_MD_CTX_copy_ex(mdctx, mdctx_clean); | |
EVP_DigestUpdate(mdctx, message, len); | |
EVP_DigestFinal_ex(mdctx, hash, &md_len); | |
} | |
// calculates the hash of pubkey y | |
void fast_digest(uint8_t hash[32], mpz_t y) { | |
int md_len; | |
uint8_t yy[256 + 8] = { 0 }; // 2048 bits + 8 byte prefix | |
uint8_t *ptr = yy + 8; | |
size_t words; | |
mpz_export(ptr, &words, 1 /* most significant word first */, | |
8, 1 /* most significant byte first */, 0, y); | |
size_t bits = mpz_sizeinbase(y, 2); | |
size_t bytes = bits >> 3; | |
ptr = ptr + (words * 8) - bytes - 1; | |
if (ptr[0] & 0x80) { | |
ptr -= 3; | |
// Prefix 0x00 | |
ptr[0] = (bits >> 8) & 0xFF; | |
ptr[1] = bits & 0xFF; | |
//ptr[2] = 0x00; | |
sha256_hash(ptr, bytes + 4, hash); | |
} else { | |
ptr -= 2; | |
// No prefix | |
ptr[0] = (bits >> 8) & 0xFF; | |
ptr[1] = bits & 0xFF; | |
sha256_hash(ptr, bytes + 3, hash); | |
} | |
} | |
// *********************************************** | |
// OVERRIDE THIS METHOD TO MATCH YOUR REQUIREMENTS | |
// *********************************************** | |
int match(const uint8_t* digest) { | |
// base64(digest) has prefix "vanity", e.g. matches 0xbd 0xa9 0xe2 0xb7 0x20 in first 36 bits | |
return digest[0] == 0xbd && | |
digest[1] == 0xa9 && | |
digest[2] == 0xe2 && | |
digest[3] == 0xb7 && | |
(digest[4] & 0xf0) == 0x20; | |
} | |
int main(int argc, char **argv) | |
{ | |
digest_init(); | |
// Require a hexadecimal 256 private key as a starting point for the search. The user has to | |
// ensure it is random enough for his purposes, and we just blindly assume that there won't be | |
// overflow (e.g. where the privkey, after sequential increments, will be greater than q). | |
if (argc != 2) { | |
printf("usage: %s <hex 256 bit privkey starting point>\n", argv[0]); | |
exit(1); | |
} | |
const char *X = argv[1]; | |
unsigned char digest[32]; | |
mpz_t p, q, g, x, y; | |
mpz_init(p); | |
mpz_init(q); | |
mpz_init(g); | |
mpz_init(x); | |
mpz_init(y); | |
mpz_set_str(p, P, 16); | |
mpz_set_str(q, Q, 16); | |
mpz_set_str(g, G, 16); | |
mpz_set_str(x, X, 16); | |
mpz_powm(y, g, x, p); | |
long unsigned int rounds = 0; | |
// Until we have found a match, increment the private key by one. This is efficient: we only | |
// have to multiply the public key by g (mod p), instead of doing the full exponentiation | |
// (mod p). | |
while(1) { | |
fast_digest(digest, y); | |
if (match(digest)) | |
{ | |
mpz_add_ui(x, x, rounds); | |
printf("\nGot a matching private key!\n>>> "); | |
mpz_out_str(stdout, 16, x); | |
printf("\nFound after trying %ld keys\n", rounds); | |
// Uh-oh, we printed the key in hex. Pad it to 256 bits with leading zeroes and base64 | |
// encode the beast (mind you, Freenet uses a custom base64 alphabet). Then you have the | |
// privkey part of your insert URI. The other parts are trivial to generate. | |
exit(0); | |
} | |
if (!(rounds & 0xfffff)) { | |
printf("\rTried %ld keys \rTried %ld keys", rounds, rounds); | |
fflush(stdout); | |
} else if (!(rounds & 0x3ffff)) { | |
printf("."); | |
fflush(stdout); | |
} | |
rounds++; | |
mpz_mul(y, y, g); | |
mpz_mod(y, y, p); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What's the realistic risk of using the code?