2

I'm trying to understand the process of creating the children's private key and, in the case of knowing the children's private key and the left 256-bit hash result, the process of returning the parent's private key.

There is a bit more text as I have tried to explain in detail exactly what I am doing as well as to mark all the articles I refer to.

I'll explain what I'm doing through an example and link the article I'm referring to.

Example and numbers are taken from here (code only)

So let's start with that we have the left 256-bit hash result (L256B) and the parent's private key (PPK):

L256B (hex): 6539ae80b3618c22f5f8cc4171d04835570bda8db11b5bf1779afae7ec7c79c3
L256B (decimal): 45785512363230816970838539051071102444734444055822171970071151407697781094851

PPK (hex): e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35
PPK (decimal): 105366245268346348601399826821003822098691517983742654654633135381666943167285

The process of obtaining the children's private key is based on the following formula according to this:

children_private_key == (parent_private_key + lefthand_hash_output) % G

That is, on the following formula according to this (where parse256(IL) is the left 256-bit of the hash result, kpar is the parent's private key, and ki denotes the child's private key):

Child private key formula -> parse256(IL) + kpar (mod n) = ki

Also, according to this again a similar formula:

The returned child key ki is parse256(IL) + kpar (mod n)

1. So my first question: is the plus (+) in all these formulas related to a normal plus operation like 2+2 = 4, 6+3 = 9, etc. or is it some kind of concatenation like 2+2 = 22, 6+3 =63 etc.? I ask because in this answer Michael Folkson said it is a concatenation…

There is concatenation where 256 bits placed next to another 256 bits makes 512 bits.

This totally confuses me.

But let's continue with the assumption that normal addition is what we have to do here.

EDIT: I misinterpreted his answer. He wrote that scalar addition is used here (as I thought), not concatenation. My mistake.

So the first thing we need to do is add L256B and PPK:

L256B (decimal): 45785512363230816970838539051071102444734444055822171970071151407697781094851

PPK (decimal): 105366245268346348601399826821003822098691517983742654654633135381666943167285

L256B + PPK (decimal): 151151757631577165572238365872074924543425962039564826624704286789364724262136

The next thing we need to do is the modulo operation with n or G (whatever is the correct label). n is (according to this):

n = 115792089237316195423570985008687907852837564279074904382605163141518161494337

So the children's private key (CPK) is:

CPK = (L256B + PPK) mod n 
CPK = 151151757631577165572238365872074924543425962039564826624704286789364724262136 mod 115792089237316195423570985008687907852837564279074904382605163141518161494337

CPK (decimal): 35359668394260970148667380863387016690588397760489922242099123647846562767799
CPK (hex): 4e2cdcf2f14e802810e878cf9e6411fc4e712edf19a06bcfcc5d5572e489a3b7

That's exactly what they got in example I am using.

Everything looks fine and correct (assuming the normal + operation is used here and not concatenation).

Now comes the bigger problem. In the example I'm using, they say you can get the parent's private key back from the children's private key. Formula taken from here says:

Solve for kpar -> kpar = ki - parse256(IL) (mod n)

This minus confuses me.

2. So my second question would be what does the minus mean in the formula above? Normal subtraction operation or something else?

I ask because when I try to return the parent's private key from the children's private key, I get the wrong result compared to what they get. The process I use is as follows:

PPK = (CPK - L256) mod n

CPK (decimal): 35359668394260970148667380863387016690588397760489922242099123647846562767799

L256B (decimal): 45785512363230816970838539051071102444734444055822171970071151407697781094851

CPK - L256B (decimal): -10425843968969846822171158187684085754146046295332249727972027759851218327052

PPK = -10425843968969846822171158187684085754146046295332249727972027759851218327052 mod 115792089237316195423570985008687907852837564279074904382605163141518161494337

PPK (decimal): -10425843968969846822171158187684085754146046295332249727972027759851218327052
PPK (hex): -170CD18DC2130BFAE5105371D36C3639089AABAE977AF021AB3DA57507F2D60C

So as you can see, I'm not getting the correct parent private key:

-170CD18DC2130BFAE5105371D36C3639089AABAE977AF021AB3DA57507F2D60C != e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35

... unlike those who get the correct key in their solution.

What am I doing wrong? Any help would be appreciated.

