Вашему вниманию релиз программы для генерации адресов Bitcoin по пасс-фразе, методом теоретически устойчивым к брутфорсу (перебору фраз). Пока что программа работает не так быстро как хотелось бы, думаю это связано с применением библиотеки Crypto++.
Алгоритм работы основан на достижении некоторой цели в публичном адресе Bitcoin исходя из вводимых пользователем данных, а не методом генерации больших псевдослучайных чисел. Первые 3 символа в сгенерированном адресе "PBK" выступают в качестве идентификатора такого адреса, для удобства идентификации адреса пользователем и они же являются нашей целью. Пасс-фраза должна быть больше 10 символов, желательно в разных раскладках, должна содержать спецсимволы и заглавные буквы, не должна содержать пробелов (проверка реализована только на кол-во символов). На калькуляцию адреса требуется некоторое кол-во времени, которое обычно занимает от 10 секунд до 2-х минут. При вводе одной и той -же пасс-фразы результат расчета всегда будет одним и тем-же.
Вопросы и предложения здесь. И если что, автор не при делах).
myPrivateBitcoinKey (скачать)(09.01.2016 Обновлено до v.0.02. Небольшие изменения в коде по оптимизации.)
Какой же алгоритм? Он прост как 3 рубля, но в то же время относительно безопасен.
После ввода пасс-фразы, из нее формируется ее хеш по алгоритму SHA256. Затем это значение просто инкрементируется до того момента, пока 2-й символ в адресе не станет "P", 3-й - "B", 4-й - "K" они же являются идентификатором. Этот измененный хеш и есть наш приватный ключ. Точки Px Py проходят рандомную проверку с последним уровнем безопасности. При совпадении происходит стандартный расчет приватного ключа в формате WIF и его кодировка в стандарт base58.
Функция кодировки в base58 была написана собственноручно для библиотеки Crypto++ и проверена многократно.
Здесь я разместил ее код.
В будущем планируется оптимизация кода для увеличения скорости генерации и соответственно для большей безопасности (хотя даже сейчас этот метод безопаснее чем например сервис brain wallet в десятки тысяч раз). А так-же экспорт ключей из программы в клиент.
Пример использования программы:
'my Private Bitcoin Key' version 0.01
Фраза должна содержать не менее 10 символов, не менее одной заглавной буквы
и не менее одного спецсимвола типа $ или & или любого другого кроме пробела
Enter pass-phrase (without space): 0987654321
Calculate......
845.keys/sec || Iterations: 10990. || ~107 sec remaining
Input (hex): 30393837363534333231
Private Key in WIF: 8017756315EBD47B7110359FC7B168179BF6F2DF3646FCC888BC8AA05C78B3B5DC8B8D8267
Private Key: 17756315ebd47b7110359fc7b168179bf6f2df3646fcc888bc8aa05c78b3b5dch
Px Py ext: 0400A21ACEC8BB30581D095B1CBC47A1D04AFA5AAD558376E11AD0EB630FFC56594D4EEC3798E87473950EC649EA66F273186B263DEF93B1CF72F8382A0420D67E
Hash Px Py ext: 5002C7502C96325B11A3F3B343398DA6A2E750D4E94146F5D0828EE87E96F4A2
Adress (hex): 000431C369D927414B519F38CF247DA8BE68D956E199D45CBF
Adress base58: 1PBKPJ38tyGgztVvPkCZPo4JgAbRBy96W
PrivateKey base58: 5HzcoGSYFLLKEGAqNEnrCWAp3KrcxGWzWB1kLSsVf8REKUdDmhg
Iterations: 11036.
Time: 14 sec
Заходим на сайт
xorbin.com и пишем в первом поле строчку "0987654321" (без кавычек), смотрим ее хеш, сравниваем с нашим приватным ключом. Убеждаемся что строчки почти идентичны, различие в нескольких последних байтах, т.к. наш приватный ключ это сумма хеша и количества итераций над этим приватным ключом.
Транзакция монет с этим адресом:
https://blockchain.info/address/1PBKPJ38tyGgztVvPkCZPo4JgAbRBy96WИсходник
#include
#include
#include
#include "osrng.h"
#include "eccrypto.h"
#include
#include
#include
#include
#include
#include
#include "sha.h"
#include "ripemd.h"
#include "hex.h"
#include "cryptlib.h"
#include "filters.h"
#include "secblock.h"
#include "algparam.h"
#include "integer.h"
#include
using namespace std;
using namespace CryptoPP;
using namespace CryptoPP::ASN1;
using CryptoPP::SecBlock;
string base58_encode(Integer num, string vers)
{
string alphabet[58] = {"1","2","3","4","5","6","7","8","9","A","B","C","D","E","F",
"G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c",
"d","e","f","g","h","i","j","k","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};
int base_count = 58; string encoded; Integer div; Integer mod;
while (num >= base_count)
{
div = num / base_count; mod = (num - (base_count * div));
encoded = alphabet[ mod.ConvertToLong() ] + encoded; num = div;
}
encoded = vers + alphabet[ num.ConvertToLong() ] + encoded;
return encoded;
}
int main()
{
setlocale(0, "");
ECDSA::PrivateKey K1;
ECDSA::PublicKey K2;
AutoSeededRandomPool prng;
byte L0[] = { 0x00 };
SecByteBlock L0_byte(L0, sizeof(L0));
byte L04[] = { 0x04 };
SecByteBlock L04_byte(L04, sizeof(L04));
byte L80[] = { 0x80 };
SecByteBlock L80_byte(L80, sizeof(L80));
RIPEMD160 hash;
SHA256 hash2;
/*-----------------------------------------------------------------------------*/
Integer number_iteration;
byte hash_sha256[32];
SecByteBlock xy_byte;
Integer privateKey;
SecByteBlock hash_rip160_temp(20);
SecByteBlock hash_rip160;
SecByteBlock hash_sha256_valid_temp(32);
SecByteBlock hash_sha256_valid(4);
SecByteBlock hex_key;
string bitcoin_adress;
SecByteBlock qx(32);
SecByteBlock qy(32);
string input;
string vers;
SecByteBlock input_hash(32);
SecByteBlock hash_pK_byte(32);
SecByteBlock hash_pK_byte_valid(4);
SecByteBlock pK_byte(32);
Integer i;
int count_it = 0;
cout << endl << "'my Private Bitcoin Key' version 0.02" << endl <<"" << endl <<
"Фраза должна содержать не менее 10 символов, не менее одной заглавной буквы" << endl << "и не менее одного спецсимвола типа $ или & или любого другого кроме пробела" << endl << endl <<
"Enter pass-phrase (without space): ";
cin >> input;
for (; input.length() < 10;) {cout << input << " <- Wrong input data! Try again: "; cin >> input;}
SHA256().CalculateDigest(input_hash, reinterpret_cast(&input[0]), input.length());
i.Decode(input_hash, 32);
cout << endl << "Calculate......" << endl;
long long now(time(0));
char ba[35];
while (ba[1] != 0x50 || ba[2] != 0x42 || ba[3] != 0x4b)
{
K1.Initialize(secp256k1(), i);
privateKey = K1.GetPrivateExponent();
K1.MakePublicKey(K2);
ECP::Point q = K2.GetPublicElement();
(q.x).Integer::Encode (qx, 32 /*(q.x).MinEncodedSize(Integer::UNSIGNED)*/, Integer::UNSIGNED);
(q.y).Integer::Encode (qy, 32 /*(q.y).MinEncodedSize(Integer::UNSIGNED)*/, Integer::UNSIGNED);
xy_byte = L04_byte;
xy_byte += qx;
xy_byte += qy;
hash2.CalculateDigest(hash_sha256, xy_byte, 65);
hash.Update(hash_sha256, 32);
hash.TruncatedFinal(hash_rip160_temp, 20/*hash_rip160_temp.size()*/);
hash_rip160 = L0_byte;
hash_rip160 += hash_rip160_temp;
hash2.Update(hash_rip160, 21);
hash2.TruncatedFinal(hash_sha256_valid_temp, 32/*sizeof(hash_sha256_valid_temp)*/);
hash2.Update(hash_sha256_valid_temp, 32);
hash2.TruncatedFinal(hash_sha256_valid, 4/*sizeof(hash_sha256_valid)*/);
/*----------------------------------------------------------------------------------------------*/
hex_key = hash_rip160;
hex_key += hash_sha256_valid;
bitcoin_adress = base58_encode(Integer (hex_key, 25, Integer::UNSIGNED), "1");
strcpy( ba, bitcoin_adress.c_str() );
number_iteration++;
count_it++;
if (count_it >= 99)
{
if (!(time(0) - now) == 0)
cout << " " << number_iteration / (time(0) - now) << "keys/sec" <<
" || Iterations: " << dec << number_iteration << " || ~" << 120 - (time(0) - now) << " sec remaining\r";
count_it = 0;
}
i++;
}
privateKey.Integer::Encode (pK_byte, 32, Integer::UNSIGNED);
pK_byte = L80_byte += pK_byte;
hash2.CalculateDigest(hash_pK_byte, pK_byte, 33);
hash2.Update(hash_pK_byte, 32);
hash2.TruncatedFinal(hash_pK_byte_valid, 4);
pK_byte += hash_pK_byte_valid;
HexEncoder encoder;
string Hash_Px_Py_ext;
encoder.Attach(new StringSink( Hash_Px_Py_ext ));
encoder.Put( hash_sha256, sizeof(hash_sha256));
string Px_Py_ext;
encoder.Attach ( new StringSink( Px_Py_ext ));
encoder.Put( xy_byte, xy_byte.size() );
string input_string;
encoder.Attach( new StringSink( input_string ));
encoder.Put( reinterpret_cast( &input[0]), input.length() );
string pK_byte_string;
encoder.Attach( new StringSink( pK_byte_string ));
encoder.Put( pK_byte, pK_byte.size() );
cout << endl << endl << endl <<
"Input (hex): " << input_string << endl <<
"Private Key in WIF: " << pK_byte_string << endl <<
"Private Key: " << hex << privateKey << endl;
cout <<
"Px Py ext: " << Px_Py_ext << endl <<
"Hash Px Py ext: " << Hash_Px_Py_ext << endl;
if (K2.Validate( prng, 3 ))
{
string hash_rip160_string_temp;
encoder.Attach( new StringSink( hash_rip160_string_temp ));
encoder.Put( hash_rip160_temp, hash_rip160_temp.size() );
string hash_sha256_valid_string;
encoder.Attach( new StringSink( hash_sha256_valid_string ));
encoder.Put( hash_sha256_valid, hash_sha256_valid.size() );
string hex_key_string;
encoder.Attach( new StringSink( hex_key_string ));
encoder.Put( hex_key, hex_key.size() );
string hash_rip160_string;
encoder.Attach( new StringSink( hash_rip160_string ));
encoder.Put( hash_rip160, hash_rip160.size() );
cout <<
"Adress (hex): " << hex_key_string << endl << endl;
cout <<
"Adress base58: " << bitcoin_adress << endl <<
"PrivateKey base58: " << base58_encode(Integer (pK_byte, 37, Integer::UNSIGNED), "") << endl << endl <<
"Iterations: " << dec << number_iteration << endl <<
"Time: " << time(0) - now << " sec\a" << endl;
_getch();
}
else cout << endl << "False! Please use another pass-phrase!";
/*-------------------------------------------------------------------*/
_getch();
}