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 sha256 = pc.Digest('SHA-256');
  final sha512 = pc.Digest('SHA-512');

  const version = '01';

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

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

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

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

  final 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 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 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 walletEncryptionKeyEncrypted = sha256.process(
    sha256.process(Uint8List.fromList(hexToUint8List(walletEncryptionKey))),
  );
  final walletEncryptionIv =
      uint8ListToHex(walletEncryptionKeyEncrypted.sublist(0, 16)).toUpperCase();
  // print(walletEncryptionIv);

  /// Encryption of Encoded Wallet (AES256 CTR)
  // print('Encryption of Encoded Wallet (AES256 CTR)');
  final ctr = pc.CTRStreamCipher(pc.AESEngine())
    ..init(
      false,
      pc.ParametersWithIV(
        pc.KeyParameter(
          Uint8List.fromList(hexToUint8List(walletEncryptionKey)),
        ),
        Uint8List.fromList(hexToUint8List(walletEncryptionIv)),
      ),
    );
  final 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 ephemeralPrivKey = curve.generatePrivateKey();
  final ephemeralPubKey = curve.privateToPublicKey(ephemeralPrivKey);
  // print(ephemeralPubKey.toHex());

  /// Origin Device Public Key
  // print('Origin Device Public Key');
  final 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 ecdhSecret =
      Uint8List.fromList(ecdh.computeSecret(ephemeralPrivKey, ledger));
  // print(uint8ListToHex(ecdhSecret));

  final 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 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 iv = uint8ListToHex(ecdhSecretEncrypted.sublist(32, 48));
  // print(iv);

  /// Encryption of Wallet Key (AES256 CBC)
  // print('Encryption of Wallet Key (AES256 CBC)');
  final cbc = pc.CBCBlockCipher(pc.AESEngine())
    ..init(
      true,
      pc.ParametersWithIV(
        pc.KeyParameter(Uint8List.fromList(hexToUint8List(aesKey))),
        Uint8List.fromList(hexToUint8List(iv)),
      ),
    );
  final encryptedWalletKey = Uint8List(
    Uint8List.fromList(hexToUint8List(walletEncryptionKey)).length,
  ); // allocate space
  var 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 authSeed =
      uint8ListToHex(ecdhSecretEncrypted.sublist(48, 64)).toUpperCase();
  // print(authSeed);

  /// Authentication Key: SHA256(Authentication Seed)
  // print('Authentication Key: SHA256(Authentication Seed)');
  final 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 hmac = pc.HMac(pc.SHA256Digest(), 64)..init(pc.KeyParameter(authKey));
  final 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 encodedWalletKey =
      ephemeralPubKey.toHex() + authTag + uint8ListToHex(encryptedWalletKey);
  // print(encodedWalletKey);

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

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

  return onChainWalletData;
}