4
  • I am attempting to write an answer but I do agree this hinges on who is correct: Michael with literal bit string concatenation or Malone with summative concatenation, I suppose it would not be proper to give a full answer until this is discovered.
    – Poseidon
    Commented Apr 3, 2023 at 18:36
  • @Poseidon Thanks for the comment. The answer would be acceptable to me if it showed me how to get back to the parent's private key. In other words, how to use the minus and where I am wrong in that calculation. So feel free to make some answer, it would help a lot.
    – dassd
    Commented Apr 3, 2023 at 18:41
  • I am going through it in detail at the moment, I do agree that upon first glance the minus operation is confusing as it results in a negative. The write up of the solution is not easy to reason about at the moment for me.
    – Poseidon
    Commented Apr 3, 2023 at 18:45
  • @Poseidon Yes, I was also confused by that negative result. Well, if you find a solution or something useful about this topic, comment, it would help a lot.
    – dassd
    Commented Apr 3, 2023 at 18:53

1 Answer 1

1

The next thing we need to do is the modulo operation with n or G (whatever is the correct label). n is (according to this):

In this specific operation the modulo is against N(Curve Order) not G(Curve Generator) because modulo G does nothing to the Parent Private Key in this instance. Not sure exactly why. Furthermore modulo N results in the expected Child Private Key.

I happened to identify exactly where you went wrong and it is quite small but entirely relevant.

Basically you did not modulo N before receiving the negative number (which was what confused me). When you modulo N on that negative you receive the correct private key.

Python Implementation:

G_hex = '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' #grabbed from https://en.bitcoin.it/wiki/Secp256k1 ***

G = int.from_bytes(bytes.fromhex(G_hex), byteorder='big')
#286650441496909734516720688912544350032790572785058722254415355376215376009112


N_hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141' # ***

N = int.from_bytes(bytes.fromhex(N_hex), byteorder='big')
#115792089237316195423570985008687907852837564279074904382605163141518161494337

#print(G)

#print(N)

L256B = 45785512363230816970838539051071102444734444055822171970071151407697781094851

PPK = 105366245268346348601399826821003822098691517983742654654633135381666943167285

CPrivK1 = (PPK + L256B)
#print("CPrivK1", CPrivK1)

CPrivK2 = (PPK + L256B) % G
#print("CPrivK2", CPrivK2)

assert(CPrivK1 == CPrivK2) #proof that % G does nothing in this instance

CPrivK = (PPK + L256B) % N #therefore we deduce that N is used for the modulo operation
                            #resulting in expected key 35359668394260970148667380863387016690588397760489922242099123647846562767799
print(CPrivK)

assert(CPrivK == 35359668394260970148667380863387016690588397760489922242099123647846562767799) #checked against expected key

Left256Bits_hex = '6539ae80b3618c22f5f8cc4171d04835570bda8db11b5bf1779afae7ec7c79c3'

L256 = int.from_bytes(bytes.fromhex(Left256Bits_hex), byteorder='big')

print(L256)

PPK_Formula = (CPrivK - L256) % N

PPK_No_modulo = (CPrivK - L256)

print("PPK without doing % N:", PPK_No_modulo)

print(PPK_Formula)

PPK_hex = format(PPK_Formula, 'x')

print(PPK_hex)

assert(PPK_hex == 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35') #proof of Parent PrivKey from Child PrivKey
5
  • 1
    Thank you for your help. Actually I did the modulo operation. Taken from my post: PPK = -10425843968969846822171158187684085754146046295332249727972027759851218327052 mod 115792089237316195423570985008687907852837564279074904382605163141518161494337 . However, you won't believe what actually happened. The tool I was using just didn't do it very well. Look at this: link.
    – dassd
    Commented Apr 4, 2023 at 10:08
  • I see, this tool seems to have thrown out the result of the operation. Perhaps they have arbitrary limits on computing integer sizes. Either way, I highly recommend a language like python for reasoning about big integer values like the ones here. Python handles modulo naturally with variable sized integers by just doing int1 % int2 .
    – Poseidon
    Commented Apr 4, 2023 at 13:48
  • Yes, I'm using golang so I retyped your code using golang syntax and got the same result as you (correct result). Otherwise, regular scalar addition is used when obtaining the child's private key. We misinterpreted Michael's response.
    – dassd
    Commented Apr 4, 2023 at 13:52
  • 1
    @Filip Ah I see, this was partially clear but obviously caused some confusion. I think some workshops refer to adding two byte arrays together as concatenation even though it is not being done at the string literal level.
    – Poseidon
    Commented Apr 4, 2023 at 14:01
  • 1
    Well, yes. Anyway, thanks for your help. All the best!
    – dassd
    Commented Apr 4, 2023 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.