javascriptnode.jscryptographyshacryptocurrency

How can I derive a key pair from mnemonic phrase successfully in NodeJS?


I'm trying to create a simple cryptocurrency for which I made a wallet in Node.JS using the bip39 and crypto module. The plan is to be able to create a wallet so that the script generates a random mnemonic phrase and from that a private and public key. The same mnemonic should result in the same key pair, while different mnemonics should result in different pairs.

For some reason, if after generation I try to generate the same private/public key from the same mnemonic as before, a different key pair is returned. I'm just learning cryptography, I might be doing something very wrong. Anyway, thanks in advance for your help, here is my code:

function generateRandomMnemonic() {
  // Generate a random 256-bit (32-byte) entropy
  const entropy = crypto.randomBytes(32);

  // Convert the entropy to a mnemonic phrase
  const mnemonic = bip39.entropyToMnemonic(entropy);

  return mnemonic;
}

function generateKeyPairFromMnemonic(mnemonic) {
  // Derive a seed from the mnemonic phrase
  const seed = bip39.mnemonicToSeedSync(mnemonic);

  // Generate key pair from the seed
  const keyPair = crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048,
    publicKeyEncoding: {
      type: 'spki',
      format: 'pem',
    },
    privateKeyEncoding: {
      type: 'pkcs8',
      format: 'pem',
    },
    seed,
  });

  return keyPair;
}

function getAddress(publicKey) {
    const addressHash = crypto.createHash('sha256').update(publicKey).digest('hex').slice(-30);
    return `csc.${addressHash}`
}

function signTransaction(transaction, privateKey) {
    const dataToSign = JSON.stringify(transaction);

    // Sign the data correctly
    const sign = crypto.createSign('sha256');
    sign.update(JSON.stringify(transaction));
    const signature = sign.sign(privateKey, 'base64');

    return signature;
}

The code where the wallet is generated:

app.get('/signup', async (req, res) => {
    const mnemonic = generateRandomMnemonic();
    const keypair = generateKeyPairFromMnemonic(mnemonic);
    const address = getAddress(keypair.publicKey);

    console.log("Generated address: ", address);

    req.session.mnemonic = mnemonic;
    req.session.address = address;

    res.render(DIRECTORY + "/register.html", {mnemonic:mnemonic});
});
app.get('/signin', async (req, res) => {
    res.render(DIRECTORY + "/login.html");
});
app.post('/signin', async (req, res) => {
    let mnemonic = req.body.mnemonic;

    const keypair = generateKeyPairFromMnemonic(mnemonic);
    const address = getAddress(keypair.publicKey);

    req.session.mnemonic = mnemonic;
    req.session.address = address;
    res.redirect('/success');
});

Should I use elliptic curve instead?


Solution

  • const bip39 = require('bip39');
    const crypto = require('crypto');
    const elliptic = require('elliptic');
    
    function generateKeyPairFromMnemonic(mnemonic) {
      // Derive a seed from the mnemonic phrase
      const seed = bip39.mnemonicToSeedSync(mnemonic);
    
      // Create an elliptic curve key pair
      const ec = new elliptic.ec('secp256k1');
      const keyPair = ec.genKeyPair({
        entropy: seed.slice(0, 32), // Take the first 32 bytes of the seed for entropy
      });
    
      return {
        privateKey: keyPair.getPrivate('hex'),
        publicKey: keyPair.getPublic('hex'),
      };
    }
    
    // Example usage
    const mnemonic = 'your twelve word mnemonic phrase';
    const keyPair = generateKeyPairFromMnemonic(mnemonic);
    console.log('Private Key:', keyPair.privateKey);
    console.log('Public Key:', keyPair.publicKey);