etotheipi / BitcoinArmory

Python-Based Bitcoin Software
Other
826 stars 621 forks source link

Question: key derivation algorithm? #204

Open olalonde opened 10 years ago

olalonde commented 10 years ago

Hi. I will try to implement Armory's key derivation algorithm for Bitcore (Javascript). Is there any existing Javascript implementation? Where is the algorithm located and is there a higher level description available? If I understand correctly, Armory uses a custom scheme but plans to implement BIP32 eventually?

olalonde commented 10 years ago

I think I found it: https://github.com/etotheipi/BitcoinArmory/blob/99c8cd151eba47f1100307e85337779537cee345/armoryengine/PyBtcAddress.py#L451

Also, brainwallet.org has an implementation here: https://github.com/brainwallet/brainwallet.github.com/blob/master/js/armory.js

Is it up to date?

olalonde commented 10 years ago

I can't find any way to get the (public key, chain code) from the UI... How is this done?

goatpig commented 10 years ago

The KdfRomix class takes care of it.

Code can be found in EncryptionUtils.cpp, notably:

void KdfRomix::computeKdfParams(double targetComputeSec, uint32_t maxMemReqts) SecureBinaryData KdfRomix::DeriveKey_OneIter(SecureBinaryData const & password)

You can right click on any address in the wallet property window to get its 65 bytes hex public key.

I dont think chaincodes are exposed to the UI anymore since we started deriving it from the rootkey.

Any address entry in the PyBtcWallet addrMap member carries a chaincode value. All chaincodes in a given wallet are the same.

olalonde commented 10 years ago

Ok, I got this out of the Backup wallet feature (I won't be using this wallet, no worries)

1PUzLkds8eHGjHPaW7v7h23bzmHjrRMVqz
   PublicX   : 9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e
   PublicY   : 66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4
   ChainIndex: 0

Not sure what is the chain code though.

olalonde commented 10 years ago

@goatpig thanks! So there's no way to get the chain code out of the UI? How could someone back up a watch-only copy of their wallet for example? Or do you always need to backup the root key as well?

goatpig commented 10 years ago

There is no option to create paper backups of WO wallets if it's what you are asking for, only digital ones.

olalonde commented 10 years ago

Ah ok I see. Is the root code alone sufficient to derive private keys? Let's say I wanted to code a Armory compatible watch only wallet... Is there any way I could do that or the Armory UI won't let me get out the necessary information? I suppose I could parse the digital watch only backup created by Armory but I'd rather find an easier way :)

goatpig commented 10 years ago

The root key is used to derive the chaincode and the first address pair. Address pair N+1 is derived from address pair N, through a scalar which is:

sha256(sha256(pubKey N)) XOR chaincode

Both the private and public keys are multiplied by this scalar to yield the next pair.

Exact code can be seen in EncryptionUtils.cpp:

SecureBinaryData CryptoECDSA::ComputeChainedPrivateKey( SecureBinaryData const & binPrivKey, SecureBinaryData const & chainCode, SecureBinaryData binPubKey, SecureBinaryData* multiplierOut)

SecureBinaryData CryptoECDSA::ComputeChainedPublicKey( SecureBinaryData const & binPubKey, SecureBinaryData const & chainCode, SecureBinaryData* multiplierOut)

This gives you 2 options:

With the root key alone, you can rebuild an entire wallet. With the chaincode and the first public key, you can reproduce the entire watching only wallet.

Also, with private key N and the chaincode, you can reproduce all private keys past N, and with pbulic key N and the chaincode, you can reproduce any public keys past N

olalonde commented 10 years ago

Ok thanks a lot for your time. It would be really great if it was possible to get this chain code out of wallet in a safe way so that it can be imported in a compatible wallet. Right now it seems the only options are

1) to copy/paste the root key and use a third party tool to generate the chain code which is not ideal because it relies on trusting a third party tool.

2) have the third party support the file Armory's digital watch-only backup format which is a bit a PITA

