1

what is the correct length for the Solana testnet wallet private keys, 64 or 88? Also, I am using the Phantom browser extension for my wallet. Is there anything else that is best and creates the secret key of 64 length?

1 Answer 1

1

All Private Keys are 64 bytes, and the first 32 are the Public-Key.

You can use the KeyPair class as well.

https://solana-labs.github.io/solana-web3.js/classes/Keypair.html

Here is some more info about it: https://solana.com/docs/clients/javascript-reference

const { Keypair } = require("@solana/web3.js");
 
let account = Keypair.generate();
 
console.log(account.publicKey.toBase58());
console.log(account.secretKey);
 
// 2DVaHtcdTf7cm18Zm9VV8rKK4oSnjmTkKE6MiXe18Qsb
// Uint8Array(64) [
//   152,  43, 116, 211, 207,  41, 220,  33, 193, 168, 118,
//    24, 176,  83, 206, 132,  47, 194,   2, 203, 186, 131,
//   197, 228, 156, 170, 154,  41,  56,  76, 159, 124,  18,
//    14, 247,  32, 210,  51, 102,  41,  43,  21,  12, 170,
//   166, 210, 195, 188,  60, 220, 210,  96, 136, 158,   6,
//   205, 189, 165, 112,  32, 200, 116, 164, 234
// ]
 
let seed = Uint8Array.from([
  70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70,
  60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100,
]);
let accountFromSeed = Keypair.fromSeed(seed);
 
console.log(accountFromSeed.publicKey.toBase58());
console.log(accountFromSeed.secretKey);
 
// 3LDverZtSC9Duw2wyGC1C38atMG49toPNW9jtGJiw9Ar
// Uint8Array(64) [
//    70,  60, 102, 100,  70,  60, 102, 100,  70,  60, 102,
//   100,  70,  60, 102, 100,  70,  60, 102, 100,  70,  60,
//   102, 100,  70,  60, 102, 100,  70,  60, 102, 100,  34,
//   164,   6,  12,   9, 193, 196,  30, 148, 122, 175,  11,
//    28, 243, 209,  82, 240, 184,  30,  31,  56, 223, 236,
//   227,  60,  72, 215,  47, 208, 209, 162,  59
// ]
 
let accountFromSecret = Keypair.fromSecretKey(account.secretKey);
 
console.log(accountFromSecret.publicKey.toBase58());
console.log(accountFromSecret.secretKey);
 
// 2DVaHtcdTf7cm18Zm9VV8rKK4oSnjmTkKE6MiXe18Qsb
// Uint8Array(64) [
//   152,  43, 116, 211, 207,  41, 220,  33, 193, 168, 118,
//    24, 176,  83, 206, 132,  47, 194,   2, 203, 186, 131,
//   197, 228, 156, 170, 154,  41,  56,  76, 159, 124,  18,
//    14, 247,  32, 210,  51, 102,  41,  43,  21,  12, 170,
//   166, 210, 195, 188,  60, 220, 210,  96, 136, 158,   6,
//   205, 189, 165, 112,  32, 200, 116, 164, 234
// ]

In case you wonder how to generate a SEED: e.g, using a combination of words or strings provided by the user.


//*************/
async function DeriveUserRootKey(email, key_seed, output_bits_length = 256) {
  const inner_digest = 'constant-string-whatever-but-constant' + key_seed;
  const enc = new TextEncoder();

  const seed_key = await crypto.subtle.importKey('raw', enc.encode(inner_digest), 'PBKDF2', false, [
    'deriveBits',
    'deriveKey',
  ]);

  if (seed_key == undefined || seed_key == null) {
    console.log('ERROR: Failed to derive seed_key');
    return null;
  }

  
  var buffer = new TextEncoder('utf-8').encode('user_salt:' + email);
  const salt = new Uint8Array(buffer);

  let root_key = await crypto.subtle.deriveBits(
    {
      name: 'PBKDF2',
      salt: salt,
      iterations: 3000000,
      hash: 'SHA-512',
    },
    seed_key,
    output_bits_length,
  );

  if (root_key == undefined || root_key == null) {
    console.log('ERROR: Failed to derive primary key bits from seed key.');
    return null;
  }

  return buf2hex(root_key);
}
//*************/
async function DeriveUserPrivateKey(root_key, key_name) {
  
  let inner_digest = root_key + ':' + key_name; 
  
  var buffer = new TextEncoder().encode(inner_digest);
  let derived_bits = await crypto.subtle.digest('SHA-256', buffer);

  if (derived_bits == undefined || derived_bits == null) {
    console.log('ERROR: Failed to derive secondary key bits from root key.');
    return null;
  }
 
  let private_key = new Uint8Array(derived_bits);

  return private_key;
}
//*************/
function buf2hex(buffer) {
  
  return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, '0')).join('');
}

This returns a Buffer or Uint8Array(64) that you use to feed the KeyPair.fromSeed() method.

  let sol_keypair = Keypair.fromSeed(sol_private_key);
  accountAddress.value = sol_keypair.publicKey.toBase58();
  privateKey.value = bs58.encode(sol_keypair.secretKey);

Security NOTE:

The above custom method does not use a SALT, using a random salt is recommended to avoid collisions. e.f. if 2 users provide the same set of random words, they would get the same KeyPair, adding a random salt prevents that but requires the users to save the RandomSalt in order to recover or regenerate the Pairs.

5
  • What does a Salt look like? Can it be a random string with random length?
    – TheFox
    Commented Apr 30 at 13:28
  • nodejs.org/api/… Commented Apr 30 at 18:48
  • youtube.com/watch?v=--tnZMuoK3E Commented Apr 30 at 18:49
  • e.g. in the code above, the salt being used is not random, but based on static seeds provided by the user. In the 1st link in the comments ( here ) you can see how to generate a random salt instead. But this random number shold be presented to the user and SAVED, then used in the same order to regenerate the Keypairs programatically. More info is also available on the PDA documentation. The PDAs use static SALTs, or no salt at all, so the Derived Accounts can be resolved programatically. solanacookbook.com/core-concepts/pdas.html#facts Commented Apr 30 at 18:52
  • According to solana code, I think the 2nd half is public key, not the first half.
    – Eric
    Commented Dec 11 at 12:28

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.