-
Notifications
You must be signed in to change notification settings - Fork 249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix child keys derivation adding bytes padding #1139
Conversation
extkeys/hdkey.go
Outdated
child.KeyData = keyBigInt.Bytes() | ||
keyData := keyBigInt.Bytes() | ||
if len(keyData) < 32 { | ||
extra := make([]byte, 32-len(keyData)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you want you could do a single allocation instead of 2
extra := make([]byte, 32-len, 32)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmitryn yes good point I'll do it!
@pilu do you have a link to relevant bip where this condition is described? |
For now I just compared to other implementation. But looking at Private parent key → private child key, the private key should always be |
@dshulyak so the problem is not in that key (the one smaller than 32 bytes), because it's still a valid one. but in the generation of the child, because the parent private key is smaller than 32 bytes |
@dshulyak in fact I think the same logic should be used even when |
actually if it's public is not actually needed because we call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since it is very non-trivial to roll out these updates, we need
-
unit-tests for that particular case to prove that it works correctly
-
we need the old version of this code still available with a flag or a separate method for migration purposes
-
we need investigations if all our Instabug reports about this problem are actually fixed by that, so we don’t have anything else that slipped in
We can only check this when recovering an account. If that happens and we discover that the addresses are different, how the process will look like? If we decide to support both versions in the UI, this will root deep into the codebase responsible for keys and accounts and it will make it harder to reason about. I would try to make avoid that. For example, we can make an announcement first and then release a version that fixes it without supporting the bugged version. If someone misses the window, they should be able to download an old release that will allow to move founds. |
Can one of the admins verify this patch? |
If this is the route we go with only going with the new proper method, I suggest a how-to guide to go with this announcement. Who should lead that? I also agree that we should go this route as it would lead to address/identity issues across the app. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as key-generation is pivotal to many parts of Status, I would also like to see unit-tests to help give confidence of proper behavior.
@corpetty yes for sure I'll do as soon as I continue with this. I just opened the PR to share that part of the code but it's just a start, so that we can look at it together. |
To avoid having these situations in the future, let's try to find a good test suite for key derivation. If there aren't, then we should make one and share it with crypto community, I think. |
@mandrigin @adambabik @dshulyak @corpetty I added tests and documentation in the PR description |
@@ -111,6 +111,21 @@ func TestBIP32Vectors(t *testing.T) { | |||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", | |||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", | |||
}, | |||
// Test vector 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you reference where this came from for future folks looking at this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point @corpetty, I'm adding it to the PR description, it's from here:
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR descriptions/commit messages are pretty ephemeral. Let's put all the references in the code so no refactorings can destroy it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mandrigin I added more comments in the code taken from the PR description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @mandrigin @corpetty is there anything else you want to change in this PR?
Please merge |
Problem
While deriving a child key, if
i ≥ 2^31
(hardened), the resulting private key is padded with a0x00
to make it 33 bytes.The specs says:
The important part is: The 0x00 pads the private key to make it 33 bytes long.
If we pad it to always be 33 bytes, we should also add more zeros when the key data is less than 32 bytes.
In this specific case the derived private key was 31 bytes.
That private key is anyway right, but the problem raises when deriving its child key.
One of the step to derive the child key is calling our
splitHMAC
that returns asecretKey
and achainCode
.Inside this function we make a
sha512
of a seed that is a 37 bytes with:0x00
In our case, if the key was less then 32 bytes, it was shifted to the left of that 32 bytes space, resulting in a different seed, and a different data returned from the
sha512
call.Changes
I added a third test vector that has been added to the BIP32 spec after this part of the code has been written (taken from the test vector 3 section of BIP32), though our old codebase without this patch is not failing with those.
I added unit test to test a specific mnemonic key that generates a key of 31 bytes during one of the steps of the derivation of a key with path
m/44'/60'/0'/0/0
.The main change is padding the key data with zeros to always have 32 bytes in the field
KeyData
ofExtendedKey
.Links