Bitcoin Forum
November 27, 2021, 06:35:17 AM *
News: Latest Bitcoin Core release: 22.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: .  (Read 1773 times)
ingrownpocket
Legendary
*
Offline Offline

Activity: 952
Merit: 1000


View Profile
.
October 27, 2014, 05:31:24 PM
Last edit: October 21, 2016, 05:41:52 PM by Carlos L.
 #1

.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction. Advertise here.
1637994917
Hero Member
*
Offline Offline

Posts: 1637994917

View Profile Personal Message (Offline)

Ignore
1637994917
Reply with quote  #2

1637994917
Report to moderator
1637994917
Hero Member
*
Offline Offline

Posts: 1637994917

View Profile Personal Message (Offline)

Ignore
1637994917
Reply with quote  #2

1637994917
Report to moderator
inf
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
October 28, 2014, 12:08:58 PM
 #2

Of course you can, but i guess you have to write that lib on your own...

Take a look at

https://github.com/etotheipi/BitcoinArmory/blob/d59e38d2904e7054b3c71728439b085033e10260/cppForSwig/EncryptionUtils.cpp#L768

how chained public keys are computed in Armory and port it to PHP. You need nothing more than the root public key and the chaincode then on your webserver.
inf
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
October 28, 2014, 07:43:05 PM
 #3

Hi,

