skip to Main Content

I have generated mnemonic using bip39.generateMnemonic();. I need to convert this mnemonic to AsymmetricKeyPair with secp256k1.

AsymmetricKeyPair<PublicKey, PrivateKey> secp256k1KeyPair() {
    var keyParams = ECKeyGeneratorParameters(ECCurve_secp256k1());
    var mnemonic = getMnemonic();
    var seed = bip39.mnemonicToSeed(mnemonic);

   
    print('mnemonicToSeed=========$seed');
    var random = FortunaRandom();
    random.seed(KeyParameter(seed));

    var generator = ECKeyGenerator();
    generator.init(ParametersWithRandom(keyParams,random));

    return generator.generateKeyPair();
  }


getMnemonic() {
    var mnemonic = bip39.generateMnemonic();
    print('mnemonic=========$mnemonic');
    return mnemonic;
  }

Unhandled Exception: Invalid argument(s): Fortuna PRNG can only be used with 256 bits keys.

Finally I’m able to generate the AsymmetricKeyPair as follows.but I’m not sure whether I did it in a correct way.Please correct me if I’m wrong.

/// generates dynamic mnemonic
  String getMnemonic() {
    var mnemonic = bip39.generateMnemonic();
    print('mnemonic=========$mnemonic');
    return mnemonic;
  }

  AsymmetricKeyPair<PublicKey, PrivateKey> secp256k1KeyPair() {

    var keyParams = ECKeyGeneratorParameters(ECCurve_secp256k1());
    var mnemonic = getMnemonic();
    var seed = bip39.mnemonicToSeed(mnemonic);
    final digest = sha256.convert(seed); // 32 bytes

    print('mnemonicToSeed=========$seed');
    var random = FortunaRandom();
    random.seed(KeyParameter(_seed(digest.bytes)));
    var generator = ECKeyGenerator();
    generator.init(ParametersWithRandom(keyParams,random));

    return generator.generateKeyPair();
  }

  Uint8List _seed(List<int> digest) {
    var seed = List<int>.generate(digest.length, (_) => digest[_]);
    return Uint8List.fromList(seed);
  }

As FortunaRandom is not robust ,Could you please suggest anything else.

2

Answers


  1. If I’m looking at the code then mnemonicToSeed for BIPS39 will generate a 512 bit output (the output size of PBKDF2, more information here). You can just use the leftmost bits/bytes of that and take 256 bits. Ye old Fortuna uses a block cipher in counter mode and likely the initial seed size must be identical to the (or a) key size.

    Note that I’ve got no idea what you are trying to do. If I’d try to generate another key pair deterministically, I’d not use your code as it is implementation dependent. If anything changes in generateKeyPair, e.g. if a different method is used to extract random bytes then it would break, while the API would remain the same. The chance of that is not that high for Elliptic Curve with secp256k1 as key pair generation is largely deterministic as well but suddenly generating a the wrong private key can certainly have terrible consequences.

    Login or Signup to reply.
  2. To avoid using a PRNG (e.g. FortunaRandom), you could apply the following approach:

    After derivation of a raw 32 bytes private key (via getMnemonic() and mnemonicToSeed()) this can be imported directly with ECPrivateKey().
    The raw public key results by a multiplication with the generator point and subsequent import with ECPublicKey():

    import 'package:bip39/bip39.dart' as bip39;
    import 'package:bip32/bip32.dart' as bip32; // if BIP32 is applied
    import 'package:pointycastle/export.dart';
    import 'package:nanodart/nanodart.dart';
    import 'package:basic_utils/basic_utils.dart'; // if PEM conversion required
    
    ...
    
    // get seed
    // var mnemonic = getMnemonic();
    var mnemonic = "viable rhythm total tissue muscle forget culture strong lobster drama swim wasp"; // apply a static mnemonic for testing
    var seed = bip39.mnemonicToSeed(mnemonic);
    
    // derive private key
    var privateKeyBytes = bip32.BIP32.fromSeed(seed).privateKey!;
    // var privateKeyBytes = seed.sublist(0, 32); // alternatively use the first 32 bytes (as suggested in the answer) or the SHA256 hash of the seed if you feel more comfortable with it
    
    // create ECPrivateKey/ECPublicKey instances (this is what you asked for in the comment)
    var privateKey = NanoHelpers.byteToBigInt(privateKeyBytes);
    var domain = ECDomainParameters('secp256k1');
    var ecPrivateKey = ECPrivateKey(privateKey, domain);
    var g = ecPrivateKey.parameters?.G;
    var ecPublicKey = ECPublicKey(g! * privateKey, domain);
    
    // output the raw keys...
    print('mnemonicToSeed=========: $seed');
    print('privateKey=========: $privateKeyBytes');
    var uncompressed = ecPublicKey.Q?.getEncoded(false);
    print('publicKey=========: $uncompressed');
    
    // ...or export as PEM
    var sec1Pem = CryptoUtils.encodeEcPrivateKeyToPem(ecPrivateKey);
    var spkiPem = CryptoUtils.encodeEcPublicKeyToPem(ecPublicKey);
    print('private as sec1=========: $sec1Pem');
    print('public as x.509/SPKI=========: $spkiPem');
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search