walletEncoder function

OnChainWalletData walletEncoder(
  1. String originPublicKey
)

Archethic Onchain Wallet Generator and Encoder, using V1 specifications https://archethic-foundation.github.io/archethic-docs/build/clients/wallet-spec

Implementation

OnChainWalletData walletEncoder(String originPublicKey) {
  final pc.Digest sha256 = pc.Digest('SHA-256');
  final pc.Digest sha512 = pc.Digest('SHA-512');

  const String version = '01';

  /// Master Seed for Onchain Wallet (32 bytes)
  // print('Master Seed for Onchain Wallet (32 bytes)');
  final String masterSeed = uint8ListToHex(Uint8List.fromList(
      List<int>.generate(32, (int i) => Random.secure().nextInt(256))));
  // print(masterSeed);

  const String totalServices = '01';
  const String coinType = '028A';
  const String account = '0000';

  /// BIP44 Path (4 bytes): [Coin type = 650, account = 0]
  // print('BIP44 Path (4 bytes): [Coin type = 650, account = 0]');
  const String bip44 = coinType + account;
  // print(bip44);

  /// Wallet Curve (0:ed25519, 1:nistp256, 2:secp256k1)
  // print('Wallet Curve (0:ed25519, 1:nistp256, 2:secp256k1)');
  const String walletCurve = '02';
  // print(walletCurve);

  final elliptic.Curve curve = elliptic.getSecp256k1();

  /// Encoded Wallet (version, master seed, number of services, bip44, wallet curve)
  // print(
  //    'Encoded Wallet (version, master seed, number of services, bip44, wallet curve)');
  final String encodedWallet =
      version + masterSeed + totalServices + bip44 + walletCurve;
  // print(encodedWallet);

  /// Wallet Key (AES256 CTR) for encrypting Onchain Wallet (32 bytes)
  // print('Wallet Key (AES256 CTR) for encrypting Onchain Wallet (32 bytes)');
  final String walletEncryptionKey = uint8ListToHex(Uint8List.fromList(
      List<int>.generate(32, (int i) => Random.secure().nextInt(256))));
  // print(walletEncryptionKey);

  /// IV encrypting Onchain Wallet (SHA256(SHA256(Wallet Key)))[0:16]
  // print('IV encrypting Onchain Wallet (SHA256(SHA256(Wallet Key)))[0:16]');
  final Uint8List walletEncryptionKeyEncrypted = sha256.process(
      sha256.process(Uint8List.fromList(hexToUint8List(walletEncryptionKey))));
  final String walletEncryptionIv =
      uint8ListToHex(walletEncryptionKeyEncrypted.sublist(0, 16)).toUpperCase();
  // print(walletEncryptionIv);

  /// Encryption of Encoded Wallet (AES256 CTR)
  // print('Encryption of Encoded Wallet (AES256 CTR)');
  final pc.CTRStreamCipher ctr = pc.CTRStreamCipher(pc.AESEngine())
    ..init(
        false,
        pc.ParametersWithIV(
            pc.KeyParameter(
                Uint8List.fromList(hexToUint8List(walletEncryptionKey))),
            Uint8List.fromList(hexToUint8List(walletEncryptionIv))));
  final String encryptedWallet = uint8ListToHex(
          ctr.process(Uint8List.fromList(hexToUint8List(encodedWallet))))
      .toUpperCase();
  // print(encryptedWallet);

  /// ECDH Curve (0:ed25519, 1:nistp256, 2:secp256k1)
  // print('ECDH Curve (0:ed25519, 1:nistp256, 2:secp256k1)');
  // const String ecdhCurve = '02';
  // print(ecdhCurve);

  /// Ephemeral Public Key
  // print('Ephemeral Public Key');
  final elliptic.PrivateKey ephemeralPrivKey = curve.generatePrivateKey();
  final elliptic.PublicKey ephemeralPubKey =
      curve.privateToPublicKey(ephemeralPrivKey);
  // print(ephemeralPubKey.toHex());

  /// Origin Device Public Key
  // print('Origin Device Public Key');
  final elliptic.PublicKey ledger =
      elliptic.PublicKey.fromHex(curve, originPublicKey.toUpperCase());
  // print(ledger.toHex());

  /// ECDH Point x: ECDH(Origin Device Public Key, Ephemeral Private Key)
  // print('ECDH Point x: ECDH(Origin Device Public Key, Ephemeral Private Key)');
  final Uint8List ecdhSecret =
      Uint8List.fromList(ecdh.computeSecret(ephemeralPrivKey, ledger));
  // print(uint8ListToHex(ecdhSecret));

  final Uint8List ecdhSecretEncrypted =
      sha512.process(sha512.process(ecdhSecret));

  /// AES256 CBC Key: SHA512(SHA512(ECDH Point x))[0:32]
  // print('AES256 CBC Key: SHA512(SHA512(ECDH Point x))[0:32]');
  final String aesKey = uint8ListToHex(ecdhSecretEncrypted.sublist(0, 32));
  // print(aesKey);

  /// IV: SHA512(SHA512(ECDH Point x))[32:48]
  // print('IV: SHA512(SHA512(ECDH Point x))[32:48]');
  final String iv = uint8ListToHex(ecdhSecretEncrypted.sublist(32, 48));
  // print(iv);

  /// Encryption of Wallet Key (AES256 CBC)
  // print('Encryption of Wallet Key (AES256 CBC)');
  final pc.CBCBlockCipher cbc = pc.CBCBlockCipher(pc.AESEngine())
    ..init(
        true,
        pc.ParametersWithIV(
            pc.KeyParameter(Uint8List.fromList(hexToUint8List(aesKey))),
            Uint8List.fromList(hexToUint8List(iv))));
  final Uint8List encryptedWalletKey = Uint8List(
      Uint8List.fromList(hexToUint8List(walletEncryptionKey))
          .length); // allocate space
  int offset = 0;
  while (
      offset < Uint8List.fromList(hexToUint8List(walletEncryptionKey)).length) {
    offset += cbc.processBlock(
        Uint8List.fromList(hexToUint8List(walletEncryptionKey)),
        offset,
        encryptedWalletKey,
        offset);
  }

  // print(uint8ListToHex(encryptedWalletKey));

  /// Authentication Seed: SHA512(SHA512(ECDH Point x))[48:64]
  // print('Authentication Seed: SHA512(SHA512(ECDH Point x))[48:64]');
  final String authSeed =
      uint8ListToHex(ecdhSecretEncrypted.sublist(48, 64)).toUpperCase();
  // print(authSeed);

  /// Authentication Key: SHA256(Authentication Seed)
  // print('Authentication Key: SHA256(Authentication Seed)');
  final Uint8List authKey =
      sha256.process(Uint8List.fromList(hexToUint8List(authSeed)));
  // print(uint8ListToHex(authKey));

  /// Authentication Tag: HMAC256(Authentication Key, Encrypted Wallet Key)[0:16]
  // print(
  //    'Authentication Tag: HMAC256(Authentication Key, Encrypted Wallet Key)[0:16]');

  final pc.HMac hmac = pc.HMac(pc.SHA256Digest(), 64)
    ..init(pc.KeyParameter(authKey));
  final String authTag =
      uint8ListToHex(hmac.process(encryptedWalletKey).sublist(0, 16));
  // print(authTag);

  /// Encoding of Encrypted Wallet Key(ephemeral public key, authentication tag, encrypted wallet key):
  // print(
  //    'Encoding of Encrypted Wallet Key(ephemeral public key, authentication tag, encrypted wallet key)');
  final String encodedWalletKey =
      ephemeralPubKey.toHex() + authTag + uint8ListToHex(encryptedWalletKey);
  // print(encodedWalletKey);

  final OnChainWalletData onChainWalletData = OnChainWalletData(
      encodedWalletKey: encodedWalletKey, encryptedWallet: encryptedWallet);

  final Uint8List payload = concatUint8List(<Uint8List>[
    Uint8List.fromList(hexToUint8List(onChainWalletData.encodedWalletKey!)),
    Uint8List.fromList(hexToUint8List(onChainWalletData.encryptedWallet!))
  ]);
  final Uint8List payloadLength = Uint8List.fromList(
      hexToUint8List(payload.lengthInBytes.toRadixString(16)));
  final Uint8List addressPayload =
      concatUint8List(<Uint8List>[payloadLength, payload]);
  dev.log('addressPayload: ${uint8ListToHex(addressPayload)}');

  return onChainWalletData;
}