Help with zip32 child keys

Not sure if this is the right forum to ask on, I’ve been trying to write my own ZIP32/Sapling script while waiting on the official clients to support view keys and am stuck on one small part.

Here is a test page entirely in JS that reproduces the problem: https://uwuforever.github.io/zcash-zip32-js/testsap.html

I can’t quite figure out what’s wrong with my child key derivation. I’m using https://paper.zecwallet.co/ as “test vectors” and trying to get the output private key to match.

The expected result would be pasting in the hex HD Key into the top box => click the key that comes up => box comes up at the bottom of the page for deriving a child key => enter 32 hardened => click resulting key => enter 133 hardened => click resulting key => enter 0 hardened => key at bottom of page should match. The majority of this script produces results that match those from the official client, so I’m assuming the error should be in Sapling.deriveMaster or Sapling.deriveChild in sapling.js.

2 Likes

I haven’t looked at the code, but there might be an issue with the way you’re using the original source of randomness. Bip39 specifies that you should use pkdf2(32 bytes of entropy from the seed) and then use that to derive the path.

You can see the Rust code for this here: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs#L152

1 Like

I’ve just been trying to follow the zip32 doc as closely as I could,

Calculate I = BLAKE2b-512("ZcashIP32Sapling", S).

where S is the hex string decoded to bytes. Am I supposed to apply pkdf2 to S before passing to Blake2b?

No, I think BIP 39 is a red herring here: https://paper.zecwallet.co/ doesn’t implement that part, and neither does zcashd, athough ZecWallet proper does.

I reviewed https://uwuforever.github.io/zcash-zip32-js/sapling.js and could not immediately see the problem, unless it is that the child \mathsf{ask}_i and \mathsf{nsk}_i keys are intended to be taken modulo r_\mathbb{J}. (This was not entirely clear in ZIP 32; I’ve updated it. Technically the ZIP was already correct because I_\mathsf{ask}, I_\mathsf{nsk}, \mathsf{ask_{par}}, and \mathsf{nsk_{par}} are all \mathbb{F}_{r_\mathbb{J}} elements, but that assumes a lot of familiarity with the spec notation.)

In sapling.js, the fact that zip32_decoded.ask and zip32_decoded.nsk change their meanings depending on whether zip32_isPrivate is set (sometimes meaning \mathsf{ak} and \mathsf{nk} rather than \mathsf{ask} and \mathsf{nsk}), is really quite confusing and I recommend changing it. That could easily be a source of bugs, whether or not it’s causing this particular issue.

If you still have problems, I suggest creating a GitHub ticket, since that will be easier for us to track.