Question regarding secret key seed length. In Stellar the seed length for secret key is 32 bytes but BIP-39 specifies the length of binary seed as 64 bytes. To get around that problem I would split the 64 byte seed and scalar multiply with each other, then feed the result into 'fromRawSeed' function. Example code:

var b = bip39.mnemonicToSeed(this.mnemonic);
var c1 = b.slice(0,32);
var c2 = b.slice(32,64);
var c = nacl.scalarMult(c1, c2);
var d = StellarSdk.Keypair.fromRawSeed(c);

Could someone please comment on this approach? Is there some 'other' proposal that I don't know about that would tackle the problem of passphrase-to-seed generation and hierarchical deterministic address pool allocation?

Thank you!

    syntaxval

    Do check with @dupe how the Ledger Nano code is doing it, so we can have one standard

    syntaxval Could you take the sha256 of b in your example? That should give you 32 bytes that you could pass to fromRawSeed.

    Guys, thank you for your comments. @dupe, yes I started looking more into BIP32 - root key, derivation path and BIP44 - registered coin types (XLM is on the list with id=148) but it seems that BIP39 Web UI has no XLM listed so I will try to look at the code tomorrow if the derivation of the seed is actually implemented. @zcc, great idea! It could be our standard for single address generation in Stellar. For multiple HD address allocation, as far as what I understand so far I think we need to follow derivation path as described in BIP-44. Will read up some more tomorrow.
    Cheers!

    • zcc replied to this.
      6 days later

      dzham That's what I've been using to make sure that the seed words generate the keys I expect

      Could you link me to some source code where you're doing this? I've been looking over several BIP39 implementations (javascript, Trezor, and Ledger Nano) and am curious how you're generating Stellar keys.

        zcc

        I haven't generated any Stellar keys.

        I've used that page to verify that the Ledger Nano S seed words generate the accounts it should, so I know my seed works outside the Nano

        4 days later

        syntaxval I have a much better answer for you since Stellar has a Bip39-like standard now! See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md

        The gist of it is that you'd use the mnemonic to generate the master key in the same way that Bitcoin does it (splitting the 64 bytes into a 32-byte secret key and a 32-byte chain code). Then, you'd use the master key to derive the path m/44'/148'/0' and pass the 32 bytes of secret key data to StellarSdk.Keypair.fromRawSeed.

          Great stuff! I just looked at the Go repository and will implement the same in JavaScript. I would like for this to run in the browser so it's all transparent to the end user.

          zcc

          Can we verify that the Ledger Nano S app generates keys in the same way?

          • zcc replied to this.

            I spent all day trying to figure this out... I am usng sjcl in JavaScript to somehow navigate this path from a bip39 seed (in hex) to derived m/44'/148' key but I am doing something wrong on the way cause that derived key output does not match test vector in sep-0005... I guess what I am confused about is how exactly you derive the path... the code in Go does not seem to align with what is described in slip-0010 and that may be due to the fact that I don't know Go Lang and/or I cannot read complicated mathematical notation describing the algorithm steps. Could someone help out and describe the intermediate steps in more details?
            @zcc I get the part that I split my bip39 hex seed in two 32-byte secret key and chain code.... then my mind breaks down on what you said after that. Which one is the master key? Please help ? Thank you!

            • zcc replied to this.

              syntaxval The bitcoinjs project has some libraries you might be able to use, although I haven't used them myself so this is all going to be a guess ?

              It sounds like you've got the mnemonic to seed bytes part figured out, so I'll focus on what comes next.

              First, you'll need a HDNode created from those 64 bytes. bitcoinjs-lib looks like it has a class that would work with this: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/hdnode.js

              However, for Stellar you'll need to use the hardcoded string "ed25519 seed" instead of "Bitcoin seed" when doing your initial sha512 hash, so you'll probably need to fork and modify their library.

              They also have a bip39 library you could then use to derive the child nodes: https://github.com/bitcoinjs/bip32-utils

              Hope that helps!

              @zcc Thank you for trying to help me. OK so I modified bitcoinjs-lib and produced the "IL" and "IR" after the first HMAC-512 op in HDNode and the output is as follows:
              IL: "5daf42e856561166e7788d292d5af4c0e3856a0e5dd815cf1f7146e64f87e775",
              IR: "05294fe2430e60a3be665f5c6c44ad100d21d09ebcde853befaba92bdb788937"
              which is exactly what I produce with my own code using sjcl lib. So everything up to this point should be correct.
              I am following the test vectors specified in https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md

              Now, (as far as I can read the Go code) I would need to feed the IR hash as chain code into another HMAC-512 with data=0x00 (representing 0 from derivation path) and again take first 32 bytes and feed it to StellarSdk.Keypair.fromRawSeed?? I already checked this scenario and the Stellar key does not match the one for m/44'/148/0'

              Maybe the simplest thing would be to clarify how to get from this point (having IL/IR hashes) to producing the "m/44'/148' key: e0eec84fe165cd427cb7bc9b6cfdef0555aa1cb6f9043ff1fe986c3c8ddd22e3" (as described in https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)

              Thanks!

              OK, I verified with python implementation too https://github.com/satoshilabs/slips/blob/master/slip-0010/testvectors.py and again the master private key hash for BIP39 seed (e4a5a632e70943ae7f07659df1332160937fad82587216a4c64315a0fb39497ee4a01f76ddab4cba68147977f3a147b6ad584c41808e8238a07f6cc4b582f186) is:
              5daf42e856561166e7788d292d5af4c0e3856a0e5dd815cf1f7146e64f87e775
              and NOT e0eec84fe165cd427cb7bc9b6cfdef0555aa1cb6f9043ff1fe986c3c8ddd22e3 (as specified in Test 1 of https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md)

              Again: bitcoinjs-lib, testvectors.py and my implementation return same result.
              testvectors.py output:

              Test vector 1 for ed25519

              Seed (hex): e4a5a632e70943ae7f07659df1332160937fad82587216a4c64315a0fb39497ee4a01f76ddab4cba68147977f3a147b6ad584c41808e8238a07f6cc4b582f186
              Chain m
              fpr: 00000000
              chain: 05294fe2430e60a3be665f5c6c44ad100d21d09ebcde853befaba92bdb788937
              prv: 5daf42e856561166e7788d292d5af4c0e3856a0e5dd815cf1f7146e64f87e775
              * pub: 0087c9725f4f05b37d6b054e0531a6a86f17723cd19fbbdc25687482811d855322

              I would need to feed the IR hash as chain code into another HMAC-512 with data=0x00 (representing 0 from derivation path) and again take first 32 bytes and feed it to StellarSdk.Keypair.fromRawSeed

              I think this may be where things are going wrong, that first 0x00 is not related to the derivation path (I believe it's padding).

              Here's some pseudo code for deriving a child node (m/44' from the master node):

              $index = 44 + 0x80000000; // all keys are hardened
              
              // this comes from the node being used to derive `44'` (aka the master node)
              $chainCodeBytes = '05294fe2430e60a3be665f5c6c44ad100d21d09ebcde853befaba92bdb788937'; 
              // Private key of the current node
              $privateKeyBytes = '5daf42e856561166e7788d292d5af4c0e3856a0e5dd815cf1f7146e64f87e775';
              // index should be encoded as a 4-byte big-endian unsigned long
              $indexBytes = toUint32($index); 
              
              $hmacInputBytes = 0x00;
              $hmacInputBytes.append($privateKeyBytes)
              $hmacInputBytes.append($indexBytes) // <--- I think this is what you're missing
              
              $hmac = hmac_init('sha512', $chainCodeBytes);
              hmac_update($hmac, $hmacInputBytes);
              
              $hashedBytes = hmac_final($hmac);

              At this point, $hashedBytes will be a new 64-byte blob that you can create the child HDNode (m/44') from (first 32 bytes of it for the private key, second 32 bytes for the chain code).

              To get m/44'/148' you'll repeat the above process.

              I think you're really close, you were just missing the 4 bytes of the index!

              Here's a link to my PHP code if it helps: https://github.com/zulucrypto/stellar-api/blob/master/src/Derivation/HdNode.php#L64

              We standardized key derivation in SEP-0005. Also test vectors can be found there. Check it out if you haven't yet. EDIT. OK, I see you have ?.