Cheers.

goatpig commented 10 years ago

Are you talking from a user or developer standpoint? It is fairly easy as a dev: Build the binaries, grab your favorite Python debugger, start Armory with a wallet, break a this line:

https://github.com/etotheipi/BitcoinArmory/blob/master/armoryengine/PyBtcWallet.py#L1846

and watch self.addrMap['ROOT'].chaincode.

For users, there is no hook in place currently that I can think of to get the chaincode through the UI.

olalonde commented 10 years ago

Yes, I was talking from a user's standpoint. I guess I could release a small self contained script that lets users get the chain code out of their paper backup phrase that they can run locally.

goatpig commented 10 years ago

I think ideally you want to look at the code early in PyBtcWalletRecovery.py, in the ProcessWallet function. It is self contained and will run on its own when spawned from Armory's source folder. It only needs to go as far as this line:

https://github.com/etotheipi/BitcoinArmory/blob/master/armoryengine/PyBtcWalletRecovery.py#L449

and can run with just a wallet path. That's enough to read any wallet and extract the first address entry.

olalonde commented 10 years ago

Question: what do PublicX and PublicY represent in the paper wallet? I tried generating an address using them but the address is not one in my wallet.

goatpig commented 10 years ago

X and Y parameters for the secp256k1 curve point.

Looking at the 65 bytes SER public key packaging, it goes 0x04 X Y

olalonde commented 10 years ago

Hey, I managed to get the public key derivation code to work :) That being said there is something that really puzzles me. When I start deriving from the public key 04 + PublicX + PublicY (049df523e718b91f59a7902d46999f9357ccf8720824d430764516b809f7abce4e66ba5d2146820dae401d95068437251679f90c564186cc5007dfc6d069891ff4), the result looks like this:

1CGrip2uQUwhP2f3ARfbcrmtdwvWzELRmj
1BfBauMP4PX1ZBYrqH4K4R8KWrFfskrs7E
15emDCBVgBJLDP5cKxuwZ4Q77sfqEcwZvC
16tDJhMYBv1szZgRZCohWrzEvzX2bG7vEQ
1c6NUxFe9xo5fsS8ACBsHNFYTvHQP3qzu

However, the expected output is:

1PUzLkds8eHGjHPaW7v7h23bzmHjrRMVqz
1CGrip2uQUwhP2f3ARfbcrmtdwvWzELRmj
1BfBauMP4PX1ZBYrqH4K4R8KWrFfskrs7E
15emDCBVgBJLDP5cKxuwZ4Q77sfqEcwZvC
16tDJhMYBv1szZgRZCohWrzEvzX2bG7vEQ

So basically it looks like 04 + PublicX + PublicY is the second public key in an Armory wallet?

http://brainwallet.org/#chains gets this right but they compute the public key from the paper wallet. 045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c87. Is there any way to compute this key from PublicX and PublicY in the paper wallet. Or am I doing something wrong?

olalonde commented 10 years ago

Oh, stupid mistake in my code, works fine now.

etotheipi commented 10 years ago

I just tasked one of our guys with adding these features. It will include a new six-line export and import function, using Armory's easy-type-16 format (so it will have the same alphabet and checksums as regular paper backups). Plan to allow paper or text-file export, and a new import-watchonly-root-key function that would allow you to type the 6 lines or load the six lines from file. We're not sure when we'll have it, but it shouldn't be terribly long, and we can include it the multisig-beta build we're releasing soon.

olalonde commented 10 years ago

Wow, awesome!!! (we had started working on a proof of concept here http://maxrann.github.io/armory-mpk/ but if you guys support something like this natively, even better!). If you're curious, we needed this to support Armory on https://www.coldmonitor.com

levino commented 9 years ago

This is still an issue, isn't it? Can one derive public keys / addresses from some read only data provided to the user of armory in decent way? I would be very excited about a headsup for the new wallet format.