I sat down half an hour and coded a function in PHP to compute chained pubkeys for armory based on Bit-Wasps bitcoin lib (https://github.com/Bit-Wasp/bitcoin-lib-php):

Code:
function compute_chained_pubkey($pubkey, $chaincode)
{
$chainMod = BitcoinLib::hash256($pubkey);
$chainXor = gmp_xor(gmp_init($chainMod, 16), gmp_init($chaincode, 16));

$x_coord = substr($pubkey, 2, 64);
$y_coord = substr($pubkey, 66, 64);

$curve = \SECcurve::curve_secp256k1();
$generator = \SECcurve::generator_secp256k1();

$pubkey_point = new \Point($curve, gmp_strval(gmp_init($x_coord, 16),10), gmp_strval(gmp_init($y_coord, 16),10), $generator->getOrder());

try {
$pubkey_point_new = \Point::mul($chainXor, $pubkey_point);
} catch (\Exception $e) {
return FALSE;
}

$xHex = str_pad(BitcoinLib::hex_encode($pubkey_point_new->getX()), 64, '0', STR_PAD_LEFT);
$yHex = str_pad(BitcoinLib::hex_encode($pubkey_point_new->getY()), 64, '0', STR_PAD_LEFT);

return '04'.$xHex.$yHex;
}

You now need to extract the root pubkey and the chaincode from the armory wallet (I do it with a hex editor according to the wallet format in https://bitcoinarmory.com/developers/armory-wallet-files/).

Then you can for example generate the first 10 addresses in the chain like this:

Code:
$rootpubkey = "04428C01906362DA5484B316C807C8B2A45603290515BBDBB2ACA1C4AFA7802DE81BB1C20BEB3B6CD32EAB4DEACCBE26EBA7FDFEA2DCC35E3ACA2704CFAD05589D";
$chaincode = "601032E5FE0EA6FAD4310C2D85A3ADC3971281ADE74447765AE296166BA1D834";


$pubkey = $rootpubkey;

for($i = 0; $i < 10; $i++) {
$pubkey = compute_chained_pubkey($pubkey, $chaincode);
echo BitcoinLib::public_key_to_address($pubkey, '00');
echo "\n";
}

If this helped you, I would appreciate some donation to the first address in the example chain Smiley
inf
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
October 28, 2014, 10:03:34 PM
Last edit: October 29, 2014, 05:08:53 PM by inf
 #4

EDIT: Please see my next post in this thread, in which I corrected some mistakes from this post!

Yep, this is the first address of my wallet.


Cool, I did not know one can export this watch-only wallet data directly as well. But it seems to be a bit more complicated than I thought this way.

The translation map is this:

NORMALCHARS  = '0123 4567 89ab cdef'
EASY16CHARS  = 'asdf ghjk wert uion'

so your export translates to


817d 9216 ee19 0036 ba

749d 0e7a 9da0 451b 23e2 cb23 26b9 baeb 8446
b0dc 4a3a 0cb7 d2a9 bc45 1e2d 28c7
3388 191f
7e9b 5347 b656 7083 4921 84af 95bb 8e3e 3db0
745b f145 bb9b fbd0 4447 b6e2 cfc7
2408 2056

The green part is the x-coord of the root public key, the red part is the chaincode. The blocks after them are checksums. I do not know yet what the root ID is for. But half the key does not help, you have to calculate the y-part... I will look into it tomorrow, or maybe someone else can elaborate quicker, it is too late in the night now here for me Wink

EDIT: Okay, the root ID simply contains the wallet id + checksum. Anyway, you have to calculate the y-coord from the x-coord via elliptic curve math (in the bitcoin-php-lib with BitcoinLib::decompress_public_key()) but you need the parity byte for that, which I do not see in this backup data. But I will look into it tomorrow again.

EDIT2:

Okay, powered by such a generous tip I found the part which is encoding the root data Cheesy:

Code:
---PART 1: Root Data ID (9 bytes)---
      - Compressed pub key's "sign byte" flag (mask 0x80) + root data format
        version (mask 0x7F)  (1 byte)
      - Wallet ID  (6 bytes)
      - Checksum of the initial byte + the wallet ID  (2 bytes)

      ---PART 2: Root Data (64 bytes)---
      - Compressed public key minus the first ("sign") byte  (32 bytes)
      - Chain code  (32 bytes)

So the parity is encoded in the MSB of the first byte, which in your case is 0x81 = 10000001b, so the sign is +. So your compressed public root key translates to

Code:
!03! 749d 0e7a 9da0 451b 23e2 cb23 26b9 baeb 8446 b0dc 4a3a 0cb7 d2a9 bc45 1e2d 28c7

Using

Code:
$rootpubkey = BitcoinLib::decompress_public_key("03749d0e7a9da0451b23e2cb2326b9baeb8446b0dc4a3a0cb7d2a9bc451e2d28c7");
$rootpubkey = $rootpubkey['public_key'];
$chaincode = "7e9b5347b6567083492184af95bb8e3e3db0745bf145bb9bfbd04447b6e2cfc7";

returns the following first 10 adresses in your wallet:

Code:
14nTEtvpN7yBUZDcDnvYfBkGMpQmp4hQn6
1EYZpyfyxK3JxQ5j3FL6KgaWvh7SHUdXhZ
1ELXudaLkrmodPneh8pitP9u2GK6Ve2HVb
1PWBwGEgfRE6FamVcXrZLvAThXL6RsoaGx
1PR3YJgsN3PLzX2P1LXJnduRqYndHAe3VE
1JJEWaUczztUJk3hZnpTnvszCUVas2hhDq
13HzTASPY8VeXMGZm7snZ6WDX11NdEoxD1
1Q7k6GcF3QEixbitxK9Lg2B2Tu3P8WhVAx
1BWjEgG7f15huoiV677iCVH7ehZWXmqqMG
1BwP4d1e9CXYMRetVZqPeVysaK7WgrE84g

Is this correct?

Good night Smiley
etotheipi
Legendary
*
Offline Offline

Activity: 1428
Merit: 1048


Core Armory Developer


View Profile WWW
October 29, 2014, 03:05:13 PM
 #5

It's kind of fun watching you guys attempt to reverse engineer this stuff Smiley 

Looks like inf got it except for one mistake (if I read his text formatting correctly):  the lines in the backup data actually have 2-bytes of checksum per line, instead of 4 bytes of checksum per two lines.  i.e. you wrote:


817d 9216 ee19 0036 ba

749d 0e7a 9da0 451b 23e2 cb23 26b9 baeb 8446
b0dc 4a3a 0cb7 d2a9 bc45 1e2d 28c7
3388 191f
7e9b 5347 b656 7083 4921 84af 95bb 8e3e 3db0
745b f145 bb9b fbd0 4447 b6e2 cfc7
2408 2056


But it should be:

Quote

817d 9216 ee19 0036 ba

749d 0e7a 9da0 451b 23e2 cb23 26b9 baeb  8446
b0dc 4a3a 0cb7 d2a9 bc45 1e2d 28c7 3388  191f
7e9b 5347 b656 7083 4921 84af 95bb 8e3e 3db0
745b f145 bb9b fbd0 4447 b6e2 cfc7 2408 2056


You can confirm your understanding of it by look up the two functions "makeSixteenBytesEasy()" and "readSixteenEasyBytes()" (both are in ArmoryUtils.py)

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
inf
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
October 29, 2014, 05:05:29 PM
 #6

Hi all,

thank you, etotheipi, I stand corrected. I wanted to help too quickly last night Smiley
And yes, I am reverse engineering the shit out of the Armory code, tx files and wallets since several weeks lately, I hope I can present soon what for Cheesy

I took another 30 mins and wrote a (now tested and working) import function in PHP:

Code:
function import_watchonly_wallet($root_id, $root_data)
{
$nc = array(' '=>'',
'a'=>'0', 's'=>'1', 'd'=>'2', 'f'=>'3',
'g'=>'4', 'h'=>'5', 'j'=>'6', 'k'=>'7',
'w'=>'8', 'e'=>'9', 'r'=>'a', 't'=>'b',
'u'=>'c', 'i'=>'d', 'o'=>'e', 'n'=>'f');

foreach($nc as $easy16 => $normal) {
$root_id = str_replace($easy16, $normal, $root_id);
$root_data =  str_replace($easy16, $normal, $root_data);
}

$sign = gmp_strval(gmp_and(gmp_init(substr($root_id, 0, 2), 16), gmp_init('80', 16))) ? '03' : '02';
$x_coord = substr($root_data, 0, 32).substr($root_data, 36, 32);

$rootpubkey = BitcoinLib::decompress_public_key($sign.$x_coord);
$chaincode = substr($root_data, 72, 32).substr($root_data, 108, 32);

return array('rootpubkey' => $rootpubkey['public_key'],
'chaincode' => $chaincode);
}

You can use it like this now:

Code:
$root_id = "wski edsj oose aafj tr";
$root_data = "kgei aokr eira ghst dfod utdf djte trot wggjtaiu grfr autk idre tugh sodi dwuk ffww sesnkoet hfgk tjhj kawf geds wgrn ehtt wofo fitakght nsgh ttet ntia gggk tjod unuk dgaw dahj";

$wallet = import_watchonly_wallet($root_id, $root_data);

$chaincode = $wallet['chaincode'];
$pubkey = $wallet['rootpubkey'];

for($i = 0; $i < 10; $i++) {
$pubkey = compute_chained_pubkey($pubkey, $chaincode);
echo BitcoinLib::public_key_to_address($pubkey, '00');
echo "\n";
}

which actually spits out now

Code:
13N77TDamXjsHH1mHFgBty3VLbHBidychP
14xUP1ZxeTdNLHuAVwKSqvgotBBo1KT59W
1Hv1PQD8n1fxdn7o8tsKjb4rrypWZWD3o4
18wrjH3VEs4LHAvPEbsvGjuCHvYunTZTfa
1GaEQsNjwiuT5W8eXjnnuR433Y2ZskNVhD
1PUrFjaV1vymvQtz4j1EePXfKAYpZUyCTV
19R5Jj4CHuWy7gozxBeHAvTAqmVtpCj4xL
1AYpG5z7fH2pKTWnujnr9iwkBSromdNxZv
1FZbWDL3qwm9HncqdMKdsNmpkBUifvVtem
16Wafgea7vyJAoqgzqDv1g1USMaj8Ms1PW

Have fun!

Btw, I would generate the rootpubkey and chaincode only once and store it somehow, then. You also do not have to start the generation again beginning with the root pubkey, just always store the last used pubkey and generate more from that point, this saves computation time.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!